factor out auth constants, remove TAlias is Alias is not an extensible type, improve SiteOptions cache clearing, improve principal validation, localization improvements

This commit is contained in:
Shaun Walker 2022-03-26 17:30:06 -04:00
parent 79f427e10a
commit b92a888583
22 changed files with 113 additions and 111 deletions

View File

@ -42,7 +42,7 @@ else
</div>
</div>
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="twofactor" HelpText="Indicates if you are using two factor authentication" ResourceKey="TwoFactor"></Label>
<Label Class="col-sm-3" For="twofactor" HelpText="Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in." ResourceKey="TwoFactor"></Label>
<div class="col-sm-9">
<select id="twofactor" class="form-select" @bind="@twofactor" required>
<option value="True">@SharedLocalizer["Yes"]</option>

View File

@ -135,8 +135,8 @@ else
<div class="col-sm-9">
<select id="providertype" class="form-select" value="@_providertype" @onchange="(e => ProviderTypeChanged(e))">
<option value="" selected>@Localizer["Not Specified"]</option>
<option value="oidc">@Localizer["OpenID Connect"]</option>
<option value="oauth2">@Localizer["OAuth 2.0"]</option>
<option value="@AuthenticationProviderTypes.OpenIDConnect">@Localizer["OpenID Connect"]</option>
<option value="@AuthenticationProviderTypes.OAuth2">@Localizer["OAuth 2.0"]</option>
</select>
</div>
</div>
@ -149,7 +149,7 @@ else
</div>
</div>
}
@if (_providertype == "oidc")
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="authority" HelpText="The Authority Url or Issuer Url associated with the OpenID Connect provider" ResourceKey="Authority">Authority:</Label>
@ -164,7 +164,7 @@ else
</div>
</div>
}
@if (_providertype == "oauth2")
@if (_providertype == AuthenticationProviderTypes.OAuth2)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="authorizationurl" HelpText="The endpoint for obtaining an Authorization Code" ResourceKey="AuthorizationUrl">Authorization Url:</Label>
@ -220,7 +220,7 @@ else
<input id="redirecturl" class="form-control" @bind="@_redirecturl" readonly />
</div>
</div>
@if (_providertype == "oidc")
@if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
{
<div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="emailclaimtype" HelpText="The type name for the email address claim provided by the provider" ResourceKey="EmailClaimType">Email Claim Type:</Label>
@ -440,7 +440,7 @@ else
private void ProviderTypeChanged(ChangeEventArgs e)
{
_providertype = (string)e.Value;
if (_providertype == "oidc")
if (_providertype == AuthenticationProviderTypes.OpenIDConnect)
{
_scopes = "openid,profile,email";
}

View File

@ -52,7 +52,7 @@
<input type="file" id="@_fileinputid" name="file" accept="@_filter" />
}
</div>
<div class="col mt-2 text-center">
<div class="col mt-2 text-end">
<button type="button" class="btn btn-success" @onclick="UploadFile">@SharedLocalizer["Upload"]</button>
@if (ShowFiles && GetFileId() != -1)
{

View File

@ -127,7 +127,7 @@
<value>User Account Could Not Be Verified. Please Contact Your Administrator For Further Instructions.</value>
</data>
<data name="Error.Login.Fail" xml:space="preserve">
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In 3 Times Unsuccessfully, Your Account Will Be Locked Out For 10 Minutes. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
<value>Login Failed. Please Remember That Passwords Are Case Sensitive. If You Have Attempted To Sign In Multiple Times Unsuccessfully, Your Account Will Be Locked Out For A Period Of Time. Note That User Accounts Require Verification When They Are Initially Created So You May Wish To Check Your Email If You Are A New User.</value>
</data>
<data name="Message.Required.UserInfo" xml:space="preserve">
<value>Please Provide All Required Fields</value>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@ -169,10 +169,10 @@
<value>Identity</value>
</data>
<data name="Confirm.HelpText" xml:space="preserve">
<value>If you are changing your password you must enter it again to confirm it matches</value>
<value>If you are changing your password you must enter it again to confirm it matches the value entered above</value>
</data>
<data name="Confirm.Text" xml:space="preserve">
<value>Confirm Password:</value>
<value>Confirmation:</value>
</data>
<data name="DisplayName.HelpText" xml:space="preserve">
<value>Your full name</value>
@ -205,9 +205,9 @@
<value>Username:</value>
</data>
<data name="TwoFactor.HelpText" xml:space="preserve">
<value>Indicates if you are using two factor authentication</value>
<value>Indicates if you are using two factor authentication. Two factor authentication requires you to enter a verification code sent via email after you sign in.</value>
</data>
<data name="TwoFactor.Text" xml:space="preserve">
<value>Two Factor Authentication?</value>
<value>Two Factor?</value>
</data>
</root>

View File

@ -21,7 +21,9 @@ namespace Oqtane.Services
_siteState = siteState;
}
private string ApiUrl => CreateApiUrl("Installation", null, ControllerRoutes.ApiRoute); // tenant agnostic
private string ApiUrl => (_siteState.Alias == null)
? CreateApiUrl("Installation", null, ControllerRoutes.ApiRoute) // tenant agnostic needed for initial installation
: CreateApiUrl("Installation", _siteState.Alias);
public async Task<Installation> IsInstalled()
{

View File

@ -17,7 +17,6 @@ namespace Oqtane.Services
public SiteService(HttpClient http, SiteState siteState) : base(http)
{
_siteState = siteState;
}

View File

@ -8,9 +8,10 @@ using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Identity;
namespace Oqtane.Controllers
{
@ -23,16 +24,16 @@ namespace Oqtane.Controllers
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
private readonly IOptionsMonitorCache<OpenIdConnectOptions> _optionsMonitorCache;
private readonly IAliasAccessor _aliasAccessor;
private readonly string _visitorCookie;
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, IOptionsMonitorCache<OpenIdConnectOptions> optionsMonitorCache, ILogManager logger)
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, IAliasAccessor aliasAccessor, ILogManager logger)
{
_settings = settings;
_pageModules = pageModules;
_userPermissions = userPermissions;
_syncManager = syncManager;
_optionsMonitorCache = optionsMonitorCache;
_aliasAccessor = aliasAccessor;
_logger = logger;
_alias = tenantManager.GetAlias();
_visitorCookie = "APP_VISITOR_" + _alias.SiteId.ToString();
@ -141,7 +142,12 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public void Clear(int id)
{
_optionsMonitorCache.Clear();
var openIdConnectOptionsCache = new SiteOptionsCache<OpenIdConnectOptions>(_aliasAccessor);
openIdConnectOptionsCache.Clear();
var oAuthOptionsCache = new SiteOptionsCache<OAuthOptions>(_aliasAccessor);
oAuthOptionsCache.Clear();
var identityOptionsCache = new SiteOptionsCache<IdentityOptions>(_aliasAccessor);
identityOptionsCache.Clear();
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Site Options Cache Cleared");
}

View File

@ -14,7 +14,6 @@ using System.Net;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Extensions;
namespace Oqtane.Controllers
{

View File

@ -7,6 +7,8 @@ using System.Net.Http;
using System.Reflection;
using System.Runtime.Loader;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
@ -15,7 +17,6 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Modules;
using Oqtane.Repository;
using Oqtane.Security;
@ -59,10 +60,9 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
public static OqtaneSiteOptionsBuilder<T> AddOqtaneSiteOptions<T>(this IServiceCollection services)
where T : class, IAlias, new()
public static OqtaneSiteOptionsBuilder AddOqtaneSiteOptions(this IServiceCollection services)
{
return new OqtaneSiteOptionsBuilder<T>(services);
return new OqtaneSiteOptionsBuilder(services);
}
internal static IServiceCollection AddOqtaneSingletonServices(this IServiceCollection services)
@ -144,6 +144,15 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
public static IServiceCollection ConfigureOqtaneAuthenticationOptions(this IServiceCollection services, IConfigurationRoot Configuration)
{
// settings defined in appsettings
services.Configure<OAuthOptions>(Configuration);
services.Configure<OpenIdConnectOptions>(Configuration);
return services;
}
public static IServiceCollection ConfigureOqtaneIdentityOptions(this IServiceCollection services, IConfigurationRoot Configuration)
{
// default settings

View File

@ -11,7 +11,6 @@ using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Oqtane.Repository;
using System.IO;
using System.Collections.Generic;
using Oqtane.Security;
using Microsoft.AspNetCore.Http;
@ -24,30 +23,19 @@ namespace Oqtane.Extensions
{
public static class OqtaneSiteAuthenticationBuilderExtensions
{
public static OqtaneSiteOptionsBuilder<TAlias> WithSiteAuthentication<TAlias>(
this OqtaneSiteOptionsBuilder<TAlias> builder)
where TAlias : class, IAlias, new()
{
builder.WithSiteAuthenticationOptions();
return builder;
}
public static OqtaneSiteOptionsBuilder<TAlias> WithSiteAuthenticationOptions<TAlias>(
this OqtaneSiteOptionsBuilder<TAlias> builder)
where TAlias : class, IAlias, new()
public static OqtaneSiteOptionsBuilder WithSiteAuthentication(this OqtaneSiteOptionsBuilder builder)
{
// site OpenIdConnect options
builder.AddSiteOptions<OpenIdConnectOptions>((options, alias) =>
{
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == "oidc")
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == AuthenticationProviderTypes.OpenIDConnect)
{
// default options
options.SignInScheme = Constants.AuthenticationScheme; // identity cookie
options.RequireHttpsMetadata = true;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oidc" : "/" + alias.Path + "/signin-oidc";
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OpenIDConnect : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OpenIDConnect;
options.ResponseType = OpenIdConnectResponseType.Code; // authorization code flow
options.ResponseMode = OpenIdConnectResponseMode.FormPost; // recommended as most secure
@ -77,11 +65,11 @@ namespace Oqtane.Extensions
// site OAuth2.0 options
builder.AddSiteOptions<OAuthOptions>((options, alias) =>
{
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == "oauth2")
if (alias.SiteSettings.GetValue("ExternalLogin:ProviderType", "") == AuthenticationProviderTypes.OAuth2)
{
// default options
options.SignInScheme = Constants.AuthenticationScheme; // identity cookie
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-oauth2" : "/" + alias.Path + "/signin-oauth2";
options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OAuth2 : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OAuth2;
options.SaveTokens = true;
// site options

View File

@ -7,9 +7,7 @@ namespace Oqtane.Extensions
{
public static class OqtaneSiteIdentityBuilderExtensions
{
public static OqtaneSiteOptionsBuilder<TAlias> WithSiteIdentity<TAlias>(
this OqtaneSiteOptionsBuilder<TAlias> builder)
where TAlias : class, IAlias, new()
public static OqtaneSiteOptionsBuilder WithSiteIdentity(this OqtaneSiteOptionsBuilder builder)
{
// site identity options
builder.AddSiteOptions<IdentityOptions>((options, alias) =>

View File

@ -6,7 +6,7 @@ using Oqtane.Models;
namespace Microsoft.Extensions.DependencyInjection
{
public partial class OqtaneSiteOptionsBuilder<TSiteOptions> where TSiteOptions : class, IAlias, new()
public partial class OqtaneSiteOptionsBuilder
{
public IServiceCollection Services { get; set; }
@ -15,13 +15,12 @@ namespace Microsoft.Extensions.DependencyInjection
Services = services;
}
public OqtaneSiteOptionsBuilder<TSiteOptions> AddSiteOptions<TOptions>(
Action<TOptions, TSiteOptions> siteOptions) where TOptions : class, new()
public OqtaneSiteOptionsBuilder AddSiteOptions<TOptions>(
Action<TOptions, Alias> alias) where TOptions : class, new()
{
Services.TryAddSingleton<IOptionsMonitorCache<TOptions>, SiteOptionsCache<TOptions, TSiteOptions>>();
Services.AddSingleton<ISiteOptions<TOptions, TSiteOptions>, SiteOptions<TOptions, TSiteOptions>>
(sp => new SiteOptions<TOptions, TSiteOptions>(siteOptions));
Services.TryAddTransient<IOptionsFactory<TOptions>, SiteOptionsFactory<TOptions, TSiteOptions>>();
Services.TryAddSingleton<IOptionsMonitorCache<TOptions>, SiteOptionsCache<TOptions>>();
Services.AddSingleton<ISiteOptions<TOptions>, SiteOptions<TOptions>> (sp => new SiteOptions<TOptions>(alias));
Services.TryAddTransient<IOptionsFactory<TOptions>, SiteOptionsFactory<TOptions>>();
Services.TryAddScoped<IOptionsSnapshot<TOptions>>(sp => BuildOptionsManager<TOptions>(sp));
Services.TryAddSingleton<IOptions<TOptions>>(sp => BuildOptionsManager<TOptions>(sp));
@ -31,7 +30,7 @@ namespace Microsoft.Extensions.DependencyInjection
private static SiteOptionsManager<TOptions> BuildOptionsManager<TOptions>(IServiceProvider sp)
where TOptions : class, new()
{
var cache = ActivatorUtilities.CreateInstance(sp, typeof(SiteOptionsCache<TOptions, TSiteOptions>));
var cache = ActivatorUtilities.CreateInstance(sp, typeof(SiteOptionsCache<TOptions>));
return (SiteOptionsManager<TOptions>)ActivatorUtilities.CreateInstance(sp, typeof(SiteOptionsManager<TOptions>), new[] { cache });
}

View File

@ -20,7 +20,9 @@ namespace Oqtane.Infrastructure
{
// check if framework is installed
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
if (config.IsInstalled())
string path = context.Request.Path.ToString();
if (config.IsInstalled() && !path.StartsWith("/_blazor"))
{
// get alias (note that this also sets SiteState.Alias)
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
@ -28,7 +30,7 @@ namespace Oqtane.Infrastructure
if (alias != null)
{
// get site settings
// add site settings to alias
var cache = context.RequestServices.GetService(typeof(IMemoryCache)) as IMemoryCache;
alias.SiteSettings = cache.GetOrCreate("sitesettings:" + alias.SiteKey, entry =>
{
@ -36,13 +38,14 @@ namespace Oqtane.Infrastructure
return settingRepository.GetSettings(EntityNames.Site, alias.SiteId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
});
// save alias in HttpContext
// save alias in HttpContext for server-side usage
context.Items.Add(Constants.HttpContextAliasKey, alias);
// remove site settings so they are not available client-side
alias.SiteSettings = null;
// rewrite path by removing alias path prefix from api and pages requests (for consistent routing)
if (!string.IsNullOrEmpty(alias.Path))
{
string path = context.Request.Path.ToString();
if (path.StartsWith("/" + alias.Path) && (path.Contains("/api/") || path.Contains("/pages/")))
{
context.Request.Path = path.Replace("/" + alias.Path, "");

View File

@ -1,12 +1,10 @@
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface ISiteOptions<TOptions, TAlias>
public interface ISiteOptions<TOptions>
where TOptions : class, new()
where TAlias : class, IAlias, new()
{
void Configure(TOptions options, TAlias siteOptions);
void Configure(TOptions options, Alias alias);
}
}

View File

@ -3,20 +3,19 @@ using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class SiteOptions<TOptions, TAlias> : ISiteOptions<TOptions, TAlias>
public class SiteOptions<TOptions> : ISiteOptions<TOptions>
where TOptions : class, new()
where TAlias : class, IAlias, new()
{
private readonly Action<TOptions, TAlias> configureOptions;
private readonly Action<TOptions, Alias> configureOptions;
public SiteOptions(Action<TOptions, TAlias> configureOptions)
public SiteOptions(Action<TOptions, Alias> configureOptions)
{
this.configureOptions = configureOptions;
}
public void Configure(TOptions options, TAlias siteOptions)
public void Configure(TOptions options, Alias alias)
{
configureOptions(options, siteOptions);
configureOptions(options, alias);
}
}
}

View File

@ -5,9 +5,8 @@ using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class SiteOptionsCache<TOptions, TAlias> : IOptionsMonitorCache<TOptions>
where TOptions : class
where TAlias : class, IAlias, new()
public class SiteOptionsCache<TOptions> : IOptionsMonitorCache<TOptions>
where TOptions : class, new()
{
private readonly IAliasAccessor _aliasAccessor;
private readonly ConcurrentDictionary<string, IOptionsMonitorCache<TOptions>> map = new ConcurrentDictionary<string, IOptionsMonitorCache<TOptions>>();

View File

@ -1,25 +1,21 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public class SiteOptionsFactory<TOptions, TAlias> : IOptionsFactory<TOptions>
public class SiteOptionsFactory<TOptions> : IOptionsFactory<TOptions>
where TOptions : class, new()
where TAlias : class, IAlias, new()
{
private readonly IConfigureOptions<TOptions>[] _configureOptions;
private readonly IPostConfigureOptions<TOptions>[] _postConfigureOptions;
private readonly IValidateOptions<TOptions>[] _validations;
private readonly ISiteOptions<TOptions, TAlias>[] _siteOptions;
private readonly ISiteOptions<TOptions>[] _siteOptions;
private readonly IAliasAccessor _aliasAccessor;
public SiteOptionsFactory(IEnumerable<IConfigureOptions<TOptions>> configureOptions, IEnumerable<IPostConfigureOptions<TOptions>> postConfigureOptions, IEnumerable<IValidateOptions<TOptions>> validations, IEnumerable<ISiteOptions<TOptions, TAlias>> siteOptions, IAliasAccessor aliasAccessor)
public SiteOptionsFactory(IEnumerable<IConfigureOptions<TOptions>> configureOptions, IEnumerable<IPostConfigureOptions<TOptions>> postConfigureOptions, IEnumerable<ISiteOptions<TOptions>> siteOptions, IAliasAccessor aliasAccessor)
{
_configureOptions = configureOptions as IConfigureOptions<TOptions>[] ?? new List<IConfigureOptions<TOptions>>(configureOptions).ToArray();
_postConfigureOptions = postConfigureOptions as IPostConfigureOptions<TOptions>[] ?? new List<IPostConfigureOptions<TOptions>>(postConfigureOptions).ToArray();
_validations = validations as IValidateOptions<TOptions>[] ?? new List<IValidateOptions<TOptions>>(validations).ToArray();
_siteOptions = siteOptions as ISiteOptions<TOptions, TAlias>[] ?? new List<ISiteOptions<TOptions, TAlias>>(siteOptions).ToArray();
_siteOptions = siteOptions as ISiteOptions<TOptions>[] ?? new List<ISiteOptions<TOptions>>(siteOptions).ToArray();
_aliasAccessor = aliasAccessor;
}
@ -44,7 +40,7 @@ namespace Oqtane.Infrastructure
{
foreach (var siteOption in _siteOptions)
{
siteOption.Configure(options, _aliasAccessor.Alias as TAlias);
siteOption.Configure(options, _aliasAccessor.Alias);
}
}

View File

@ -6,6 +6,8 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Models;
using System.Collections.Generic;
using Oqtane.Extensions;
using Oqtane.Shared;
namespace Oqtane.Security
{
@ -13,50 +15,48 @@ namespace Oqtane.Security
{
public static Task ValidateAsync(CookieValidatePrincipalContext context)
{
if (context != null && context.Principal.Identity.IsAuthenticated)
if (context != null && context.Principal.Identity.IsAuthenticated && context.Principal.Identity.Name != null)
{
// check if framework is installed
var config = context.HttpContext.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
if (config.IsInstalled())
{
var tenantManager = context.HttpContext.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
var alias = tenantManager.GetAlias();
// get current site
var alias = context.HttpContext.GetAlias();
if (alias != null)
{
// verify principal was authenticated for current tenant
// check if principal matches current site
if (context.Principal.Claims.FirstOrDefault(item => item.Type == ClaimTypes.GroupSid)?.Value != alias.SiteKey)
{
// tenant agnostic requests must be ignored
string path = context.Request.Path.ToString().ToLower();
if (path.StartsWith("/_blazor") || path.StartsWith("/api/installation/"))
{
return Task.CompletedTask;
}
// refresh principal
// principal does not match site
var userRepository = context.HttpContext.RequestServices.GetService(typeof(IUserRepository)) as IUserRepository;
var userRoleRepository = context.HttpContext.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
var _logger = context.HttpContext.RequestServices.GetService(typeof(ILogManager)) as ILogManager;
string path = context.Request.Path.ToString().ToLower();
if (context.Principal.Identity.Name != null)
{
User user = userRepository.GetUser(context.Principal.Identity.Name);
if (user != null)
{
// replace principal with roles for current site
List<UserRole> userroles = userRoleRepository.GetUserRoles(user.UserId, alias.SiteId).ToList();
var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles);
context.ReplacePrincipal(new ClaimsPrincipal(identity));
context.ShouldRenew = true;
}
else
if (!path.StartsWith("/api/")) // reduce log verbosity
{
context.RejectPrincipal();
}
}
_logger.Log(alias.SiteId, LogLevel.Information, "LoginValidation", Enums.LogFunction.Security, "Permissions Updated For User {Username} Accessing Resource {Url}", context.Principal.Identity.Name, path);
}
}
else
{
// user has no roles for site - remove principal
context.RejectPrincipal();
if (!path.StartsWith("/api/")) // reduce log verbosity
{
_logger.Log(alias.SiteId, LogLevel.Information, "LoginValidation", Enums.LogFunction.Security, "Permissions Removed For User {Username} Accessing Resource {Url}", context.Principal.Identity.Name, path);
}
}
}
}
}
}

View File

@ -115,12 +115,13 @@ namespace Oqtane
options.DefaultChallengeScheme = Constants.AuthenticationScheme;
})
.AddCookie(Constants.AuthenticationScheme)
.AddOpenIdConnect("oidc", options => { })
.AddOAuth("oauth2", options => { });
.AddOpenIdConnect(AuthenticationProviderTypes.OpenIDConnect, options => { })
.AddOAuth(AuthenticationProviderTypes.OAuth2, options => { });
services.ConfigureOqtaneCookieOptions();
services.ConfigureOqtaneAuthenticationOptions(Configuration);
services.AddOqtaneSiteOptions<Alias>()
services.AddOqtaneSiteOptions()
.WithSiteIdentity()
.WithSiteAuthentication();

View File

@ -82,7 +82,7 @@ namespace Oqtane.Models
}
/// <summary>
/// Site-specific settings
/// Site-specific settings (only available on the server via HttpContext for security reasons)
/// </summary>
[NotMapped]
public Dictionary<string, string> SiteSettings { get; set; }

View File

@ -0,0 +1,6 @@
namespace Oqtane.Shared {
public class AuthenticationProviderTypes {
public const string OpenIDConnect = "oidc";
public const string OAuth2 = "oauth2";
}
}