-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Vec7or
committed
Jun 3, 2021
1 parent
bb09675
commit 8f15de1
Showing
8 changed files
with
444 additions
and
44 deletions.
There are no files selected for viewing
115 changes: 112 additions & 3 deletions
115
Pronto-MIA/Pronto-MIA/BusinessLogic/API/Interceptors/UserStateRequestInterceptor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,116 @@ | ||
namespace Pronto_MIA.BusinessLogic.API.Interceptors | ||
{ | ||
public class UserStateRequestInterceptor | ||
using System; | ||
using System.Globalization; | ||
using System.Linq; | ||
using System.Security.Authentication; | ||
using System.Security.Claims; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using HotChocolate; | ||
using HotChocolate.Execution; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.Extensions.Configuration; | ||
using Pronto_MIA.BusinessLogic.API.Types; | ||
using Pronto_MIA.DataAccess; | ||
using Pronto_MIA.Domain.Entities; | ||
using Pronto_MIA.Services; | ||
|
||
/// <summary> | ||
/// Class containing the interceptor method used to add the | ||
/// <see cref="ApiUserState"/> to every request. | ||
/// </summary> | ||
public static class UserStateRequestInterceptor | ||
{ | ||
|
||
/// <summary> | ||
/// Adds <see cref="AddUserState"/> to every request so that | ||
/// information on the requesting user is available if necessary. | ||
/// Also checks if the user is existing and allowed to authenticate. | ||
/// </summary> | ||
/// <param name="cfg">The configuration of the application used | ||
/// to create a dbContext.</param> | ||
/// <param name="context">The httpContext of the current request. | ||
/// </param> | ||
/// <param name="executor">The executor of the current request.</param> | ||
/// <param name="builder">The query request builder which will be | ||
/// extended by the <see cref="ApiUserState"/>.</param> | ||
/// <param name="token">The cancellation token of the request.</param> | ||
/// <returns>A task that can be awaited.</returns> | ||
/// <exception cref="QueryException">Throws a query exception if | ||
/// the user could not be found or the token used for authentication | ||
/// has been invalidated.</exception> | ||
public static async ValueTask AddUserState( | ||
IConfiguration cfg, | ||
HttpContext context, | ||
IRequestExecutor executor, | ||
IQueryRequestBuilder builder, | ||
CancellationToken token) | ||
{ | ||
ApiUserState apiUserState = default; | ||
var identity = context.User.Identity; | ||
if (identity is { IsAuthenticated: true }) | ||
{ | ||
var user = await GetUser(cfg, context); | ||
CheckIfTokenRevoked(context, user.LastInvalidated); | ||
apiUserState = new ApiUserState(user); | ||
} | ||
|
||
builder.SetProperty( | ||
ApiUserGlobalState.ApiUserStateName, | ||
apiUserState); | ||
} | ||
|
||
private static async Task<User> GetUser( | ||
IConfiguration cfg, | ||
HttpContext context) | ||
{ | ||
await using (var dbContext = new ProntoMiaDbContext( | ||
DatabaseService.GetOptions(cfg))) | ||
{ | ||
var userId = int.Parse(context | ||
.User.FindFirstValue(ClaimTypes.NameIdentifier)); | ||
var userName = context | ||
.User.FindFirstValue(ClaimTypes.Name); | ||
var user = dbContext.Users | ||
.Include(u => u.Department) | ||
.Include(u => u.AccessControlList) | ||
.SingleOrDefault(u => u.Id == userId); | ||
if (user == null) | ||
{ | ||
throw GetAuthorizationException(); | ||
} | ||
|
||
return user; | ||
} | ||
} | ||
|
||
private static void CheckIfTokenRevoked( | ||
HttpContext context, | ||
DateTime lastInvalidated) | ||
{ | ||
var issuedAt = context | ||
.User.FindFirstValue("issuedAt"); | ||
if (issuedAt == null) | ||
{ | ||
throw GetAuthorizationException(); | ||
} | ||
|
||
var issuedAtTime = DateTime.Parse( | ||
issuedAt, CultureInfo.InvariantCulture); | ||
if (lastInvalidated > issuedAtTime) | ||
{ | ||
throw new AuthenticationException(); | ||
} | ||
} | ||
|
||
private static GraphQLException GetAuthorizationException() | ||
{ | ||
return new ( | ||
ErrorBuilder.New() | ||
.SetMessage("The current user is not authorized " + | ||
"to access this resource.") | ||
.SetCode("AUTH_NOT_AUTHORIZED").Build()); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.