|
|
|
|
@@ -27,9 +27,13 @@ abstract class AccessKontrol {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A helper to convert a list of subject into one response
|
|
|
|
|
*
|
|
|
|
|
* @throws [NoDecision] if the list of responses is empty
|
|
|
|
|
*/
|
|
|
|
|
protected fun <S : List<T>, T> canAll(items: S, action: (T) -> AccessResponse): AccessResponses = items
|
|
|
|
|
.map { action(it) }.let { responses ->
|
|
|
|
|
.ifEmpty { throw NoDecision() }
|
|
|
|
|
.map { action(it) }
|
|
|
|
|
.let { responses ->
|
|
|
|
|
if (responses.any { it is DeniedResponse }) {
|
|
|
|
|
DeniedResponses(responses)
|
|
|
|
|
} else {
|
|
|
|
|
@@ -50,9 +54,12 @@ typealias AccessResponses = List<AccessResponse>
|
|
|
|
|
/**
|
|
|
|
|
* Check all responses and return DENIED if one is DENIED
|
|
|
|
|
*
|
|
|
|
|
* If the list of responses is empty, return GRANTED
|
|
|
|
|
* @throws [NoDecision] if the list of responses is empty
|
|
|
|
|
*/
|
|
|
|
|
fun AccessResponses.getFirstDecisionResponse(): AccessResponse = this.firstOrNull { it.decision == AccessDecision.DENIED } ?: this.first { it.decision == AccessDecision.GRANTED }
|
|
|
|
|
fun AccessResponses.getFirstDecisionResponse(): AccessResponse {
|
|
|
|
|
ifEmpty { throw NoDecision() }
|
|
|
|
|
return firstOrNull { it.decision == AccessDecision.DENIED } ?: first()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Throw an Exception if one response is DENIED
|
|
|
|
|
@@ -62,8 +69,8 @@ fun AccessResponses.assert() {
|
|
|
|
|
throw AccessDeniedException(this)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
val AccessResponses.grantedResponses get(): AccessResponses = this.filterIsInstance<GrantedResponse>()
|
|
|
|
|
val AccessResponses.deniedResponses get(): AccessResponses = this.filterIsInstance<DeniedResponse>()
|
|
|
|
|
val AccessResponses.grantedResponses get(): List<GrantedResponse> = this.filterIsInstance<GrantedResponse>()
|
|
|
|
|
val AccessResponses.deniedResponses get(): List<DeniedResponse> = this.filterIsInstance<DeniedResponse>()
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert responses as boolean
|
|
|
|
|
@@ -78,6 +85,9 @@ class AccessDeniedException(val accessResponses: AccessResponses) : Throwable(ac
|
|
|
|
|
*/
|
|
|
|
|
fun first(): AccessResponse = accessResponses.deniedResponses.first()
|
|
|
|
|
|
|
|
|
|
val deniedResponses: List<DeniedResponse>
|
|
|
|
|
get() = this.accessResponses.deniedResponses
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if the error code is present into the responses
|
|
|
|
|
*/
|
|
|
|
|
@@ -97,12 +107,12 @@ class AccessDeniedException(val accessResponses: AccessResponses) : Throwable(ac
|
|
|
|
|
*/
|
|
|
|
|
fun getMessages(): List<String> = accessResponses
|
|
|
|
|
.deniedResponses
|
|
|
|
|
.map { it.message!! }
|
|
|
|
|
.map { it.message }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the first message
|
|
|
|
|
*/
|
|
|
|
|
fun getFirstMessage(): String? = accessResponses
|
|
|
|
|
fun getFirstMessage(): String = accessResponses
|
|
|
|
|
.deniedResponses
|
|
|
|
|
.first()
|
|
|
|
|
.message
|
|
|
|
|
@@ -116,8 +126,8 @@ class AccessDeniedException(val accessResponses: AccessResponses) : Throwable(ac
|
|
|
|
|
sealed class AccessResponse(
|
|
|
|
|
val decision: AccessDecision,
|
|
|
|
|
val accessControl: AccessKontrol,
|
|
|
|
|
val message: String?,
|
|
|
|
|
val code: String?
|
|
|
|
|
open val message: String?,
|
|
|
|
|
open val code: String?
|
|
|
|
|
) {
|
|
|
|
|
/**
|
|
|
|
|
* Convert response as boolean
|
|
|
|
|
@@ -142,8 +152,8 @@ open class GrantedResponse(
|
|
|
|
|
|
|
|
|
|
open class DeniedResponse(
|
|
|
|
|
accessControl: AccessKontrol,
|
|
|
|
|
message: String,
|
|
|
|
|
code: String
|
|
|
|
|
override val message: String,
|
|
|
|
|
override val code: String
|
|
|
|
|
) : AccessResponse(AccessDecision.DENIED, accessControl, message, code)
|
|
|
|
|
|
|
|
|
|
class GrantedResponses(
|
|
|
|
|
@@ -159,7 +169,9 @@ class DeniedResponses(
|
|
|
|
|
accessResponses: List<AccessResponse>
|
|
|
|
|
) : AccessResponses by accessResponses,
|
|
|
|
|
DeniedResponse(
|
|
|
|
|
accessResponses.deniedResponses.first().accessControl,
|
|
|
|
|
accessResponses.deniedResponses.firstOrNull()?.message ?: error("DeniedResponses cannot be empty"),
|
|
|
|
|
accessResponses.deniedResponses.firstOrNull()?.code ?: error("DeniedResponses cannot be empty")
|
|
|
|
|
accessResponses.deniedResponses.firstOrNull()?.accessControl ?: error("DeniedResponses cannot be empty"),
|
|
|
|
|
accessResponses.deniedResponses.first().message,
|
|
|
|
|
accessResponses.deniedResponses.first().code
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class NoDecision : RuntimeException("No decision has been taken")
|
|
|
|
|
|