mirror of
https://github.com/hmaxnl/SharpRSS.git
synced 2025-01-18 12:54:20 +01:00
Implemented simple authorization, with users, groups & sessions.
This commit is contained in:
parent
9338d8c106
commit
25ca94ae3a
|
@ -3,9 +3,9 @@ using System.Linq;
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using SharpRSS.API.Contracts.Models;
|
||||
using SharpRSS.API.Data;
|
||||
using SharpRSS.API.Models;
|
||||
using ToolQit;
|
||||
using ToolQit.Extensions;
|
||||
using ToolQit.Logging;
|
||||
|
@ -23,8 +23,7 @@ namespace SharpRSS.API.Auth
|
|||
|
||||
private readonly ILog _log;
|
||||
private readonly bool _admin;
|
||||
|
||||
public async void OnAuthorization(AuthorizationFilterContext context)
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
if (context.ActionDescriptor.EndpointMetadata.Any(obj => obj.GetType() == typeof(AllowAnonymousAttribute)))
|
||||
return;
|
||||
|
@ -43,19 +42,26 @@ namespace SharpRSS.API.Auth
|
|||
context.Result = new UnauthorizedObjectResult(new Result("Invalid session ID"));
|
||||
return;
|
||||
}
|
||||
var authSet = await authService.ValidateSession(headerVal);
|
||||
if (!authSet.Success || authSet.Value == null)
|
||||
var authSetResult = authService.ValidateSession(headerVal).Result;
|
||||
if (!authSetResult.Success || authSetResult.Value == null)
|
||||
{
|
||||
context.Result = new UnauthorizedResult();
|
||||
return;
|
||||
}
|
||||
if (authSet.Value.Session is { Expired: true })
|
||||
if (authSetResult.Value.Session is { Expired: true })
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult(new Result("Session expired", ResultStatus.Failed));
|
||||
return;
|
||||
}
|
||||
authSet.Value.AdminRequired = _admin;
|
||||
context.HttpContext.Items["auth"] = authSet.Value;
|
||||
if (!authSetResult.Value.User.Active)
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult(new Result(
|
||||
"User is not active, contact your administrator to enable this account!", ResultStatus.Failed));
|
||||
return;
|
||||
}
|
||||
|
||||
authSetResult.Value.AdminRequired = _admin;
|
||||
context.RouteData.Values.Add(nameof(AuthorizationSet), authSetResult.Value);
|
||||
return;
|
||||
}
|
||||
context.Result = new UnauthorizedResult();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -15,7 +16,6 @@ using ToolQit.Logging;
|
|||
namespace SharpRSS.API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[SessionAuthorize(false)]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
|
@ -44,13 +44,29 @@ namespace SharpRSS.API.Controllers
|
|||
|
||||
// To update only fill the values that need to be updated.
|
||||
[HttpPost("user")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
public async Task<ActionResult<ResultOr<User>>> InsertUser(InsertUser payload)
|
||||
{
|
||||
object? authSetObj = RouteData.Values[nameof(AuthorizationSet)];
|
||||
if (authSetObj is AuthorizationSet authSet)
|
||||
{
|
||||
if (!authSet.Group.Administrator)
|
||||
{
|
||||
if (payload.Uid == authSet.User.Uid) // User can self change own information, but not the group property!
|
||||
payload.GroupId = string.Empty;
|
||||
else
|
||||
return new UnauthorizedObjectResult(new Result(
|
||||
$"User '{authSet.User.Uid}' in group '{authSet.Group.DisplayName}' does not has the right permission to change user '{payload.Uid}'!"));
|
||||
}
|
||||
}
|
||||
else
|
||||
return StatusCode(StatusCodes.Status500InternalServerError, new Result("Failed to get the authorization data!", ResultStatus.InternalFail));
|
||||
|
||||
var createdUserResult = await _authService.InsertUserAsync(payload);
|
||||
return createdUserResult.Success ? Created("", createdUserResult) :
|
||||
createdUserResult.Status == ResultStatus.Failed ? BadRequest(createdUserResult) :
|
||||
|
@ -58,7 +74,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("user")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
@ -72,7 +88,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("user")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
@ -83,7 +99,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("users")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
@ -98,7 +114,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpPost("group")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
@ -112,7 +128,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpDelete("group")]
|
||||
[SessionAuthorize]
|
||||
[SessionAuthorize(true)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
@ -126,12 +142,13 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("groups")]
|
||||
[SessionAuthorize(false)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<ActionResult<ListResult<Group>>> GetGroups(string search = "", int results = 20, int skip = 0)
|
||||
{ //TODO: Change DTO to GroupItem!
|
||||
public async Task<ActionResult<ListResult<GroupItem>>> GetGroups(string search = "", int results = 20, int skip = 0)
|
||||
{
|
||||
var groupsResult = await _authService.GetGroupsAsync(results, skip, search);
|
||||
return groupsResult.Success ? Ok(groupsResult) :
|
||||
groupsResult.Status == ResultStatus.Failed ? BadRequest(groupsResult) :
|
||||
|
@ -139,6 +156,7 @@ namespace SharpRSS.API.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("group")]
|
||||
[SessionAuthorize(false)]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
|
|
|
@ -61,9 +61,23 @@ namespace SharpRSS.API.Data
|
|||
DbAccess access = new DbAccess(_configuration);
|
||||
var dbSession = await access.Sessions.Where(s => s.Sid == sessionId).FirstOrDefaultAsync();
|
||||
if (dbSession == null) return ResultOr<AuthorizationSet>.Failed($"Could not get session: '{sessionId}'");
|
||||
if (!dbSession.Expired && dbSession.Expires - DateTime.Now <= TimeSpan.FromDays(1))
|
||||
{
|
||||
DbSession extended = dbSession.Extend();
|
||||
access.Sessions.Update(extended);
|
||||
bool sessionExtended = await access.SaveChangesAsync() > 0;
|
||||
if (sessionExtended)
|
||||
{
|
||||
dbSession = extended;
|
||||
_log.Information("Extended session: '{SessionId}' to '{SessionExpirationDate}'", dbSession.Sid, dbSession.Expires);
|
||||
}
|
||||
else
|
||||
_log.Warning("Failed to extend session: '{SessionId}'", dbSession.Sid);
|
||||
}
|
||||
var dbUser = await access.Users.Where(u => u.Uid == dbSession.Uid).FirstOrDefaultAsync();
|
||||
if (dbUser == null) return ResultOr<AuthorizationSet>.Failed($"Could not get user: '{dbSession.Uid}'");
|
||||
var dbGroup = await access.Groups.Where(g => g.Gid == dbUser.Gid).FirstOrDefaultAsync();
|
||||
if (dbGroup == null) return ResultOr<AuthorizationSet>.Failed($"Could not get group: '{dbUser.Gid}'");
|
||||
var set = new AuthorizationSet()
|
||||
{
|
||||
Session = dbSession,
|
||||
|
|
|
@ -2,9 +2,9 @@ namespace SharpRSS.API.Models
|
|||
{
|
||||
internal class AuthorizationSet
|
||||
{
|
||||
public DbSession? Session { get; set; }
|
||||
public DbUser? User { get; set; }
|
||||
public DbGroup? Group { get; set; }
|
||||
public DbSession Session { get; set; }
|
||||
public DbUser User { get; set; }
|
||||
public DbGroup Group { get; set; }
|
||||
public bool AdminRequired { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Serilog;
|
||||
using SharpRSS.API.Contracts.DTOs;
|
||||
using SharpRSS.API.Contracts.DTOs.Sessions;
|
||||
|
||||
namespace SharpRSS.API.Models
|
||||
{
|
||||
|
@ -20,21 +19,26 @@ namespace SharpRSS.API.Models
|
|||
Uid = uid;
|
||||
Created = DateTime.Now;
|
||||
Expires = Created.AddMinutes(expiresMinutes);
|
||||
using SHA1 sha1 = SHA1.Create();
|
||||
string uidHash = Convert.ToHexString(sha1.ComputeHash(Encoding.UTF8.GetBytes(Uid)));
|
||||
Sid = $"{new DateTimeOffset(Created).ToUnixTimeMilliseconds()}.{uidHash}.{new DateTimeOffset(Expires).ToUnixTimeMilliseconds()}";
|
||||
string uidHash = Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(Uid)));
|
||||
string createdHash = Convert.ToHexString(SHA1.HashData(Encoding.UTF8.GetBytes(Created.ToString(CultureInfo.CurrentCulture))));
|
||||
Sid = Convert.ToHexString(SHA512.HashData(Encoding.UTF8.GetBytes($"{createdHash}.{uidHash}")));
|
||||
}
|
||||
public string Uid { get; set; }
|
||||
|
||||
public string Uid { get; set; } = string.Empty;
|
||||
public string Sid { get; set; } = string.Empty;
|
||||
public DateTime Created { get; set; }
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
public bool Expired => Expires < DateTime.Now;
|
||||
|
||||
public static DbSession CreateSession(string uid, double expiresMinutes = 10080)
|
||||
public DbSession Extend(double extendMinutes = 10080) => new DbSession()
|
||||
{
|
||||
DbSession newSession = new DbSession(uid, expiresMinutes);
|
||||
return newSession;
|
||||
}
|
||||
Uid = Uid,
|
||||
Sid = Sid,
|
||||
Created = Created,
|
||||
Expires = DateTime.Now.AddMinutes(extendMinutes)
|
||||
};
|
||||
|
||||
public static DbSession CreateSession(string uid, double expiresMinutes = 10080) => new DbSession(uid, expiresMinutes);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user