diff --git a/cosec-api/src/main/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipal.kt b/cosec-api/src/main/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipal.kt index dd38a53a..00c39f4b 100644 --- a/cosec-api/src/main/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipal.kt +++ b/cosec-api/src/main/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipal.kt @@ -32,7 +32,7 @@ interface CoSecPrincipal : Principal, PolicyCapable, RoleCapable { return id } - val attrs: Map + val attributes: Map fun anonymous(): Boolean { return ANONYMOUS_ID == id } diff --git a/cosec-api/src/test/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipalTest.kt b/cosec-api/src/test/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipalTest.kt index 1399466d..fe0e0efd 100644 --- a/cosec-api/src/test/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipalTest.kt +++ b/cosec-api/src/test/kotlin/me/ahoo/cosec/api/principal/CoSecPrincipalTest.kt @@ -30,7 +30,7 @@ class CoSecPrincipalTest { throw UnsupportedOperationException() } - override val attrs: Map + override val attributes: Map get() = throw UnsupportedOperationException() override val policies: Set get() = throw UnsupportedOperationException() @@ -50,7 +50,7 @@ class CoSecPrincipalTest { throw UnsupportedOperationException() } - override val attrs: Map + override val attributes: Map get() = throw UnsupportedOperationException() override val policies: Set get() = throw UnsupportedOperationException() diff --git a/cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/PartExtractor.kt b/cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/PartExtractor.kt index 43f5e32b..0b40f867 100644 --- a/cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/PartExtractor.kt +++ b/cosec-core/src/main/kotlin/me/ahoo/cosec/policy/condition/part/PartExtractor.kt @@ -37,7 +37,7 @@ object SecurityContextParts { const val TENANT_ID = PREFIX + "tenantId" const val PRINCIPAL_PREFIX = PREFIX + "principal." const val PRINCIPAL_ID = PRINCIPAL_PREFIX + "id" - const val PRINCIPAL_NAME = PRINCIPAL_PREFIX + "name" + const val PRINCIPAL_ATTRIBUTES_PREFIX = PRINCIPAL_PREFIX + "attributes." } data class DefaultPartExtractor(val part: String) : PartExtractor { @@ -51,12 +51,16 @@ data class DefaultPartExtractor(val part: String) : PartExtractor { RequestParts.REFERER -> request.referer SecurityContextParts.TENANT_ID -> securityContext.tenant.tenantId SecurityContextParts.PRINCIPAL_ID -> securityContext.principal.id - SecurityContextParts.PRINCIPAL_NAME -> securityContext.principal.name else -> { if (part.startsWith(RequestParts.HEADER_PREFIX)) { val headerKey = part.substring(RequestParts.HEADER_PREFIX.length) return request.getHeader(headerKey) } + if (part.startsWith(SecurityContextParts.PRINCIPAL_ATTRIBUTES_PREFIX)) { + val headerKey = part.substring(SecurityContextParts.PRINCIPAL_ATTRIBUTES_PREFIX.length) + return securityContext.principal.attributes[headerKey].orEmpty() + } + throw IllegalArgumentException("Unsupported part: $part") } } diff --git a/cosec-core/src/main/kotlin/me/ahoo/cosec/principal/SimplePrincipal.kt b/cosec-core/src/main/kotlin/me/ahoo/cosec/principal/SimplePrincipal.kt index 46d91339..0a822a39 100644 --- a/cosec-core/src/main/kotlin/me/ahoo/cosec/principal/SimplePrincipal.kt +++ b/cosec-core/src/main/kotlin/me/ahoo/cosec/principal/SimplePrincipal.kt @@ -23,7 +23,7 @@ data class SimplePrincipal( override val id: String, override val policies: Set = emptySet(), override val roles: Set = emptySet(), - override val attrs: Map = emptyMap() + override val attributes: Map = emptyMap() ) : CoSecPrincipal { companion object { diff --git a/cosec-core/src/test/kotlin/me/ahoo/cosec/policy/condition/part/DefaultPartExtractorTest.kt b/cosec-core/src/test/kotlin/me/ahoo/cosec/policy/condition/part/DefaultPartExtractorTest.kt index a6058c98..a83b6a9b 100644 --- a/cosec-core/src/test/kotlin/me/ahoo/cosec/policy/condition/part/DefaultPartExtractorTest.kt +++ b/cosec-core/src/test/kotlin/me/ahoo/cosec/policy/condition/part/DefaultPartExtractorTest.kt @@ -95,13 +95,18 @@ class DefaultPartExtractorTest { } @Test - fun extractContextPrincipalName() { + fun extractContextPrincipalAttributes() { val context: SecurityContext = mockk { - every { principal.name } returns "principal.name" + every { principal.attributes["key"] } returns "value" + every { principal.attributes["not_exist"] } returns null } assertThat( - DefaultPartExtractor(SecurityContextParts.PRINCIPAL_NAME).extract(mockk(), context), - equalTo("principal.name") + DefaultPartExtractor(SecurityContextParts.PRINCIPAL_ATTRIBUTES_PREFIX + "key").extract(mockk(), context), + equalTo("value") + ) + assertThat( + DefaultPartExtractor(SecurityContextParts.PRINCIPAL_ATTRIBUTES_PREFIX + "not_exist").extract(mockk(), context), + equalTo("") ) } diff --git a/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/JwtTokenConverter.kt b/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/JwtTokenConverter.kt index c21079c6..b3c91411 100644 --- a/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/JwtTokenConverter.kt +++ b/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/JwtTokenConverter.kt @@ -42,7 +42,7 @@ class JwtTokenConverter( val accessTokenId = idGenerator.generateAsString() val now = Date() val accessTokenExp = Date(System.currentTimeMillis() + accessTokenValidity.toMillis()) - val payloadClaims: Map = principal.attrs + val payloadClaims: Map = principal.attributes .filter { !Jwts.isRegisteredClaim(it.key) } diff --git a/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/Jwts.kt b/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/Jwts.kt index c449f161..223ec553 100644 --- a/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/Jwts.kt +++ b/cosec-jwt/src/main/kotlin/me/ahoo/cosec/jwt/Jwts.kt @@ -65,16 +65,18 @@ object Jwts { fun asPrincipal(decodedAccessToken: DecodedJWT): T { val accessTokenId = decodedAccessToken.id val principalId = decodedAccessToken.subject - val attrs = decodedAccessToken + val attributes = decodedAccessToken .claims .filter { !isRegisteredClaim(it.key) } - + .mapValues { + it.value.asString() + } val policyClaim = decodedAccessToken.getClaim(PolicyCapable.POLICY_KEY) val policies = if (policyClaim.isMissing) emptySet() else policyClaim.asList(String::class.java).toSet() val rolesClaim = decodedAccessToken.getClaim(RoleCapable.ROLE_KEY) val roles = if (rolesClaim.isMissing) emptySet() else rolesClaim.asList(String::class.java).toSet() - val principal = SimplePrincipal(principalId, policies, roles, attrs) + val principal = SimplePrincipal(principalId, policies, roles, attributes) val tenantId = decodedAccessToken.getClaim(TENANT_ID_KEY).asString() val tokenPrincipal = SimpleTokenPrincipal(accessTokenId, principal) if (tenantId.isNullOrEmpty()) { diff --git a/cosec-oauth/src/main/kotlin/me/ahoo/cosec/oauth/client/DirectOAuthClientPrincipalConverter.kt b/cosec-oauth/src/main/kotlin/me/ahoo/cosec/oauth/client/DirectOAuthClientPrincipalConverter.kt index 3269415d..11b4e86b 100644 --- a/cosec-oauth/src/main/kotlin/me/ahoo/cosec/oauth/client/DirectOAuthClientPrincipalConverter.kt +++ b/cosec-oauth/src/main/kotlin/me/ahoo/cosec/oauth/client/DirectOAuthClientPrincipalConverter.kt @@ -31,7 +31,7 @@ object DirectOAuthClientPrincipalConverter : OAuthClientPrincipalConverter { id = asClientUserId(client, authUser), policies = emptySet(), roles = emptySet(), - attrs = authUser.rawInfo + attributes = authUser.rawInfo.mapValues { it.value.toString() } ).toMono() } diff --git a/document/cosec-policy.schema.json b/document/cosec-policy.schema.json index 7f87d341..da960f48 100644 --- a/document/cosec-policy.schema.json +++ b/document/cosec-policy.schema.json @@ -103,7 +103,7 @@ "request.header.", "context.tenantId", "context.principal.id", - "context.principal.name" + "context.principal.attributes." ] }, "pathOptions": {