Merge pull request #2095 from sbwalker/dev

better seperation of concerns
This commit is contained in:
Shaun Walker 2022-03-31 08:35:29 -04:00 committed by GitHub
commit f8e04656cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 24 deletions

View File

@ -95,15 +95,18 @@ else
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> @if (!string.IsNullOrEmpty(PageState.Alias.Path))
<Label Class="col-sm-3" For="cookietype" HelpText="Cookies are usually managed per domain. However you can also choose to have distinct cookies for each site." ResourceKey="CookieType">Cookie Type:</Label> {
<div class="col-sm-9"> <div class="row mb-1 align-items-center">
<select id="cookietype" class="form-select" @bind="@_cookietype"> <Label Class="col-sm-3" For="cookietype" HelpText="Cookies are usually managed per domain. However you can also choose to have distinct cookies for each site (this option is only applicable to micro-sites)." ResourceKey="CookieType">Cookie Type:</Label>
<option value="domain">@Localizer["Domain"]</option> <div class="col-sm-9">
<option value="site">@Localizer["Site"]</option> <select id="cookietype" class="form-select" @bind="@_cookietype">
</select> <option value="domain">@Localizer["Domain"]</option>
<option value="site">@Localizer["Site"]</option>
</select>
</div>
</div> </div>
</div> }
</Section> </Section>
<Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings"> <Section Name="Password" Heading="Password Settings" ResourceKey="PasswordSettings">
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">

View File

@ -319,7 +319,7 @@
<value>User Settings</value> <value>User Settings</value>
</data> </data>
<data name="CookieType.HelpText" xml:space="preserve"> <data name="CookieType.HelpText" xml:space="preserve">
<value>Login cookies are usually managed per domain. However you can also choose to have distinct cookies for each site.</value> <value>Cookies are usually managed per domain. However you can also choose to have distinct cookies for each site (this option is only applicable to micro-sites).</value>
</data> </data>
<data name="CookieType.Text" xml:space="preserve"> <data name="CookieType.Text" xml:space="preserve">
<value>Login Cookie Type:</value> <value>Login Cookie Type:</value>

View File

@ -3,6 +3,7 @@ using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Oqtane.Extensions; using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository; using Oqtane.Repository;
using Oqtane.Security; using Oqtane.Security;
using Oqtane.Shared; using Oqtane.Shared;
@ -33,14 +34,22 @@ namespace Oqtane.Infrastructure
var jwtManager = context.RequestServices.GetService(typeof(IJwtManager)) as IJwtManager; var jwtManager = context.RequestServices.GetService(typeof(IJwtManager)) as IJwtManager;
var token = context.Request.Headers["Authorization"].First().Split(" ").Last(); var token = context.Request.Headers["Authorization"].First().Split(" ").Last();
var user = jwtManager.ValidateToken(token, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", "")); var identity = jwtManager.ValidateToken(token, secret, sitesettings.GetValue("JwtOptions:Issuer", ""), sitesettings.GetValue("JwtOptions:Audience", ""));
if (user != null) if (identity != null && identity.Claims.Any())
{ {
// populate principal (reload user roles to ensure most accurate permissions) // create user identity using jwt claims (note the difference in claimtype names)
var user = new User
{
UserId = int.Parse(identity.Claims.FirstOrDefault(item => item.Type == "nameid")?.Value),
Username = identity.Claims.FirstOrDefault(item => item.Type == "name")?.Value
};
// jwt already contains the roles - we are reloading to ensure most accurate permissions
var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository; var _userRoles = context.RequestServices.GetService(typeof(IUserRoleRepository)) as IUserRoleRepository;
identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
// populate principal
var principal = (ClaimsIdentity)context.User.Identity; var principal = (ClaimsIdentity)context.User.Identity;
UserSecurity.ResetClaimsIdentity(principal); UserSecurity.ResetClaimsIdentity(principal);
var identity = UserSecurity.CreateClaimsIdentity(alias, user, _userRoles.GetUserRoles(user.UserId, alias.SiteId).ToList());
principal.AddClaims(identity.Claims); principal.AddClaims(identity.Claims);
logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For User {Username}", user.Username); logger.Log(alias.SiteId, LogLevel.Information, "TokenValidation", Enums.LogFunction.Security, "Token Validated For User {Username}", user.Username);
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
@ -10,19 +9,19 @@ namespace Oqtane.Security
{ {
public interface IJwtManager public interface IJwtManager
{ {
string GenerateToken(Alias alias, ClaimsIdentity user, string secret, string issuer, string audience, int lifetime); string GenerateToken(Alias alias, ClaimsIdentity identity, string secret, string issuer, string audience, int lifetime);
User ValidateToken(string token, string secret, string issuer, string audience); ClaimsIdentity ValidateToken(string token, string secret, string issuer, string audience);
} }
public class JwtManager : IJwtManager public class JwtManager : IJwtManager
{ {
public string GenerateToken(Alias alias, ClaimsIdentity user, string secret, string issuer, string audience, int lifetime) public string GenerateToken(Alias alias, ClaimsIdentity identity, string secret, string issuer, string audience, int lifetime)
{ {
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(secret); var key = Encoding.ASCII.GetBytes(secret);
var tokenDescriptor = new SecurityTokenDescriptor var tokenDescriptor = new SecurityTokenDescriptor
{ {
Subject = new ClaimsIdentity(user), Subject = new ClaimsIdentity(identity),
Issuer = issuer, Issuer = issuer,
Audience = audience, Audience = audience,
Expires = DateTime.UtcNow.AddMinutes(lifetime), Expires = DateTime.UtcNow.AddMinutes(lifetime),
@ -32,7 +31,7 @@ namespace Oqtane.Security
return tokenHandler.WriteToken(token); return tokenHandler.WriteToken(token);
} }
public User ValidateToken(string token, string secret, string issuer, string audience) public ClaimsIdentity ValidateToken(string token, string secret, string issuer, string audience)
{ {
if (!string.IsNullOrEmpty(token)) if (!string.IsNullOrEmpty(token))
{ {
@ -53,12 +52,12 @@ namespace Oqtane.Security
}, out SecurityToken validatedToken); }, out SecurityToken validatedToken);
var jwtToken = (JwtSecurityToken)validatedToken; var jwtToken = (JwtSecurityToken)validatedToken;
var user = new User var identity = new ClaimsIdentity();
foreach (var claim in jwtToken.Claims)
{ {
UserId = int.Parse(jwtToken.Claims.FirstOrDefault(item => item.Type == "nameid")?.Value), identity.AddClaim(claim);
Username = jwtToken.Claims.FirstOrDefault(item => item.Type == "name")?.Value }
}; return identity;
return user;
} }
catch catch
{ {