(feat): custom authorization

This commit is contained in:
Maksym Sadovnychyy 2022-10-28 22:22:24 +02:00
parent c1e343346d
commit 916c1ff1b3
29 changed files with 750 additions and 397 deletions

View File

@ -45,7 +45,7 @@
"confirmed": false
}
],
"tokens": [],
"billingAddress": {
"street": "",
"city": "",
@ -57,6 +57,67 @@
"city": "",
"postCode": "",
"country": ""
}
},
"tokens": [],
"authorizations": [
{
"controller": 0,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 1,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 2,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 3,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 4,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 5,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 6,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 7,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 8,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 9,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 10,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 11,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 12,
"actions": [ 0, 1, 2, 3 ]
},
{
"controller": 13,
"actions": [ 0, 1, 2, 3 ]
}
]
}
]

View File

@ -13,6 +13,7 @@ namespace Core.DomainObjects {
public Passwords Passwords { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public List<Contact> Contacts { get; set; }
@ -23,6 +24,8 @@ namespace Core.DomainObjects {
public List<Token> Tokens { get; set; }
public List<UserAuthorizations> Authorizations { get; set; }
public override int GetHashCode() {
throw new NotImplementedException();
}

View File

@ -0,0 +1,14 @@
using Core.Enumerations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Core.DomainObjects {
public class UserAuthorizations {
public WebapiControllers Controller { get; set; }
public List<CrudActions> Actions { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using Core.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Core.Enumerations {
public class CrudActions : Enumeration {
public static CrudActions Create = new (0, "Create");
public static CrudActions Read = new (1, "Read");
public static CrudActions Update = new (2, "Update");
public static CrudActions Delete = new (3, "Delete");
private CrudActions(int id, string displayName) : base(id, displayName) { }
}
}

View File

@ -0,0 +1,33 @@
using Core.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Core.Enumerations {
public class WebapiControllers : Enumeration {
public static WebapiControllers Authentication = new (0, "Authentication");
public static WebapiControllers BlogItem = new(1, "BlogItem");
public static WebapiControllers BlogItems = new(2, "BlogItems");
public static WebapiControllers CategoryItem = new(3, "CategoryItem");
public static WebapiControllers CategoryItems = new(4, "CategoryItems");
public static WebapiControllers File = new(5, "File");
public static WebapiControllers Files = new(6, "Files");
public static WebapiControllers Image = new(7, "Image");
public static WebapiControllers Password = new(8, "Password");
public static WebapiControllers ShopCartItem = new(9, "ShopCartItem");
public static WebapiControllers ShopCartItems = new(10, "ShopCartItems");
public static WebapiControllers ShopItem = new(11, "ShopItem");
public static WebapiControllers ShopItems = new(12, "ShopItems");
public static WebapiControllers Account = new(13, "Account");
public static WebapiControllers Content = new(14, "Content");
private WebapiControllers(int id, string displayName) : base(id, displayName) { }
}
}

View File

@ -161,6 +161,18 @@ namespace DataProviders {
cm.AutoMap();
});
}
if (!BsonClassMap.IsClassMapRegistered(typeof(UserAuthorizations))) {
BsonClassMap.RegisterClassMap<UserAuthorizations>(cm => {
cm.AutoMap();
cm.GetMemberMap(c => c.Controller)
.SetSerializer(new EnumerationSerializer<WebapiControllers>());
cm.GetMemberMap(c => c.Actions)
.SetSerializer(new EnumerationListSerializer<CrudActions>());
});
}
#endregion
@ -347,11 +359,6 @@ namespace DataProviders {
}
#endregion
#region User
if (!BsonClassMap.IsClassMapRegistered(typeof(User))) {
BsonClassMap.RegisterClassMap<User>(cm => {

View File

@ -20,8 +20,6 @@ namespace Extensions {
return headers != null ? headers : new List<string>();
}
/// <summary>
/// Return clean JWT Bearer token from Authorisation Header
/// </summary>
@ -31,15 +29,5 @@ namespace Extensions {
? header.Replace("Bearer ", "")
: default;
}
/// <summary>
/// Returns JWT Bearer token Vault path from custom AuthorizationPath Header
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static string? GetBearerPath(this HttpRequest request) {
var header = request.GetHeader("AuthorizationPath").FirstOrDefault();
return header;
}
}
}

View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class AccountController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
/// <summary>
///
/// </summary>
public AccountController(
IAuthorizationService authorizationService
) {
_authorizationService = authorizationService;
}
}

View File

@ -1,9 +1,10 @@

using Core.Enumerations;
using DomainResults.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using WeatherForecast.Services;
namespace WeatherForecast.Controllers;
@ -11,21 +12,26 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class AuthenticationController : ControllerBase {
private readonly IAuthenticationService _authenticationService;
private readonly IAuthorizationService _authorizationService;
private readonly IUserService _userService;
private readonly WebapiControllers _webapiController = WebapiControllers.Authentication;
/// <summary>
///
/// </summary>
/// <param name="authenticationService"></param>
public AuthenticationController(
IAuthenticationService authenticationService
IAuthorizationService authorizationService,
IUserService authenticationService
) {
_authenticationService = authenticationService;
_authorizationService = authorizationService;
_userService = authenticationService;
}
/// <summary>
@ -33,10 +39,9 @@ public class AuthenticationController : ControllerBase {
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost()]
[HttpPost]
public IActionResult Post([FromBody] AuthenticationRequestModel requestData) {
var result = _authenticationService.Post(requestData);
var result = _userService.CreateToken(requestData);
return result.ToActionResult();
}
@ -44,11 +49,16 @@ public class AuthenticationController : ControllerBase {
///
/// </summary>
/// <returns></returns>
[Authorize(Policy = "WhitelistToken")]
[HttpGet()]
[HttpGet]
public async Task<IActionResult> Get() {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
public IActionResult Get() {
return Ok();
}
else {
return Unauthorized();
}
}
}

View File

@ -5,26 +5,33 @@ using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class BlogItemController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IBlogItemService _blogItemService;
private readonly WebapiControllers _webapiController = WebapiControllers.BlogItem;
/// <summary>
///
/// </summary>
/// <param name="authorizationService"></param>
/// <param name="blogItemService"></param>
public BlogItemController(
IAuthorizationService authorizationService,
IBlogItemService blogItemService
) {
_authorizationService = authorizationService;
_blogItemService = blogItemService;
}
@ -35,20 +42,30 @@ public class BlogItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPost("{siteId}")]
public IActionResult Post([FromRoute] Guid siteId, [FromBody] BlogItemRequestModel requestData) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromBody] BlogItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _blogItemService.Post(siteId, requestData);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
/// Returns full object
/// </summary>
/// <returns></returns>
[HttpGet("{siteId}/{blogId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid blogId) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] Guid blogId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
var result = _blogItemService.Get(siteId, blogId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
///
@ -56,7 +73,6 @@ public class BlogItemController : ControllerBase {
/// <param name="siteId"></param>
/// <param name="slug"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{siteId}")]
public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) {
var result = _blogItemService.GetSlug(siteId, slug);
@ -71,10 +87,15 @@ public class BlogItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPut("{siteId}/{blogId}")]
public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid blogId, [FromBody] BlogItemRequestModel requestData) {
public async Task<IActionResult> Update([FromRoute] Guid siteId, [FromRoute] Guid blogId, [FromBody] BlogItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) {
var result = _blogItemService.Update(siteId, blogId, requestData);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
///
@ -83,9 +104,14 @@ public class BlogItemController : ControllerBase {
/// <param name="blogId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{blogId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid blogId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid blogId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) {
var result = _blogItemService.Delete(siteId, blogId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
}

View File

@ -4,26 +4,33 @@ using Microsoft.AspNetCore.Authorization;
using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class BlogItemsController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IBlogItemsService _blogItemsService;
private readonly WebapiControllers _webapiController = WebapiControllers.BlogItems;
/// <summary>
///
/// </summary>
/// <param name="authorizationService"></param>
/// <param name="blogItemsService"></param>
public BlogItemsController(
IAuthorizationService authorizationService,
IBlogItemsService blogItemsService
) {
_authorizationService = authorizationService;
_blogItemsService = blogItemsService;
}
@ -37,7 +44,6 @@ public class BlogItemsController : ControllerBase {
/// <param name="itemsPerPage"></param>
/// <param name="locale"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{siteId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromQuery] Guid? category, [FromQuery] string? searchText, [FromQuery] int? currentPage, [FromQuery] int? itemsPerPage, [FromQuery] string? locale) {
var result = _blogItemsService.Get(siteId, category, currentPage ?? 1, itemsPerPage ?? 8, locale, searchText);
@ -49,11 +55,16 @@ public class BlogItemsController : ControllerBase {
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
[Authorize]
[HttpDelete("{siteId}")]
public IActionResult Delete([FromRoute] Guid siteId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _blogItemsService.Delete(siteId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
}

View File

@ -5,25 +5,33 @@ using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class CategoryItemController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly ICategoryItemService _categoryItemService;
private readonly WebapiControllers _webapiController = WebapiControllers.CategoryItem;
/// <summary>
///
/// </summary>
/// <param name="authorizationService"></param>
/// <param name="categoryItemService"></param>
public CategoryItemController(
ICategoryItemService categoryItemService) {
IAuthorizationService authorizationService,
ICategoryItemService categoryItemService
) {
_authorizationService = authorizationService;
_categoryItemService = categoryItemService;
}
@ -34,10 +42,15 @@ public class CategoryItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPost("{siteId}")]
public IActionResult Post([FromRoute] Guid siteId, [FromBody] CategoryItemRequestModel requestData) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromBody] CategoryItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _categoryItemService.Post(siteId, requestData);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
/// Returns full object
@ -46,17 +59,21 @@ public class CategoryItemController : ControllerBase {
/// <param name="categoryId"></param>
/// <returns></returns>
[HttpGet("{siteId}/{categoryId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid categoryId) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] Guid categoryId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
var result = _categoryItemService.Get(siteId, categoryId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="slug"></param>
[AllowAnonymous]
[HttpGet("{siteId}")]
public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) {
var result = _categoryItemService.GetSlug(siteId, slug);
@ -71,10 +88,15 @@ public class CategoryItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPut("{siteId}/{categoryId}")]
public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid categoryId, [FromBody] CategoryItemRequestModel requestData) {
public async Task<IActionResult> Update([FromRoute] Guid siteId, [FromRoute] Guid categoryId, [FromBody] CategoryItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) {
var result = _categoryItemService.Update(siteId, categoryId, requestData);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
/// <summary>
///
@ -83,9 +105,14 @@ public class CategoryItemController : ControllerBase {
/// <param name="categoryId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{categoryId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid categoryId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid categoryId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _categoryItemService.Delete(siteId, categoryId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
}

View File

@ -4,26 +4,33 @@ using Microsoft.AspNetCore.Authorization;
using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class CategoryItemsController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly ICategoryItemsService _categoryItemsService;
private readonly WebapiControllers _webapiController = WebapiControllers.CategoryItem;
/// <summary>
///
/// </summary>
/// <param name="authorizationService"></param>
/// <param name="categoryItemsService"></param>
public CategoryItemsController(
IAuthorizationService authorizationService,
ICategoryItemsService categoryItemsService
) {
_authorizationService = authorizationService;
_categoryItemsService = categoryItemsService;
}
@ -44,11 +51,15 @@ public class CategoryItemsController : ControllerBase {
/// </summary>
/// <param name="siteId"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpDelete("{siteId}")]
public IActionResult Delete([FromRoute] Guid siteId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _categoryItemsService.Delete(siteId);
return result.ToActionResult();
}
else {
return Unauthorized();
}
}
}

View File

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authorization;
using DomainResults.Mvc;
using WeatherForecast.Services;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
@ -11,20 +12,24 @@ namespace WeatherForecast.Controllers;
///
/// </summary>
[ApiController]
[AllowAnonymous]
[Route("api/[controller]")]
public class ContentController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IContentService _contentService;
private readonly WebapiControllers _webapiController = WebapiControllers.Content;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="authorizationService"></param>
/// <param name="contentService"></param>
public ContentController(
IAuthorizationService authorizationService,
IContentService contentService
) {
_authorizationService = authorizationService;
_contentService = contentService;
}

View File

@ -1,7 +1,9 @@
using DomainResults.Mvc;
using Core.Enumerations;
using DomainResults.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Net.Http.Headers;
using WeatherForecast.Policies;
using WeatherForecast.Services;
namespace WeatherForecast.Controllers;
@ -9,20 +11,24 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[AllowAnonymous]
[Route("api/[controller]")]
public class FileController : Controller {
public class FileController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IFileService _fileService;
private readonly WebapiControllers _webapiController = WebapiControllers.File;
/// <summary>
///
/// </summary>
/// <param name="fileService"></param>
public FileController(
IAuthorizationService authorizationService,
IFileService fileService
) {
_authorizationService = authorizationService;
_fileService = fileService;
}
@ -34,11 +40,15 @@ public class FileController : Controller {
/// <param name="file"></param>
/// <returns></returns>
[HttpPost("{siteId}/{userId}")]
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, IFormFile file) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] Guid userId, IFormFile file) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _fileService.Post(siteId, userId, file);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
/// https://www.c-sharpcorner.com/article/fileresult-in-asp-net-core-mvc2/
/// </summary>
@ -47,7 +57,9 @@ public class FileController : Controller {
/// <param name="fileId"></param>
/// <returns></returns>
[HttpGet("{siteId}/{userId}/{fileId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userid, [FromRoute] Guid fileId) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] Guid userid, [FromRoute] Guid fileId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
var (file, result) = _fileService.Get(siteId, userid, fileId);
if (!result.IsSuccess || file == null)
@ -59,6 +71,9 @@ public class FileController : Controller {
};
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -67,9 +82,13 @@ public class FileController : Controller {
/// <param name="fileId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{userId}/{fileId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] Guid fileId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] Guid fileId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _fileService.Delete(siteId, userId, fileId);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -1,6 +1,8 @@
using DomainResults.Mvc;
using Core.Enumerations;
using DomainResults.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WeatherForecast.Policies;
using WeatherForecast.Services;
namespace WeatherForecast.Controllers;
@ -8,20 +10,24 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[AllowAnonymous]
[Route("api/[controller]")]
public class FilesController : Controller {
public class FilesController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IFilesService _filesService;
private readonly WebapiControllers _webapiController = WebapiControllers.Files;
/// <summary>
///
/// </summary>
/// <param name="filesService"></param>
public FilesController(
IAuthorizationService authorizationService,
IFilesService filesService
) {
_authorizationService = authorizationService;
_filesService = filesService;
}
@ -33,11 +39,16 @@ public class FilesController : Controller {
/// <param name="file"></param>
/// <returns></returns>
[HttpPost("{siteId}/{userId}")]
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, List<IFormFile> file) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] Guid userId, List<IFormFile> file) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _filesService.Post(siteId, userId, file);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -46,9 +57,12 @@ public class FilesController : Controller {
/// <returns></returns>
[HttpDelete("{siteId}/{userId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _filesService.Delete(siteId, userId);
return result.ToActionResult();
}
}
return Unauthorized();
}
}

View File

@ -9,11 +9,11 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[AllowAnonymous]
[Route("api/[controller]")]
[ApiController]
public class ImageController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IImageService _imageService;
/// <summary>
@ -21,8 +21,10 @@ public class ImageController : ControllerBase {
/// </summary>
/// <param name="imgeService"></param>
public ImageController(
IAuthorizationService authorizationService,
IImageService imgeService
) {
_authorizationService = authorizationService;
_imageService = imgeService;
}

View File

@ -1,8 +1,10 @@
using DomainResults.Mvc;
using Core.Enumerations;
using DomainResults.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using WeatherForecast.Services;
namespace WeatherForecast.Controllers;
@ -10,20 +12,24 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[Route("api/[controller]")]
[ApiController]
public class PasswordController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IPasswordService _passwordService;
private readonly WebapiControllers _webapiController = WebapiControllers.Password;
/// <summary>
///
/// </summary>
/// <param name="passwordService"></param>
public PasswordController(
IAuthorizationService authorizationService,
IPasswordService passwordService
) {
_authorizationService = authorizationService;
_passwordService = passwordService;
}
@ -35,9 +41,13 @@ public class PasswordController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPost("{siteId}/{userId}")]
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromBody] PasswordRequestModel requestData) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromBody] PasswordRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _passwordService.Post(siteId, userId, requestData);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -5,6 +5,8 @@ using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
@ -12,20 +14,24 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[AllowAnonymous]
[Route("api/[controller]")]
public class ShopCartItemController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IShopCartItemService _shopCartItemService;
private readonly WebapiControllers _webapiController = WebapiControllers.ShopCartItem;
/// <summary>
///
/// </summary>
/// <param name="shopCartItemService"></param>
public ShopCartItemController(
IAuthorizationService authorizationService,
IShopCartItemService shopCartItemService
) {
_authorizationService = authorizationService;
_shopCartItemService = shopCartItemService;
}
@ -38,21 +44,29 @@ public class ShopCartItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPost("{siteId}/{userId}/{sku}")]
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _shopCartItemService.Post(siteId, userId, sku, requestData);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
[HttpGet("{siteId}/{userId}/{sku}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromQuery] string? locale) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromQuery] string? locale) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
var result = _shopCartItemService.Get(siteId, userId, sku, locale);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -62,11 +76,15 @@ public class ShopCartItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPut("{siteId}/{userId}/{sku}")]
public IActionResult Update([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) {
public async Task<IActionResult> Update([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku, [FromBody] ShopCartItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) {
var result = _shopCartItemService.Update(siteId, userId, sku, requestData);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -75,9 +93,13 @@ public class ShopCartItemController : ControllerBase {
/// <param name="sku"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{userId}/{sku}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromRoute] string sku) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _shopCartItemService.Delete(siteId, userId, sku);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Authorization;
using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
@ -11,20 +13,24 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[AllowAnonymous]
[ApiController]
[Route("api/[controller]")]
public class ShopCartItemsController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IShopCartItemsService _shopCartItemsService;
private readonly WebapiControllers _webapiController = WebapiControllers.ShopCartItems;
/// <summary>
///
/// </summary>
/// <param name="shopCartItemsService"></param>
public ShopCartItemsController(
IAuthorizationService authorizationService,
IShopCartItemsService shopCartItemsService
) {
_authorizationService = authorizationService;
_shopCartItemsService = shopCartItemsService;
}
@ -33,11 +39,15 @@ public class ShopCartItemsController : ControllerBase {
/// </summary>
/// <returns></returns>
[HttpGet("{siteId}/{userId}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromQuery] string? locale) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] Guid userId, [FromQuery] string? locale) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(WebapiControllers.ShopCartItems, CrudActions.Read))).Succeeded) {
var result = _shopCartItemsService.Get(siteId, userId, locale);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -45,9 +55,13 @@ public class ShopCartItemsController : ControllerBase {
/// <param name="userId"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{userId}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] Guid userId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _shopCartItemsService.Delete(siteId, userId);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -5,26 +5,32 @@ using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Models.Requests;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class ShopItemController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IShopItemService _shopItemService;
private readonly WebapiControllers _webapiController = WebapiControllers.ShopItem;
/// <summary>
///
/// </summary>
/// <param name="shopItemService"></param>
public ShopItemController(
IAuthorizationService authorizationService,
IShopItemService shopItemService
) {
_authorizationService = authorizationService;
_shopItemService = shopItemService;
}
@ -36,11 +42,16 @@ public class ShopItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPost("{siteId}/{sku}")]
public IActionResult Post([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) {
public async Task<IActionResult> Post([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _shopItemService.Post(siteId, sku, requestData);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
/// Returns full object
/// </summary>
@ -48,18 +59,21 @@ public class ShopItemController : ControllerBase {
/// <param name="sku"></param>
/// <returns></returns>
[HttpGet("{siteId}/{sku}")]
public IActionResult Get([FromRoute] Guid siteId, [FromRoute] string sku) {
public async Task<IActionResult> Get([FromRoute] Guid siteId, [FromRoute] string sku) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Read))).Succeeded) {
var result = _shopItemService.Get(siteId, sku);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
/// <param name="siteId"></param>
/// <param name="slug"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet("{siteId}")]
public IActionResult GetSlug([FromRoute] Guid siteId, [FromQuery] string slug) {
var result = _shopItemService.GetSlug(siteId, slug);
@ -74,11 +88,15 @@ public class ShopItemController : ControllerBase {
/// <param name="requestData"></param>
/// <returns></returns>
[HttpPut("{siteId}/{sku}")]
public IActionResult Update([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) {
public async Task<IActionResult> Update([FromRoute] Guid siteId, [FromRoute] string sku, [FromBody] ShopItemRequestModel requestData) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Update))).Succeeded) {
var result = _shopItemService.Update(siteId, sku, requestData);
return result.ToActionResult();
}
return Unauthorized();
}
/// <summary>
///
/// </summary>
@ -86,9 +104,13 @@ public class ShopItemController : ControllerBase {
/// <param name="sku"></param>
/// <returns></returns>
[HttpDelete("{siteId}/{sku}")]
public IActionResult Delete([FromRoute] Guid siteId, [FromRoute] string sku) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId, [FromRoute] string sku) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Delete))).Succeeded) {
var result = _shopItemService.Delete(siteId, sku);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -4,26 +4,32 @@ using Microsoft.AspNetCore.Authorization;
using DomainResults.Mvc;
using WeatherForecast.Services;
using WeatherForecast.Policies;
using Core.Enumerations;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class ShopItemsController : ControllerBase {
private readonly IAuthorizationService _authorizationService;
private readonly IShopItemsService _shopItemsService;
private readonly WebapiControllers _webapiController = WebapiControllers.ShopItem;
/// <summary>
///
/// </summary>
/// <param name="shopCatalogService"></param>
public ShopItemsController(
IAuthorizationService authorizationService,
IShopItemsService shopCatalogService
) {
_authorizationService = authorizationService;
_shopItemsService = shopCatalogService;
}
@ -49,8 +55,12 @@ public class ShopItemsController : ControllerBase {
/// <param name="siteId"></param>
/// <returns></returns>
[HttpDelete("{siteId}")]
public IActionResult Delete([FromRoute] Guid siteId) {
public async Task<IActionResult> Delete([FromRoute] Guid siteId) {
if ((await _authorizationService.AuthorizeAsync(User, null, new CrudActionRequirement(_webapiController, CrudActions.Create))).Succeeded) {
var result = _shopItemsService.Delete(siteId);
return result.ToActionResult();
}
return Unauthorized();
}
}

View File

@ -1,18 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[Authorize(Policy = "WhitelistToken")]
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase {
/// <summary>
///
/// </summary>
public UserController() { }
}

View File

@ -8,7 +8,6 @@ namespace WeatherForecast.Controllers;
/// <summary>
///
/// </summary>
[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase {

View File

@ -0,0 +1,83 @@
using Core.Enumerations;
using ExtensionMethods;
using Extensions;
using Microsoft.AspNetCore.Authorization;
using WeatherForecast.Services;
namespace WeatherForecast.Policies {
/// <summary>
///
/// </summary>
public class CrudAuthorizationHandler : AuthorizationHandler<CrudActionRequirement> {
private readonly IHttpContextAccessor _contextAccessor;
private readonly IUserService _authenticationService;
/// <summary>
///
/// </summary>
/// <param name="contextAccessor"></param>
/// <param name="authenticationService"></param>
public CrudAuthorizationHandler(
IHttpContextAccessor contextAccessor,
IUserService authenticationService
) {
_contextAccessor = contextAccessor;
_authenticationService = authenticationService;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CrudActionRequirement requirement) {
var user = context.User;
var identity = user.Identity;
var name = identity?.Name;
var userId = name?.ToNullableGuid();
var request = _contextAccessor?.HttpContext?.Request;
var bearerToken = request?.GeBearerToken();
if (_authenticationService.VerifyToken(userId, bearerToken, requirement.Controller, requirement.Action).IsSuccess)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
/// <summary>
///
/// </summary>
public class CrudActionRequirement : IAuthorizationRequirement {
/// <summary>
///
/// </summary>
public WebapiControllers Controller { get; }
/// <summary>
///
/// </summary>
public CrudActions Action { get; }
/// <summary>
///
/// </summary>
public bool SameAuthor { get; }
/// <summary>
///
/// </summary>
/// <param name="crudAction"></param>
public CrudActionRequirement(WebapiControllers controller, CrudActions crudAction, bool sameAuthor = false) {
Controller = controller;
Action = crudAction;
SameAuthor = sameAuthor;
}
}
}

View File

@ -1,55 +0,0 @@
using Extensions;
using Microsoft.AspNetCore.Authorization;
using WeatherForecast.Services;
namespace WeatherForecast.Policies {
/// <summary>
///
/// </summary>
public class WhitelistTokenRequirement : IAuthorizationRequirement {
// public string WhiteListToken { get; }
/// <summary>
///
/// </summary>
public WhitelistTokenRequirement() {
// WhiteListToken = whiteListToken;
}
}
/// <summary>
///
/// </summary>
public class WhitelistTokenHandler : AuthorizationHandler<WhitelistTokenRequirement> {
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAuthenticationService _athenticationService;
/// <summary>
///
/// </summary>
/// <param name="httpContextAccessor"></param>
/// <param name="athenticationService"></param>
public WhitelistTokenHandler(
IHttpContextAccessor httpContextAccessor,
IAuthenticationService athenticationService
) {
_httpContextAccessor = httpContextAccessor;
_athenticationService = athenticationService;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="requirement"></param>
/// <returns></returns>
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, WhitelistTokenRequirement requirement) {
var request = _httpContextAccessor?.HttpContext?.Request;
if (request != null && _athenticationService.Get(request.GeBearerToken()).IsSuccess)
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}

View File

@ -1,157 +0,0 @@
using Core.Abstractions;
using Core.DomainObjects;
using DataProviders.Collections;
using DomainResults.Common;
using ExtensionMethods;
using CryptoProvider;
using JWTService;
using WeatherForecast.Models.Requests;
using Microsoft.Extensions.Options;
namespace WeatherForecast.Services {
/// <summary>
///
/// </summary>
public interface IAuthenticationService {
/// <summary>
///
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
(string?, IDomainResult) Post(AuthenticationRequestModel requestData);
/// <summary>
///
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
IDomainResult Get(string? token);
}
/// <summary>
///
/// </summary>
public class AutheticationService : ServiceBase<AutheticationService>, IAuthenticationService {
private readonly IAesKey? _aesKey;
private readonly IUserDataProvider _userDataProvider;
private readonly IJWTService _jwtService;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="options"></param>
/// <param name="userDataProvider"></param>
/// <param name="jwtService"></param>
public AutheticationService (
ILogger<AutheticationService> logger,
IOptions<Configuration> options,
IUserDataProvider userDataProvider,
IJWTService jwtService
) : base(logger) {
_aesKey = options.Value.JwtTokenEncryption;
_userDataProvider = userDataProvider;
_jwtService = jwtService;
}
/// <summary>
///
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
public (string?, IDomainResult) Post(AuthenticationRequestModel requestData) {
if (_aesKey?.IV == null || _aesKey?.Key == null)
return IDomainResult.Failed<string?>("IV or Key are not set");
// Retrieve user from database by userName
var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username);
if (!getUserResult.IsSuccess || user == null)
return (null, getUserResult);
if (user.Passwords.Password == null)
return IDomainResult.Failed<string?>("Password is not set, create new password.");
// Check provided password hash with the stored one
var (salt, hash) = HashService.CreateSaltedHash(requestData.Password);
if (!HashService.ValidateHash(requestData.Password, salt, hash))
return IDomainResult.Unauthorized<string?>();
// Check password expiration if enabled
if (user.Passwords.Expiration.Enabled && DateTime.UtcNow > user.Passwords.Password.Created.AddDays(user.Passwords.Expiration.Days)) {
user.Passwords.Expired.Add(user.Passwords.Password.Prototype());
user.Passwords.Password = null;
user.Tokens = new List<Token>();
return IDomainResult.Failed<string?>("Password is expired, create new password.");
}
// Creating JWT token
var claims = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("UserId", $"{user.Id}")
};
var created = DateTime.UtcNow;
var expires = created.AddDays(365);
var token = _jwtService.CreateJwtToken(expires, claims);
user.Tokens.Add(new Token {
Value = AesService.EncryptString(_aesKey.IV, _aesKey.Key, token),
Created = created,
Expires = expires,
});
var (_, usdateUserResult) = _userDataProvider.Update(user);
if (!usdateUserResult.IsSuccess)
return IDomainResult.Failed<string?>();
return IDomainResult.Success(token);
}
/// <summary>
///
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public IDomainResult Get(string? token) {
if (_aesKey?.IV == null || _aesKey?.Key == null)
return IDomainResult.Failed("IV or Key are not set");
if (token == null)
return IDomainResult.Failed();
#region Retrieve user id from token claim
var (claims, getClaimsResult) = _jwtService.JwtTokenClaims(token);
if (!getClaimsResult.IsSuccess || claims == null)
return IDomainResult.Failed();
var userId = claims.SingleOrDefault(x => x.Key == "UserId").Value.ToGuid();
if (userId == Guid.Empty)
return IDomainResult.Failed();
#endregion
var (user, getUserResult) = _userDataProvider.Get(userId);
if (!getUserResult.IsSuccess || user == null)
return IDomainResult.Failed();
#region Tokens cleanup
var userTokens = user.Tokens.Where(x => x.Expires > DateTime.UtcNow).ToList();
if (user.Tokens.Count != userTokens.Count) {
user.Tokens = userTokens;
_userDataProvider.Update(user);
}
#endregion
return userTokens.Select(x => AesService.DecryptString(_aesKey.IV, _aesKey.Key, x.Value)).Any(x => string.Compare(x, token) == 0)
? IDomainResult.Success()
: IDomainResult.Failed();
}
}
}

View File

@ -1,16 +1,168 @@
namespace WeatherForecast.Services {
using Core.Abstractions;
using Core.DomainObjects;
using DataProviders.Collections;
using DomainResults.Common;
using CryptoProvider;
using JWTService;
using WeatherForecast.Models.Requests;
using Microsoft.Extensions.Options;
using Core.Enumerations;
namespace WeatherForecast.Services {
/// <summary>
///
/// </summary>
public interface IUserService {
/// <summary>
///
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
(string?, IDomainResult) CreateToken(AuthenticationRequestModel requestData);
/// <summary>
///
/// </summary>
/// <param name="userId"></param>
/// <param name="token"></param>
/// <param name="controller"></param>
/// <param name="actions"></param>
/// <returns></returns>
IDomainResult VerifyToken(Guid? userId, string? token, WebapiControllers? controller, CrudActions? actions);
}
/// <summary>
///
/// </summary>
public class UserService : IUserService {
public class UserService : ServiceBase<UserService>, IUserService {
private readonly IAesKey? _aesKey;
private readonly IUserDataProvider _userDataProvider;
private readonly IJWTService _jwtService;
/// <summary>
///
/// </summary>
/// <param name="logger"></param>
/// <param name="options"></param>
/// <param name="userDataProvider"></param>
/// <param name="jwtService"></param>
public UserService (
ILogger<UserService> logger,
IOptions<Configuration> options,
IUserDataProvider userDataProvider,
IJWTService jwtService
) : base(logger) {
_aesKey = options.Value.JwtTokenEncryption;
_userDataProvider = userDataProvider;
_jwtService = jwtService;
}
/// <summary>
///
/// </summary>
/// <param name="requestData"></param>
/// <returns></returns>
public (string?, IDomainResult) CreateToken(AuthenticationRequestModel requestData) {
if (_aesKey?.IV == null || _aesKey?.Key == null)
return IDomainResult.Failed<string?>("IV or Key are not set");
// Retrieve user from database by userName
var (user, getUserResult) = _userDataProvider.GetByUsername(requestData.Username);
if (!getUserResult.IsSuccess || user == null)
return (null, getUserResult);
if (user.Passwords.Password == null)
return IDomainResult.Failed<string?>("Password is not set, create new password.");
// Check provided password hash with the stored one
var (salt, hash) = HashService.CreateSaltedHash(requestData.Password);
if (!HashService.ValidateHash(requestData.Password, salt, hash))
return IDomainResult.Unauthorized<string?>();
// Check password expiration if enabled
if (user.Passwords.Expiration.Enabled && DateTime.UtcNow > user.Passwords.Password.Created.AddDays(user.Passwords.Expiration.Days)) {
user.Passwords.Expired.Add(user.Passwords.Password.Prototype());
user.Passwords.Password = null;
user.Tokens = new List<Token>();
return IDomainResult.Failed<string?>("Password is expired, create new password.");
}
// Creating JWT token
var claims = new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("unique_name", $"{user.Id}"),
// (JWT ID): Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only once)
new KeyValuePair<string, string>("jti", $"{Guid.NewGuid()}")
};
var created = DateTime.UtcNow;
var expires = created.AddDays(365);
var token = _jwtService.CreateJwtToken(expires, claims);
user.Tokens.Add(new Token {
Value = AesService.EncryptString(_aesKey.IV, _aesKey.Key, token),
Created = created,
Expires = expires,
});
var (_, usdateUserResult) = _userDataProvider.Update(user);
if (!usdateUserResult.IsSuccess)
return IDomainResult.Failed<string?>();
return IDomainResult.Success(token);
}
/// <summary>
///
/// </summary>
/// <param name="userId"></param>
/// <param name="token"></param>
/// <param name="controller"></param>
/// <param name="action"></param>
/// <returns></returns>
public IDomainResult VerifyToken(Guid? userId, string? token, WebapiControllers? controller, CrudActions? action) {
if (_aesKey?.IV == null || _aesKey?.Key == null)
return IDomainResult.Failed("IV or Key are not set");
if (token == null || userId == null)
return IDomainResult.Failed();
var (user, getUserResult) = _userDataProvider.Get(userId.Value);
if (!getUserResult.IsSuccess || user == null)
return IDomainResult.Failed();
#region Tokens cleanup
var userTokens = user.Tokens.Where(x => x.Expires > DateTime.UtcNow).OrderByDescending(x => x.Expires).Take(10).ToList();
if (user.Tokens.Count != userTokens.Count) {
user.Tokens = userTokens;
_userDataProvider.Update(user);
}
#endregion
// Check if whitelisted
if(!userTokens.Select(x => AesService.DecryptString(_aesKey.IV, _aesKey.Key, x.Value)).Any(x => string.Compare(x, token) == 0))
IDomainResult.Unauthorized();
// Check if authorized
if (controller != null) {
if (!user.Authorizations.Any(x => x.Controller == controller))
return IDomainResult.Unauthorized();
if(action != null)
if (!user.Authorizations.SingleOrDefault(x => x.Controller == controller).Actions.Any(x => x == action))
return IDomainResult.Unauthorized();
}
return IDomainResult.Success();
}
}
}

View File

@ -12,6 +12,7 @@ using JWTService.Extensions;
using Core.Middlewares;
using Microsoft.AspNetCore.Authorization;
using WeatherForecast.Policies;
using Microsoft.Extensions.DependencyInjection;
namespace WeatherForecast {
@ -62,7 +63,7 @@ namespace WeatherForecast {
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options => {
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.SaveToken = false;
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(appSettings.JwtConfig.Secret)),
@ -76,16 +77,13 @@ namespace WeatherForecast {
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1#use-httpcontext-from-custom-components
services.AddHttpContextAccessor();
#region Policy Authorizations https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0
services.AddScoped<IAuthorizationHandler, WhitelistTokenHandler>();
// services.AddScoped<IAuthorizationHandler, RecaptchaTokenHandler>();
services.AddAuthorization(options => {
options.AddPolicy("WhitelistToken", policy => policy.Requirements.Add(new WhitelistTokenRequirement()));
// options.AddPolicy("RecaptchaToken", policy => policy.Requirements.Add(new RecaptchaTokenRequirement("/swagger/index.html")));
});
#region Policy Handlers
services.AddScoped<IAuthorizationHandler, CrudAuthorizationHandler>();
#endregion
services.AddScoped<IContentService, ContentService>();
services.AddScoped<IShopItemService, ShopItemService>();
services.AddScoped<IShopItemsService, ShopItemsService>();
@ -95,13 +93,12 @@ namespace WeatherForecast {
services.AddScoped<IBlogItemsService, BlogItemsService>();
services.AddScoped<ICategoryItemService, CategoryItemService>();
services.AddScoped<ICategoryItemsService, CategoryItemsService>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IFileService, FileService>();
services.AddScoped<IFilesService, FilesService>();
services.AddScoped<IImageService, ImageService>();
services.AddScoped<IPasswordService, PasswordService>();
services.AddScoped<IAuthenticationService, AutheticationService>();
services.AddScoped<IUserService, UserService>();
services.RegisterDataproviders(appSettings);
services.RegisterFileSecurityService();