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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,33 +1,53 @@
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)
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;
} }
private User CheckCredentials(string username, string password) public async Task<User> Authenticate(string token)
{ {
if (_paramProvider.Credentials.UserName == username && _paramProvider.Credentials.Password == password) var tokenValidation = _securityStore.ValidateToken(token);
return new User() { UserName = username, Id = 1 }; if (tokenValidation.Success)
else {
var user = _paramProvider.Users.FirstOrDefault(z => z.UserId == tokenValidation.UserId);
if (user != null)
return user;
}
return null; 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 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; }
} }

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 namespace NetworkResurrector.Domain.Services
{ {
public interface IParamProvider public interface IParamProvider
{ {
Credentials Credentials { get; } User[] Users { get; }
} }
} }