diff --git a/SharpRSS.API.Contracts/ApiError.cs b/SharpRSS.API.Contracts/ApiError.cs new file mode 100644 index 0000000..aa673fe --- /dev/null +++ b/SharpRSS.API.Contracts/ApiError.cs @@ -0,0 +1,15 @@ +using System; + +namespace SharpRSS.API.Contracts +{ + public class ApiError + { + public ApiError() + { + + } + public bool Logged { get; set; } + public string Message { get; set; } = string.Empty; + public DateTime Date { get; set; } + } +} \ No newline at end of file diff --git a/SharpRSS.API.Contracts/DTO/ApiListResult.cs b/SharpRSS.API.Contracts/DTO/ApiListResult.cs new file mode 100644 index 0000000..52bfd0d --- /dev/null +++ b/SharpRSS.API.Contracts/DTO/ApiListResult.cs @@ -0,0 +1,16 @@ +namespace SharpRSS.API.Contracts.DTO +{ + public class ApiListResult + { + public ApiListResult(int hits, int total, TResultValue? data) + { + Hits = hits; + Total = total; + Data = data; + } + + public int Hits { get; } + public int Total { get; } + public TResultValue? Data { get; } + } +} \ No newline at end of file diff --git a/SharpRSS.API.Contracts/Models/User/AuthenticateUser.cs b/SharpRSS.API.Contracts/Models/User/AuthenticateUser.cs new file mode 100644 index 0000000..cb23739 --- /dev/null +++ b/SharpRSS.API.Contracts/Models/User/AuthenticateUser.cs @@ -0,0 +1,8 @@ +namespace SharpRSS.API.Contracts.Models.User +{ + public class AuthenticateUser + { + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/SharpRSS.API/Auth/SessionAuthorizeAttribute.cs b/SharpRSS.API/Auth/SessionAuthorizeAttribute.cs new file mode 100644 index 0000000..31e1034 --- /dev/null +++ b/SharpRSS.API/Auth/SessionAuthorizeAttribute.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using ToolQit; +using ToolQit.Logging; + +namespace SharpRSS.API.Auth +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public class SessionAuthorizeAttribute : Attribute, IAuthorizationFilter + { + public SessionAuthorizeAttribute(string permission = "") + { + _log = LogManager.CreateLogger(typeof(SessionAuthorizeAttribute)); + _perm = permission; + } + + private readonly ILog _log; + private readonly string _perm; + + public void OnAuthorization(AuthorizationFilterContext context) + { + if (context.ActionDescriptor.EndpointMetadata.Any(obj => obj.GetType() == typeof(AllowAnonymousAttribute))) + { + context.Result = new OkResult(); + return; + } + //TODO: Check session ID! + context.Result = new UnauthorizedResult(); + } + } +} \ No newline at end of file diff --git a/SharpRSS.API/Controllers/AuthController.cs b/SharpRSS.API/Controllers/AuthController.cs index 82d4695..ece77b5 100644 --- a/SharpRSS.API/Controllers/AuthController.cs +++ b/SharpRSS.API/Controllers/AuthController.cs @@ -1,7 +1,12 @@ +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using SharpRSS.API.Auth; using SharpRSS.API.Contracts; using SharpRSS.API.Contracts.DTO; +using SharpRSS.API.Contracts.Models.User; using SharpRSS.API.Data; using SharpRSS.API.Models; using SharpRSS.API.Models.Auth; @@ -9,6 +14,7 @@ using SharpRSS.API.Models.Auth; namespace SharpRSS.API.Controllers { [ApiController] + [SessionAuthorize] [Route("api/[controller]")] public class AuthController : ControllerBase { @@ -18,14 +24,29 @@ namespace SharpRSS.API.Controllers } private readonly AuthService _authService; - - [HttpPost("create")] - public async Task> CreateUser(UserRequest user) + + [HttpPost("[action]")] + [AllowAnonymous] + public async Task> Authenticate(AuthenticateUser authenticateUser) { - Result result = await _authService.CreateUser(user); - if (result.Success) + return Ok("Ok!"); + } + + [HttpPost("user")] + public async Task> CreateUser(AuthenticateUser authenticateUser) + { + Result result = await _authService.CreateUser(authenticateUser); + if (result.Success) return Ok(Models.Auth.User.ToDto(result.Value ?? new User())); return BadRequest(new ApiResult(result.Message, ApiResults.Error)); } + + [HttpGet("user")] + public async Task>>> GetUsers(int take, int skip) + { + var usersAuth = await _authService.GetUsers(take, skip); + List users = usersAuth.Value?.Select(Models.Auth.User.ToDto).ToList() ?? new List(); + return Ok(new ApiListResult>(users.Count, await _authService.UserCount(), users)); + } } } \ No newline at end of file diff --git a/SharpRSS.API/Cryptography/Hasher.cs b/SharpRSS.API/Cryptography/Hasher.cs index 95831bc..a010743 100644 --- a/SharpRSS.API/Cryptography/Hasher.cs +++ b/SharpRSS.API/Cryptography/Hasher.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -9,7 +8,7 @@ namespace SharpRSS.API.Cryptography { private const int KeySize = 128; private const int Iterations = 420069; - static readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA512; + private static readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA512; public static byte[] HashPassword(string password, out byte[] salt) { diff --git a/SharpRSS.API/Data/AuthService.cs b/SharpRSS.API/Data/AuthService.cs index 20f0ae2..3a88e03 100644 --- a/SharpRSS.API/Data/AuthService.cs +++ b/SharpRSS.API/Data/AuthService.cs @@ -1,8 +1,9 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using SharpRSS.API.Contracts.Models.User; using SharpRSS.API.Cryptography; using SharpRSS.API.Models; using SharpRSS.API.Models.Auth; @@ -23,19 +24,22 @@ namespace SharpRSS.API.Data private readonly IConfiguration _configuration; private readonly ILog _log; - public async Task> CreateUser(UserRequest userRequest) + public async Task> CreateUser(AuthenticateUser authenticateUserRequest) { - bool result = false; + bool result; + if (authenticateUserRequest.UserName.Any(char.IsWhiteSpace)) + return new Result(null, message: "Username should not contain space/whitespaces!"); + await using DbAccess access = new DbAccess(_configuration); - - var user = access.Users.FirstOrDefault(u => u.UserName == userRequest.UserName); + var user = access.Users.FirstOrDefault(u => u.UserName == authenticateUserRequest.UserName); if (user != null) return new Result(user, message:"User name already exists!"); - byte[] hashedPwdBytes = Hasher.HashPassword(userRequest.Password, out byte[] salt); + + byte[] hashedPwdBytes = Hasher.HashPassword(authenticateUserRequest.Password, out byte[] salt); user = new User() { - UserName = userRequest.UserName, - Mail = userRequest.EMail, + UserName = authenticateUserRequest.UserName, + Mail = "", Password = hashedPwdBytes, Salt = salt }; @@ -50,7 +54,24 @@ namespace SharpRSS.API.Data _log.Error(e, "Error creating user: {UserName}", user.UserName); return new Result(user, message: "Could not create user!"); } - return new Result(user, result); + return new Result(user, result, "Ok"); + } + + public async Task>> GetUsers(int take = 50, int skip = 0) + { + if (take is 0 or > 50) + take = 50; + await using DbAccess access = new DbAccess(_configuration); + IEnumerable users = access.Users.Skip(skip).Take(take).ToList(); + if (!users.Any()) + return new Result>(users, false, "No users found!"); + return new Result>(users, true, "Ok"); + } + + public async Task UserCount() + { + await using DbAccess access = new DbAccess(_configuration); + return access.Users.Count(); } } } \ No newline at end of file diff --git a/SharpRSS.API/Models/Auth/UserRequest.cs b/SharpRSS.API/Models/Auth/UserRequest.cs deleted file mode 100644 index 9dbdb09..0000000 --- a/SharpRSS.API/Models/Auth/UserRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SharpRSS.API.Models.Auth -{ - public class UserRequest - { - public string UserName { get; set; } - public string EMail { get; set; } - public string Password { get; set; } - } -} \ No newline at end of file diff --git a/SharpRSS.API/Models/Result.cs b/SharpRSS.API/Models/Result.cs index 7db58e3..e3cb5a4 100644 --- a/SharpRSS.API/Models/Result.cs +++ b/SharpRSS.API/Models/Result.cs @@ -2,7 +2,7 @@ namespace SharpRSS.API.Models { public class Result { - public Result(TValue value, bool success = false, string message = "") + public Result(TValue? value, bool success = false, string message = "") { Value = value; Success = success;