-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Keycloak Refactor #3624
Merged
FuriousLlama
merged 3 commits into
bcgov:dev
from
FuriousLlama:tech_debt/keycloak-cleanup
Dec 1, 2023
Merged
Keycloak Refactor #3624
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Pims.Keycloak.Models; | ||
|
||
namespace Pims.Keycloak | ||
{ | ||
public interface IKeycloakRepository | ||
{ | ||
#region Users | ||
|
||
Task<UserModel> GetUserAsync(Guid id); | ||
|
||
Task<List<UserModel>> GetUsersAsync(Guid id); | ||
|
||
Task<HttpResponseMessage> AddRolesToUser(string username, IEnumerable<RoleModel> roles); | ||
|
||
Task<HttpResponseMessage> DeleteRoleFromUsers(string username, string roleName); | ||
|
||
Task<RoleModel[]> GetUserGroupsAsync(Guid id); | ||
|
||
Task ModifyUserRoleMappings(IEnumerable<UserRoleOperation> operations); | ||
|
||
Task<ResponseWrapper<RoleModel>> GetAllRoles(); | ||
|
||
Task<ResponseWrapper<RoleModel>> GetAllGroupRoles(string groupName); | ||
|
||
Task<ResponseWrapper<RoleModel>> GetUserRoles(string username); | ||
|
||
Task<HttpResponseMessage> AddKeycloakRole(RoleModel role); | ||
|
||
Task<HttpResponseMessage> AddKeycloakRolesToGroup(string groupName, IEnumerable<RoleModel> roles); | ||
|
||
Task<HttpResponseMessage> DeleteRole(string roleName); | ||
|
||
Task<HttpResponseMessage> DeleteRoleFromGroup(string groupName, string roleName); | ||
#endregion | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Options; | ||
using Pims.Core.Extensions; | ||
using Pims.Core.Http; | ||
using Pims.Keycloak.Extensions; | ||
using Pims.Keycloak.Models; | ||
|
||
namespace Pims.Keycloak | ||
{ | ||
/// <summary> | ||
/// KeycloakRepository class, provides a service for sending HTTP requests to the keycloak admin API. | ||
/// - https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_overview. | ||
/// </summary> | ||
public partial class KeycloakRepository : IKeycloakRepository | ||
{ | ||
#region Variables | ||
private readonly IOpenIdConnectRequestClient _client; | ||
#endregion | ||
|
||
#region Properties | ||
|
||
/// <summary> | ||
/// get - The configuration options for keycloak. | ||
/// </summary> | ||
public Configuration.KeycloakOptions Options { get; } | ||
#endregion | ||
|
||
#region Constructors | ||
|
||
/// <summary> | ||
/// Creates a new instance of a KeycloakAdmin class, initializes it with the specified arguments. | ||
/// </summary> | ||
/// <param name="client"></param> | ||
/// <param name="options"></param> | ||
public KeycloakRepository(IOpenIdConnectRequestClient client, IOptions<Configuration.KeycloakOptions> options) | ||
{ | ||
this.Options = options.Value; | ||
this.Options.Validate(); | ||
this.Options.ServiceAccount.Validate(); | ||
_client = client; | ||
_client.AuthClientOptions.Audience = this.Options.ServiceAccount.Audience ?? this.Options.Audience; | ||
_client.AuthClientOptions.Authority = this.Options.ServiceAccount.Authority ?? this.Options.Authority; | ||
_client.AuthClientOptions.Client = this.Options.ServiceAccount.Client; | ||
_client.AuthClientOptions.Secret = this.Options.ServiceAccount.Secret; | ||
} | ||
|
||
/// <summary> | ||
/// Get the user for the specified 'id'. | ||
/// </summary> | ||
/// <param name="id"></param> | ||
/// <returns></returns> | ||
public async Task<UserModel> GetUserAsync(Guid id) | ||
{ | ||
var users = await GetUsersAsync(id); | ||
return users.FirstOrDefault(); | ||
} | ||
|
||
public async Task<List<UserModel>> GetUsersAsync(Guid id) | ||
{ | ||
var response = await _client.GetAsync($"{this.Options.ServiceAccount.Api}/{this.Options.ServiceAccount.Environment}/idir/users?guid={id.ToString().Replace("-", string.Empty)}"); | ||
var result = await response.HandleResponseAsync<ResponseWrapper<UserModel>>(); | ||
|
||
return result.Data.ToList(); | ||
} | ||
|
||
public async Task<HttpResponseMessage> AddRolesToUser(string username, IEnumerable<RoleModel> roles) | ||
{ | ||
return await _client.PostJsonAsync($"{GetIntegrationUrl()}/users/{Uri.EscapeDataString(username)}/roles", roles); | ||
} | ||
|
||
public async Task<HttpResponseMessage> DeleteRoleFromUsers(string username, string roleName) | ||
{ | ||
return await _client.DeleteAsync($"{GetIntegrationUrl()}/users/{Uri.EscapeDataString(username)}/roles/{Uri.EscapeDataString(roleName)}"); | ||
} | ||
|
||
/// <summary> | ||
/// Get an array of the groups the user for the specified 'id' is a member of. | ||
/// </summary> | ||
/// <param name="id"></param> | ||
/// <returns></returns> | ||
public async Task<RoleModel[]> GetUserGroupsAsync(Guid id) | ||
{ | ||
var response = await _client.GetAsync($"{GetIntegrationUrl()}/user-role-mappings/?username={id.ToString().Replace("-", string.Empty)}@idir"); | ||
|
||
var userRoleModel = await response.HandleResponseAsync<UserRoleModel>(); | ||
|
||
return userRoleModel.Roles.Where(r => r.Composite.HasValue && r.Composite.Value).ToArray(); | ||
} | ||
|
||
/// <summary> | ||
/// Get the total number of groups the user for the specified 'id' is a member of. | ||
/// </summary> | ||
/// <param name="id"></param> | ||
/// <returns></returns> | ||
public async Task<int> GetUserGroupCountAsync(Guid id) | ||
{ | ||
var response = await GetUserGroupsAsync(id); | ||
return response.Length; | ||
} | ||
|
||
/// <summary> | ||
/// execute all passed operations. | ||
/// </summary> | ||
/// <param name="operations"></param> | ||
/// <returns></returns> | ||
public async Task ModifyUserRoleMappings(IEnumerable<UserRoleOperation> operations) | ||
{ | ||
foreach (UserRoleOperation operation in operations) | ||
{ | ||
var json = operation.Serialize(); | ||
using var content = new StringContent(json, Encoding.UTF8, "application/json"); | ||
var response = await _client.PostAsync($"{GetIntegrationUrl()}/user-role-mappings", content); | ||
await response.HandleResponseAsync<UserRoleModel>(); | ||
} | ||
} | ||
|
||
public async Task<ResponseWrapper<RoleModel>> GetAllRoles() | ||
{ | ||
var response = await _client.GetAsync($"{GetIntegrationUrl()}/roles"); | ||
|
||
var allKeycloakRoles = await response.HandleResponseAsync<ResponseWrapper<RoleModel>>(); | ||
return allKeycloakRoles; | ||
} | ||
|
||
public async Task<ResponseWrapper<RoleModel>> GetAllGroupRoles(string groupName) | ||
{ | ||
var response = await _client.GetAsync($"{GetIntegrationUrl()}/roles/{Uri.EscapeDataString(groupName)}/composite-roles"); | ||
|
||
var groupedRoles = await response.HandleResponseAsync<ResponseWrapper<RoleModel>>(); | ||
return groupedRoles; | ||
} | ||
|
||
public async Task<ResponseWrapper<RoleModel>> GetUserRoles(string username) | ||
{ | ||
var response = await _client.GetAsync($"{GetIntegrationUrl()}/users/{Uri.EscapeDataString(username)}/roles"); | ||
|
||
var groupedRoles = await response.HandleResponseAsync<ResponseWrapper<RoleModel>>(); | ||
return groupedRoles; | ||
} | ||
|
||
public async Task<HttpResponseMessage> AddKeycloakRole(RoleModel role) | ||
{ | ||
var response = await _client.PostJsonAsync($"{GetIntegrationUrl()}/roles", role); | ||
return response; | ||
} | ||
|
||
public async Task<HttpResponseMessage> AddKeycloakRolesToGroup(string groupName, IEnumerable<RoleModel> roles) | ||
{ | ||
var response = await _client.PostJsonAsync($"{GetIntegrationUrl()}/roles/{Uri.EscapeDataString(groupName)}/composite-roles", roles); | ||
return response; | ||
} | ||
|
||
public async Task<HttpResponseMessage> DeleteRole(string roleName) | ||
{ | ||
var response = await _client.DeleteAsync($"{GetIntegrationUrl()}/roles/{Uri.EscapeDataString(roleName)}"); | ||
return response; | ||
} | ||
|
||
public async Task<HttpResponseMessage> DeleteRoleFromGroup(string groupName, string roleName) | ||
{ | ||
var response = await _client.DeleteAsync($"{GetIntegrationUrl()}/roles/{Uri.EscapeDataString(groupName)}/composite-roles/{Uri.EscapeDataString(roleName)}"); | ||
return response; | ||
} | ||
|
||
private string GetIntegrationUrl() | ||
{ | ||
return $"{this.Options.ServiceAccount.Api}/integrations/{this.Options.ServiceAccount.Integration}/{this.Options.ServiceAccount.Environment}"; | ||
} | ||
#endregion | ||
|
||
#region Methods | ||
#endregion | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this incorrect in the current implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was inconsistent between the two models