Claim Logic

This commit is contained in:
Andre Beging
2022-04-11 15:51:11 +02:00
parent 1b2c6c4062
commit 5026196b46
9 changed files with 137 additions and 47 deletions

View File

@@ -98,6 +98,7 @@ namespace FoodsharingSiegen.Server.Auth
{ {
Name = "Andre", Name = "Andre",
Mail = "fs@beging.de", Mail = "fs@beging.de",
GroupsList = new List<UserGroup> { UserGroup.Ambassador },
Type = UserType.Admin, Type = UserType.Admin,
Created = DateTime.UtcNow, Created = DateTime.UtcNow,
EncryptedPassword = "qSIxTZo7J8M=" EncryptedPassword = "qSIxTZo7J8M="
@@ -118,6 +119,14 @@ namespace FoodsharingSiegen.Server.Auth
var serializedToken = AuthHelper.CreateToken(_user); var serializedToken = AuthHelper.CreateToken(_user);
await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken); await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
if (_user.ForceLogout)
{
_user.ForceLogout = false;
await Context.SaveChangesAsync();
}
Context.Entry(_user).State = EntityState.Detached;
return new OperationResult(); return new OperationResult();
} }

View File

@@ -1,6 +1,7 @@
using System.Security.Claims; using System.Security.Claims;
using FoodsharingSiegen.Contracts; using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Server.Auth; using FoodsharingSiegen.Server.Auth;
using FoodsharingSiegen.Server.Data.Service;
using FoodsharingSiegen.Shared.Helper; using FoodsharingSiegen.Shared.Helper;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
@@ -17,6 +18,8 @@ namespace FoodsharingSiegen.Server.Service
/// <summary> LocalStorageService </summary> /// <summary> LocalStorageService </summary>
private readonly LocalStorageService _localStorageService; private readonly LocalStorageService _localStorageService;
private readonly UserService _userService;
#endregion #endregion
#region Setup/Teardown #region Setup/Teardown
@@ -25,7 +28,12 @@ namespace FoodsharingSiegen.Server.Service
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="localStorageService"></param> /// <param name="localStorageService"></param>
public TokenAuthStateProvider(LocalStorageService localStorageService) => _localStorageService = localStorageService; /// <param name="userService"></param>
public TokenAuthStateProvider(LocalStorageService localStorageService, UserService userService)
{
_localStorageService = localStorageService;
_userService = userService;
}
#endregion #endregion
@@ -38,7 +46,11 @@ namespace FoodsharingSiegen.Server.Service
public override async Task<AuthenticationState> GetAuthenticationStateAsync() public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{ {
var token = await _localStorageService.GetItem<string>(StorageKeys.TokenKey); var token = await _localStorageService.GetItem<string>(StorageKeys.TokenKey);
var tokenValid = AuthHelper.ValidateToken(token, out _); var tokenValid = AuthHelper.ValidateToken(token, out var user);
var checkR = await _userService.CheckForceLogout(user);
if (checkR.Success && checkR.Data)
tokenValid = false;
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
if (tokenValid) if (tokenValid)

View File

@@ -2,7 +2,7 @@ using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Server.Auth; using FoodsharingSiegen.Server.Auth;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
namespace FoodsharingSiegen.Server.Pages namespace FoodsharingSiegen.Server.BaseClasses
{ {
/// <summary> /// <summary>
/// The fs base class (a. beging, 08.04.2022) /// The fs base class (a. beging, 08.04.2022)

View File

@@ -29,6 +29,9 @@
[Parameter] [Parameter]
public int Minimum { get; set; } = 1; public int Minimum { get; set; } = 1;
[Parameter]
public bool AllowAddInteraction { get; set; }
private List<Interaction> Interactions => Prospect?.Interactions?.Where(x => x.Type == Type).ToList() ?? new List<Interaction>(); private List<Interaction> Interactions => Prospect?.Interactions?.Where(x => x.Type == Type).ToList() ?? new List<Interaction>();
private bool Done => Interactions.Count >= Minimum; private bool Done => Interactions.Count >= Minimum;
@@ -62,7 +65,7 @@
<span>(<i>@interaction.Info</i>)</span> <span>(<i>@interaction.Info</i>)</span>
} }
@if (!Prospect.Complete || interaction.Type == InteractionType.Complete) @if ((!Prospect.Complete || interaction.Type == InteractionType.Complete) && AllowAddInteraction)
{ {
<span>&nbsp;<a href=""><i class="fa-solid fa-square-xmark" @onclick="async () => await RemoveClick.InvokeAsync(interaction.Id)" @onclick:preventDefault></i></a></span> <span>&nbsp;<a href=""><i class="fa-solid fa-square-xmark" @onclick="async () => await RemoveClick.InvokeAsync(interaction.Id)" @onclick:preventDefault></i></a></span>
} }
@@ -71,7 +74,7 @@
} }
} }
@if (!Prospect.Complete && (Interactions.Count == 0 || Multiple)) @if (!Prospect.Complete && (Interactions.Count == 0 || Multiple) && AllowAddInteraction)
{ {
if (Multiple) ButtonText = "+"; if (Multiple) ButtonText = "+";
<Button Size="Size.Small" Clicked="AddClick">@ButtonText</Button> <Button Size="Size.Small" Clicked="AddClick">@ButtonText</Button>

View File

@@ -1,4 +1,7 @@
@using FoodsharingSiegen.Contracts.Entity @using FoodsharingSiegen.Contracts.Entity
@using FoodsharingSiegen.Contracts.Helper
@using FoodsharingSiegen.Server.BaseClasses
@inherits FsBase
@{ @{
var divClass = "pc-main"; var divClass = "pc-main";
@@ -18,6 +21,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
Type="InteractionType.Welcome" Type="InteractionType.Welcome"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.WelcomeTeam)"
AddClick="() => AddInteraction(InteractionType.Welcome)" AddClick="() => AddInteraction(InteractionType.Welcome)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
Caption="Begrüßung" Caption="Begrüßung"
@@ -28,6 +32,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
Type="InteractionType.EinAb" Type="InteractionType.EinAb"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.WelcomeTeam, UserGroup.StoreManager, UserGroup.Ambassador)"
AddClick="() => AddInteraction(InteractionType.EinAb)" AddClick="() => AddInteraction(InteractionType.EinAb)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
Caption="Einführungen" Caption="Einführungen"
@@ -40,6 +45,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
Type="InteractionType.IdCheck" Type="InteractionType.IdCheck"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
AddClick="() => AddInteraction(InteractionType.IdCheck)" AddClick="() => AddInteraction(InteractionType.IdCheck)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
Caption="Perso prüfen" Caption="Perso prüfen"
@@ -50,6 +56,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
Type="InteractionType.PdfPass" Type="InteractionType.PdfPass"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
AddClick="() => AddInteraction(InteractionType.PdfPass)" AddClick="() => AddInteraction(InteractionType.PdfPass)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
Caption="FS-Ausweis (digital)" Caption="FS-Ausweis (digital)"
@@ -60,6 +67,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
Type="InteractionType.PrintPass" Type="InteractionType.PrintPass"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
AddClick="() => AddInteraction(InteractionType.PrintPass)" AddClick="() => AddInteraction(InteractionType.PrintPass)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
Caption="FS-Ausweis (print)" Caption="FS-Ausweis (print)"
@@ -69,6 +77,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
Type="InteractionType.Verify" Type="InteractionType.Verify"
AddClick="() => AddInteraction(InteractionType.Verify)" AddClick="() => AddInteraction(InteractionType.Verify)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"
@@ -79,6 +88,7 @@
<InteractionRow <InteractionRow
Prospect="Prospect" Prospect="Prospect"
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
Type="InteractionType.Complete" Type="InteractionType.Complete"
AddClick="() => AddInteraction(InteractionType.Complete)" AddClick="() => AddInteraction(InteractionType.Complete)"
RemoveClick="@RemoveInteraction" RemoveClick="@RemoveInteraction"

View File

@@ -1,16 +1,35 @@
using FoodsharingSiegen.Contracts; using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity; using FoodsharingSiegen.Contracts.Entity;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace FoodsharingSiegen.Server.Data.Service namespace FoodsharingSiegen.Server.Data.Service
{ {
/// <summary>
/// The user service class (a. beging, 11.04.2022)
/// </summary>
/// <seealso cref="ServiceBase"/>
public class UserService : ServiceBase public class UserService : ServiceBase
{ {
#region Setup/Teardown
/// <summary>
/// Initializes a new instance of the <see cref="UserService"/> class
/// </summary>
/// <param name="context">The context</param>
public UserService(FsContext context) : base(context) public UserService(FsContext context) : base(context)
{ {
} }
#endregion
#region Public Method AddUserAsync
/// <summary>
/// Adds the user using the specified user (a. beging, 11.04.2022)
/// </summary>
/// <param name="user">The user</param>
/// <returns>A task containing an operation result of user</returns>
public async Task<OperationResult<User>> AddUserAsync(User user) public async Task<OperationResult<User>> AddUserAsync(User user)
{ {
try try
@@ -37,6 +56,61 @@ namespace FoodsharingSiegen.Server.Data.Service
} }
#endregion
#region Public Method CheckForceLogout
/// <summary>
/// Checks the force logout using the specified user (a. beging, 11.04.2022)
/// </summary>
/// <param name="user">The user</param>
/// <returns>A task containing an operation result of bool</returns>
public async Task<OperationResult<bool>> CheckForceLogout(User user)
{
try
{
var anyR = await Context.Users.AnyAsync(x => x.Id == user.Id && x.ForceLogout);
return new OperationResult<bool>(anyR);
}
catch (Exception e)
{
return new OperationResult<bool>(e);
}
}
#endregion
#region Public Method GetUsersAsync
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Gets users asynchronous. </summary>
///
/// <remarks> A Beging, 20.10.2021. </remarks>
///
/// <returns> An asynchronous result that yields the users. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
public async Task<OperationResult<List<User>>> GetUsersAsync()
{
try
{
var users = await Context.Users.AsNoTracking().ToListAsync();
return new OperationResult<List<User>>(users);
}
catch (Exception e)
{
return new OperationResult<List<User>>(e);
}
}
#endregion
#region Public Method Update
/// <summary>
/// Updates the user (a. beging, 11.04.2022)
/// </summary>
/// <param name="user">The user</param>
/// <returns>A task containing the operation result</returns>
public async Task<OperationResult> Update(User user) public async Task<OperationResult> Update(User user)
{ {
try try
@@ -44,6 +118,14 @@ namespace FoodsharingSiegen.Server.Data.Service
var entityUser = await Context.Users.FirstOrDefaultAsync(x => x.Id == user.Id); var entityUser = await Context.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
if (entityUser == null) return new OperationResult(new Exception("User not found")); if (entityUser == null) return new OperationResult(new Exception("User not found"));
if (entityUser.Mail != user.Mail ||
entityUser.Verified != user.Verified ||
entityUser.Type != user.Type ||
entityUser.Groups != user.Groups)
{
entityUser.ForceLogout = true;
}
entityUser.Mail = user.Mail; entityUser.Mail = user.Mail;
entityUser.Name = user.Name; entityUser.Name = user.Name;
entityUser.Type = user.Type; entityUser.Type = user.Type;
@@ -61,24 +143,6 @@ namespace FoodsharingSiegen.Server.Data.Service
} }
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// #endregion
/// <summary> Gets users asynchronous. </summary>
///
/// <remarks> A Beging, 20.10.2021. </remarks>
///
/// <returns> An asynchronous result that yields the users. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
public async Task<OperationResult<List<User>>> GetUsersAsync()
{
try
{
var users = await Context.Users.ToListAsync();
return new OperationResult<List<User>>(users);
}
catch (Exception e)
{
return new OperationResult<List<User>>(e);
}
}
} }
} }

View File

@@ -5,6 +5,7 @@
@using FoodsharingSiegen.Server.Controls @using FoodsharingSiegen.Server.Controls
@using FoodsharingSiegen.Contracts.Entity @using FoodsharingSiegen.Contracts.Entity
@using FoodsharingSiegen.Contracts.Helper @using FoodsharingSiegen.Contracts.Helper
@using FoodsharingSiegen.Server.BaseClasses
@inherits FsBase @inherits FsBase

View File

@@ -2,7 +2,7 @@
@page "/users" @page "/users"
@using FoodsharingSiegen.Contracts.Entity @using FoodsharingSiegen.Contracts.Entity
@inherits FsBase @inherits FoodsharingSiegen.Server.BaseClasses.FsBase
@code { @code {

View File

@@ -20,8 +20,10 @@ namespace FoodsharingSiegen.Shared.Helper
/// <returns>The string</returns> /// <returns>The string</returns>
public static string CreateToken(User user) public static string CreateToken(User user)
{ {
user.Password = ""; var userClone = user.Clone();
var serializedUser = JsonSerializer.Serialize(user);
userClone.Password = "";
var serializedUser = JsonSerializer.Serialize(userClone);
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor var tokenDescriptor = new SecurityTokenDescriptor
@@ -57,17 +59,6 @@ namespace FoodsharingSiegen.Shared.Helper
try try
{ {
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = Cryptor.GetSigningKey(),
ValidateAudience = true,
ValidAudience = Audience,
ValidateIssuer = true,
ValidIssuer = Issuer
}, out var stuff);
var result = tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters var result = tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters
{ {
@@ -81,15 +72,15 @@ namespace FoodsharingSiegen.Shared.Helper
ValidIssuer = Issuer ValidIssuer = Issuer
}).Result; }).Result;
if (result.Claims.TryGetValue(ClaimTypes.UserData, out var jsonObj)) if (result.Claims.TryGetValue(ClaimTypes.UserData, out var jsonObj) && jsonObj != null)
{ {
user = JsonSerializer.Deserialize<User>(jsonObj.ToString()); user = JsonSerializer.Deserialize<User>(jsonObj.ToString()!);
if (user != null) user.Password = string.Empty; if (user != null) user.Password = string.Empty;
} }
return result.IsValid; return result.IsValid;
} }
catch (Exception e) catch (Exception)
{ {
return false; return false;
} }