diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index c17b755c3..3101d04b4 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -129,6 +129,17 @@ else + + + Cookie Type: + + + @Localizer["Domain"] + @Localizer["Site"] + + + + Provider Type: @@ -255,6 +266,23 @@ else } + + + Site Secret: + + + + + + Access Token: + + + + @Localizer["CreateToken"] + + + + @SharedLocalizer["Save"] @@ -277,6 +305,8 @@ else private string _maximumfailures; private string _lockoutduration; + private string _cookietype; + private string _providertype; private string _providername; private string _authority; @@ -294,6 +324,9 @@ else private string _createusers; private string _allowsitelogin; + private string _secret; + private string _token; + public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Admin; protected override async Task OnInitializedAsync() @@ -311,9 +344,12 @@ else _requireupper = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireUppercase", "true"); _requirelower = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireLowercase", "true"); _requirepunctuation = SettingService.GetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", "true"); + _maximumfailures = SettingService.GetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", "5"); _lockoutduration = TimeSpan.Parse(SettingService.GetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", "00:05:00")).TotalMinutes.ToString(); + _cookietype = SettingService.GetSetting(settings, "CookieOptions:CookieType", "domain"); + _providertype = SettingService.GetSetting(settings, "ExternalLogin:ProviderType", ""); _providername = SettingService.GetSetting(settings, "ExternalLogin:ProviderName", ""); _authority = SettingService.GetSetting(settings, "ExternalLogin:Authority", ""); @@ -330,6 +366,8 @@ else _domainfilter = SettingService.GetSetting(settings, "ExternalLogin:DomainFilter", ""); _createusers = SettingService.GetSetting(settings, "ExternalLogin:CreateUsers", "true"); _allowsitelogin = SettingService.GetSetting(settings, "ExternalLogin:AllowSiteLogin", "true"); + + _secret = SettingService.GetSetting(settings, "JwtOptions:Secret", ""); } private List Search(string search) @@ -406,9 +444,12 @@ else settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireUppercase", _requireupper, true); settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireLowercase", _requirelower, true); settings = SettingService.SetSetting(settings, "IdentityOptions:Password:RequireNonAlphanumeric", _requirepunctuation, true); + settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:MaxFailedAccessAttempts", _maximumfailures, true); settings = SettingService.SetSetting(settings, "IdentityOptions:Lockout:DefaultLockoutTimeSpan", TimeSpan.FromMinutes(Convert.ToInt64(_lockoutduration)).ToString(), true); + settings = SettingService.SetSetting(settings, "CookieOptions:CookieType", _cookietype, true); + settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderType", _providertype, false); settings = SettingService.SetSetting(settings, "ExternalLogin:ProviderName", _providername, false); settings = SettingService.SetSetting(settings, "ExternalLogin:Authority", _authority, true); @@ -425,6 +466,9 @@ else settings = SettingService.SetSetting(settings, "ExternalLogin:CreateUsers", _createusers, true); settings = SettingService.SetSetting(settings, "ExternalLogin:AllowSiteLogin", _allowsitelogin, false); + if (!string.IsNullOrEmpty(_secret) && _secret.Length < 16) _secret = (_secret + "????????????????").Substring(0, 16); + settings = SettingService.SetSetting(settings, "JwtOptions:Secret", _secret, true); + await SettingService.UpdateSiteSettingsAsync(settings, site.SiteId); await SettingService.ClearSiteSettingsCacheAsync(site.SiteId); @@ -451,4 +495,9 @@ else _redirecturl = PageState.Uri.Scheme + "://" + PageState.Alias.Name + "/signin-" + _providertype; StateHasChanged(); } + + private async Task CreateToken() + { + _token = await UserService.GetTokenAsync(); + } } diff --git a/Oqtane.Client/Services/Interfaces/IUserService.cs b/Oqtane.Client/Services/Interfaces/IUserService.cs index 483e5284d..9a80c7bde 100644 --- a/Oqtane.Client/Services/Interfaces/IUserService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserService.cs @@ -104,5 +104,10 @@ public interface IUserService /// Task ValidatePasswordAsync(string password); + /// + /// Get token for current user + /// + /// + Task GetTokenAsync(); } } diff --git a/Oqtane.Client/Services/UserService.cs b/Oqtane.Client/Services/UserService.cs index f8eeeef75..625d5cd1a 100644 --- a/Oqtane.Client/Services/UserService.cs +++ b/Oqtane.Client/Services/UserService.cs @@ -79,5 +79,10 @@ public async Task ValidatePasswordAsync(string password) { return await GetJsonAsync($"{Apiurl}/validate/{WebUtility.UrlEncode(password)}"); } + + public async Task GetTokenAsync() + { + return await GetStringAsync($"{Apiurl}/token"); + } } } diff --git a/Oqtane.Server/Controllers/SettingController.cs b/Oqtane.Server/Controllers/SettingController.cs index c409887bb..1d2ed70c3 100644 --- a/Oqtane.Server/Controllers/SettingController.cs +++ b/Oqtane.Server/Controllers/SettingController.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Authentication.Cookies; namespace Oqtane.Controllers { @@ -142,6 +143,8 @@ public void Delete(string entityName, int id) [Authorize(Roles = RoleNames.Admin)] public void Clear(int id) { + var cookieAuthenticationOptionsCache = new SiteOptionsCache(_aliasAccessor); + cookieAuthenticationOptionsCache.Clear(); var openIdConnectOptionsCache = new SiteOptionsCache(_aliasAccessor); openIdConnectOptionsCache.Clear(); var oAuthOptionsCache = new SiteOptionsCache(_aliasAccessor); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index e6b65e3ae..11288c211 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -14,6 +14,8 @@ using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; +using Oqtane.Security; +using Oqtane.Extensions; namespace Oqtane.Controllers { @@ -30,9 +32,10 @@ public class UserController : Controller private readonly IFolderRepository _folders; private readonly ISyncManager _syncManager; private readonly ISiteRepository _sites; + private readonly IJwtManager _jwtManager; private readonly ILogManager _logger; - public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager identityUserManager, SignInManager identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, ILogManager logger) + public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager identityUserManager, SignInManager identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, IJwtManager jwtManager, ILogManager logger) { _users = users; _roles = roles; @@ -44,6 +47,7 @@ public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRep _notifications = notifications; _syncManager = syncManager; _sites = sites; + _jwtManager = jwtManager; _logger = logger; } @@ -516,6 +520,24 @@ public async Task Validate(string password) return result.Succeeded; } + // GET api//token + [HttpGet("token")] + [Authorize(Roles = RoleNames.Admin)] + public string Token() + { + var token = ""; + var user = _users.GetUser(User.Identity.Name); + if (user != null) + { + var secret = HttpContext.GetSiteSettings().GetValue("JwtOptions:Secret", ""); + if (!string.IsNullOrEmpty(secret)) + { + token = _jwtManager.GenerateToken(user, secret); + } + } + return token; + } + // GET api//authenticate [HttpGet("authenticate")] public User Authenticate() diff --git a/Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs b/Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs index ea17e391b..839fe57d7 100644 --- a/Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/ApplicationBuilderExtensions.cs @@ -41,5 +41,8 @@ public static IApplicationBuilder UseOqtaneLocalization(this IApplicationBuilder public static IApplicationBuilder UseTenantResolution(this IApplicationBuilder builder) => builder.UseMiddleware(); + + public static IApplicationBuilder UseJwtAuthorization(this IApplicationBuilder builder) + => builder.UseMiddleware(); } } diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs index f580f5f9a..726175fb3 100644 --- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs @@ -85,10 +85,12 @@ internal static IServiceCollection AddOqtaneTransientServices(this IServiceColle { services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); + + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -115,6 +117,7 @@ internal static IServiceCollection AddOqtaneTransientServices(this IServiceColle services.AddTransient(); services.AddTransient(); services.AddTransient(); + // obsolete - replaced by ITenantManager services.AddTransient(); @@ -181,7 +184,7 @@ public static IServiceCollection ConfigureOqtaneIdentityOptions(this IServiceCol options.SignIn.RequireConfirmedPhoneNumber = false; // User settings - options.User.RequireUniqueEmail = false; + options.User.RequireUniqueEmail = true; options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; }); diff --git a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs index ca7f6bb8f..d233f5542 100644 --- a/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs +++ b/Oqtane.Server/Extensions/OqtaneSiteAuthenticationBuilderExtensions.cs @@ -11,13 +11,13 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Oqtane.Repository; -using System.Collections.Generic; using Oqtane.Security; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Authentication.OAuth; using System.Net.Http; using System.Net.Http.Headers; using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Authentication.Cookies; namespace Oqtane.Extensions { @@ -25,7 +25,21 @@ public static class OqtaneSiteAuthenticationBuilderExtensions { public static OqtaneSiteOptionsBuilder WithSiteAuthentication(this OqtaneSiteOptionsBuilder builder) { - // site OpenIdConnect options + // site cookie authentication options + builder.AddSiteOptions((options, alias, sitesettings) => + { + if (sitesettings.GetValue("CookieOptions:CookieType", "domain") == "domain") + { + options.Cookie.Name = ".AspNetCore.Identity.Application"; + } + else + { + // use unique cookie name for site + options.Cookie.Name = ".AspNetCore.Identity.Application" + alias.SiteKey; + } + }); + + // site OpenId Connect options builder.AddSiteOptions((options, alias, sitesettings) => { if (sitesettings.GetValue("ExternalLogin:ProviderType", "") == AuthenticationProviderTypes.OpenIDConnect) @@ -33,7 +47,7 @@ public static OqtaneSiteOptionsBuilder WithSiteAuthentication(this OqtaneSiteOpt // default options options.SignInScheme = Constants.AuthenticationScheme; // identity cookie options.RequireHttpsMetadata = true; - options.SaveTokens = true; + options.SaveTokens = false; options.GetClaimsFromUserInfoEndpoint = true; options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OpenIDConnect : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OpenIDConnect; options.ResponseType = OpenIdConnectResponseType.Code; // authorization code flow @@ -62,7 +76,7 @@ public static OqtaneSiteOptionsBuilder WithSiteAuthentication(this OqtaneSiteOpt } }); - // site OAuth2.0 options + // site OAuth 2.0 options builder.AddSiteOptions((options, alias, sitesettings) => { if (sitesettings.GetValue("ExternalLogin:ProviderType", "") == AuthenticationProviderTypes.OAuth2) @@ -70,7 +84,7 @@ public static OqtaneSiteOptionsBuilder WithSiteAuthentication(this OqtaneSiteOpt // default options options.SignInScheme = Constants.AuthenticationScheme; // identity cookie options.CallbackPath = string.IsNullOrEmpty(alias.Path) ? "/signin-" + AuthenticationProviderTypes.OAuth2 : "/" + alias.Path + "/signin-" + AuthenticationProviderTypes.OAuth2; - options.SaveTokens = true; + options.SaveTokens = false; // site options options.AuthorizationEndpoint = sitesettings.GetValue("ExternalLogin:AuthorizationUrl", ""); @@ -264,11 +278,9 @@ private static async Task LoginUser(string email, HttpContext httpContext, Claim // add claims to principal if (user != null) { - // add Oqtane claims var principal = (ClaimsIdentity)claimsPrincipal.Identity; UserSecurity.ResetClaimsIdentity(principal); - List userroles = _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList(); - var identity = UserSecurity.CreateClaimsIdentity(httpContext.GetAlias(), user, userroles); + var identity = UserSecurity.CreateClaimsIdentity(httpContext.GetAlias(), user, _userRoles.GetUserRoles(user.UserId, user.SiteId).ToList()); principal.AddClaims(identity.Claims); // update user @@ -277,7 +289,7 @@ private static async Task LoginUser(string email, HttpContext httpContext, Claim _users.UpdateUser(user); _logger.Log(LogLevel.Information, "ExternalLogin", Enums.LogFunction.Security, "External User Login Successful For {Username} Using Provider {Provider}", user.Username, providerType); } - else // user not logged in + else // user not valid { await httpContext.SignOutAsync(); } diff --git a/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs b/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs new file mode 100644 index 000000000..78333a098 --- /dev/null +++ b/Oqtane.Server/Infrastructure/Middleware/JwtMiddleware.cs @@ -0,0 +1,57 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Oqtane.Extensions; +using Oqtane.Repository; +using Oqtane.Security; +using Oqtane.Shared; + +namespace Oqtane.Infrastructure +{ + internal class JwtMiddleware + { + private readonly RequestDelegate _next; + + public JwtMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task Invoke(HttpContext context) + { + if (context.Request.Headers.ContainsKey("Authorization")) + { + var alias = context.GetAlias(); + if (alias != null) + { + var secret = context.GetSiteSettings().GetValue("JwtOptions:Secret", ""); + if (!string.IsNullOrEmpty(secret)) + { + var logger = context.RequestServices.GetService(typeof(ILogManager)) as ILogManager; + var jwtManager = context.RequestServices.GetService(typeof(IJwtManager)) as IJwtManager; + + var token = context.Request.Headers["Authorization"].First().Split(" ").Last(); + var user = jwtManager.ValidateToken(token, secret); + if (user != null) + { + // populate principal + var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository; + var principal = (ClaimsIdentity)context.User.Identity; + UserSecurity.ResetClaimsIdentity(principal); + var identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList()); + principal.AddClaims(identity.Claims); + logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For User {Username}", user.Username); + } + else + { + logger.Log(alias.SiteId, LogLevel.Error, "TokenValidation", Enums.LogFunction.Security, "Token Validation Error"); + } + } + } + } + + await _next(context); + } + } +} diff --git a/Oqtane.Server/Security/IUserPermissions.cs b/Oqtane.Server/Security/IUserPermissions.cs deleted file mode 100644 index b79643837..000000000 --- a/Oqtane.Server/Security/IUserPermissions.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Oqtane.Models; -using System.Security.Claims; - -namespace Oqtane.Security -{ - public interface IUserPermissions - { - bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName); - bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions); - User GetUser(ClaimsPrincipal user); - User GetUser(); - } -} diff --git a/Oqtane.Server/Security/JwtManager.cs b/Oqtane.Server/Security/JwtManager.cs new file mode 100644 index 000000000..5d64bf103 --- /dev/null +++ b/Oqtane.Server/Security/JwtManager.cs @@ -0,0 +1,66 @@ +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using Microsoft.IdentityModel.Tokens; +using Oqtane.Models; + +namespace Oqtane.Security +{ + public interface IJwtManager + { + string GenerateToken(User user, string secret); + User ValidateToken(string token, string secret); + } + + public class JwtManager : IJwtManager + { + public string GenerateToken(User user, string secret) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(secret); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new[] { new Claim("id", user.UserId.ToString()), new Claim("name", user.Username) }), + Expires = DateTime.UtcNow.AddYears(1), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + + public User ValidateToken(string token, string secret) + { + if (!string.IsNullOrEmpty(token)) + { + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(secret); + try + { + tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuer = false, + ValidateAudience = false, + ClockSkew = TimeSpan.Zero + }, out SecurityToken validatedToken); + + var jwtToken = (JwtSecurityToken)validatedToken; + var user = new User + { + UserId = int.Parse(jwtToken.Claims.FirstOrDefault(item => item.Type == "id")?.Value), + Username = jwtToken.Claims.FirstOrDefault(item => item.Type == "name")?.Value + }; + return user; + } + catch + { + // error validating token + } + } + return null; + } + } +} diff --git a/Oqtane.Server/Security/PermissionHandler.cs b/Oqtane.Server/Security/PermissionHandler.cs index 0b507e743..b441691b0 100644 --- a/Oqtane.Server/Security/PermissionHandler.cs +++ b/Oqtane.Server/Security/PermissionHandler.cs @@ -22,7 +22,7 @@ public PermissionHandler(IHttpContextAccessor httpContextAccessor, IUserPermissi protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement) { - // permission is scoped based on entitynames and ids passed as querystring parameters or headers + // permission is scoped based on entitynames and ids passed as querystring parameters var ctx = _httpContextAccessor.HttpContext; if (ctx != null) { diff --git a/Oqtane.Server/Security/PrincipalValidator.cs b/Oqtane.Server/Security/PrincipalValidator.cs index c254f32db..e4449a5c2 100644 --- a/Oqtane.Server/Security/PrincipalValidator.cs +++ b/Oqtane.Server/Security/PrincipalValidator.cs @@ -25,10 +25,11 @@ public static Task ValidateAsync(CookieValidatePrincipalContext context) var alias = context.HttpContext.GetAlias(); if (alias != null) { - // check if principal matches current site - if (context.Principal.Claims.FirstOrDefault(item => item.Type == ClaimTypes.GroupSid)?.Value != alias.SiteKey) + var claims = context.Principal.Claims; + + // check if principal has roles and matches current site + if (!claims.Any(item => item.Type == ClaimTypes.Role) || claims.FirstOrDefault(item => item.Type == ClaimTypes.GroupSid)?.Value != alias.SiteKey) { - // 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; @@ -39,28 +40,43 @@ public static Task ValidateAsync(CookieValidatePrincipalContext context) { // replace principal with roles for current site List userroles = userRoleRepository.GetUserRoles(user.UserId, alias.SiteId).ToList(); - var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles); - context.ReplacePrincipal(new ClaimsPrincipal(identity)); - context.ShouldRenew = true; - if (!path.StartsWith("/api/")) // reduce log verbosity + if (userroles.Any()) + { + var identity = UserSecurity.CreateClaimsIdentity(alias, user, userroles); + context.ReplacePrincipal(new ClaimsPrincipal(identity)); + context.ShouldRenew = true; + Log(_logger, alias, "Permissions Updated For User {Username} Accessing {Url}", context.Principal.Identity.Name, path); + } + else { - _logger.Log(alias.SiteId, LogLevel.Information, "LoginValidation", Enums.LogFunction.Security, "Permissions Updated For User {Username} Accessing Resource {Url}", context.Principal.Identity.Name, path); + // user has no roles - remove principal + context.RejectPrincipal(); + Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path); } } else { - // user has no roles for site - remove principal + // user does not exist - 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); - } + Log(_logger, alias, "Permissions Removed For User {Username} Accessing {Url}", context.Principal.Identity.Name, path); } } } + else + { + // user is signed in but tenant cannot be determined + } } } return Task.CompletedTask; } + + private static void Log (ILogManager logger, Alias alias, string message, string username, string path) + { + if (!path.StartsWith("/api/")) // reduce log verbosity + { + logger.Log(alias.SiteId, LogLevel.Information, "LoginValidation", Enums.LogFunction.Security, message, username, path); + } + } } } diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index f58c4d7eb..a62c827a6 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -6,6 +6,14 @@ namespace Oqtane.Security { + public interface IUserPermissions + { + bool IsAuthorized(ClaimsPrincipal user, string entityName, int entityId, string permissionName); + bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions); + User GetUser(ClaimsPrincipal user); + User GetUser(); + } + public class UserPermissions : IUserPermissions { private readonly IPermissionRepository _permissions; diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 67bf7bd57..ac33acaf7 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -167,6 +167,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ISyncMan app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseTenantResolution(); + app.UseJwtAuthorization(); app.UseBlazorFrameworkFiles(); app.UseRouting(); app.UseAuthentication(); diff --git a/Oqtane.Shared/Security/UserSecurity.cs b/Oqtane.Shared/Security/UserSecurity.cs index 0520670fc..2aec26a49 100644 --- a/Oqtane.Shared/Security/UserSecurity.cs +++ b/Oqtane.Shared/Security/UserSecurity.cs @@ -170,6 +170,11 @@ public static void ResetClaimsIdentity(ClaimsIdentity identity) { identity.RemoveClaim(claim); } + var roles = identity.Claims.Where(item => item.Type == ClaimTypes.Role); + foreach (var role in roles) + { + identity.RemoveClaim(role); + } } } }