authorization system

master
Tudor Stanciu 2020-07-10 00:29:39 +03:00
parent 0e9d048a74
commit 3d635dab60
15 changed files with 149 additions and 37 deletions

View File

@ -3,11 +3,8 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NetworkResurrector.Application.Services;
using NetworkResurrector.Domain.Entities;
using System;
using System.Linq;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
@ -31,12 +28,9 @@ namespace NetworkResurrector.Api.Authentication
User user;
try
{
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':');
var username = credentials.First();
var password = credentials.Last();
user = await _userService.Authenticate(username, password);
var authorizationHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
var token = authorizationHeader.Parameter;
user = await _userService.Authenticate(token);
}
catch
{
@ -47,7 +41,7 @@ namespace NetworkResurrector.Api.Authentication
return AuthenticateResult.Fail("Invalid Username or Password");
var claims = new[] {
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
};

View File

@ -18,11 +18,18 @@ namespace NetworkResurrector.Api.Controllers
_mediator = mediator;
}
[HttpGet("token")]
[AllowAnonymous]
[HttpGet("token/{userName}/{password}")]
public async Task<IActionResult> GetToken([FromRoute] GetToken.Query query)
{
var result = await _mediator.Send(query);
return Ok(result);
}
[HttpGet("validate-token")]
public IActionResult ValidateToken()
{
return Ok("Valid");
}
}
}

View File

@ -11,8 +11,11 @@
}
},
"AllowedHosts": "*",
"Credentials": {
"UserName": "***REMOVED***",
"Password": "***REMOVED***"
}
"Users": [
{
"UserId": 1,
"UserName": "***REMOVED***",
"Password": "***REMOVED***"
}
]
}

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using NetworkResurrector.Application.Services;
using NetworkResurrector.Application.Stores;
using NetworkResurrector.Domain.Services;
namespace NetworkResurrector.Application
@ -10,6 +11,12 @@ namespace NetworkResurrector.Application
{
services.AddSingleton<IParamProvider, ParamProvider>();
services.AddScoped<IUserService, UserService>();
services.AddStores();
}
private static void AddStores(this IServiceCollection services)
{
services.AddSingleton<ISecurityStore, SecurityStore>();
}
}
}

View File

@ -1,4 +1,6 @@
using AutoMapper;
using NetworkResurrector.Application.Queries;
using NetworkResurrector.Domain.Models;
namespace NetworkResurrector.Application.Mappings
{
@ -6,6 +8,7 @@ namespace NetworkResurrector.Application.Mappings
{
public MappingProfile()
{
CreateMap<SecurityToken, GetToken.Model>();
}
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>

View File

@ -1,5 +1,6 @@
using AutoMapper;
using MediatR;
using NetworkResurrector.Application.Services;
using System;
using System.Threading;
using System.Threading.Tasks;
@ -17,23 +18,29 @@ namespace NetworkResurrector.Application.Queries
public class Model
{
public Guid Token { get; set; }
public string Token { get; set; }
public DateTime ValidUntil { get; set; }
}
public class QueryHandler : IRequestHandler<Query, Model>
{
private readonly IUserService _userService;
private readonly IMapper _mapper;
public QueryHandler(IMapper mapper)
public QueryHandler(IUserService userService, IMapper mapper)
{
_userService = userService;
_mapper = mapper;
}
public async Task<Model> Handle(Query request, CancellationToken cancellationToken)
{
var securityToken = await _userService.Login(request.UserName, request.Password);
if (securityToken == null)
return null;
return new Model() { Token = Guid.NewGuid(), ValidUntil = DateTime.Now };
var result = _mapper.Map<Model>(securityToken);
return result;
}
}
}

View File

@ -1,5 +1,5 @@
using Microsoft.Extensions.Configuration;
using NetworkResurrector.Domain.Models.Settings;
using NetworkResurrector.Domain.Entities;
using NetworkResurrector.Domain.Services;
namespace NetworkResurrector.Application.Services
@ -13,6 +13,6 @@ namespace NetworkResurrector.Application.Services
_configuration = configuration;
}
public Credentials Credentials => _configuration.GetSection("Credentials").Get<Credentials>();
public User[] Users => _configuration.GetSection("Users").Get<User[]>();
}
}

View File

@ -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 System;
using System.Linq;
using System.Threading.Tasks;
namespace NetworkResurrector.Application.Services
{
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
{
private readonly IParamProvider _paramProvider;
private readonly ISecurityStore _securityStore;
public UserService(IParamProvider paramProvider)
public UserService(IParamProvider paramProvider, ISecurityStore securityStore)
{
_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));
}
private User CheckCredentials(string username, string password)
{
if (_paramProvider.Credentials.UserName == username && _paramProvider.Credentials.Password == password)
return new User() { UserName = username, Id = 1 };
else
var user = _paramProvider.Users.FirstOrDefault(z => z.UserName == userName && z.Password == password);
if (user == 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;
}
}
}

View File

@ -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);
}
}

View File

@ -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 };
}
}

View File

@ -2,7 +2,7 @@
{
public class User
{
public int Id { get; set; }
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}

View File

@ -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; }
}
}

View File

@ -0,0 +1,8 @@
namespace NetworkResurrector.Domain.Models
{
public class TokenValidation
{
public bool Success { get; set; }
public int UserId { get; set; }
}
}

View File

@ -1,9 +1,9 @@
using NetworkResurrector.Domain.Models.Settings;
using NetworkResurrector.Domain.Entities;
namespace NetworkResurrector.Domain.Services
{
public interface IParamProvider
{
Credentials Credentials { get; }
User[] Users { get; }
}
}