/// <summary> /// Get the keycloak user that matches the PIMS user. /// If it doesn't exist return 'null'. /// </summary> /// <param name="user"></param> /// <returns></returns> private async Task <KModel.UserModel> GetUserAsync(PModel.UserModel user) { try { // Make a request to keycloak to find a matching user. return(await _client.HandleRequestAsync <KModel.UserModel>(HttpMethod.Get, $"{_options.Auth.Keycloak.Admin.Authority}/users/{user.KeycloakUserId}")); } catch (HttpClientRequestException ex) { if (ex.StatusCode == HttpStatusCode.NotFound) { return(null); } throw; } }
/// <summary> /// Fetch all Users in PIMS and update keycloak so they have the same roles and claims. /// </summary> /// <param name="log"></param> /// <returns></returns> private async Task SyncUsersAsync(StringBuilder log) { // Fetch all users in keycloak so that they can be synced. // This is useful when the database has been refreshed. var kusers = await _client.HandleGetAsync <KModel.UserModel[]>(_client.AdminRoute($"users")); var page = 1; var quantity = 50; var users = new List <PModel.UserModel>(); var pageOfUsers = await _client.HandleRequestAsync <PModel.PageModel <PModel.UserModel> >(HttpMethod.Get, $"{_options.Api.Uri}/admin/users?page={page}&quantity={quantity}"); // TODO: Replace paging with specific requests for a user. users.AddRange(pageOfUsers.Items); // Keep asking for pages of users until we have them all. while (pageOfUsers.Items.Count() == quantity) { pageOfUsers = await _client.HandleRequestAsync <PModel.PageModel <PModel.UserModel> >(HttpMethod.Get, $"{_options.Api.Uri}/admin/users?page={++page}&quantity={quantity}"); users.AddRange(pageOfUsers.Items); } foreach (var user in users) { var kuser = await GetUserAsync(user); // Ignore users that only exist in PIMS. if (kuser == null) { continue; } // Sync user organizations. if (kuser.Attributes == null) { kuser.Attributes = new Dictionary <string, string[]>(); } kuser.Enabled = !user.IsDisabled; kuser.FirstName = user.FirstName; kuser.LastName = user.Surname; kuser.EmailVerified = false; kuser.Attributes["organizations"] = user.Organizations.Select(a => a.Id.ToString()).ToArray(); _logger.LogInformation($"Updating User in Keycloak '{user.BusinessIdentifier}'."); log.Append($"Keycloak - User updated '{user.BusinessIdentifier}'{Environment.NewLine}"); var userResponse = await _client.SendJsonAsync($"{_options.Auth.Keycloak.Admin.Authority}/users/{kuser.Id}", HttpMethod.Put, kuser); if (!userResponse.IsSuccessStatusCode) { throw new HttpClientRequestException(userResponse, $"Failed to update the user '{user.BusinessIdentifier}' in keycloak"); } // Sync user roles. foreach (var role in user.Roles) { var groupResponse = await _client.SendAsync($"{_options.Auth.Keycloak.Admin.Authority}/users/{kuser.Id}/groups/{role.KeycloakGroupId}", HttpMethod.Put); if (!groupResponse.IsSuccessStatusCode) { throw new HttpClientRequestException(groupResponse, $"Failed to add the group '{role.Name}' to the user '{user.BusinessIdentifier}' keycloak"); } } } // Add keycloak users to PIMS. // Only add users who don't exist. foreach (var kuser in kusers.Where(u => !users.Any(pu => pu.BusinessIdentifier == u.Username))) { try { if (String.IsNullOrWhiteSpace(kuser.Email) || String.IsNullOrWhiteSpace(kuser.FirstName) || String.IsNullOrWhiteSpace(kuser.LastName)) { _logger.LogInformation($"Unable to add user to PIMS '{kuser.Username}'."); continue; } _logger.LogInformation($"Adding User to PIMS '{kuser.Username}'."); var user = new PModel.UserModel(kuser); var uRoles = user.Roles as List <PModel.RoleModel>; // Check if the organizations listed in Keycloak exist in PIMS. If they don't report the issue in the summary. var removeOrganizations = new List <PModel.OrganizationModel>(); if (user.Organizations?.Any() == true) { var organizations = user.Organizations.ToArray(); foreach (var organization in organizations) { var aexists = await _client.HandleRequestAsync <PModel.OrganizationModel>(HttpMethod.Get, $"{_options.Api.Uri}/admin/organizations/{organization.Id}", r => { _logger.LogError($"Organization '{organization.Id}' does not exist in PIMS.", user); log.Append($"PIMS - Organization missing '{organization.Id}'{Environment.NewLine}"); removeOrganizations.Add(organization); return(true); }); } } // Remove any organizations. removeOrganizations.ForEach(a => ((List <PModel.OrganizationModel>)user.Organizations).Remove(a)); // Fetch the users groups from keycloak. var kgroups = await _client.HandleGetAsync <KModel.GroupModel[]>(_client.AdminRoute($"users/{kuser.Id}/groups")); // Fetch the group from PIMS. foreach (var kgroup in kgroups) { var role = await _client.HandleGetAsync <PModel.RoleModel>($"{_options.Api.Uri}/admin/roles/name/{kgroup.Name}", r => true); if (role != null) { uRoles.Add(role); } } user.Roles = uRoles; // Add the user to PIMS. user = await _client.HandleRequestAsync <PModel.UserModel, PModel.UserModel>(HttpMethod.Post, $"{_options.Api.Uri}/admin/users", user); log.Append($"Keycloak User added to PIMS '{user.BusinessIdentifier}'{Environment.NewLine}"); } catch (HttpClientRequestException ex) { _logger.LogError($"Failed to add keycloak user '{kuser.Email}' to PIMS.", ex); } } }