feat(access): 접근 정책 모델을 추가한다
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.access
|
||||||
|
|
||||||
|
sealed interface AccessDecision {
|
||||||
|
data object Allowed : AccessDecision
|
||||||
|
data object LoginRequired : AccessDecision
|
||||||
|
data object AdultVerificationRequired : AccessDecision
|
||||||
|
data object AdultContentSettingRequired : AccessDecision
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.access
|
||||||
|
|
||||||
|
class AccessPolicy {
|
||||||
|
fun decide(requirement: AccessRequirement, state: AccessState): AccessDecision {
|
||||||
|
return when (requirement) {
|
||||||
|
AccessRequirement.Login -> decideLogin(state)
|
||||||
|
AccessRequirement.AdultContent -> decideAdultContent(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decideLogin(state: AccessState): AccessDecision {
|
||||||
|
return if (state.token.isNotBlank() && state.token.length > LOGIN_TOKEN_MIN_LENGTH) {
|
||||||
|
AccessDecision.Allowed
|
||||||
|
} else {
|
||||||
|
AccessDecision.LoginRequired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val LOGIN_TOKEN_MIN_LENGTH = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decideAdultContent(state: AccessState): AccessDecision {
|
||||||
|
val loginDecision = decideLogin(state)
|
||||||
|
if (loginDecision != AccessDecision.Allowed) return loginDecision
|
||||||
|
|
||||||
|
val isKoreanCountry = state.countryCode.ifBlank { "KR" } == "KR"
|
||||||
|
if (isKoreanCountry && !state.isAuth) {
|
||||||
|
return AccessDecision.AdultVerificationRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.isAdultContentVisible) {
|
||||||
|
return AccessDecision.AdultContentSettingRequired
|
||||||
|
}
|
||||||
|
|
||||||
|
return AccessDecision.Allowed
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.access
|
||||||
|
|
||||||
|
sealed interface AccessRequirement {
|
||||||
|
data object Login : AccessRequirement
|
||||||
|
data object AdultContent : AccessRequirement
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun forAdultContent(requiresAdultContentAccess: Boolean): AccessRequirement {
|
||||||
|
return if (requiresAdultContentAccess) AdultContent else Login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.access
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
|
|
||||||
|
data class AccessState(
|
||||||
|
val token: String,
|
||||||
|
val countryCode: String,
|
||||||
|
val isAuth: Boolean,
|
||||||
|
val isAdultContentVisible: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun interface AccessStateProvider {
|
||||||
|
fun current(): AccessState
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedPreferenceAccessStateProvider : AccessStateProvider {
|
||||||
|
override fun current(): AccessState = AccessState(
|
||||||
|
token = SharedPreferenceManager.token,
|
||||||
|
countryCode = SharedPreferenceManager.countryCode,
|
||||||
|
isAuth = SharedPreferenceManager.isAuth,
|
||||||
|
isAdultContentVisible = SharedPreferenceManager.isAdultContentVisible
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.access
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class AccessPolicyTest {
|
||||||
|
|
||||||
|
private val policy = AccessPolicy()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `로그인 요구사항은 토큰이 비어 있으면 로그인이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.Login,
|
||||||
|
state = accessState(token = "")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.LoginRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `로그인 요구사항은 토큰이 10자 이하면 로그인이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.Login,
|
||||||
|
state = accessState(token = "1234567890")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.LoginRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `로그인 요구사항은 토큰이 11자 이상 공백이면 로그인이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.Login,
|
||||||
|
state = accessState(token = " ")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.LoginRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `로그인 요구사항은 토큰이 11자 이상이면 성인 조건을 확인하지 않고 허용한다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.Login,
|
||||||
|
state = accessState(
|
||||||
|
token = "12345678901",
|
||||||
|
countryCode = "KR",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.Allowed, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 토큰이 10자 이하면 로그인이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(token = "1234567890")
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.LoginRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 빈 국가 코드를 KR로 처리한다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.AdultVerificationRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 국내 미인증 사용자의 본인인증이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "KR",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.AdultVerificationRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 본인인증 필요 결과를 성인 콘텐츠 설정보다 먼저 반환한다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "KR",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.AdultVerificationRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 국외 사용자의 국내 본인인증을 건너뛴다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "US",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.Allowed, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 성인 콘텐츠 표시 설정이 꺼져 있으면 설정이 필요하다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "US",
|
||||||
|
isAuth = false,
|
||||||
|
isAdultContentVisible = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.AdultContentSettingRequired, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `성인 콘텐츠 요구사항은 모든 조건을 충족하면 허용한다`() {
|
||||||
|
val decision = policy.decide(
|
||||||
|
requirement = AccessRequirement.AdultContent,
|
||||||
|
state = accessState(
|
||||||
|
countryCode = "KR",
|
||||||
|
isAuth = true,
|
||||||
|
isAdultContentVisible = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(AccessDecision.Allowed, decision)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun accessState(
|
||||||
|
token: String = "12345678901",
|
||||||
|
countryCode: String = "KR",
|
||||||
|
isAuth: Boolean = true,
|
||||||
|
isAdultContentVisible: Boolean = true
|
||||||
|
): AccessState = AccessState(
|
||||||
|
token = token,
|
||||||
|
countryCode = countryCode,
|
||||||
|
isAuth = isAuth,
|
||||||
|
isAdultContentVisible = isAdultContentVisible
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user