diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs b/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs index 360be6c8..7e4d7c52 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/Constants/KeycloakClientApiConstants.cs @@ -39,6 +39,13 @@ internal static class KeycloakClientApiConstants internal const string SendVerifyEmail = $"{GetRealm}/users/{{id}}/send-verify-email"; internal const string ExecuteActionsEmail = $"{GetRealm}/users/{{id}}/execute-actions-email"; + internal const string GetUserGroups = $"{GetRealm}/users/{{id}}/groups"; + + + + internal const string GetGroups = $"{GetRealm}/groups"; + + internal const string GetGroup = $"{GetRealm}/groups/{{id}}"; #endregion } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs index c0b9ee90..cfeeb70e 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakClient.cs @@ -7,6 +7,6 @@ namespace Keycloak.AuthServices.Sdk.Admin; /// Aggregates multiple clients. and /// public interface IKeycloakClient - : IKeycloakRealmClient, IKeycloakProtectedResourceClient, IKeycloakUserClient + : IKeycloakRealmClient, IKeycloakProtectedResourceClient, IKeycloakUserClient, IKeycloakGroupClient { } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs new file mode 100644 index 00000000..acd65722 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs @@ -0,0 +1,33 @@ +namespace Keycloak.AuthServices.Sdk.Admin; + +using System.Threading.Tasks; +using Constants; +using Models; +using Refit; +using Requests.Groups; + +/// +/// Group management +/// +[Headers("Accept: application/json")] +public interface IKeycloakGroupClient +{ + + /// + /// Get a stream of groups on the realm. + /// + /// Realm name. + /// Optional query parameters. + /// A stream of groups, filtered according to query parameters. + [Get(KeycloakClientApiConstants.GetGroups)] + Task> GetGroups(string realm, [Query] GetGroupRequestParameters? parameters = default); + + /// + /// Get representation of a group. + /// + /// Realm name. + /// group ID. + /// The group representation. + [Get(KeycloakClientApiConstants.GetGroup)] + Task GetGroup(string realm, [AliasAs("id")] string groupId); +} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index 61e36d9d..0d3e52a5 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -3,6 +3,7 @@ using Constants; using Models; using Refit; +using Requests.Groups; using Requests.Users; /// @@ -84,4 +85,14 @@ Task ExecuteActionsEmail(string realm, [AliasAs("id")] string userId, [Query] int? lifespan = default, [Query][AliasAs("redirect_uri")] string? redirectUri = default, [Body] List? actions = default); + + /// + /// Get a users's groups. + /// + /// Realm name (not ID). + /// User ID. + /// Optional query parameters. + /// A stream of users, filtered according to query parameters. + [Get(KeycloakClientApiConstants.GetUserGroups)] + Task> GetUserGroups(string realm, [AliasAs("id")] string userId, [Query] GetGroupRequestParameters? parameters = default); } diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs b/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs new file mode 100644 index 00000000..987de774 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Models/Group/Group.cs @@ -0,0 +1,18 @@ +#pragma warning disable CS1591, CS8618 +namespace Keycloak.AuthServices.Sdk.Admin.Models; + +using System.Collections.Generic; + +/// +/// Group representation. +/// +public class Group +{ + public string Id { get; init; } = default!; + public string? Name { get; init; } + public string? Path { get; init; } + public Dictionary? ClientRoles { get; init; } + public string[]? RealmRoles { get; init; } + public Group[]? SubGroups { get; init; } + public Dictionary? Attributes { get; init; } +} diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs new file mode 100644 index 00000000..17637e92 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupRequestParameters.cs @@ -0,0 +1,71 @@ +namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups; + +using Keycloak.AuthServices.Sdk.Admin.Models; +using Refit; + +/// +/// Optional request parameters for the endpoint. +/// It can be called in three different ways. +/// +/// +/// +/// Don’t specify any criteria. A stream of all groups within that realm will be returned (limited by pagination). +/// +/// +/// +/// +/// If is specified, other criteria such as +/// will be ignored even though you may set them. The string will be matched against +/// the , , +/// and the of a . +/// +/// +/// +/// +/// If is unspecified but any of , , +/// or are specified, then those criteria are matched against +/// their respective fields on a entity. Combined with a logical AND. +/// +/// +/// +/// +public class GetGroupRequestParameters +{ + /// + /// Defines whether brief representations are returned. Default is false. + /// + [AliasAs("briefRepresentation")] + public bool? BriefRepresentation { get; init; } + + /// + /// Defines whether the params , , + /// and must match exactly + /// + [AliasAs("exact")] + public bool? Exact { get; init; } + + /// + /// Pagination offset. + /// + [AliasAs("first")] + public int? First { get; init; } + + /// + /// Maximum results size. Default is 100. + /// + [AliasAs("max")] + public int? Max { get; init; } + + /// + /// A query to search for custom attributes, in the format "key1:value2 key2:value2". + /// + [AliasAs("q")] + public string? Query { get; init; } + + /// + /// Search for a string contained in , , + /// or . + /// + [AliasAs("search")] + public string? Search { get; init; } +}