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