improve support for external login roles

This commit is contained in:
sbwalker 2024-09-13 07:34:57 -04:00
parent 69bc06685f
commit caa2073d48
3 changed files with 83 additions and 10 deletions

View File

@ -333,12 +333,29 @@ else
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the role claim provided by the provider" ResourceKey="RoleClaimType">Role Claim:</Label>
<Label Class="col-sm-3" For="roleclaimtype" HelpText="The name of the roles claim provided by the provider" ResourceKey="RoleClaimType">Roles Claim:</Label>
<div class="col-sm-9">
<input id="roleclaimtype" class="form-control" @bind="@_roleclaimtype" />
</div>
</div>
<div class="row mb-1 align-items-center">
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="roleclaimmappings" HelpText="Optionally provide a comma delimited list of role names provided by the identity provider, as well as mappings to your site roles." ResourceKey="RoleClaimMappings">Role Claim Mappings:</Label>
<div class="col-sm-9">
<input id="roleclaimmappings" class="form-control" @bind="@_roleclaimmappings" />
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="synchronizeroles" HelpText="This option will add or remove role assignments so that the site roles exactly match the roles provided by the identity provider" ResourceKey="SynchronizeRoles">Synchronize Roles?</Label>
<div class="col-sm-9">
<div class="input-group">
<select id="synchronizeroles" class="form-select" @bind="@_synchronizeroles" required>
<option value="true">@SharedLocalizer["Yes"]</option>
<option value="false">@SharedLocalizer["No"]</option>
</select>
</div>
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="profileclaimtypes" HelpText="A comma delimited list of user profile claims provided by the provider, as well as mappings to your user profile definition. For example if the provider includes a 'given_name' claim and you have a 'FirstName' user profile definition you should specify 'given_name:FirstName'." ResourceKey="ProfileClaimTypes">User Profile Claims:</Label>
<div class="col-sm-9">
<input id="profileclaimtypes" class="form-control" @bind="@_profileclaimtypes" />
@ -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);

View File

@ -385,10 +385,22 @@
<value>Parameters:</value>
</data>
<data name="RoleClaimType.HelpText" xml:space="preserve">
<value>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.</value>
<value>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.</value>
</data>
<data name="RoleClaimType.Text" xml:space="preserve">
<value>Role Claim:</value>
<value>Roles Claim:</value>
</data>
<data name="RoleClaimMappings.HelpText" xml:space="preserve">
<value>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'.</value>
</data>
<data name="RoleClaimMappings.Text" xml:space="preserve">
<value>Role Claim Mappings:</value>
</data>
<data name="SynchronizeRoles.HelpText" xml:space="preserve">
<value>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</value>
</data>
<data name="SynchronizeRoles.Text" xml:space="preserve">
<value>Synchronize Roles?</value>
</data>
<data name="ProfileClaimTypes.HelpText" xml:space="preserve">
<value>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'.</value>

View File

@ -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<IUserRoleRepository>();
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<IRoleRepository>();
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);
}
}
}
}
}