Skip to content

Commit efb965f

Browse files
authored
Merge pull request #25 from UnityFoundation-io/add-endpoint-to-retrive-list-of-user-permissions
Add endpoint to retrive list of user permissions
2 parents bc2423c + a4ba784 commit efb965f

File tree

3 files changed

+103
-10
lines changed

3 files changed

+103
-10
lines changed

UnityAuth/src/main/java/io/unityfoundation/auth/AuthController.java

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import io.micronaut.core.annotation.Introspected;
44
import io.micronaut.core.annotation.Nullable;
55
import io.micronaut.http.HttpResponse;
6+
import io.micronaut.http.HttpStatus;
67
import io.micronaut.http.annotation.Body;
78
import io.micronaut.http.annotation.Controller;
89
import io.micronaut.http.annotation.Post;
10+
import io.micronaut.http.exceptions.HttpStatusException;
911
import io.micronaut.security.annotation.Secured;
1012
import io.micronaut.security.authentication.Authentication;
1113
import io.micronaut.security.rules.SecurityRule;
@@ -15,11 +17,14 @@
1517
import io.unityfoundation.auth.entities.Service.ServiceStatus;
1618
import io.unityfoundation.auth.entities.ServiceRepo;
1719
import io.unityfoundation.auth.entities.Tenant;
20+
import io.unityfoundation.auth.entities.Tenant.TenantStatus;
1821
import io.unityfoundation.auth.entities.TenantRepo;
1922
import io.unityfoundation.auth.entities.User;
2023
import io.unityfoundation.auth.entities.UserRepo;
24+
import jakarta.validation.constraints.NotNull;
2125
import java.util.List;
2226
import java.util.Optional;
27+
import java.util.function.BiPredicate;
2328

2429
@Secured(SecurityRule.IS_AUTHENTICATED)
2530
@Controller("/api")
@@ -35,6 +40,43 @@ public AuthController(UserRepo userRepo, ServiceRepo serviceRepo, TenantRepo ten
3540
this.tenantRepo = tenantRepo;
3641
}
3742

