Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: add Configuration api #44

Merged
merged 1 commit into from
Jan 2, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
}
}