@@ -15,13 +15,13 @@ import io.ktor.server.application.ApplicationCallPipeline
1515import io.ktor.server.application.BaseRouteScopedPlugin
1616import io.ktor.server.application.call
1717import io.ktor.server.application.install
18+ import io.ktor.server.application.plugin
1819import io.ktor.server.auth.Authentication
1920import io.ktor.server.auth.AuthenticationContext
2021import io.ktor.server.auth.AuthenticationProvider
2122import io.ktor.server.auth.authenticate
2223import io.ktor.server.auth.jwt.jwt
2324import io.ktor.server.auth.principal
24- import io.ktor.server.html.respondHtml
2525import io.ktor.server.plugins.forwardedheaders.XForwardedHeaders
2626import io.ktor.server.plugins.statuspages.StatusPages
2727import io.ktor.server.response.respond
@@ -31,14 +31,15 @@ import io.ktor.server.routing.application
3131import io.ktor.server.routing.get
3232import io.ktor.server.routing.routing
3333import io.ktor.util.AttributeKey
34+ import kotlinx.coroutines.Dispatchers
35+ import kotlinx.coroutines.launch
3436import org.modelix.authorization.permissions.PermissionEvaluator
3537import org.modelix.authorization.permissions.PermissionInstanceReference
3638import org.modelix.authorization.permissions.PermissionParser
3739import org.modelix.authorization.permissions.PermissionParts
3840import 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
4243import java.util.concurrent.TimeUnit
4344
4445private 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
169176class 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