43+
@Post("/principal/permissions")
44+
public UserPermissionsResponse permissions(@Body UserPermissionsRequest requestDTO,
45+
Authentication authentication) {
46+
Optional<Tenant> maybeTenant = tenantRepo.findById(requestDTO.tenantId());
47+
if (maybeTenant.isEmpty()){
48+
return new UserPermissionsResponse.Failure("No tenant found.");
49+
}
50+
Tenant tenant = maybeTenant.get();
51+
52+
if (!tenant.getStatus().equals(TenantStatus.ENABLED)){
53+
return new UserPermissionsResponse.Failure("The tenant is not enabled.");
54+
}
55+
56+
User user = userRepo.findByEmail(authentication.getName()).orElse(null);
57+
if (checkUserStatus(user)) {
58+
return new UserPermissionsResponse.Failure("The users account has been disabled.");
59+
}
60+
61+
Service service = serviceRepo.findById(requestDTO.serviceId())
62+
.orElseThrow(() -> new HttpStatusException(HttpStatus.NOT_FOUND, "Service not found."));
63+
64+
if (service.getStatus() == ServiceStatus.DISABLED) {
65+
throw new HttpStatusException(HttpStatus.FORBIDDEN, "The service is disabled.");
66+
} else if (service.getStatus() == ServiceStatus.DOWN_FOR_MAINTENANCE) {
67+
68+
throw new HttpStatusException(HttpStatus.SERVICE_UNAVAILABLE,
69+
"The service is down for maintenance.");
70+
}
71+
72+
if (!userRepo.isServiceAvailable(user.getId(), service.getId())) {
73+
return new UserPermissionsResponse.Failure(
74+
"The Tenant and/or Service is not available for this user");
75+
}
76+
77+
return new UserPermissionsResponse.Success(getPermissionsFor(user, tenant));
78+
}
79+
3880
@Post("/hasPermission")
3981
public HttpResponse<HasPermissionResponse> hasPermission(@Body HasPermissionRequest requestDTO,
4082
Authentication authentication) {
@@ -86,20 +128,26 @@ private String checkServiceStatus(Optional<Service> service) {
86128
return null;
87129
}
88130

131+
private final BiPredicate<TenantPermission, Tenant> isTenantOrSystemOrSubtenantScopeAndBelongsToTenant = (tp, t) ->
132+
PermissionScope.SYSTEM.equals(tp.permissionScope()) || (
133+
(PermissionScope.TENANT.equals(tp.permissionScope())
134+
|| PermissionScope.SUBTENANT.equals(tp.permissionScope()))
135+
&& tp.tenantId == t.getId());
136+
137+
89138
private List<String> checkUserPermission(User user, Tenant tenant, List<String> permissions) {
90-
List<TenantPermission> userPermissions = userRepo.getTenantPermissionsFor(user.getId()).stream()
91-
.filter(tenantPermission ->
92-
PermissionScope.SYSTEM.equals(tenantPermission.permissionScope()) ||
93-
((PermissionScope.TENANT.equals(tenantPermission.permissionScope()) || PermissionScope.SUBTENANT.equals(tenantPermission.permissionScope()))
94-
&& tenantPermission.tenantId == tenant.getId()))
95-
.toList();
139+
List<String> commonPermissions = getPermissionsFor(user, tenant).stream()
140+
.filter(permissions::contains).toList();
96141

97-
List<String> commonPermissions = userPermissions.stream()
142+
return commonPermissions;
143+
}
144+
145+
private List<String> getPermissionsFor(User user, Tenant tenant) {
146+
return userRepo.getTenantPermissionsFor(user.getId()).stream()
147+
.filter(tenantPermission ->
148+
isTenantOrSystemOrSubtenantScopeAndBelongsToTenant.test(tenantPermission, tenant))
98149
.map(TenantPermission::permissionName)
99-
.filter(permissions::contains)
100150
.toList();
101-
102-
return commonPermissions;
103151
}
104152

105153
private HttpResponse<HasPermissionResponse> createHasPermissionResponse(boolean hasPermission,
@@ -129,4 +177,18 @@ public record TenantPermission(
129177

130178
}
131179

180+
181+
public sealed interface UserPermissionsResponse {
182+
@Serdeable
183+
record Success(List<String> permissions) implements UserPermissionsResponse {}
184+
@Serdeable
185+
record Failure(String errorMessage) implements UserPermissionsResponse {}
186+
}
187+
188+
@Serdeable
189+
public record UserPermissionsRequest(@NotNull Long tenantId,
190+
@NotNull Long serviceId) {
191+
192+
}
193+
132194
}

UnityAuth/src/test/java/io/unityfoundation/UnityIamTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import io.micronaut.security.token.render.BearerAccessRefreshToken;
1414
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
1515
import io.unityfoundation.auth.AuthController.HasPermissionResponse;
16+
import io.unityfoundation.auth.AuthController.UserPermissionsRequest;
17+
import io.unityfoundation.auth.AuthController.UserPermissionsResponse;
1618
import io.unityfoundation.auth.HasPermissionRequest;
1719
import jakarta.inject.Inject;
1820

@@ -137,6 +139,31 @@ void testHasNoSubtenantPermission() {
137139
assertNull(response.getBody().get().permissions());
138140
}
139141

142+
143+
@Test
144+
void testGetUserPermissionsDisabled() {
145+
String accessToken = login("[email protected]");
146+
HttpRequest<?> hasPermissionRequest = HttpRequest.POST("/api/principal/permissions",
147+
new UserPermissionsRequest(1L, 1L))
148+
.bearerAuth(accessToken);
149+
HttpResponse<UserPermissionsResponse.Failure> response = client.toBlocking()
150+
.exchange(hasPermissionRequest, UserPermissionsResponse.Failure.class);
151+
UserPermissionsResponse.Failure failure = response.getBody().orElseThrow();
152+
assertEquals("The user's account has been disabled.", failure.errorMessage());
153+
}
154+
155+
@Test
156+
void testGetUserPermissionsHappyPath() {
157+
String accessToken = login("[email protected]");
158+
HttpRequest<?> hasPermissionRequest = HttpRequest.POST("/api/principal/permissions",
159+
new UserPermissionsRequest(1L, 1L))
160+
.bearerAuth(accessToken);
161+
HttpResponse<UserPermissionsResponse.Success> response = client.toBlocking()
162+
.exchange(hasPermissionRequest, UserPermissionsResponse.Success.class);
163+
164+
assertEquals(response.getBody().get().permissions(), List.of("AUTH_SERVICE_EDIT-SYSTEM"));
165+
}
166+
140167
private String login(String username) {
141168
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, "test");
142169
HttpRequest<?> request = HttpRequest.POST("/api/login", creds);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
micronaut:
2+
http:
3+
client:
4+
read-timeout: 1m

0 commit comments

Comments
 (0)