11package io .unityfoundation .auth ;
22
3- import io .micronaut .core .annotation .Introspected ;
43import io .micronaut .core .annotation .Nullable ;
54import io .micronaut .http .HttpResponse ;
65import 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 .*;
107import io .micronaut .http .exceptions .HttpStatusException ;
118import io .micronaut .security .annotation .Secured ;
129import io .micronaut .security .authentication .Authentication ;
1310import io .micronaut .security .rules .SecurityRule ;
1411import 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 .*;
1713import 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 ;
2414import jakarta .validation .constraints .NotNull ;
2515import java .util .List ;
2616import java .util .Optional ;
27- import java .util .function .BiPredicate ;
2817
2918@ Secured (SecurityRule .IS_AUTHENTICATED )
3019@ Controller ("/api" )
@@ -33,11 +22,15 @@ public class AuthController {
3322 private final UserRepo userRepo ;
3423 private final ServiceRepo serviceRepo ;
3524 private final TenantRepo tenantRepo ;
25+ private final RoleRepo roleRepo ;
26+ private final PermissionsService permissionsService ;
3627
37- public AuthController (UserRepo userRepo , ServiceRepo serviceRepo , TenantRepo tenantRepo ) {
28+ public AuthController (UserRepo userRepo , ServiceRepo serviceRepo , TenantRepo tenantRepo , RoleRepo roleRepo , PermissionsService permissionsService ) {
3829 this .userRepo = userRepo ;
3930 this .serviceRepo = serviceRepo ;
4031 this .tenantRepo = tenantRepo ;
32+ this .roleRepo = roleRepo ;
33+ this .permissionsService = permissionsService ;
4134 }
4235
4336 @ Post ("/principal/permissions" )
@@ -49,13 +42,13 @@ public UserPermissionsResponse permissions(@Body UserPermissionsRequest requestD
4942 }
5043 Tenant tenant = maybeTenant .get ();
5144
52- if (!tenant .getStatus ().equals (TenantStatus .ENABLED )){
45+ if (!tenant .getStatus ().equals (Tenant . TenantStatus .ENABLED )){
5346 return new UserPermissionsResponse .Failure ("The tenant is not enabled." );
5447 }
5548
5649 User user = userRepo .findByEmail (authentication .getName ()).orElse (null );
5750 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." );
5952 }
6053
6154 Service service = serviceRepo .findById (requestDTO .serviceId ())
@@ -74,7 +67,7 @@ public UserPermissionsResponse permissions(@Body UserPermissionsRequest requestD
7467 "The Tenant and/or Service is not available for this user" );
7568 }
7669
77- return new UserPermissionsResponse .Success (getPermissionsFor (user , tenant ));
70+ return new UserPermissionsResponse .Success (permissionsService . getPermissionsFor (user , tenant ));
7871 }
7972
8073 @ Post ("/hasPermission" )
@@ -102,14 +95,85 @@ public HttpResponse<HasPermissionResponse> hasPermission(@Body HasPermissionRequ
10295 return createHasPermissionResponse (false , user .getEmail (), "The requested service is not enabled for the requested tenant!" , List .of ());
10396 }
10497
105- List <String > commonPermissions = checkUserPermission (user , tenantOptional .get (), requestDTO .permissions ());
98+ List <String > commonPermissions = permissionsService . checkUserPermission (user , tenantOptional .get (), requestDTO .permissions ());
10699 if (commonPermissions .isEmpty ()) {
107100 return createHasPermissionResponse (false , user .getEmail (), "The user does not have permission!" , commonPermissions );
108101 }
109102
110103 return createHasPermissionResponse (true , user .getEmail (), null , commonPermissions );
111104 }
112105
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+
113177 private boolean checkUserStatus (User user ) {
114178 return user == null || user .getStatus () != User .UserStatus .ENABLED ;
115179 }
@@ -128,55 +192,33 @@ private String checkServiceStatus(Optional<Service> service) {
128192 return null ;
129193 }
130194
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-
153195 private HttpResponse <HasPermissionResponse > createHasPermissionResponse (boolean hasPermission ,
154196 String userEmail ,
155197 String message ,
156198 List <String > permissions ) {
157199 return HttpResponse .ok (new HasPermissionResponse (hasPermission , userEmail , message , permissions ));
158200 }
159201
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+
160215 @ Serdeable
161216 public record HasPermissionResponse (
162217 boolean hasPermission ,
163218 @ Nullable String userEmail ,
164219 @ Nullable String errorMessage ,
165220 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+ ) {}
180222
181223 public sealed interface UserPermissionsResponse {
182224 @ Serdeable
@@ -187,8 +229,6 @@ record Failure(String errorMessage) implements UserPermissionsResponse {}
187229
188230 @ Serdeable
189231 public record UserPermissionsRequest (@ NotNull Long tenantId ,
190- @ NotNull Long serviceId ) {
191-
192- }
232+ @ NotNull Long serviceId ) {}
193233
194234}
0 commit comments