@@ -15,13 +15,13 @@ import io.ktor.server.application.ApplicationCallPipeline
15
15
import io.ktor.server.application.BaseRouteScopedPlugin
16
16
import io.ktor.server.application.call
17
17
import io.ktor.server.application.install
18
+ import io.ktor.server.application.plugin
18
19
import io.ktor.server.auth.Authentication
19
20
import io.ktor.server.auth.AuthenticationContext
20
21
import io.ktor.server.auth.AuthenticationProvider
21
22
import io.ktor.server.auth.authenticate
22
23
import io.ktor.server.auth.jwt.jwt
23
24
import io.ktor.server.auth.principal
24
- import io.ktor.server.html.respondHtml
25
25
import io.ktor.server.plugins.forwardedheaders.XForwardedHeaders
26
26
import io.ktor.server.plugins.statuspages.StatusPages
27
27
import io.ktor.server.response.respond
@@ -31,14 +31,15 @@ import io.ktor.server.routing.application
31
31
import io.ktor.server.routing.get
32
32
import io.ktor.server.routing.routing
33
33
import io.ktor.util.AttributeKey
34
+ import kotlinx.coroutines.Dispatchers
35
+ import kotlinx.coroutines.launch
34
36
import org.modelix.authorization.permissions.PermissionEvaluator
35
37
import org.modelix.authorization.permissions.PermissionInstanceReference
36
38
import org.modelix.authorization.permissions.PermissionParser
37
39
import org.modelix.authorization.permissions.PermissionParts
38
40
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
42
43
import java.util.concurrent.TimeUnit
43
44
44
45
private val LOG = mu.KotlinLogging .logger { }
@@ -85,7 +86,18 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
85
86
}
86
87
validate {
87
88
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 )
89
101
} catch (e: Exception ) {
90
102
LOG .warn(e) { " Failed to read JWT token" }
91
103
null
@@ -146,11 +158,6 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
146
158
)
147
159
}
148
160
}
149
- get(" permissions" ) {
150
- call.respondHtml {
151
- buildPermissionPage(call.getPermissionEvaluator())
152
- }
153
- }
154
161
}
155
162
if (config.permissionManagementEnabled) {
156
163
installPermissionManagementHandlers()
@@ -168,13 +175,10 @@ object ModelixAuthorization : BaseRouteScopedPlugin<IModelixAuthorizationConfig,
168
175
169
176
class ModelixAuthorizationPluginInstance (val config : ModelixAuthorizationConfig ) {
170
177
171
- private val deniedPermissionRequests: MutableSet <DeniedPermissionRequest > = Collections .synchronizedSet(LinkedHashSet ())
172
178
private val permissionCache = CacheBuilder .newBuilder()
173
179
.expireAfterWrite(5 , TimeUnit .SECONDS )
174
180
.build<Pair <AccessTokenPrincipal , PermissionInstanceReference >, Boolean > ()
175
181
176
- fun getDeniedPermissions (): Set <DeniedPermissionRequest > = deniedPermissionRequests.toSet()
177
-
178
182
fun hasPermission (call : ApplicationCall , permissionToCheck : PermissionParts ): Boolean {
179
183
return hasPermission(call, PermissionParser (config.permissionSchema).parse(permissionToCheck))
180
184
}
@@ -184,23 +188,7 @@ class ModelixAuthorizationPluginInstance(val config: ModelixAuthorizationConfig)
184
188
185
189
val principal = call.principal<AccessTokenPrincipal >() ? : throw NotLoggedInException ()
186
190
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)
204
192
}
205
193
}
206
194
@@ -227,14 +215,6 @@ class ModelixAuthorizationPluginInstance(val config: ModelixAuthorizationConfig)
227
215
}
228
216
}
229
217
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
-
238
218
/* *
239
219
* Returns an [JWTVerifier] that wraps our common authorization logic,
240
220
* so that it can be configured in the verification with Ktor's JWT authorization.
0 commit comments