Skip to content

Commit 9acb6e8

Browse files
committed
🔖 Release v1.0
1 parent 0c36dfe commit 9acb6e8

File tree

193 files changed

+5536
-384
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+5536
-384
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38+
39+
### deploy
40+
**/deploy/
41+
42+
### gen
43+
branch-logic-engine/**/gen/

account-role/src/main/kotlin/com/samsung/healthcare/account/application/accesscontrol/AccessControlAspect.kt

+20-23
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.samsung.healthcare.account.application.accesscontrol
22

33
import com.samsung.healthcare.account.application.context.ContextHolder
4-
import com.samsung.healthcare.account.domain.AccessProjectAuthority
54
import com.samsung.healthcare.account.domain.Account
65
import com.samsung.healthcare.account.domain.AssignRoleAuthority
76
import com.samsung.healthcare.account.domain.GlobalAuthority
7+
import com.samsung.healthcare.account.domain.ProjectAuthority
88
import com.samsung.healthcare.account.domain.Role.ProjectRole
99
import org.aspectj.lang.ProceedingJoinPoint
1010
import org.aspectj.lang.annotation.Around
@@ -22,7 +22,7 @@ class AccessControlAspect {
2222
fun withAuthorize(joinPoint: ProceedingJoinPoint, requires: Requires): Any {
2323
return ContextHolder.getAccount()
2424
.switchIfEmpty { Mono.error(IllegalAccessException()) }
25-
.map { account -> checkRoles(account, requires, joinPoint) }
25+
.map { account -> checkAuthorities(account, requires, joinPoint) }
2626
.onErrorMap { ex -> ex.printStackTrace(); IllegalAccessException() }
2727
.then(proceed(joinPoint))
2828
}
@@ -33,45 +33,42 @@ class AccessControlAspect {
3333
}
3434
}
3535

36-
private fun checkRoles(account: Account, requires: Requires, joinPoint: ProceedingJoinPoint) {
36+
private fun checkAuthorities(account: Account, requires: Requires, joinPoint: ProceedingJoinPoint) {
3737
checkGlobalAuthorities(account, requires)
3838
checkProjectAuthorities(account, requires, joinPoint)
3939
}
4040

4141
private fun checkProjectAuthorities(account: Account, requires: Requires, joinPoint: ProceedingJoinPoint) {
42-
checkAccessProjectAuthorities(account, requires, joinPoint)
43-
checkAssignRoleAuthorities(account, requires, joinPoint)
44-
}
45-
46-
private fun checkAccessProjectAuthorities(account: Account, requires: Requires, joinPoint: ProceedingJoinPoint) {
47-
val projectIds = joinPoint.args.filterIsInstance(String::class.java)
48-
checkPermissions(account, requires, projectIds, AccessProjectAuthority::class)
42+
val requiredAuthorities = requires.authorities.toSet()
43+
.filter { it.isSubclassOf(ProjectAuthority::class) }
44+
if (requiredAuthorities.any { it.isSubclassOf(AssignRoleAuthority::class) })
45+
checkAssignRoleAuthorities(account, joinPoint)
46+
else {
47+
val projectIds = joinPoint.args.filterIsInstance(String::class.java)
48+
requiredAuthorities.forEach {
49+
checkPermissions(account, it, projectIds)
50+
}
51+
}
4952
}
5053

51-
private fun checkAssignRoleAuthorities(account: Account, requires: Requires, joinPoint: ProceedingJoinPoint) {
54+
private fun checkAssignRoleAuthorities(account: Account, joinPoint: ProceedingJoinPoint) {
5255
val projectIds = joinPoint.args.filterIsInstance(Collection::class.java)
5356
.flatten()
5457
.filterIsInstance(ProjectRole::class.java)
5558
.map { it.projectId }
56-
checkPermissions(account, requires, projectIds, AssignRoleAuthority::class)
59+
checkPermissions(account, AssignRoleAuthority::class, projectIds)
5760
}
5861

5962
private fun checkPermissions(
6063
account: Account,
61-
requires: Requires,
64+
authorityClass: KClass<*>,
6265
projectIds: List<String>,
63-
authorityClass: KClass<*>
6466
) {
65-
val hasAllPermissions = requires.authorities
66-
.filter { it.isSubclassOf(authorityClass) }
67-
.flatMap { kClass ->
68-
projectIds.map {
69-
kClass.primaryConstructor!!.call(it)
70-
}
71-
}
72-
.all { authority -> account.hasPermission(authority as GrantedAuthority) }
67+
val hasPermission = projectIds.map {
68+
authorityClass.primaryConstructor!!.call(it)
69+
}.all { authority -> account.hasPermission(authority as GrantedAuthority) }
7370

74-
if (!hasAllPermissions) throw IllegalAccessException()
71+
if (!hasPermission) throw IllegalAccessException()
7572
}
7673

7774
private fun checkGlobalAuthorities(account: Account, requires: Requires) {

account-role/src/main/kotlin/com/samsung/healthcare/account/domain/Account.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.samsung.healthcare.account.domain
22

33
import com.samsung.healthcare.account.domain.Role.ProjectRole
4-
import com.samsung.healthcare.account.domain.Role.ProjectRole.ProjectOwner
4+
import com.samsung.healthcare.account.domain.Role.ProjectRole.StudyCreator
55
import com.samsung.healthcare.account.domain.Role.ServiceAccount
66
import org.springframework.security.core.GrantedAuthority
77

@@ -12,7 +12,7 @@ data class Account(
1212
val profiles: Map<String, Any> = emptyMap()
1313
) {
1414
fun canAssignProjectRole(projectId: String): Boolean =
15-
roles.filterIsInstance<ProjectOwner>()
15+
roles.filterIsInstance<StudyCreator>()
1616
.any { it.canAccessProject(projectId) }
1717

1818
fun canCreateRole(): Boolean =

account-role/src/main/kotlin/com/samsung/healthcare/account/domain/GrantedAuthorities.kt

+41-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,49 @@ object CreateRoleAuthority : GlobalAuthority {
1515
interface ProjectAuthority : GrantedAuthority
1616

1717
data class AssignRoleAuthority(val projectId: String) : ProjectAuthority {
18-
1918
override fun getAuthority(): String = "assign-role:$projectId"
2019
}
2120

22-
data class AccessProjectAuthority(val projectId: String) : ProjectAuthority {
21+
data class ReadStudyOverviewAuthority(val projectId: String) : ProjectAuthority {
22+
override fun getAuthority(): String = "read-study-overview:$projectId"
23+
}
24+
25+
data class ReadParticipantDataAuthority(val projectId: String) : ProjectAuthority {
26+
override fun getAuthority(): String = "read-participant-data:$projectId"
27+
}
28+
29+
data class ReadDeIdentifiedParticipantDataAuthority(val projectId: String) : ProjectAuthority {
30+
override fun getAuthority(): String = "read-de-identified-participant-data:$projectId"
31+
}
32+
33+
data class AccessInLabVisitAuthority(val projectId: String) : ProjectAuthority {
34+
override fun getAuthority(): String = "access-in-lab-visit:$projectId"
35+
}
36+
37+
data class AccessTaskAuthority(val projectId: String) : ProjectAuthority {
38+
override fun getAuthority(): String = "access-task:$projectId"
39+
}
40+
41+
data class AccessDocumentAuthority(val projectId: String) : ProjectAuthority {
42+
override fun getAuthority(): String = "access-document:$projectId"
43+
}
44+
45+
data class ReadAggSensorDataAuthority(val projectId: String) : ProjectAuthority {
46+
override fun getAuthority(): String = "read-aggregated-sensor-data:$projectId"
47+
}
48+
49+
data class QueryRawDataAuthority(val projectId: String) : ProjectAuthority {
50+
override fun getAuthority(): String = "query-raw-data:$projectId"
51+
}
52+
53+
data class QueryDeIdentifiedDataAuthority(val projectId: String) : ProjectAuthority {
54+
override fun getAuthority(): String = "query-de-identified-data:$projectId"
55+
}
56+
57+
data class ReadProjectMemberAuthority(val projectId: String) : ProjectAuthority {
58+
override fun getAuthority(): String = "read-project-member:$projectId"
59+
}
2360

24-
override fun getAuthority(): String = "access-project:$projectId"
61+
data class AccessProjectMemberAuthority(val projectId: String) : ProjectAuthority {
62+
override fun getAuthority(): String = "access-project-member:$projectId"
2563
}

account-role/src/main/kotlin/com/samsung/healthcare/account/domain/Role.kt

+48-15
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ sealed class Role private constructor(val roleName: String) {
66
companion object {
77
const val TEAM_ADMIN = "team-admin"
88
const val SERVICE_ACCOUNT = "service-account"
9-
const val PROJECT_OWNER = "project-owner"
10-
const val HEAD_RESEARCHER = "head-researcher"
11-
const val RESEARCHER = "researcher"
9+
const val STUDY_CREATOR = "study-creator"
10+
const val PRINCIPAL_INVESTIGATOR = "principal-investigator"
11+
const val RESEARCH_ASSISTANT = "research-assistant"
12+
const val DATA_SCIENTIST = "data-scientist"
1213
}
1314

1415
abstract val authorities: Collection<GrantedAuthority>
@@ -39,33 +40,65 @@ sealed class Role private constructor(val roleName: String) {
3940

4041
fun canAccessProject(pid: String): Boolean = projectId == pid
4142

42-
class ProjectOwner(projectId: String) : ProjectRole(projectId, PROJECT_OWNER) {
43+
class StudyCreator(projectId: String) : ProjectRole(projectId, STUDY_CREATOR) {
4344
override val authorities: Collection<GrantedAuthority> = listOf(
4445
AssignRoleAuthority(projectId),
45-
AccessProjectAuthority(projectId)
46+
ReadStudyOverviewAuthority(projectId),
47+
ReadParticipantDataAuthority(projectId),
48+
ReadDeIdentifiedParticipantDataAuthority(projectId),
49+
AccessInLabVisitAuthority(projectId),
50+
AccessTaskAuthority(projectId),
51+
AccessDocumentAuthority(projectId),
52+
ReadAggSensorDataAuthority(projectId),
53+
QueryRawDataAuthority(projectId),
54+
QueryDeIdentifiedDataAuthority(projectId),
55+
ReadProjectMemberAuthority(projectId),
56+
AccessProjectMemberAuthority(projectId),
4657
)
4758
}
4859

49-
class HeadResearcher(projectId: String) : ProjectRole(projectId, HEAD_RESEARCHER) {
60+
class PrincipalInvestigator(projectId: String) : ProjectRole(projectId, PRINCIPAL_INVESTIGATOR) {
5061
override val authorities: Collection<GrantedAuthority> = listOf(
5162
AssignRoleAuthority(projectId),
52-
AccessProjectAuthority(projectId)
63+
ReadStudyOverviewAuthority(projectId),
64+
ReadParticipantDataAuthority(projectId),
65+
ReadDeIdentifiedParticipantDataAuthority(projectId),
66+
AccessInLabVisitAuthority(projectId),
67+
AccessTaskAuthority(projectId),
68+
AccessDocumentAuthority(projectId),
69+
ReadAggSensorDataAuthority(projectId),
70+
QueryRawDataAuthority(projectId),
71+
QueryDeIdentifiedDataAuthority(projectId),
72+
ReadProjectMemberAuthority(projectId),
73+
AccessProjectMemberAuthority(projectId),
5374
)
5475
}
5576

56-
class Researcher(projectId: String) : ProjectRole(projectId, RESEARCHER) {
77+
class ResearchAssistant(projectId: String) : ProjectRole(projectId, RESEARCH_ASSISTANT) {
5778
override val authorities: Collection<GrantedAuthority> = listOf(
58-
AccessProjectAuthority(projectId)
79+
ReadStudyOverviewAuthority(projectId),
80+
ReadParticipantDataAuthority(projectId),
81+
ReadDeIdentifiedParticipantDataAuthority(projectId),
82+
AccessInLabVisitAuthority(projectId),
83+
AccessTaskAuthority(projectId),
84+
AccessDocumentAuthority(projectId),
85+
ReadAggSensorDataAuthority(projectId),
86+
QueryRawDataAuthority(projectId),
87+
QueryDeIdentifiedDataAuthority(projectId),
88+
ReadProjectMemberAuthority(projectId),
89+
AccessProjectMemberAuthority(projectId),
5990
)
6091
}
6192

62-
class CustomRole(projectId: String, projectRoleName: String) : ProjectRole(projectId, projectRoleName) {
63-
init {
64-
require(projectRoleName.isNotBlank())
65-
}
66-
93+
class DataScientist(projectId: String) : ProjectRole(projectId, DATA_SCIENTIST) {
6794
override val authorities: Collection<GrantedAuthority> = listOf(
68-
AccessProjectAuthority(projectId)
95+
ReadStudyOverviewAuthority(projectId),
96+
ReadDeIdentifiedParticipantDataAuthority(projectId),
97+
AccessTaskAuthority(projectId),
98+
AccessDocumentAuthority(projectId),
99+
ReadAggSensorDataAuthority(projectId),
100+
QueryDeIdentifiedDataAuthority(projectId),
101+
ReadProjectMemberAuthority(projectId),
69102
)
70103
}
71104
}

account-role/src/main/kotlin/com/samsung/healthcare/account/domain/RoleFactory.kt

+10-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.samsung.healthcare.account.domain
22

33
import com.samsung.healthcare.account.domain.Role.ProjectRole
4-
import com.samsung.healthcare.account.domain.Role.ProjectRole.CustomRole
5-
import com.samsung.healthcare.account.domain.Role.ProjectRole.HeadResearcher
6-
import com.samsung.healthcare.account.domain.Role.ProjectRole.ProjectOwner
7-
import com.samsung.healthcare.account.domain.Role.ProjectRole.Researcher
4+
import com.samsung.healthcare.account.domain.Role.ProjectRole.DataScientist
5+
import com.samsung.healthcare.account.domain.Role.ProjectRole.PrincipalInvestigator
6+
import com.samsung.healthcare.account.domain.Role.ProjectRole.StudyCreator
7+
import com.samsung.healthcare.account.domain.Role.ProjectRole.ResearchAssistant
88
import com.samsung.healthcare.account.domain.Role.ServiceAccount
99
import com.samsung.healthcare.account.domain.Role.TeamAdmin
1010

@@ -27,11 +27,13 @@ object RoleFactory {
2727
}
2828

2929
private fun createProjectRole(projectId: String, roleName: String): Role {
30+
require(roleName.isNotBlank())
3031
return when (roleName) {
31-
Role.HEAD_RESEARCHER -> HeadResearcher(projectId)
32-
Role.RESEARCHER -> Researcher(projectId)
33-
Role.PROJECT_OWNER -> ProjectOwner(projectId)
34-
else -> CustomRole(projectId, roleName)
32+
Role.PRINCIPAL_INVESTIGATOR -> PrincipalInvestigator(projectId)
33+
Role.RESEARCH_ASSISTANT -> ResearchAssistant(projectId)
34+
Role.STUDY_CREATOR -> StudyCreator(projectId)
35+
Role.DATA_SCIENTIST -> DataScientist(projectId)
36+
else -> throw IllegalArgumentException()
3537
}
3638
}
3739
}

account-role/src/test/kotlin/com/samsung/healthcare/account/application/accesscontrol/AccessControlAspectTest.kt

+18-18
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ package com.samsung.healthcare.account.application.accesscontrol
33
import com.samsung.healthcare.account.NEGATIVE_TEST
44
import com.samsung.healthcare.account.POSITIVE_TEST
55
import com.samsung.healthcare.account.application.context.ContextHolder
6-
import com.samsung.healthcare.account.domain.AccessProjectAuthority
76
import com.samsung.healthcare.account.domain.Account
87
import com.samsung.healthcare.account.domain.AssignRoleAuthority
98
import com.samsung.healthcare.account.domain.Email
9+
import com.samsung.healthcare.account.domain.ReadStudyOverviewAuthority
1010
import com.samsung.healthcare.account.domain.Role
11-
import com.samsung.healthcare.account.domain.Role.ProjectRole.ProjectOwner
12-
import com.samsung.healthcare.account.domain.Role.ProjectRole.Researcher
11+
import com.samsung.healthcare.account.domain.Role.ProjectRole.StudyCreator
12+
import com.samsung.healthcare.account.domain.Role.ProjectRole.ResearchAssistant
1313
import org.junit.jupiter.api.Tag
1414
import org.junit.jupiter.api.Test
1515
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory
@@ -19,15 +19,15 @@ import reactor.test.StepVerifier
1919

2020
interface Target {
2121
fun testAssignRole(roles: List<Role>): Mono<Void>
22-
fun testAccessProject(projectId: String): Mono<Void>
22+
fun testReadStudyOverviewAuthority(projectId: String): Mono<Void>
2323
}
2424

2525
open class TestTarget : Target {
2626
@Requires([AssignRoleAuthority::class])
2727
override fun testAssignRole(roles: List<Role>) = Mono.empty<Void>()
2828

29-
@Requires([AccessProjectAuthority::class])
30-
override fun testAccessProject(projectId: String) = Mono.empty<Void>()
29+
@Requires([ReadStudyOverviewAuthority::class])
30+
override fun testReadStudyOverviewAuthority(projectId: String) = Mono.empty<Void>()
3131
}
3232

3333
internal class AccessControlAspectTest {
@@ -45,23 +45,23 @@ internal class AccessControlAspectTest {
4545
StepVerifier.create(
4646
withAccountContext(
4747
target.testAssignRole(
48-
listOf(Researcher(projectId))
48+
listOf(ResearchAssistant(projectId))
4949
),
50-
testAccount(Researcher(projectId))
50+
testAccount(ResearchAssistant(projectId))
5151
)
5252
).verifyError<IllegalAccessException>()
5353
}
5454

5555
@Test
5656
@Tag(POSITIVE_TEST)
57-
fun `should return ok when account does have project owner roles and assign researcher`() {
57+
fun `should return ok when account does have study creator roles and assign research-assistant`() {
5858
val projectId = "project-id"
59-
val roles = listOf(Researcher(projectId))
59+
val roles = listOf(ResearchAssistant(projectId))
6060

6161
StepVerifier.create(
6262
withAccountContext(
6363
target.testAssignRole(roles),
64-
testAccount(ProjectOwner(projectId))
64+
testAccount(StudyCreator(projectId))
6565
)
6666
).verifyComplete()
6767
}
@@ -74,7 +74,7 @@ internal class AccessControlAspectTest {
7474
).verifyError<IllegalAccessException>()
7575

7676
StepVerifier.create(
77-
target.testAccessProject("project-id")
77+
target.testReadStudyOverviewAuthority("project-id")
7878
).verifyError<IllegalAccessException>()
7979
}
8080

@@ -84,10 +84,10 @@ internal class AccessControlAspectTest {
8484
val projectId = "project-id"
8585
StepVerifier.create(
8686
withAccountContext(
87-
target.testAccessProject(
87+
target.testReadStudyOverviewAuthority(
8888
projectId
8989
),
90-
testAccount(Researcher("another-project-id"))
90+
testAccount(ResearchAssistant("another-project-id"))
9191
)
9292
).verifyError<IllegalAccessException>()
9393
}
@@ -99,15 +99,15 @@ internal class AccessControlAspectTest {
9999

100100
StepVerifier.create(
101101
withAccountContext(
102-
target.testAccessProject(projectId),
103-
testAccount(Researcher(projectId))
102+
target.testReadStudyOverviewAuthority(projectId),
103+
testAccount(ResearchAssistant(projectId))
104104
)
105105
).verifyComplete()
106106

107107
StepVerifier.create(
108108
withAccountContext(
109-
target.testAccessProject(projectId),
110-
testAccount(ProjectOwner(projectId))
109+
target.testReadStudyOverviewAuthority(projectId),
110+
testAccount(StudyCreator(projectId))
111111
)
112112
).verifyComplete()
113113
}

0 commit comments

Comments
 (0)