Skip to content

Commit 1f4b7f4

Browse files
committed
Change UserFilter to only match if all given dimensions are a match
1 parent 6cc5e56 commit 1f4b7f4

File tree

3 files changed

+60
-22
lines changed

3 files changed

+60
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ _Note that this repo only contains the User Roles service_
3535
```
3636
{
3737
"username": "user123",
38-
"userroles": [
38+
"roles": [
3939
{
4040
"applicationName": "application1",
4141
"orgId": "orgId1",

src/main/kotlin/no/liflig/userroles/administration/UserFilter.kt

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,35 +10,35 @@ data class UserFilter(
1010
val applicationName: String?,
1111
val roleName: String?,
1212
) {
13+
/**
14+
* Returns true if the given user role has a role that matches [orgId], [applicationName] and
15+
* [roleName].
16+
*/
1317
fun matches(userRole: UserRole): Boolean {
18+
/** Special case for no filter, to handle users with no roles. */
19+
if (orgId == null && applicationName == null && roleName == null) {
20+
return true
21+
}
22+
1423
/**
1524
* Super-admins match all orgId / applicationName filters, since they are implicitly part of all
1625
* orgs and applications. We only do this if no filter is provided for `roleName`.
1726
*/
18-
val matchAllOrganizationsAndApplications =
27+
val matchAllOrgsAndApps =
1928
userRole.isSuperAdmin() && (this.roleName == null || this.roleName == SUPER_ADMIN_ROLE_NAME)
2029

21-
if (
22-
orgId != null &&
23-
userRole.roles.none { it.orgId == this.orgId } &&
24-
!matchAllOrganizationsAndApplications
25-
) {
26-
return false
27-
}
30+
return userRole.roles.any { role ->
31+
val orgIdMatches = (orgId == null || role.orgId == orgId || matchAllOrgsAndApps)
2832

29-
if (
30-
applicationName != null &&
31-
userRole.roles.none { it.applicationName == this.applicationName } &&
32-
!matchAllOrganizationsAndApplications
33-
) {
34-
return false
35-
}
33+
val applicationMatches =
34+
(applicationName == null ||
35+
role.applicationName == applicationName ||
36+
matchAllOrgsAndApps)
3637

37-
if (roleName != null && userRole.roles.none { it.roleName == this.roleName }) {
38-
return false
39-
}
38+
val roleNameMatches = (roleName == null || role.roleName == roleName)
4039

41-
return true
40+
orgIdMatches && applicationMatches && roleNameMatches
41+
}
4242
}
4343

4444
fun getCognitoFilterString(): String? {

src/test/kotlin/no/liflig/userroles/administration/UserFilterTest.kt

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package no.liflig.userroles.administration
22

33
import io.kotest.matchers.booleans.shouldBeFalse
44
import io.kotest.matchers.booleans.shouldBeTrue
5+
import io.kotest.matchers.collections.shouldBeEmpty
56
import no.liflig.userroles.testutils.DEFAULT_TEST_USERNAME
67
import no.liflig.userroles.testutils.createRole
78
import no.liflig.userroles.testutils.createUserRole
@@ -16,7 +17,7 @@ class UserFilterTest {
1617
fun `filter matches if any of the user's roles is a match`() {
1718
val userRole =
1819
createUserRole(
19-
username = DEFAULT_TEST_USERNAME,
20+
DEFAULT_TEST_USERNAME,
2021
createRole(orgId = "org2", applicationName = "app1", roleName = "role1"),
2122
createRole(orgId = "org1", applicationName = "app1", roleName = "role1"),
2223
)
@@ -25,11 +26,34 @@ class UserFilterTest {
2526
filter.matches(userRole).shouldBeTrue()
2627
}
2728

29+
@Test
30+
fun `filter only matches if user has a role that fulfills all dimensions`() {
31+
val filter = createUserFilter(orgId = "org1", applicationName = "app1", roleName = "role1")
32+
33+
val userRole =
34+
createUserRole(
35+
DEFAULT_TEST_USERNAME,
36+
// Roles that match the filter in all dimensions except 1
37+
createRole(orgId = "org1", applicationName = "app1", roleName = "role2"),
38+
createRole(orgId = "org1", applicationName = "app2", roleName = "role1"),
39+
createRole(orgId = "org2", applicationName = "app1", roleName = "role1"),
40+
)
41+
filter.matches(userRole).shouldBeFalse()
42+
43+
val matchingUserRole =
44+
userRole.copy(
45+
roles =
46+
userRole.roles +
47+
createRole(orgId = "org1", applicationName = "app1", roleName = "role1")
48+
)
49+
filter.matches(matchingUserRole).shouldBeTrue()
50+
}
51+
2852
@Test
2953
fun `user with SUPER_ADMIN role matches all orgs and applications`() {
3054
val superAdmin =
3155
createUserRole(
32-
username = DEFAULT_TEST_USERNAME,
56+
DEFAULT_TEST_USERNAME,
3357
createRole(roleName = "SUPER_ADMIN", orgId = null, applicationName = null),
3458
)
3559

@@ -45,6 +69,20 @@ class UserFilterTest {
4569
val appAndRoleFilter = createUserFilter(applicationName = "app1", roleName = "MEMBER")
4670
appAndRoleFilter.matches(superAdmin).shouldBeFalse()
4771
}
72+
73+
@Test
74+
fun `empty filter always matches`() {
75+
val emptyFilter = createUserFilter()
76+
77+
val userWithoutRoles = createUserRole()
78+
userWithoutRoles.roles.shouldBeEmpty()
79+
80+
emptyFilter.matches(userWithoutRoles).shouldBeTrue()
81+
82+
val userWithRole = createUserRole(DEFAULT_TEST_USERNAME, createRole())
83+
84+
emptyFilter.matches(userWithRole).shouldBeTrue()
85+
}
4886
}
4987

5088
fun createUserFilter(

0 commit comments

Comments
 (0)