Skip to content

Commit

Permalink
Merge pull request #18 from RADAR-base/release-1.2.1
Browse files Browse the repository at this point in the history
Release 1.2.1
  • Loading branch information
mpgxvii authored Mar 13, 2024
2 parents 1b19837 + d28dfa2 commit fd341f6
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 61 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {

allprojects {
group = "org.radarbase"
version = "1.2.0"
version = "1.2.1"
}

subprojects {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ nexusPluginVersion=1.3.0
jacksonVersion=2.14.2
jsoupVersion=1.15.4

mpVersion=2.0.0
mpVersion=2.1.0
springVersion=6.0.6
slf4jVersion=2.0.6
aspectJVersion=1.9.19
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package radar.spring.auth.common

/** Abstract Authorization interface to be used with a custom token [T].
* See [radar.spring.auth.managementportal.ManagementPortalAuthorization]
* **/
interface Authorization<T> {
import kotlinx.coroutines.runBlocking

/**
* Abstract Authorization interface to be used with a custom token [T]. See
* [radar.spring.auth.managementportal.ManagementPortalAuthorization]
*/
interface Authorization<T> {
fun authorize(
token: T,
permission: String,
Expand All @@ -19,12 +21,14 @@ interface Authorization<T> {
user: String? = null,
source: String? = null
): Boolean {
return hasPermission(token, permission, entity, permissionOn, project, user, source) &&
hasRole(token, project, role) &&
hasScopes(token, scopes) &&
hasAuthorities(token, authorities) &&
hasAudiences(token, audiences) &&
hasGrantTypes(token, grantTypes)
return runBlocking {
hasPermission(token, permission, entity, permissionOn, project, user, source) &&
hasRole(token, project, role) &&
hasScopes(token, scopes) &&
hasAuthorities(token, authorities) &&
hasAudiences(token, audiences) &&
hasGrantTypes(token, grantTypes)
}
}

fun hasPermission(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package radar.spring.auth.config

import java.net.URI

open class ManagementPortalAuthProperties @JvmOverloads constructor(
val baseUrl: String,
val resourceName: String,
val publicKeyUrl: String = "$baseUrl/oauth/token_key"
val publicKeyEndpoints: List<URI> = emptyList(),
val publicKeyUrl: String = "$baseUrl/oauth/token_key",
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,40 @@ package radar.spring.auth.managementportal

import jakarta.servlet.http.HttpServletRequest
import org.radarbase.auth.authentication.TokenValidator
import org.radarbase.auth.config.TokenValidatorConfig
import org.radarbase.auth.config.TokenVerifierPublicKeyConfig
import org.radarbase.auth.authentication.TokenVerifierLoader
import org.radarbase.auth.exception.TokenValidationException
import org.radarbase.auth.jwks.JwkAlgorithmParser
import org.radarbase.auth.jwks.JwksTokenVerifierLoader
import org.radarbase.auth.token.RadarToken
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import radar.spring.auth.common.RadarAuthValidator
import radar.spring.auth.config.ManagementPortalAuthProperties
import java.net.URI

/** The [radar.spring.auth.common.AuthValidator] for Management Portal tokens. **/
/** The [radar.spring.auth.common.AuthValidator] for Management Portal tokens. */
@Component
class ManagementPortalAuthValidator @JvmOverloads constructor(
class ManagementPortalAuthValidator
@JvmOverloads
constructor(
@Autowired private val managementPortalProperties: ManagementPortalAuthProperties,
private val tokenValidatorConfig: TokenValidatorConfig = TokenVerifierPublicKeyConfig().apply {
publicKeyEndpoints = listOf(URI(managementPortalProperties.publicKeyUrl))
resourceName = managementPortalProperties.resourceName
},
private val tokenValidator: TokenValidator = TokenValidator(tokenValidatorConfig)
) :
RadarAuthValidator {

private val tokenVerifiers: List<TokenVerifierLoader> =
managementPortalProperties.publicKeyEndpoints.map {
JwksTokenVerifierLoader(
it.toString(),
managementPortalProperties.resourceName,
JwkAlgorithmParser()
)
} +
listOf(
JwksTokenVerifierLoader(
managementPortalProperties.publicKeyUrl,
managementPortalProperties.resourceName,
JwkAlgorithmParser()
)
),
private val tokenValidator: TokenValidator = TokenValidator(tokenVerifiers)
) : RadarAuthValidator {
init {
try {
this.tokenValidator.refresh()
Expand All @@ -38,8 +49,11 @@ class ManagementPortalAuthValidator @JvmOverloads constructor(
}

@Throws(TokenValidationException::class)
override fun verify(token: String, request: HttpServletRequest): RadarToken? {
return tokenValidator.validateAccessToken(token)
override fun verify(
token: String,
request: HttpServletRequest
): RadarToken? {
return tokenValidator.validateBlocking(token)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package radar.spring.auth.managementportal

import kotlinx.coroutines.runBlocking
import org.radarbase.auth.authorization.EntityDetails
import org.radarbase.auth.authorization.EntityRelationService
import org.radarbase.auth.authorization.MPAuthorizationOracle
import org.radarbase.auth.authorization.Permission
import org.radarbase.auth.token.RadarToken
import org.slf4j.LoggerFactory
import radar.spring.auth.common.Authorization
import radar.spring.auth.common.PermissionOn

class ManagementPortalAuthorization : Authorization<RadarToken> {
class ManagementPortalAuthorization() : Authorization<RadarToken> {
private val DEFAULT_ORG = "main"
private val relationService =
object : EntityRelationService {
override suspend fun findOrganizationOfProject(project: String): String? {
// NOTE: This will default to the default "main" project for now since we are not using organizations
// TODO: Implement organizations
return DEFAULT_ORG
}
}
val oracle: MPAuthorizationOracle = MPAuthorizationOracle(relationService)

override fun hasPermission(
token: RadarToken,
Expand All @@ -17,43 +31,55 @@ class ManagementPortalAuthorization : Authorization<RadarToken> {
user: String?,
source: String?
): Boolean {
val mpPermission = Permission.of(
Permission.Entity.valueOf(entity),
Permission.Operation.valueOf(permission)
)
return when (permissionOn) {
PermissionOn.PROJECT -> checkPermissionOnProject(token, mpPermission, project)
PermissionOn.SUBJECT -> checkPermissionOnSubject(token, mpPermission, project, user)
PermissionOn.SOURCE -> checkPermissionOnSource(
token,
mpPermission,
project,
user,
source
)
else -> token.hasPermission(mpPermission)
return runBlocking {
val subject = user ?: token.subject
val project = project ?: token.roles?.firstOrNull()?.referent

val mpPermission =
Permission.of(
Permission.Entity.valueOf(entity),
Permission.Operation.valueOf(permission)
)
when (permissionOn) {
PermissionOn.PROJECT ->
checkPermissionOnProject(token, mpPermission, project, subject)
PermissionOn.SUBJECT ->
checkPermissionOnSubject(token, mpPermission, project, subject)
PermissionOn.SOURCE ->
checkPermissionOnSource(token, mpPermission, project, subject, source)
else ->
oracle.hasPermission(
token,
mpPermission,
EntityDetails(project = project, subject = subject, source = source)
)
}
}
}

override fun hasRole(token: RadarToken, project: String?, role: String?): Boolean {
override fun hasRole(
token: RadarToken,
project: String?,
role: String?
): Boolean {
if (role.isNullOrBlank()) {
return true
}
if (project.isNullOrBlank()) {
logger.warn("Project must be specified when checking a role.")
return false
}
return token.roles.asSequence()
.filter { it.referent == project }
.any { it.authority == role }
return token.roles.asSequence().filter { it.referent == project }.any {
it.authority == role
}
}

override fun hasScopes(token: RadarToken, scopes: Array<String>): Boolean {
return token.scopes.containsAll(scopes.toList())
}

override fun hasAuthorities(token: RadarToken, authorities: Array<String>): Boolean {
return token.authorities.containsAll(authorities.toList())
return token.roles.asIterable().map { it.authority }.containsAll(authorities.toList())
}

override fun hasAudiences(token: RadarToken, audiences: Array<String>): Boolean {
Expand All @@ -67,36 +93,43 @@ class ManagementPortalAuthorization : Authorization<RadarToken> {
return grantTypes.contains(token.grantType)
}

private fun checkPermissionOnProject(
private suspend fun checkPermissionOnProject(
token: RadarToken,
mpPermission: Permission,
project: String?
project: String?,
subject: String?
): Boolean {
if (project.isNullOrBlank()) {
logger.warn(
"The project must be specified when checking permissions on PROJECT."
)
logger.warn("The project must be specified when checking permissions on PROJECT.")
return false
}
return token.hasPermissionOnProject(mpPermission, project)
return oracle.hasPermission(
token,
mpPermission,
EntityDetails(subject = subject, project = project)
)
}

private fun checkPermissionOnSubject(
private suspend fun checkPermissionOnSubject(
token: RadarToken,
mpPermission: Permission,
project: String?,
user: String?
subject: String?
): Boolean {
if (project.isNullOrBlank() || user.isNullOrBlank()) {
if (project.isNullOrBlank() || subject.isNullOrBlank()) {
logger.warn(
"The project and subject must be specified when checking permissions on SUBJECT."
)
return false
}
return token.hasPermissionOnSubject(mpPermission, project, user)
return oracle.hasPermission(
token,
mpPermission,
EntityDetails(subject = subject, project = project)
)
}

private fun checkPermissionOnSource(
private suspend fun checkPermissionOnSource(
token: RadarToken,
mpPermission: Permission,
project: String?,
Expand All @@ -110,7 +143,11 @@ class ManagementPortalAuthorization : Authorization<RadarToken> {
)
return false
}
return token.hasPermissionOnSource(mpPermission, project, user, source)
return oracle.hasPermission(
token,
mpPermission,
EntityDetails(user = user, source = source)
)
}

companion object {
Expand Down

0 comments on commit fd341f6

Please sign in to comment.