Last active
August 16, 2024 06:46
-
-
Save Shreemanarjun/bfe4ffb6664bdcd4514ab53e9007f299 to your computer and use it in GitHub Desktop.
plugin for role-based authorization
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package example.com.plugins | |
| import io.ktor.http.* | |
| import io.ktor.server.application.* | |
| import io.ktor.server.auth.* | |
| import io.ktor.server.auth.jwt.* | |
| import io.ktor.server.request.* | |
| import io.ktor.server.response.* | |
| routing { | |
| authenticate("authJWT") { | |
| authorized( | |
| //Define the user role and its acces levels for authority | |
| authorityWithAccessLevels = setOf( | |
| AuthorityWithAccessLevel( | |
| UserRole.PATIENT, | |
| accessLevels = setOf(AccessLevel.Read), | |
| ) | |
| ), | |
| build = { | |
| patientRoute() | |
| }) | |
| } | |
| } | |
| ///authorization plugin for Route | |
| fun Route.authorized( | |
| authorityWithAccessLevels: Set<AuthorityWithAccessLevel>, | |
| build: Route.() -> Unit, | |
| ) { | |
| install(RoleBasedAuthorizationPlugin) { | |
| authorityConfigs = authorityWithAccessLevels | |
| build() | |
| } | |
| } | |
| @Serializable | |
| enum class UserRole { | |
| //Patient | |
| PATIENT, | |
| //Doctor | |
| DOCTOR, | |
| //Pharmacy | |
| PHARMACY, | |
| //Diagnostic Center | |
| DIAGNOSTICS, | |
| //Admin | |
| ADMIN, | |
| } | |
| /** | |
| * User With Access Levels | |
| * | |
| * */ | |
| enum class AccessLevel { | |
| Read, Write, Delete, Update | |
| } | |
| data class AuthorityWithAccessLevel(var userRole: UserRole, var accessLevels: Set<AccessLevel> = emptySet()) | |
| class PluginConfiguration { | |
| var authorityConfigs: Set<AuthorityWithAccessLevel> = emptySet() | |
| } | |
| val RoleBasedAuthorizationPlugin = createRouteScopedPlugin( | |
| name = "Role Based Authentication Plugin", | |
| createConfiguration = ::PluginConfiguration | |
| ) { | |
| ///get the user defined roles here | |
| val definedRoles = pluginConfig.authorityConfigs.map { it.userRole } | |
| pluginConfig.apply { | |
| //After the JWT Token Verified do check on roles and access levels | |
| on(AuthenticationChecked) { call -> | |
| /// get the token role from claim | |
| val tokenRule = call.getRoleFromToken() | |
| println("token rule: $tokenRule") | |
| if (tokenRule != null) { | |
| /// Check whether current user token have the required role | |
| val authorized = definedRoles.contains(tokenRule) | |
| println("authorized token: $authorized") | |
| when { | |
| !authorized -> { | |
| call.respond( | |
| status = HttpStatusCode.Forbidden, | |
| MyResult.Error( | |
| exception = "Invalid Role", | |
| message = "User doesn't have permission to authenticate! ${ | |
| call.request.uri | |
| }", | |
| reasons = listOf("${definedRoles.joinToString()} role not found") | |
| ) | |
| ) | |
| } | |
| //User have authorized according to role defined | |
| else -> { | |
| //get all the requested access levels of user | |
| val currentRequestedAccessLevels = call.getAuthorityAccess() | |
| //get all the aceess levels defined in the route | |
| val authorizedaccessLevels = | |
| pluginConfig.authorityConfigs.find { it.userRole == tokenRule }?.accessLevels | |
| if (!authorizedaccessLevels!!.containsAll(currentRequestedAccessLevels)) { | |
| call.respond( | |
| status = HttpStatusCode.Forbidden, | |
| MyResult.Error( | |
| exception = "User $tokenRule doesn't have permission to ${currentRequestedAccessLevels.joinToString()}", | |
| message = "User doesn't have permission to authenticate! ${ | |
| call.request.uri | |
| }", | |
| reasons = listOf("${currentRequestedAccessLevels.joinToString()} access not found") | |
| ) | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| else { | |
| call.respond( | |
| status = HttpStatusCode.BadRequest, | |
| MyResult.Error( | |
| exception = "Invalid Role", | |
| message = "Check Header", | |
| reasons = listOf("$definedRoles role not found ") | |
| ) | |
| ) | |
| } | |
| } | |
| } | |
| } | |
| fun ApplicationCall.getAuthorityAccess(): Set<AccessLevel> { | |
| val currentAccessLevel = mutableSetOf<AccessLevel>() | |
| when (this.request.httpMethod) { | |
| HttpMethod.Get, HttpMethod.Head -> { | |
| currentAccessLevel.add(AccessLevel.Read) | |
| } | |
| HttpMethod.Put -> { | |
| currentAccessLevel.add(AccessLevel.Write) | |
| } | |
| HttpMethod.Delete -> { | |
| currentAccessLevel.add(AccessLevel.Delete) | |
| } | |
| HttpMethod.Patch, HttpMethod.Post -> { | |
| currentAccessLevel.add(AccessLevel.Update) | |
| } | |
| } | |
| return currentAccessLevel | |
| } | |
| fun ApplicationCall.getRoleFromToken(): UserRole? { | |
| val role = this.principal<JWTPrincipal>()?.payload?.getClaim("role")?.asString() | |
| if (role != null) { | |
| println("role in claim $role") | |
| println("getRoleFromToken: ${UserRole.valueOf(role)}") | |
| return UserRole.valueOf(role) | |
| } | |
| return null | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment