Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/main/kotlin/com/cosmotech/api/rbac/CsmAdmin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ class CsmAdmin(val csmPlatformProperties: CsmPlatformProperties) {

fun verifyRolesAdmin(roles: List<String>): Boolean {
logger.debug("Verifying if token roles contains Platform Admin")
return roles.contains(ROLE_PLATFORM_ADMIN)
val customAdminGroup = csmPlatformProperties.identityProvider?.adminGroup
if (customAdminGroup.isNullOrBlank()) {
return roles.contains(ROLE_PLATFORM_ADMIN)
}
return roles.any { it == ROLE_PLATFORM_ADMIN || it == customAdminGroup }
}

fun verifyCurrentRolesAdmin(): Boolean {
Expand Down
34 changes: 28 additions & 6 deletions src/main/kotlin/com/cosmotech/api/utils/SecurityUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.nimbusds.jwt.JWTParser
import org.springframework.security.core.Authentication
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken

fun getCurrentAuthentication(): Authentication? = SecurityContextHolder.getContext().authentication

Expand All @@ -23,17 +24,31 @@ fun getCurrentAuthenticatedIssuer(): String {
throw IllegalStateException("User Authentication not found in Security Context")
}

val authentication = getCurrentAuthentication() as BearerTokenAuthentication
return authentication.token.tokenValue.let { JWTParser.parse(it).jwtClaimsSet.issuer }
val authentication = getCurrentAuthentication()

if (authentication is JwtAuthenticationToken) {
return authentication.token.tokenValue.let { JWTParser.parse(it).jwtClaimsSet.issuer }
}

return (authentication as BearerTokenAuthentication).token.tokenValue.let {
JWTParser.parse(it).jwtClaimsSet.issuer
}
}

fun getCurrentAuthenticatedMail(configuration: CsmPlatformProperties): String {
if (getCurrentAuthentication() == null) {
throw IllegalStateException("User Authentication not found in Security Context")
}

val authentication = getCurrentAuthentication() as BearerTokenAuthentication
return authentication.token.tokenValue.let {
val authentication = getCurrentAuthentication()

if (authentication is JwtAuthenticationToken) {
return authentication.token.tokenValue.let {
JWTParser.parse(it).jwtClaimsSet.getStringClaim(configuration.authorization.mailJwtClaim)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is present 2 times :
JWTParser.parse(it).jwtClaimsSet.getStringClaim(configuration.authorization.mailJwtClaim)
To refactor? Maybe with when

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will change it (certainly) use when syntax when we'll integrate Keycloak as identity provider ;)

}
}

return (authentication as BearerTokenAuthentication).token.tokenValue.let {
JWTParser.parse(it).jwtClaimsSet.getStringClaim(configuration.authorization.mailJwtClaim)
}
}
Expand All @@ -43,8 +58,15 @@ fun getCurrentAuthenticatedRoles(configuration: CsmPlatformProperties): List<Str
throw IllegalStateException("User Authentication not found in Security Context")
}

val authentication = getCurrentAuthentication() as BearerTokenAuthentication
return authentication.token.tokenValue.let {
val authentication = getCurrentAuthentication()

if (authentication is JwtAuthenticationToken) {
return authentication.token.tokenValue.let {
JWTParser.parse(it).jwtClaimsSet.getStringListClaim(configuration.authorization.rolesJwtClaim)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same, like before, line 2 times: JWTParser.parse(it).jwtClaimsSet.getStringListClaim(configuration.authorization.rolesJwtClaim)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will change it (certainly) use when syntax when we'll integrate Keycloak as identity provider ;)

}
}

return (authentication as BearerTokenAuthentication).token.tokenValue.let {
JWTParser.parse(it).jwtClaimsSet.getStringListClaim(configuration.authorization.rolesJwtClaim)
}
}
32 changes: 32 additions & 0 deletions src/test/kotlin/com/cosmotech/api/rbac/CsmRbacTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,15 @@ const val OWNER_ID = "3a869905-e9f5-4851-a7a9-3079aad49dfa"
const val USER_ID = "2a869905-e9f5-4851-a7a9-3079aad49dfb"
const val COMPONENT_ID = "component_id"

@Suppress("LargeClass")
class CsmRbacTests {
private val ROLE_NONE_PERMS: List<String> = listOf()
private val ROLE_READER_PERMS = listOf(PERM_READ)
private val ROLE_WRITER_PERMS = listOf(PERM_READ, PERM_WRITE)
private val ROLE_ADMIN_PERMS = listOf(PERM_ADMIN)
val CUSTOM_ADMIN_GROUP = "MyCustomAdminGroup"
val CUSTOM_USER_GROUP = "MyCustomUserGroup"
val CUSTOM_VIEWER_GROUP = "MyCustomViewerGroup"

private val USER_READER_ROLE = ROLE_READER
private val USER_WRITER_ROLE = ROLE_WRITER
Expand All @@ -82,13 +86,23 @@ class CsmRbacTests {
lateinit var parentRbacSecurity: RbacSecurity
lateinit var rbacSecurity: RbacSecurity

private val DEFAULT_IDENTITY_PROVIDER =
CsmPlatformProperties.CsmIdentityProvider(
"identityProviderCode",
authorizationUrl = "http://my-fake-authorization.url/autorize",
tokenUrl = "http://my-fake-token.url/token",
adminGroup = CUSTOM_ADMIN_GROUP,
userGroup = CUSTOM_USER_GROUP,
viewerGroup = CUSTOM_VIEWER_GROUP)

@BeforeTest
fun beforeEachTest() {
logger.trace("Begin test")
csmPlatformProperties = mockk<CsmPlatformProperties>(relaxed = true)
every { csmPlatformProperties.rbac.enabled } answers { true }
every { csmPlatformProperties.authorization.rolesJwtClaim } answers { "roles" }
every { csmPlatformProperties.authorization.mailJwtClaim } answers { "upn" }
every { csmPlatformProperties.identityProvider } answers { DEFAULT_IDENTITY_PROVIDER }
rolesDefinition =
RolesDefinition(
adminRole = ROLE_ADMIN,
Expand Down Expand Up @@ -161,6 +175,24 @@ class CsmRbacTests {
assertTrue(admin.verifyRolesAdmin(userRoles))
}

@Test
fun `Custom role Platform Admin OK`() {
val userRoles = listOf(CUSTOM_ADMIN_GROUP)
assertTrue(admin.verifyRolesAdmin(userRoles))
}

@Test
fun `Custom role and regular Platform Admin OK`() {
val userRoles = listOf(CUSTOM_ADMIN_GROUP, ROLE_PLATFORM_ADMIN)
assertTrue(admin.verifyRolesAdmin(userRoles))
}

@Test
fun `Custom role Platform Admin NOK`() {
val userRoles = listOf(CUSTOM_USER_GROUP, CUSTOM_VIEWER_GROUP)
assertFalse(admin.verifyRolesAdmin(userRoles))
}

@Test
fun `roles with Platform Admin OK`() {
val userRoles = listOf(ROLE_PLATFORM_ADMIN, ROLE_ORGANIZATION_USER)
Expand Down