Skip to content

Commit e3fb1da

Browse files
authored
Merge pull request #1269 from modelix/MODELIX-1051-Nicer-UI-for-permission-management
New UI for permission management
2 parents d05eaf6 + 5add048 commit e3fb1da

File tree

11 files changed

+412
-266
lines changed

11 files changed

+412
-266
lines changed

authorization/src/main/kotlin/org/modelix/authorization/AuthorizationPlugin.kt

Lines changed: 18 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ import io.ktor.server.application.ApplicationCallPipeline
1515
import io.ktor.server.application.BaseRouteScopedPlugin
1616
import io.ktor.server.application.call
1717
import io.ktor.server.application.install
18+
import io.ktor.server.application.plugin
1819
import io.ktor.server.auth.Authentication
1920
import io.ktor.server.auth.AuthenticationContext
2021
import io.ktor.server.auth.AuthenticationProvider
2122
import io.ktor.server.auth.authenticate
2223
import io.ktor.server.auth.jwt.jwt
2324
import io.ktor.server.auth.principal
24-
import io.ktor.server.html.respondHtml
2525
import io.ktor.server.plugins.forwardedheaders.XForwardedHeaders
2626
import io.ktor.server.plugins.statuspages.StatusPages
2727
import io.ktor.server.response.respond
@@ -31,14 +31,15 @@ import io.ktor.server.routing.application
3131
import io.ktor.server.routing.get
3232
import io.ktor.server.routing.routing
3333
import io.ktor.util.AttributeKey
34+
import kotlinx.coroutines.Dispatchers
35+
import kotlinx.coroutines.launch
3436
import org.modelix.authorization.permissions.PermissionEvaluator
3537
import org.modelix.authorization.permissions.PermissionInstanceReference
3638
import org.modelix.authorization.permissions.PermissionParser
3739
import org.modelix.authorization.permissions.PermissionParts
3840
import org.modelix.authorization.permissions.SchemaInstance
39-
import java.nio.charset.StandardCharsets
40-
import java.util.Base64
41-
import java.util.Collections
41+
import org.modelix.authorization.permissions.recordKnownRoles
42+
import org.modelix.authorization.permissions.recordKnownUser
4243
import java.util.concurrent.TimeUnit
4344

4445
private val LOG = mu.KotlinLogging.logger { }
@@ -85,7 +86,18 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
8586
}
8687
validate {
8788
try {
88-
jwtFromHeaders()?.let(::AccessTokenPrincipal)
89+
val authPlugin = application.plugin(ModelixAuthorization)
90+
val authConfig = authPlugin.config
91+
jwtFromHeaders()
92+
?.let { authConfig.nullIfInvalid(it) }
93+
?.also { jwt ->
94+
application.launch(Dispatchers.IO) {
95+
val accessControlPersistence = authConfig.accessControlPersistence
96+
accessControlPersistence.recordKnownUser(authConfig.jwtUtil.extractUserId(jwt))
97+
accessControlPersistence.recordKnownRoles(authConfig.jwtUtil.extractUserRoles(jwt))
98+
}
99+
}
100+
?.let(::AccessTokenPrincipal)
89101
} catch (e: Exception) {
90102
LOG.warn(e) { "Failed to read JWT token" }
91103
null
@@ -146,11 +158,6 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
146158
)
147159
}
148160
}
149-
get("permissions") {
150-
call.respondHtml {
151-
buildPermissionPage(call.getPermissionEvaluator())
152-
}
153-
}
154161
}
155162
if (config.permissionManagementEnabled) {
156163
installPermissionManagementHandlers()
@@ -168,13 +175,10 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
168175

169176
class ModelixAuthorizationPluginInstance(val config: ModelixAuthorizationConfig) {
170177

171-
private val deniedPermissionRequests: MutableSet<DeniedPermissionRequest> = Collections.synchronizedSet(LinkedHashSet())
172178
private val permissionCache = CacheBuilder.newBuilder()
173179
.expireAfterWrite(5, TimeUnit.SECONDS)
174180
.build<Pair<AccessTokenPrincipal, PermissionInstanceReference>, Boolean>()
175181

176-
fun getDeniedPermissions(): Set<DeniedPermissionRequest> = deniedPermissionRequests.toSet()
177-
178182
fun hasPermission(call: ApplicationCall, permissionToCheck: PermissionParts): Boolean {
179183
return hasPermission(call, PermissionParser(config.permissionSchema).parse(permissionToCheck))
180184
}
@@ -184,23 +188,7 @@ class ModelixAuthorizationPluginInstance(val config: ModelixAuthorizationConfig)
184188

185189
val principal = call.principal<AccessTokenPrincipal>() ?: throw NotLoggedInException()
186190
return permissionCache.get(principal to permissionToCheck) {
187-
getPermissionEvaluator(principal).hasPermission(permissionToCheck).also { granted ->
188-
if (!granted) {
189-
val userId = principal.getUserName()
190-
if (userId != null) {
191-
synchronized(deniedPermissionRequests) {
192-
deniedPermissionRequests += DeniedPermissionRequest(
193-
permissionRef = permissionToCheck,
194-
userId = userId,
195-
jwtPayload = principal.jwt.payload,
196-
)
197-
while (deniedPermissionRequests.size >= 100) {
198-
deniedPermissionRequests.iterator().also { it.next() }.remove()
199-
}
200-
}
201-
}
202-
}
203-
}
191+
getPermissionEvaluator(principal).hasPermission(permissionToCheck)
204192
}
205193
}
206194

@@ -227,14 +215,6 @@ class ModelixAuthorizationPluginInstance(val config: ModelixAuthorizationConfig)
227215
}
228216
}
229217

230-
data class DeniedPermissionRequest(
231-
val permissionRef: PermissionInstanceReference,
232-
val userId: String,
233-
val jwtPayload: String,
234-
) {
235-
fun jwtPayloadJson() = String(Base64.getUrlDecoder().decode(jwtPayload), StandardCharsets.UTF_8)
236-
}
237-
238218
/**
239219
* Returns an [JWTVerifier] that wraps our common authorization logic,
240220
* so that it can be configured in the verification with Ktor's JWT authorization.

0 commit comments

Comments
 (0)