diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index a04be4c3..fa493c36 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -333,12 +333,29 @@ else
- +
-
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+
@@ -457,6 +474,8 @@ else private string _nameclaimtype; private string _emailclaimtype; private string _roleclaimtype; + private string _roleclaimmappings; + private string _synchronizeroles; private string _profileclaimtypes; private string _domainfilter; private string _createusers; @@ -521,6 +540,8 @@ else _nameclaimtype = SettingService.GetSetting(settings, "ExternalLogin:NameClaimType", "name"); _emailclaimtype = SettingService.GetSetting(settings, "ExternalLogin:EmailClaimType", "email"); _roleclaimtype = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimType", ""); + _roleclaimmappings = SettingService.GetSetting(settings, "ExternalLogin:RoleClaimMappings", ""); + _synchronizeroles = SettingService.GetSetting(settings, "ExternalLogin:SynchronizeRoles", "false"); _profileclaimtypes = SettingService.GetSetting(settings, "ExternalLogin:ProfileClaimTypes", ""); _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); @@ -614,7 +635,9 @@ else settings = SettingService.SetSetting(settings, "ExternalLogin:NameClaimType", _nameclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:EmailClaimType", _emailclaimtype, true); settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimType", _roleclaimtype, true); - settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:RoleClaimMappings", _roleclaimmappings, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:SynchronizeRoles", _synchronizeroles, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:ProfileClaimTypes", _profileclaimtypes, true); settings = SettingService.SetSetting(settings, "ExternalLogin:DomainFilter", _domainfilter, true); settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); settings = SettingService.SetSetting(settings, "ExternalLogin:VerifyUsers", _verifyusers, true); diff --git a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx index 6baae9c1..46c19999 100644 --- a/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx +++ b/Oqtane.Client/Resources/Modules/Admin/Users/Index.resx @@ -385,10 +385,22 @@ Parameters: - Optionally provide the type name of the role claim provided by the identity provider. These roles will be used in addition to any internal user roles assigned within the site. + Optionally provide the type name of the roles claim provided by the identity provider (the standard default is 'roles'). If role names from the identity provider do not exactly match your site role names, please use the Role Claim Mappings. - Role Claim: + Roles Claim: + + + Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles. For example if the identity provider includes an 'Admin' role name and you want it to map to the 'Administrators' site role you should specify 'Admin:Administrators'. + + + Role Claim Mappings: + + + This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider for a user + + + Synchronize Roles? Optionally provide a comma delimited list of user profile claim type names provided by the identity provider, as well as mappings to your user profile definition. For example if the identity provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'. diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index 604270c1..28a69745 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using System.Net; using System.Text.Json.Nodes; using System.Globalization; +using System.Net.WebSockets; namespace Oqtane.Extensions { @@ -529,7 +530,8 @@ namespace Oqtane.Extensions { // create claims identity var _userRoles = httpContext.RequestServices.GetRequiredService(); - identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); + var userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList(); + identity = UserSecurity.CreateClaimsIdentity(alias, user, userRoles); identity.Label = ExternalLoginStatus.Success; // update user @@ -540,13 +542,49 @@ namespace Oqtane.Extensions // external roles if (!string.IsNullOrEmpty(httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - if (claimsPrincipal.Claims.Any(item => item.Type == ClaimTypes.Role)) + if (claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == ClaimTypes.Role)) + var _roles = httpContext.RequestServices.GetRequiredService(); + var roles = _roles.GetRoles(user.SiteId).ToList(); // global roles excluded ie. host users cannot be added/deleted + + var mappings = httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimMappings", "").Split(','); + foreach (var claim in claimsPrincipal.Claims.Where(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", ""))) { - if (!identity.Claims.Any(item => item.Type == ClaimTypes.Role && item.Value == claim.Value)) + var rolename = claim.Value; + if (mappings.Any(item => item.StartsWith(rolename + ":"))) { - identity.AddClaim(new Claim(ClaimTypes.Role, claim.Value)); + rolename = mappings.First(item => item.StartsWith(rolename + ":")).Split(':')[1]; + } + var role = roles.FirstOrDefault(item => item.Name == rolename); + if (role != null) + { + if (!userRoles.Any(item => item.RoleId == role.RoleId && item.UserId == user.UserId)) + { + var userRole = new UserRole(); + userRole.RoleId = role.RoleId; + userRole.UserId = user.UserId; + _userRoles.AddUserRole(userRole); + } + } + } + if (bool.Parse(httpContext.GetSiteSettings().GetValue("ExternalLogin:SynchronizeRoles", "false"))) + { + userRoles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList(); + foreach (var userRole in userRoles) + { + var role = roles.FirstOrDefault(item => item.RoleId == userRole.RoleId); + if (role != null) + { + var rolename = role.Name; + if (mappings.Any(item => item.EndsWith(":" + rolename))) + { + rolename = mappings.First(item => item.EndsWith(":" + rolename)).Split(':')[0]; + } + if (!claimsPrincipal.Claims.Any(item => item.Type == httpContext.GetSiteSettings().GetValue("ExternalLogin:RoleClaimType", "") && item.Value == rolename)) + { + _userRoles.DeleteUserRole(userRole.UserRoleId); + } + } } } }