diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor
index d09866d6..f36ed1dc 100644
--- a/Oqtane.Client/Modules/Admin/Users/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Users/Index.razor
@@ -170,8 +170,8 @@ else
@@ -229,7 +229,7 @@ else
_clientsecret = SettingService.GetSetting(settings, "OpenIdConnectOptions:ClientSecret", "");
_metadata = SettingService.GetSetting(settings, "OpenIdConnectOptions:MetadataAddress", "");
_logouturl = SettingService.GetSetting(settings, "OpenIdConnectOptions:LogoutUrl", "");
- _allowsitelogin = SettingService.GetSetting(settings, "AllowSiteLogin", "True");
+ _allowsitelogin = SettingService.GetSetting(settings, "AllowSiteLogin", "true");
}
private List Search(string search)
diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
index 5e9f7d05..09953d86 100644
--- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs
@@ -2,7 +2,6 @@ using System;
using System.Linq;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Oqtane.Infrastructure;
using Oqtane.Models;
@@ -15,7 +14,6 @@ using Oqtane.Repository;
using System.IO;
using System.Collections.Generic;
using Oqtane.Security;
-using System.Net;
using Microsoft.AspNetCore.Http;
namespace Oqtane.Extensions
@@ -26,22 +24,11 @@ namespace Oqtane.Extensions
this OqtaneSiteOptionsBuilder builder)
where TAlias : class, IAlias, new()
{
- builder.WithSiteAuthenticationCore();
builder.WithSiteAuthenticationOptions();
return builder;
}
- public static OqtaneSiteOptionsBuilder WithSiteAuthenticationCore(
- this OqtaneSiteOptionsBuilder builder)
- where TAlias : class, IAlias, new()
- {
- builder.Services.DecorateService>();
- builder.Services.Replace(ServiceDescriptor.Singleton());
-
- return builder;
- }
-
public static OqtaneSiteOptionsBuilder WithSiteAuthenticationOptions(
this OqtaneSiteOptionsBuilder builder)
where TAlias : class, IAlias, new()
@@ -75,8 +62,8 @@ namespace Oqtane.Extensions
// openid connect events
options.Events.OnTokenValidated = OnTokenValidated;
- options.Events.OnRedirectToIdentityProvider = OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProviderForSignOut = OnRedirectToIdentityProviderForSignOut;
+ options.Events.OnAccessDenied = OnAccessDenied;
options.Events.OnRemoteFailure = OnRemoteFailure;
});
@@ -97,6 +84,7 @@ namespace Oqtane.Extensions
var email = context.Principal.FindFirstValue(ClaimTypes.Email);
var providerKey = context.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var loginProvider = context.HttpContext.GetAlias().SiteSettings["OpenIdConnectOptions:Authority"];
+ var alias = context.HttpContext.GetAlias();
var _logger = context.HttpContext.RequestServices.GetRequiredService();
if (email != null)
@@ -117,10 +105,10 @@ namespace Oqtane.Extensions
if (result.Succeeded)
{
// add user login
- await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(loginProvider, providerKey, ""));
+ await _identityUserManager.AddLoginAsync(identityuser, new UserLoginInfo(loginProvider, providerKey, email));
user = new User();
- user.SiteId = context.HttpContext.GetAlias().SiteId;
+ user.SiteId = alias.SiteId;
user.Username = email;
user.DisplayName = email;
user.Email = email;
@@ -145,11 +133,11 @@ namespace Oqtane.Extensions
Capacity = Constants.UserFolderCapacity,
IsSystem = true,
Permissions = new List
- {
- new Permission(PermissionNames.Browse, user.UserId, true),
- new Permission(PermissionNames.View, RoleNames.Everyone, true),
- new Permission(PermissionNames.Edit, user.UserId, true)
- }.EncodePermissions()
+ {
+ new Permission(PermissionNames.Browse, user.UserId, true),
+ new Permission(PermissionNames.View, RoleNames.Everyone, true),
+ new Permission(PermissionNames.Edit, user.UserId, true)
+ }.EncodePermissions()
});
}
@@ -210,8 +198,8 @@ namespace Oqtane.Extensions
}
// add Oqtane claims
- List userroles = _userRoles.GetUserRoles(user.UserId, context.HttpContext.GetAlias().SiteId).ToList();
- var identity = UserSecurity.CreateClaimsIdentity(context.HttpContext.GetAlias(), user, userroles);
+ List userroles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList();
+ var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles);
principal.AddClaims(identity.Claims);
// add provider
@@ -224,12 +212,6 @@ namespace Oqtane.Extensions
}
}
- private static Task OnRedirectToIdentityProvider(RedirectContext context)
- {
- //context.ProtocolMessage.SetParameter("key", "value");
- return Task.CompletedTask;
- }
-
private static Task OnRedirectToIdentityProviderForSignOut(RedirectContext context)
{
var logoutUrl = context.HttpContext.GetAlias().SiteSettings.GetValue("OpenIdConnectOptions:LogoutUrl", "");
@@ -251,59 +233,25 @@ namespace Oqtane.Extensions
return Task.CompletedTask;
}
- private static Task OnRemoteFailure(RemoteFailureContext context)
+ private static Task OnAccessDenied(AccessDeniedContext context)
{
var _logger = context.HttpContext.RequestServices.GetRequiredService();
- _logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Remote Failure {Error}", context.Failure.Message);
- context.Response.Redirect(context.Properties.RedirectUri);
+ _logger.Log(LogLevel.Information, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Access Denied - User May Have Cancelled Their External Login Attempt");
+ // redirect to login page
+ var alias = context.HttpContext.GetAlias();
+ context.Response.Redirect(alias.Path + "/login?returnurl=" + context.Properties.RedirectUri);
context.HandleResponse();
return Task.CompletedTask;
}
- public static bool DecorateService(this IServiceCollection services, params object[] parameters)
+ private static Task OnRemoteFailure(RemoteFailureContext context)
{
- var existingService = services.SingleOrDefault(s => s.ServiceType == typeof(TService));
- if (existingService == null)
- return false;
-
- var newService = new ServiceDescriptor(existingService.ServiceType,
- sp =>
- {
- TService inner = (TService)ActivatorUtilities.CreateInstance(sp, existingService.ImplementationType!);
-
- var parameters2 = new object[parameters.Length + 1];
- Array.Copy(parameters, 0, parameters2, 1, parameters.Length);
- parameters2[0] = inner;
-
- return ActivatorUtilities.CreateInstance(sp, parameters2)!;
- },
- existingService.Lifetime);
-
- if (existingService.ImplementationInstance != null)
- {
- newService = new ServiceDescriptor(existingService.ServiceType,
- sp =>
- {
- TService inner = (TService)existingService.ImplementationInstance;
- return ActivatorUtilities.CreateInstance(sp, inner, parameters)!;
- },
- existingService.Lifetime);
- }
- else if (existingService.ImplementationFactory != null)
- {
- newService = new ServiceDescriptor(existingService.ServiceType,
- sp =>
- {
- TService inner = (TService)existingService.ImplementationFactory(sp);
- return ActivatorUtilities.CreateInstance(sp, inner, parameters)!;
- },
- existingService.Lifetime);
- }
-
- services.Remove(existingService);
- services.Add(newService);
-
- return true;
+ var _logger = context.HttpContext.RequestServices.GetRequiredService();
+ _logger.Log(LogLevel.Error, nameof(OqtaneSiteAuthenticationBuilderExtensions), Enums.LogFunction.Security, "OpenId Connect Remote Failure - {Error}", context.Failure.Message);
+ // redirect to original page
+ context.Response.Redirect(context.Properties.RedirectUri);
+ context.HandleResponse();
+ return Task.CompletedTask;
}
}
}
diff --git a/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationSchemeProvider.cs b/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationSchemeProvider.cs
deleted file mode 100644
index 359e8183..00000000
--- a/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationSchemeProvider.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.Extensions.Options;
-
-namespace Oqtane.Infrastructure
-{
- internal class SiteAuthenticationSchemeProvider : IAuthenticationSchemeProvider
- {
- public SiteAuthenticationSchemeProvider(IOptions options)
- : this(options, new Dictionary(StringComparer.Ordinal))
- {
- }
-
- public SiteAuthenticationSchemeProvider(IOptions options, IDictionary schemes)
- {
- _optionsProvider = options;
-
- _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes));
- _requestHandlers = new List();
-
- foreach (var builder in _optionsProvider.Value.Schemes)
- {
- var scheme = builder.Build();
- AddScheme(scheme);
- }
- }
-
- private readonly IOptions _optionsProvider;
- private readonly object _lock = new object();
-
- private readonly IDictionary _schemes;
- private readonly List _requestHandlers;
-
- private Task GetDefaultSchemeAsync()
- => _optionsProvider.Value.DefaultScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultScheme)
- : Task.FromResult(null);
-
- public virtual Task GetDefaultAuthenticateSchemeAsync()
- => _optionsProvider.Value.DefaultAuthenticateScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultAuthenticateScheme)
- : GetDefaultSchemeAsync();
-
- public virtual Task GetDefaultChallengeSchemeAsync()
- => _optionsProvider.Value.DefaultChallengeScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultChallengeScheme)
- : GetDefaultSchemeAsync();
-
- public virtual Task GetDefaultForbidSchemeAsync()
- => _optionsProvider.Value.DefaultForbidScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultForbidScheme)
- : GetDefaultChallengeSchemeAsync();
-
- public virtual Task GetDefaultSignInSchemeAsync()
- => _optionsProvider.Value.DefaultSignInScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultSignInScheme)
- : GetDefaultSchemeAsync();
-
- public virtual Task GetDefaultSignOutSchemeAsync()
- => _optionsProvider.Value.DefaultSignOutScheme != null
- ? GetSchemeAsync(_optionsProvider.Value.DefaultSignOutScheme)
- : GetDefaultSignInSchemeAsync();
-
- public virtual Task GetSchemeAsync(string name)
- => Task.FromResult(_schemes.ContainsKey(name) ? _schemes[name] : null);
-
- public virtual Task> GetRequestHandlerSchemesAsync()
- => Task.FromResult>(_requestHandlers);
-
- public virtual void AddScheme(AuthenticationScheme scheme)
- {
- if (_schemes.ContainsKey(scheme.Name))
- {
- throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
- }
- lock (_lock)
- {
- if (_schemes.ContainsKey(scheme.Name))
- {
- throw new InvalidOperationException("Scheme already exists: " + scheme.Name);
- }
- if (typeof(IAuthenticationRequestHandler).IsAssignableFrom(scheme.HandlerType))
- {
- _requestHandlers.Add(scheme);
- }
- _schemes[scheme.Name] = scheme;
- }
- }
-
- public virtual void RemoveScheme(string name)
- {
- if (!_schemes.ContainsKey(name))
- {
- return;
- }
- lock (_lock)
- {
- if (_schemes.ContainsKey(name))
- {
- var scheme = _schemes[name];
- _requestHandlers.Remove(scheme);
- _schemes.Remove(name);
- }
- }
- }
-
- public virtual Task> GetAllSchemesAsync()
- => Task.FromResult>(_schemes.Values);
- }
-}
diff --git a/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationService.cs b/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationService.cs
deleted file mode 100644
index 40ccb519..00000000
--- a/Oqtane.Server/Infrastructure/Internal/SiteAuthenticationService.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System.Security.Claims;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authentication;
-using Microsoft.AspNetCore.Http;
-using Oqtane.Extensions;
-using Oqtane.Models;
-using Oqtane.Shared;
-
-namespace Oqtane.Infrastructure
-{
- internal class SiteAuthenticationService : IAuthenticationService
- where TAlias : class, IAlias, new()
- {
- private readonly IAuthenticationService _inner;
-
- public SiteAuthenticationService(IAuthenticationService inner)
- {
- _inner = inner ?? throw new System.ArgumentNullException(nameof(inner));
- }
-
- private static void AddTenantIdentifierToProperties(HttpContext context, ref AuthenticationProperties properties)
- {
- // add site identifier to the authentication properties so on the callback we can use it to set context
- var alias = context.GetAlias();
- if (alias != null)
- {
- properties ??= new AuthenticationProperties();
- if (!properties.Items.Keys.Contains(Constants.SiteToken))
- {
- properties.Items.Add(Constants.SiteToken, alias.SiteKey);
- }
- }
- }
-
- public Task AuthenticateAsync(HttpContext context, string scheme)
- => _inner.AuthenticateAsync(context, scheme);
-
- public async Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties)
- {
- AddTenantIdentifierToProperties(context, ref properties);
- await _inner.ChallengeAsync(context, scheme, properties);
- }
-
- public async Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties)
- {
- AddTenantIdentifierToProperties(context, ref properties);
- await _inner.ForbidAsync(context, scheme, properties);
- }
-
- public async Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
- {
- AddTenantIdentifierToProperties(context, ref properties);
- await _inner.SignInAsync(context, scheme, principal, properties);
- }
-
- public async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties)
- {
- AddTenantIdentifierToProperties(context, ref properties);
- await _inner.SignOutAsync(context, scheme, properties);
- }
- }
-}
diff --git a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
index ff16773e..9a06903f 100644
--- a/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
+++ b/Oqtane.Server/Infrastructure/Middleware/TenantMiddleware.cs
@@ -33,7 +33,7 @@ namespace Oqtane.Infrastructure
alias.SiteSettings = cache.GetOrCreate("sitesettings:" + alias.SiteKey, entry =>
{
var settingRepository = context.RequestServices.GetService(typeof(ISettingRepository)) as ISettingRepository;
- return settingRepository.GetSettings(EntityNames.Site)
+ return settingRepository.GetSettings(EntityNames.Site, alias.SiteId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
});
// save alias in HttpContext
diff --git a/Oqtane.Server/Security/PrincipalValidator.cs b/Oqtane.Server/Security/PrincipalValidator.cs
index 5a45c820..ea668689 100644
--- a/Oqtane.Server/Security/PrincipalValidator.cs
+++ b/Oqtane.Server/Security/PrincipalValidator.cs
@@ -24,7 +24,7 @@ namespace Oqtane.Security
if (alias != null)
{
// verify principal was authenticated for current tenant
- if (context.Principal.Claims.FirstOrDefault(item => item.Type == ClaimTypes.GroupSid)?.Value != alias.AliasId.ToString())
+ 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();
diff --git a/Oqtane.Shared/Security/UserSecurity.cs b/Oqtane.Shared/Security/UserSecurity.cs
index c052e903..7ef1ea55 100644
--- a/Oqtane.Shared/Security/UserSecurity.cs
+++ b/Oqtane.Shared/Security/UserSecurity.cs
@@ -134,7 +134,7 @@ namespace Oqtane.Security
{
identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
identity.AddClaim(new Claim(ClaimTypes.PrimarySid, user.UserId.ToString()));
- identity.AddClaim(new Claim(ClaimTypes.GroupSid, alias.AliasId.ToString()));
+ identity.AddClaim(new Claim(ClaimTypes.GroupSid, alias.SiteKey));
if (user.Roles.Contains(RoleNames.Host))
{
// host users are site admins by default