@@ -20,6 +20,7 @@ public interface IUserManagementService
2020{
2121 Task < UserListResponse > GetUsersAsync ( int page = 1 , int pageSize = 20 , string ? search = null , string ? role = null , string ? status = null ) ;
2222 Task < UserModel ? > GetUserAsync ( Guid id ) ;
23+ Task < ( List < string > DirectPermissions , List < string > EffectivePermissions , bool UsesRoleDefaults ) > GetUserPermissionsAsync ( Guid id ) ;
2324 Task CreateUserAsync ( string username , string email , string password , string role ) ;
2425 Task CreateUserAsync ( UserFormModel model ) ;
2526 Task CreateMerchandiserAccountAsync ( CreateMerchandiserAccountFormModel model , CancellationToken cancellationToken = default ) ;
@@ -100,6 +101,25 @@ public async Task<UserListResponse> GetUsersAsync(int page = 1, int pageSize = 2
100101 }
101102 }
102103
104+ public async Task < ( List < string > DirectPermissions , List < string > EffectivePermissions , bool UsesRoleDefaults ) > GetUserPermissionsAsync ( Guid id )
105+ {
106+ var client = await CreateAuthenticatedClientAsync ( ) ;
107+ var response = await client . GetAsync ( $ "api/usermanagement/{ id } /permissions") ;
108+
109+ if ( ! response . IsSuccessStatusCode )
110+ {
111+ await ThrowApiExceptionAsync ( response , "Failed to load user permissions." ) ;
112+ }
113+
114+ using var document = JsonDocument . Parse ( await response . Content . ReadAsStringAsync ( ) ) ;
115+ var root = document . RootElement ;
116+
117+ return (
118+ ReadStringListProperty ( root , "permissions" ) ,
119+ ReadStringListProperty ( root , "effectivePermissions" ) ,
120+ ReadBooleanProperty ( root , "usesRoleDefaults" ) ) ;
121+ }
122+
103123 public async Task CreateUserAsync ( string username , string email , string password , string role )
104124 {
105125 var client = await CreateAuthenticatedClientAsync ( ) ;
@@ -471,6 +491,48 @@ private static async Task ThrowApiExceptionAsync(HttpResponseMessage response, s
471491 response . StatusCode ) ;
472492 }
473493
494+ private static List < string > ReadStringListProperty ( JsonElement root , string propertyName )
495+ {
496+ if ( ! TryGetPropertyIgnoreCase ( root , propertyName , out var propertyElement ) || propertyElement . ValueKind != JsonValueKind . Array )
497+ {
498+ return new List < string > ( ) ;
499+ }
500+
501+ return propertyElement . EnumerateArray ( )
502+ . Where ( item => item . ValueKind == JsonValueKind . String )
503+ . Select ( item => item . GetString ( ) )
504+ . Where ( value => ! string . IsNullOrWhiteSpace ( value ) )
505+ . Select ( value => value ! . Trim ( ) )
506+ . Distinct ( StringComparer . OrdinalIgnoreCase )
507+ . OrderBy ( value => value , StringComparer . OrdinalIgnoreCase )
508+ . ToList ( ) ;
509+ }
510+
511+ private static bool ReadBooleanProperty ( JsonElement root , string propertyName )
512+ {
513+ return TryGetPropertyIgnoreCase ( root , propertyName , out var propertyElement )
514+ && ( propertyElement . ValueKind == JsonValueKind . True || propertyElement . ValueKind == JsonValueKind . False )
515+ && propertyElement . GetBoolean ( ) ;
516+ }
517+
518+ private static bool TryGetPropertyIgnoreCase ( JsonElement root , string propertyName , out JsonElement propertyElement )
519+ {
520+ if ( root . ValueKind == JsonValueKind . Object )
521+ {
522+ foreach ( var property in root . EnumerateObject ( ) )
523+ {
524+ if ( string . Equals ( property . Name , propertyName , StringComparison . OrdinalIgnoreCase ) )
525+ {
526+ propertyElement = property . Value ;
527+ return true ;
528+ }
529+ }
530+ }
531+
532+ propertyElement = default ;
533+ return false ;
534+ }
535+
474536 private static string ExtractApiErrorMessage (
475537 string errorBody ,
476538 HttpStatusCode ? statusCode = null ,
0 commit comments