add Jwt authorization support for for API
This commit is contained in:
@ -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();
|
||||
}
|
||||
}
|
66
Oqtane.Server/Security/JwtManager.cs
Normal file
66
Oqtane.Server/Security/JwtManager.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace Oqtane.Security
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -25,10 +25,11 @@ namespace Oqtane.Security
|
||||
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 @@ namespace Oqtane.Security
|
||||
{
|
||||
// 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;
|
||||
if (!path.StartsWith("/api/")) // reduce log verbosity
|
||||
if (userroles.Any())
|
||||
{
|
||||
_logger.Log(alias.SiteId, LogLevel.Information, "LoginValidation", Enums.LogFunction.Security, "Permissions Updated For User {Username} Accessing Resource {Url}", context.Principal.Identity.Name, path);
|
||||
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
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,14 @@ using Oqtane.Repository;
|
||||
|
||||
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;
|
||||
|
Reference in New Issue
Block a user