authorization system
parent
0e9d048a74
commit
3d635dab60
|
@ -3,11 +3,8 @@ using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using NetworkResurrector.Application.Services;
|
using NetworkResurrector.Application.Services;
|
||||||
using NetworkResurrector.Domain.Entities;
|
using NetworkResurrector.Domain.Entities;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -31,12 +28,9 @@ namespace NetworkResurrector.Api.Authentication
|
||||||
User user;
|
User user;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
|
||||||
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
|
var token = authorizationHeader.Parameter;
|
||||||
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
|
user = await _userService.Authenticate(token);
|
||||||
var username = credentials.First();
|
|
||||||
var password = credentials.Last();
|
|
||||||
user = await _userService.Authenticate(username, password);
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -47,7 +41,7 @@ namespace NetworkResurrector.Api.Authentication
|
||||||
return AuthenticateResult.Fail("Invalid Username or Password");
|
return AuthenticateResult.Fail("Invalid Username or Password");
|
||||||
|
|
||||||
var claims = new[] {
|
var claims = new[] {
|
||||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
|
||||||
new Claim(ClaimTypes.Name, user.UserName),
|
new Claim(ClaimTypes.Name, user.UserName),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,18 @@ namespace NetworkResurrector.Api.Controllers
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("token")]
|
[AllowAnonymous]
|
||||||
|
[HttpGet("token/{userName}/{password}")]
|
||||||
public async Task<IActionResult> GetToken([FromRoute] GetToken.Query query)
|
public async Task<IActionResult> GetToken([FromRoute] GetToken.Query query)
|
||||||
{
|
{
|
||||||
var result = await _mediator.Send(query);
|
var result = await _mediator.Send(query);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("validate-token")]
|
||||||
|
public IActionResult ValidateToken()
|
||||||
|
{
|
||||||
|
return Ok("Valid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"Credentials": {
|
"Users": [
|
||||||
"UserName": "***REMOVED***",
|
{
|
||||||
"Password": "***REMOVED***"
|
"UserId": 1,
|
||||||
}
|
"UserName": "***REMOVED***",
|
||||||
|
"Password": "***REMOVED***"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NetworkResurrector.Application.Services;
|
using NetworkResurrector.Application.Services;
|
||||||
|
using NetworkResurrector.Application.Stores;
|
||||||
using NetworkResurrector.Domain.Services;
|
using NetworkResurrector.Domain.Services;
|
||||||
|
|
||||||
namespace NetworkResurrector.Application
|
namespace NetworkResurrector.Application
|
||||||
|
@ -10,6 +11,12 @@ namespace NetworkResurrector.Application
|
||||||
{
|
{
|
||||||
services.AddSingleton<IParamProvider, ParamProvider>();
|
services.AddSingleton<IParamProvider, ParamProvider>();
|
||||||
services.AddScoped<IUserService, UserService>();
|
services.AddScoped<IUserService, UserService>();
|
||||||
|
services.AddStores();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddStores(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<ISecurityStore, SecurityStore>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using NetworkResurrector.Application.Queries;
|
||||||
|
using NetworkResurrector.Domain.Models;
|
||||||
|
|
||||||
namespace NetworkResurrector.Application.Mappings
|
namespace NetworkResurrector.Application.Mappings
|
||||||
{
|
{
|
||||||
|
@ -6,6 +8,7 @@ namespace NetworkResurrector.Application.Mappings
|
||||||
{
|
{
|
||||||
public MappingProfile()
|
public MappingProfile()
|
||||||
{
|
{
|
||||||
|
CreateMap<SecurityToken, GetToken.Model>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
using NetworkResurrector.Application.Services;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -17,23 +18,29 @@ namespace NetworkResurrector.Application.Queries
|
||||||
|
|
||||||
public class Model
|
public class Model
|
||||||
{
|
{
|
||||||
public Guid Token { get; set; }
|
public string Token { get; set; }
|
||||||
public DateTime ValidUntil { get; set; }
|
public DateTime ValidUntil { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QueryHandler : IRequestHandler<Query, Model>
|
public class QueryHandler : IRequestHandler<Query, Model>
|
||||||
{
|
{
|
||||||
|
private readonly IUserService _userService;
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
public QueryHandler(IMapper mapper)
|
public QueryHandler(IUserService userService, IMapper mapper)
|
||||||
{
|
{
|
||||||
|
_userService = userService;
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
|
public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
var securityToken = await _userService.Login(request.UserName, request.Password);
|
||||||
return new Model() { Token = Guid.NewGuid(), ValidUntil = DateTime.Now };
|
if (securityToken == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var result = _mapper.Map<Model>(securityToken);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using NetworkResurrector.Domain.Models.Settings;
|
using NetworkResurrector.Domain.Entities;
|
||||||
using NetworkResurrector.Domain.Services;
|
using NetworkResurrector.Domain.Services;
|
||||||
|
|
||||||
namespace NetworkResurrector.Application.Services
|
namespace NetworkResurrector.Application.Services
|
||||||
|
@ -13,6 +13,6 @@ namespace NetworkResurrector.Application.Services
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Credentials Credentials => _configuration.GetSection("Credentials").Get<Credentials>();
|
public User[] Users => _configuration.GetSection("Users").Get<User[]>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,54 @@
|
||||||
using NetworkResurrector.Domain.Entities;
|
using NetworkResurrector.Application.Stores;
|
||||||
|
using NetworkResurrector.Domain.Entities;
|
||||||
|
using NetworkResurrector.Domain.Models;
|
||||||
using NetworkResurrector.Domain.Services;
|
using NetworkResurrector.Domain.Services;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace NetworkResurrector.Application.Services
|
namespace NetworkResurrector.Application.Services
|
||||||
{
|
{
|
||||||
public interface IUserService
|
public interface IUserService
|
||||||
{
|
{
|
||||||
Task<User> Authenticate(string username, string password);
|
Task<SecurityToken> Login(string userName, string password);
|
||||||
|
Task<User> Authenticate(string token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserService : IUserService
|
public class UserService : IUserService
|
||||||
{
|
{
|
||||||
private readonly IParamProvider _paramProvider;
|
private readonly IParamProvider _paramProvider;
|
||||||
|
private readonly ISecurityStore _securityStore;
|
||||||
|
|
||||||
public UserService(IParamProvider paramProvider)
|
public UserService(IParamProvider paramProvider, ISecurityStore securityStore)
|
||||||
{
|
{
|
||||||
_paramProvider = paramProvider;
|
_paramProvider = paramProvider;
|
||||||
|
_securityStore = securityStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User> Authenticate(string username, string password)
|
public async Task<SecurityToken> Login(string userName, string password)
|
||||||
{
|
{
|
||||||
return await Task.Run(() => CheckCredentials(username, password));
|
var user = _paramProvider.Users.FirstOrDefault(z => z.UserName == userName && z.Password == password);
|
||||||
}
|
if (user == null)
|
||||||
|
|
||||||
private User CheckCredentials(string username, string password)
|
|
||||||
{
|
|
||||||
if (_paramProvider.Credentials.UserName == username && _paramProvider.Credentials.Password == password)
|
|
||||||
return new User() { UserName = username, Id = 1 };
|
|
||||||
else
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
var token = $"{Guid.NewGuid()}-{Guid.NewGuid()}-{user.UserId}";
|
||||||
|
_securityStore.SetToken(token, user.UserId);
|
||||||
|
|
||||||
|
var securityToken = new SecurityToken() { UserId = user.UserId, Token = token };
|
||||||
|
return securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User> Authenticate(string token)
|
||||||
|
{
|
||||||
|
var tokenValidation = _securityStore.ValidateToken(token);
|
||||||
|
if (tokenValidation.Success)
|
||||||
|
{
|
||||||
|
var user = _paramProvider.Users.FirstOrDefault(z => z.UserId == tokenValidation.UserId);
|
||||||
|
if (user != null)
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
using NetworkResurrector.Domain.Models;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Stores
|
||||||
|
{
|
||||||
|
public interface ISecurityStore
|
||||||
|
{
|
||||||
|
void SetToken(string token, int userId);
|
||||||
|
TokenValidation ValidateToken(string token);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using NetworkResurrector.Domain.Models;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Application.Stores
|
||||||
|
{
|
||||||
|
public class SecurityStore : ISecurityStore
|
||||||
|
{
|
||||||
|
private List<SecurityToken> Tokens { get; }
|
||||||
|
|
||||||
|
public SecurityStore()
|
||||||
|
{
|
||||||
|
Tokens = new List<SecurityToken>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetToken(string token, int userId)
|
||||||
|
{
|
||||||
|
var registered = Tokens.FirstOrDefault(z => z.UserId == userId);
|
||||||
|
if (registered == null)
|
||||||
|
Tokens.Add(new SecurityToken() { UserId = userId, Token = token });
|
||||||
|
else
|
||||||
|
registered.Token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenValidation ValidateToken(string token)
|
||||||
|
{
|
||||||
|
var lastIndexOfSeparator = token.LastIndexOf('-') + 1;
|
||||||
|
var userIdString = token.Substring(lastIndexOfSeparator, token.Length - lastIndexOfSeparator);
|
||||||
|
|
||||||
|
if (!int.TryParse(userIdString, out int userId))
|
||||||
|
return InvalidToken;
|
||||||
|
|
||||||
|
var valid = Tokens.FirstOrDefault(z => z.UserId == userId && z.Token == token);
|
||||||
|
if (valid != null)
|
||||||
|
return new TokenValidation() { Success = true, UserId = userId };
|
||||||
|
|
||||||
|
return InvalidToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TokenValidation InvalidToken => new TokenValidation() { Success = false };
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
{
|
{
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int UserId { get; set; }
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NetworkResurrector.Domain.Models
|
||||||
|
{
|
||||||
|
public class SecurityToken
|
||||||
|
{
|
||||||
|
public int UserId { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
public DateTime ValidUntil { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NetworkResurrector.Domain.Models
|
||||||
|
{
|
||||||
|
public class TokenValidation
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public int UserId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
using NetworkResurrector.Domain.Models.Settings;
|
using NetworkResurrector.Domain.Entities;
|
||||||
|
|
||||||
namespace NetworkResurrector.Domain.Services
|
namespace NetworkResurrector.Domain.Services
|
||||||
{
|
{
|
||||||
public interface IParamProvider
|
public interface IParamProvider
|
||||||
{
|
{
|
||||||
Credentials Credentials { get; }
|
User[] Users { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue