1
1
package io .unityfoundation .auth ;
2
2
3
- import io .micronaut .core .annotation .Introspected ;
4
3
import io .micronaut .core .annotation .Nullable ;
5
4
import io .micronaut .http .HttpResponse ;
6
5
import io .micronaut .http .HttpStatus ;
7
- import io .micronaut .http .annotation .Body ;
8
- import io .micronaut .http .annotation .Controller ;
9
- import io .micronaut .http .annotation .Post ;
6
+ import io .micronaut .http .annotation .*;
10
7
import io .micronaut .http .exceptions .HttpStatusException ;
11
8
import io .micronaut .security .annotation .Secured ;
12
9
import io .micronaut .security .authentication .Authentication ;
13
10
import io .micronaut .security .rules .SecurityRule ;
14
11
import io .micronaut .serde .annotation .Serdeable ;
15
- import io .unityfoundation .auth .entities .Permission .PermissionScope ;
16
- import io .unityfoundation .auth .entities .Service ;
12
+ import io .unityfoundation .auth .entities .*;
17
13
import io .unityfoundation .auth .entities .Service .ServiceStatus ;
18
- import io .unityfoundation .auth .entities .ServiceRepo ;
19
- import io .unityfoundation .auth .entities .Tenant ;
20
- import io .unityfoundation .auth .entities .Tenant .TenantStatus ;
21
- import io .unityfoundation .auth .entities .TenantRepo ;
22
- import io .unityfoundation .auth .entities .User ;
23
- import io .unityfoundation .auth .entities .UserRepo ;
24
14
import jakarta .validation .constraints .NotNull ;
25
15
import java .util .List ;
26
16
import java .util .Optional ;
27
- import java .util .function .BiPredicate ;
28
17
29
18
@ Secured (SecurityRule .IS_AUTHENTICATED )
30
19
@ Controller ("/api" )
@@ -33,11 +22,15 @@ public class AuthController {
33
22
private final UserRepo userRepo ;
34
23
private final ServiceRepo serviceRepo ;
35
24
private final TenantRepo tenantRepo ;
25
+ private final RoleRepo roleRepo ;
26
+ private final PermissionsService permissionsService ;
36
27
37
- public AuthController (UserRepo userRepo , ServiceRepo serviceRepo , TenantRepo tenantRepo ) {
28
+ public AuthController (UserRepo userRepo , ServiceRepo serviceRepo , TenantRepo tenantRepo , RoleRepo roleRepo , PermissionsService permissionsService ) {
38
29
this .userRepo = userRepo ;
39
30
this .serviceRepo = serviceRepo ;
40
31
this .tenantRepo = tenantRepo ;
32
+ this .roleRepo = roleRepo ;
33
+ this .permissionsService = permissionsService ;
41
34
}
42
35
43
36
@ Post ("/principal/permissions" )
@@ -49,13 +42,13 @@ public UserPermissionsResponse permissions(@Body UserPermissionsRequest requestD
49
42
}
50
43
Tenant tenant = maybeTenant .get ();
51
44
52
- if (!tenant .getStatus ().equals (TenantStatus .ENABLED )){
45
+ if (!tenant .getStatus ().equals (Tenant . TenantStatus .ENABLED )){
53
46
return new UserPermissionsResponse .Failure ("The tenant is not enabled." );
54
47
}
55
48
56
49
User user = userRepo .findByEmail (authentication .getName ()).orElse (null );
57
50
if (checkUserStatus (user )) {
58
- return new UserPermissionsResponse .Failure ("The users account has been disabled." );
51
+ return new UserPermissionsResponse .Failure ("The user's account has been disabled." );
59
52
}
60
53
61
54
Service service = serviceRepo .findById (requestDTO .serviceId ())
@@ -74,7 +67,7 @@ public UserPermissionsResponse permissions(@Body UserPermissionsRequest requestD
74
67
"The Tenant and/or Service is not available for this user" );
75
68
}
76
69
77
- return new UserPermissionsResponse .Success (getPermissionsFor (user , tenant ));
70
+ return new UserPermissionsResponse .Success (permissionsService . getPermissionsFor (user , tenant ));
78
71
}
79
72
80
73
@ Post ("/hasPermission" )
@@ -102,14 +95,85 @@ public HttpResponse<HasPermissionResponse> hasPermission(@Body HasPermissionRequ
102
95
return createHasPermissionResponse (false , user .getEmail (), "The requested service is not enabled for the requested tenant!" , List .of ());
103
96
}
104
97
105
- List <String > commonPermissions = checkUserPermission (user , tenantOptional .get (), requestDTO .permissions ());
98
+ List <String > commonPermissions = permissionsService . checkUserPermission (user , tenantOptional .get (), requestDTO .permissions ());
106
99
if (commonPermissions .isEmpty ()) {
107
100
return createHasPermissionResponse (false , user .getEmail (), "The user does not have permission!" , commonPermissions );
108
101
}
109
102
110
103
return createHasPermissionResponse (true , user .getEmail (), null , commonPermissions );
111
104
}
112
105
106
+ @ Get ("/roles" )
107
+ public HttpResponse <List <RoleDTO >> getRoles (Authentication authentication ) {
108
+
109
+ User user = userRepo .findByEmail (authentication .getName ()).orElse (null );
110
+ if (checkUserStatus (user )) {
111
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user is disabled." );
112
+ }
113
+
114
+ List <String > commonPermissions = permissionsService .checkUserPermissionsAcrossAllTenants (
115
+ user , List .of ("AUTH_SERVICE_VIEW-SYSTEM" , "AUTH_SERVICE_VIEW-TENANT" ));
116
+ if (commonPermissions .isEmpty ()) {
117
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user does not have permission!" );
118
+ }
119
+
120
+ return HttpResponse .ok (roleRepo .findAll ().stream ()
121
+ .map (role -> new RoleDTO (role .getId (), role .getName (), role .getDescription ()))
122
+ .toList ());
123
+ }
124
+
125
+ @ Get ("/tenants" )
126
+ public HttpResponse <List <TenantDTO >> getTenants (Authentication authentication ) {
127
+
128
+ String authenticatedUserEmail = authentication .getName ();
129
+ User user = userRepo .findByEmail (authenticatedUserEmail ).orElse (null );
130
+ if (checkUserStatus (user )) {
131
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user is disabled." );
132
+ }
133
+
134
+ List <String > commonPermissions = permissionsService .checkUserPermissionsAcrossAllTenants (
135
+ user , List .of ("AUTH_SERVICE_VIEW-SYSTEM" , "AUTH_SERVICE_VIEW-TENANT" ));
136
+ if (commonPermissions .isEmpty ()) {
137
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user does not have permission!" );
138
+ }
139
+
140
+ List <Tenant > tenants = userRepo .existsByEmailAndRoleEqualsUnityAdmin (authenticatedUserEmail ) ?
141
+ tenantRepo .findAll () : tenantRepo .findAllByUserEmail (authenticatedUserEmail );
142
+
143
+ return HttpResponse .ok (tenants .stream ()
144
+ .map (tenant -> new TenantDTO (tenant .getId (), tenant .getName ()))
145
+ .toList ());
146
+ }
147
+
148
+ @ Get ("/tenants/{id}/users" )
149
+ public HttpResponse <List <UserResponse >> getTenantUsers (@ PathVariable Long id , Authentication authentication ) {
150
+
151
+ // reject if the declared tenant does not exist
152
+ Optional <Tenant > tenantOptional = tenantRepo .findById (id );
153
+ if (tenantOptional .isEmpty ()) {
154
+ throw new HttpStatusException (HttpStatus .NOT_FOUND , "Tenant not found." );
155
+ }
156
+
157
+ User user = userRepo .findByEmail (authentication .getName ()).orElse (null );
158
+ if (checkUserStatus (user )) {
159
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user is disabled." );
160
+ }
161
+
162
+ List <String > commonPermissions = permissionsService .checkUserPermission (user , tenantOptional .get (),
163
+ List .of ("AUTH_SERVICE_VIEW-SYSTEM" , "AUTH_SERVICE_VIEW-TENANT" ));
164
+ if (commonPermissions .isEmpty ()) {
165
+ throw new HttpStatusException (HttpStatus .FORBIDDEN , "The user does not have permission!" );
166
+ }
167
+
168
+ // todo: it would be nice to capture the roles and have them automatically mapped to UserResponse.roles
169
+ List <UserResponse > tenantUsers = userRepo .findAllByTenantId (id ).stream ().map (tenantUser ->
170
+ new UserResponse (tenantUser .getId (), tenantUser .getEmail (), tenantUser .getFirstName (), tenantUser .getLastName (),
171
+ userRepo .getUserRolesByUserId (tenantUser .getId ())
172
+ )).toList ();
173
+
174
+ return HttpResponse .ok (tenantUsers );
175
+ }
176
+
113
177
private boolean checkUserStatus (User user ) {
114
178
return user == null || user .getStatus () != User .UserStatus .ENABLED ;
115
179
}
@@ -128,55 +192,33 @@ private String checkServiceStatus(Optional<Service> service) {
128
192
return null ;
129
193
}
130
194
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
-
138
- private List <String > checkUserPermission (User user , Tenant tenant , List <String > permissions ) {
139
- List <String > commonPermissions = getPermissionsFor (user , tenant ).stream ()
140
- .filter (permissions ::contains ).toList ();
141
-
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 ))
149
- .map (TenantPermission ::permissionName )
150
- .toList ();
151
- }
152
-
153
195
private HttpResponse <HasPermissionResponse > createHasPermissionResponse (boolean hasPermission ,
154
196
String userEmail ,
155
197
String message ,
156
198
List <String > permissions ) {
157
199
return HttpResponse .ok (new HasPermissionResponse (hasPermission , userEmail , message , permissions ));
158
200
}
159
201
202
+ @ Serdeable
203
+ public record TenantDTO (
204
+ Long id ,
205
+ String name
206
+ ) {}
207
+
208
+ @ Serdeable
209
+ public record RoleDTO (
210
+ Long id ,
211
+ String name ,
212
+ String description
213
+ ) {}
214
+
160
215
@ Serdeable
161
216
public record HasPermissionResponse (
162
217
boolean hasPermission ,
163
218
@ Nullable String userEmail ,
164
219
@ Nullable String errorMessage ,
165
220
List <String > permissions
166
- ) {
167
-
168
- }
169
-
170
- @ Introspected
171
- public record TenantPermission (
172
- long tenantId ,
173
- String permissionName ,
174
- PermissionScope permissionScope
175
-
176
- ) {
177
-
178
- }
179
-
221
+ ) {}
180
222
181
223
public sealed interface UserPermissionsResponse {
182
224
@ Serdeable
@@ -187,8 +229,6 @@ record Failure(String errorMessage) implements UserPermissionsResponse {}
187
229
188
230
@ Serdeable
189
231
public record UserPermissionsRequest (@ NotNull Long tenantId ,
190
- @ NotNull Long serviceId ) {
191
-
192
- }
232
+ @ NotNull Long serviceId ) {}
193
233
194
234
}
0 commit comments