Skip to content

Commit

Permalink
feat: Support dynamic configuration of security policies (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahoo-Wang authored Apr 10, 2023
1 parent 52cbd36 commit 602ade9
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ abstract class RedisEventListenerContainer<T, E>(
}
.doOnSubscribe {
if (log.isInfoEnabled) {
log.info("Listen {} - topic[{}].", this, topic)
log.info("Receive {} - topic[{}].", this, topic)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ import me.ahoo.cosec.api.authorization.Authorization
import me.ahoo.cosec.api.authorization.AuthorizeResult
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.Policy
import me.ahoo.cosec.api.policy.VerifyResult
import me.ahoo.cosec.api.principal.CoSecPrincipal.Companion.isRoot
import me.ahoo.cosec.policy.DefaultPolicyEvaluator
import me.ahoo.cosec.serialization.CoSecJsonSerializer
import me.ahoo.cosky.rest.security.rbac.Action.Companion.httpMethodAsAction
import me.ahoo.cosky.rest.security.authorization.NamespaceRequestAttributesAppender.getNamespace
import me.ahoo.cosky.rest.security.rbac.Action.Companion.httpMethodAsAction
import me.ahoo.cosky.rest.security.rbac.RbacService
import me.ahoo.cosky.rest.security.rbac.ResourceAction
import org.springframework.stereotype.Service
Expand All @@ -19,39 +16,36 @@ import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono

@Service
class CoSkyAuthorization(private val rbacService: RbacService) : Authorization {
private val policy: Policy by lazy {
requireNotNull(javaClass.classLoader.getResource("cosky-policy.json")).let { resource ->
resource.openStream().use {
val policy = CoSecJsonSerializer.readValue(it, Policy::class.java)
DefaultPolicyEvaluator.evaluate(policy)
policy
}
}
}
class CoSkyAuthorization(
private val rbacService: RbacService,
private val coSkyPolicy: CoSkyPolicy
) : Authorization {

override fun authorize(request: Request, context: SecurityContext): Mono<AuthorizeResult> {
if (context.principal.isRoot()) {
return AuthorizeResult.ALLOW.toMono()
}
val verifyResult = policy.verify(request, context)
if (verifyResult == VerifyResult.ALLOW) {
return AuthorizeResult.ALLOW.toMono()
}
if (verifyResult == VerifyResult.EXPLICIT_DENY) {
return AuthorizeResult.EXPLICIT_DENY.toMono()
}
return coSkyPolicy.getPolicy().flatMap { policy ->
val verifyResult = policy.verify(request, context)
if (verifyResult == VerifyResult.ALLOW) {
return@flatMap AuthorizeResult.ALLOW.toMono()
}
if (verifyResult == VerifyResult.EXPLICIT_DENY) {
return@flatMap AuthorizeResult.EXPLICIT_DENY.toMono()
}

val namespace = request.getNamespace() ?: return AuthorizeResult.IMPLICIT_DENY.toMono()
val namespace = request.getNamespace() ?: return@flatMap AuthorizeResult.IMPLICIT_DENY.toMono()

val requestAction = ResourceAction(namespace, request.method.httpMethodAsAction())
return checkRolePermissions(context.principal.roles, requestAction)
.map { result: Boolean ->
if (result) {
return@map AuthorizeResult.ALLOW
val requestAction = ResourceAction(namespace, request.method.httpMethodAsAction())
return@flatMap checkRolePermissions(context.principal.roles, requestAction)
.map {
if (it) {
return@map AuthorizeResult.ALLOW
}
AuthorizeResult.IMPLICIT_DENY
}
AuthorizeResult.IMPLICIT_DENY
}
}

}

private fun checkRolePermissions(roleBind: Set<String>, requestAction: ResourceAction): Mono<Boolean> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package me.ahoo.cosky.rest.security.authorization

import me.ahoo.cosec.api.policy.Policy
import me.ahoo.cosec.serialization.CoSecJsonSerializer
import me.ahoo.cosky.config.ConfigEventListenerContainer
import me.ahoo.cosky.config.ConfigService
import me.ahoo.cosky.core.Namespaced
import me.ahoo.cosky.rest.security.authorization.InitialPolicyLoader.policyResourceName
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.InitializingBean
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
import reactor.kotlin.core.publisher.toMono

@Service
class CoSkyPolicy(
private val configService: ConfigService,
private val configEventListenerContainer: ConfigEventListenerContainer
) : InitializingBean {
companion object {
const val policyId = policyResourceName
val namespacedPolicyId = me.ahoo.cosky.config.NamespacedConfigId(Namespaced.SYSTEM, policyId)
private val log = LoggerFactory.getLogger(CoSkyPolicy::class.java)
}

private var policyCache: Mono<Policy> = getPolicyCache()

override fun afterPropertiesSet() {
configEventListenerContainer.receive(namespacedPolicyId)
.subscribe {
if (log.isInfoEnabled) {
log.info("Policy[{}] is updated - ${it.event}.", policyId)
}
policyCache = getPolicyCache()
}
}

private fun getPolicyCache(): Mono<Policy> {
return configService.getConfig(Namespaced.SYSTEM, policyId)
.map { config ->
CoSecJsonSerializer.readValue(config.data, Policy::class.java)
}.switchIfEmpty {
if (log.isInfoEnabled) {
log.info("Policy[{}] is not found, using initial policy.", policyId)
}
InitialPolicyLoader.policy.toMono()
}.cache()
}

fun getPolicy(): Mono<Policy> {
return policyCache
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package me.ahoo.cosky.rest.security.authorization

import me.ahoo.cosec.api.policy.Policy
import me.ahoo.cosec.policy.DefaultPolicyEvaluator
import me.ahoo.cosec.serialization.CoSecJsonSerializer

object InitialPolicyLoader {
const val policyResourceName = "cosky-policy.json"
val policy: Policy by lazy(this) {
requireNotNull(javaClass.classLoader.getResource(policyResourceName)).let { resource ->
resource.openStream().use {
val policy = CoSecJsonSerializer.readValue(it, Policy::class.java)
DefaultPolicyEvaluator.evaluate(policy)
policy
}
}
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# limitations under the License.
#
group=me.ahoo.cosky
version=3.3.9
version=3.3.10
description=High-performance, low-cost microservice governance platform. Service Discovery and Configuration Service.
website=https://github.com/Ahoo-Wang/cosky
issues=https://github.com/Ahoo-Wang/cosky/issues
Expand Down

0 comments on commit 602ade9

Please sign in to comment.