2 Commits
0.1.1 ... 0.2.0

2 changed files with 43 additions and 14 deletions

View File

@@ -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")

View File

@@ -59,6 +59,14 @@ class AccessKontrolTest {
}
}
@Test
fun `test empty canAllGranted`() {
assertThrows(NoDecision::class.java) {
AccessControlSample()
.canView(listOf(), User(""))
}
}
@Test
fun `test CanAllDenied`() {
AccessControlSample().run {
@@ -98,6 +106,7 @@ class AccessKontrolTest {
assertEquals(null, getErrorCode("notExists")?.code)
assertEquals("KO2", getMessages().last())
assertEquals("KO", getFirstMessage())
assertEquals("ko", deniedResponses.firstOrNull()?.code)
}
}
@@ -128,6 +137,14 @@ class AccessKontrolTest {
}
}
@Test
fun `test getFirstDecisionResponse on empty`() {
assertThrows(NoDecision::class.java) {
listOf<AccessResponse>()
.getFirstDecisionResponse()
}
}
@Test
fun `GrantedResponse instantiation test`() {
assertEquals(AccessDecision.GRANTED, GrantedResponse(AccessControlSample()).decision)