Skip to content

Instantly share code, notes, and snippets.

@Shreemanarjun
Last active August 16, 2024 06:46
Show Gist options
  • Select an option

  • Save Shreemanarjun/bfe4ffb6664bdcd4514ab53e9007f299 to your computer and use it in GitHub Desktop.

Select an option

Save Shreemanarjun/bfe4ffb6664bdcd4514ab53e9007f299 to your computer and use it in GitHub Desktop.
plugin for role-based authorization
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