Skip to content

Commit

Permalink
Feature: add Configuration api (#44)
Browse files Browse the repository at this point in the history
Feature: enhance `RequestMatcher` api
Feature: enhance `ActionMatcherFactory` SPI
Feature: enhance `ConditionMatcherFactory` SPI
  • Loading branch information
Ahoo-Wang committed Jan 2, 2023
1 parent a685525 commit 3ae5bff
Show file tree
Hide file tree
Showing 62 changed files with 576 additions and 213 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosec.api.configuration

interface Configuration {
fun get(key: String): Configuration?
fun getRequired(key: String): Configuration = requireNotNull(get(key)) {
"Configuration[$key] is required!"
}

fun asList(): List<Configuration>
fun asMap(): Map<String, Configuration>
fun asString(): String
fun asBoolean(): Boolean
fun asInt(): Int
fun asLong(): Long
fun <T> asPojo(pojoClass: Class<T>): T

fun has(key: String): Boolean = get(key) != null
fun asStringList(): List<String> = asList().map { it.asString() }
fun asStringMap(): Map<String, String> = asMap().mapValues { it.value.asString() }
}

inline fun <reified T> Configuration.asPojo(): T = asPojo(T::class.java)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ interface Request : Tenant {
* Http Request: {url}:{method}
*/
val action: String

val remoteIp: String
val origin: String
val referer: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@

package me.ahoo.cosec.api.principal

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request

interface RequestMatcher {
val type: String
val pattern: String
val configuration: Configuration
fun match(request: Request, securityContext: SecurityContext): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosec.configuration

import com.fasterxml.jackson.core.ObjectCodec
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.NullNode
import me.ahoo.cosec.Delegated
import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.serialization.CoSecJsonSerializer

class JsonConfiguration(
override val delegate: JsonNode,
private val objectCodec: ObjectCodec
) : Configuration,
Delegated<JsonNode> {

companion object {
val EMPTY: JsonConfiguration by lazy {
JsonConfiguration(NullNode.getInstance(), CoSecJsonSerializer)
}

fun Map<String, *>.asConfiguration(): JsonConfiguration {
val jsonString = CoSecJsonSerializer.writeValueAsString(this)
return JsonConfiguration(CoSecJsonSerializer.readTree(jsonString), CoSecJsonSerializer)
}
}

override fun get(key: String): Configuration? {
return delegate.get(key)?.let { JsonConfiguration(it, objectCodec) }
}

override fun asList(): List<Configuration> {
return buildList {
val elements = delegate.elements()
while (elements.hasNext()) {
add(JsonConfiguration(elements.next(), objectCodec))
}
}
}

override fun asMap(): Map<String, Configuration> {
return buildMap {
val fields = delegate.fields()
while (fields.hasNext()) {
val field = fields.next()
put(field.key, JsonConfiguration(field.value, objectCodec))
}
}
}

override fun asString(): String {
return delegate.asText()
}

override fun asBoolean(): Boolean {
return delegate.asBoolean()
}

override fun asInt(): Int {
return delegate.asInt()
}

override fun asLong(): Long {
return delegate.asLong()
}

override fun <T> asPojo(pojoClass: Class<T>): T {
return delegate.traverse(objectCodec).use {
it.readValueAs(pojoClass)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright [2021-present] [ahoo wang <ahoowang@qq.com> (https://github.com/Ahoo-Wang)].
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package me.ahoo.cosec.policy

import me.ahoo.cosec.api.configuration.Configuration

const val MATCHER_TYPE_KEY = "type"
const val MATCHER_PATTERN_KEY = "pattern"

fun Configuration.getMatcherType(): String {
return getRequired(MATCHER_TYPE_KEY).asString()
}

fun Configuration.getMatcherPattern(): String {
return getRequired(MATCHER_PATTERN_KEY).asString()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@

package me.ahoo.cosec.policy.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.policy.ActionMatcher

interface ActionMatcherFactory {
val type: String
fun create(pattern: String): ActionMatcher
fun create(configuration: Configuration): ActionMatcher
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@

package me.ahoo.cosec.policy.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ActionMatcher

object AllActionMatcher : ActionMatcher {
class AllActionMatcher(override val configuration: Configuration) : ActionMatcher {
override val type: String
get() = AllActionMatcherFactory.TYPE
override val pattern: String
get() = "*"

override fun match(request: Request, securityContext: SecurityContext): Boolean {
return true
Expand All @@ -36,7 +35,7 @@ class AllActionMatcherFactory : ActionMatcherFactory {
override val type: String
get() = TYPE

override fun create(pattern: String): ActionMatcher {
return AllActionMatcher
override fun create(configuration: Configuration): ActionMatcher {
return AllActionMatcher(configuration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@

package me.ahoo.cosec.policy.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ActionMatcher

object NoneActionMatcher : ActionMatcher {
class NoneActionMatcher(override val configuration: Configuration) : ActionMatcher {
override val type: String
get() = NoneActionMatcherFactory.TYPE
override val pattern: String
get() = "!"

override fun match(request: Request, securityContext: SecurityContext): Boolean {
return false
Expand All @@ -36,7 +35,7 @@ class NoneActionMatcherFactory : ActionMatcherFactory {
override val type: String
get() = TYPE

override fun create(pattern: String): ActionMatcher {
return NoneActionMatcher
override fun create(configuration: Configuration): ActionMatcher {
return NoneActionMatcher(configuration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,31 @@

package me.ahoo.cosec.policy.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ActionMatcher
import me.ahoo.cosec.policy.getMatcherPattern
import org.springframework.http.server.PathContainer
import org.springframework.web.util.pattern.PathPatternParser

data class PathActionMatcher(override val pattern: String) : ActionMatcher {
class PathActionMatcher(override val configuration: Configuration) : ActionMatcher {
override val type: String
get() = PathActionMatcherFactory.TYPE
private val pathPattern = PathPatternParser.defaultInstance.parse(pattern)
private val pathPattern = PathPatternParser.defaultInstance.parse(configuration.getMatcherPattern())
override fun match(request: Request, securityContext: SecurityContext): Boolean {
PathContainer.parsePath(request.action).let {
return pathPattern.matches(it)
}
}
}

data class ReplaceablePathActionMatcher(override val pattern: String) : ActionMatcher {
class ReplaceablePathActionMatcher(override val configuration: Configuration) : ActionMatcher {
override val type: String
get() = PathActionMatcherFactory.TYPE

override fun match(request: Request, securityContext: SecurityContext): Boolean {
val pathPattern = ActionPatternReplacer.replace(pattern, securityContext)
val pathPattern = ActionPatternReplacer.replace(configuration.getMatcherPattern(), securityContext)
PathPatternParser.defaultInstance.parse(pathPattern).let {
return it.matches(PathContainer.parsePath(request.action))
}
Expand All @@ -50,14 +52,15 @@ class PathActionMatcherFactory : ActionMatcherFactory {
override val type: String
get() = TYPE

override fun create(pattern: String): ActionMatcher {
override fun create(configuration: Configuration): ActionMatcher {
val pattern = configuration.getMatcherPattern()
return if (ActionPatternReplacer.isTemplate(pattern)) {
ReplaceablePathActionMatcher(
pattern
configuration
)
} else {
PathActionMatcher(
pattern
configuration
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@

package me.ahoo.cosec.policy.action

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ActionMatcher
import me.ahoo.cosec.policy.getMatcherPattern

data class RegularActionMatcher(override val pattern: String) : ActionMatcher {
class RegularActionMatcher(override val configuration: Configuration) : ActionMatcher {

private val matcher: Regex = pattern.toRegex(RegexOption.IGNORE_CASE)
private val matcher: Regex = configuration.getMatcherPattern().toRegex(RegexOption.IGNORE_CASE)
override val type: String
get() = RegularActionMatcherFactory.TYPE

Expand All @@ -32,12 +34,12 @@ data class RegularActionMatcher(override val pattern: String) : ActionMatcher {
}
}

data class ReplaceableRegularActionMatcher(override val pattern: String) : ActionMatcher {
class ReplaceableRegularActionMatcher(override val configuration: Configuration) : ActionMatcher {
override val type: String
get() = RegularActionMatcherFactory.TYPE

override fun match(request: Request, securityContext: SecurityContext): Boolean {
val pattern = ActionPatternReplacer.replace(pattern, securityContext)
val pattern = ActionPatternReplacer.replace(configuration.getMatcherPattern(), securityContext)
return pattern.toRegex(RegexOption.IGNORE_CASE).matches(request.action)
}
}
Expand All @@ -50,14 +52,15 @@ class RegularActionMatcherFactory : ActionMatcherFactory {
override val type: String
get() = TYPE

override fun create(pattern: String): ActionMatcher {
override fun create(configuration: Configuration): ActionMatcher {
val pattern = configuration.getMatcherPattern()
return if (ActionPatternReplacer.isTemplate(pattern)) {
ReplaceableRegularActionMatcher(
pattern
configuration
)
} else {
RegularActionMatcher(
pattern
configuration
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@

package me.ahoo.cosec.policy.condition

import me.ahoo.cosec.api.configuration.Configuration
import me.ahoo.cosec.api.context.SecurityContext
import me.ahoo.cosec.api.context.request.Request
import me.ahoo.cosec.api.policy.ConditionMatcher

object AllConditionMatcher : ConditionMatcher {
class AllConditionMatcher(override val configuration: Configuration) : ConditionMatcher {

override val type: String
get() = AllConditionMatcherFactory.TYPE
override val pattern: String
get() = "*"

override fun match(request: Request, securityContext: SecurityContext): Boolean {
return true
Expand All @@ -37,7 +36,7 @@ class AllConditionMatcherFactory : ConditionMatcherFactory {
override val type: String
get() = TYPE

override fun create(pattern: String): ConditionMatcher {
return AllConditionMatcher
override fun create(configuration: Configuration): ConditionMatcher {
return AllConditionMatcher(configuration)
}
}

0 comments on commit 3ae5bff

Please sign in to comment.