Ejemplo n.º 1
0
        /// <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;
            }
        }
Ejemplo n.º 2
0
        /// <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);
                }
            }
        }