Claim Logic
This commit is contained in:
@@ -98,6 +98,7 @@ namespace FoodsharingSiegen.Server.Auth
|
||||
{
|
||||
Name = "Andre",
|
||||
Mail = "fs@beging.de",
|
||||
GroupsList = new List<UserGroup> { UserGroup.Ambassador },
|
||||
Type = UserType.Admin,
|
||||
Created = DateTime.UtcNow,
|
||||
EncryptedPassword = "qSIxTZo7J8M="
|
||||
@@ -117,7 +118,15 @@ namespace FoodsharingSiegen.Server.Auth
|
||||
{
|
||||
var serializedToken = AuthHelper.CreateToken(_user);
|
||||
await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
|
||||
|
||||
|
||||
if (_user.ForceLogout)
|
||||
{
|
||||
_user.ForceLogout = false;
|
||||
await Context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
Context.Entry(_user).State = EntityState.Detached;
|
||||
|
||||
return new OperationResult();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Security.Claims;
|
||||
using FoodsharingSiegen.Contracts;
|
||||
using FoodsharingSiegen.Server.Auth;
|
||||
using FoodsharingSiegen.Server.Data.Service;
|
||||
using FoodsharingSiegen.Shared.Helper;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
@@ -17,6 +18,8 @@ namespace FoodsharingSiegen.Server.Service
|
||||
/// <summary> LocalStorageService </summary>
|
||||
private readonly LocalStorageService _localStorageService;
|
||||
|
||||
private readonly UserService _userService;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Setup/Teardown
|
||||
@@ -25,7 +28,12 @@ namespace FoodsharingSiegen.Server.Service
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <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
|
||||
|
||||
@@ -38,8 +46,12 @@ namespace FoodsharingSiegen.Server.Service
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
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();
|
||||
if (tokenValid)
|
||||
identity = new ClaimsIdentity(new[]
|
||||
|
||||
@@ -2,7 +2,7 @@ using FoodsharingSiegen.Contracts.Entity;
|
||||
using FoodsharingSiegen.Server.Auth;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace FoodsharingSiegen.Server.Pages
|
||||
namespace FoodsharingSiegen.Server.BaseClasses
|
||||
{
|
||||
/// <summary>
|
||||
/// The fs base class (a. beging, 08.04.2022)
|
||||
@@ -28,6 +28,9 @@
|
||||
|
||||
[Parameter]
|
||||
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>();
|
||||
|
||||
@@ -62,7 +65,7 @@
|
||||
<span>(<i>@interaction.Info</i>)</span>
|
||||
}
|
||||
|
||||
@if (!Prospect.Complete || interaction.Type == InteractionType.Complete)
|
||||
@if ((!Prospect.Complete || interaction.Type == InteractionType.Complete) && AllowAddInteraction)
|
||||
{
|
||||
<span> <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 = "+";
|
||||
<Button Size="Size.Small" Clicked="AddClick">@ButtonText</Button>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@using FoodsharingSiegen.Contracts.Entity
|
||||
@using FoodsharingSiegen.Contracts.Helper
|
||||
@using FoodsharingSiegen.Server.BaseClasses
|
||||
@inherits FsBase
|
||||
|
||||
@{
|
||||
var divClass = "pc-main";
|
||||
@@ -18,6 +21,7 @@
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
Type="InteractionType.Welcome"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.WelcomeTeam)"
|
||||
AddClick="() => AddInteraction(InteractionType.Welcome)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
Caption="Begrüßung"
|
||||
@@ -28,6 +32,7 @@
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
Type="InteractionType.EinAb"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.WelcomeTeam, UserGroup.StoreManager, UserGroup.Ambassador)"
|
||||
AddClick="() => AddInteraction(InteractionType.EinAb)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
Caption="Einführungen"
|
||||
@@ -40,6 +45,7 @@
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
Type="InteractionType.IdCheck"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
|
||||
AddClick="() => AddInteraction(InteractionType.IdCheck)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
Caption="Perso prüfen"
|
||||
@@ -50,6 +56,7 @@
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
Type="InteractionType.PdfPass"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
|
||||
AddClick="() => AddInteraction(InteractionType.PdfPass)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
Caption="FS-Ausweis (digital)"
|
||||
@@ -60,6 +67,7 @@
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
Type="InteractionType.PrintPass"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
|
||||
AddClick="() => AddInteraction(InteractionType.PrintPass)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
Caption="FS-Ausweis (print)"
|
||||
@@ -69,6 +77,7 @@
|
||||
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
|
||||
Type="InteractionType.Verify"
|
||||
AddClick="() => AddInteraction(InteractionType.Verify)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
@@ -79,6 +88,7 @@
|
||||
|
||||
<InteractionRow
|
||||
Prospect="Prospect"
|
||||
AllowAddInteraction="@CurrentUser.IsInGroup(UserGroup.Ambassador)"
|
||||
Type="InteractionType.Complete"
|
||||
AddClick="() => AddInteraction(InteractionType.Complete)"
|
||||
RemoveClick="@RemoveInteraction"
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
using FoodsharingSiegen.Contracts;
|
||||
using FoodsharingSiegen.Contracts;
|
||||
using FoodsharingSiegen.Contracts.Entity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace FoodsharingSiegen.Server.Data.Service
|
||||
{
|
||||
/// <summary>
|
||||
/// The user service class (a. beging, 11.04.2022)
|
||||
/// </summary>
|
||||
/// <seealso cref="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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
try
|
||||
@@ -36,14 +55,77 @@ 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
var entityUser = await Context.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
|
||||
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.Name = user.Name;
|
||||
entityUser.Type = user.Type;
|
||||
@@ -60,25 +142,7 @@ namespace FoodsharingSiegen.Server.Data.Service
|
||||
return new OperationResult(e);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
@using FoodsharingSiegen.Server.Controls
|
||||
@using FoodsharingSiegen.Contracts.Entity
|
||||
@using FoodsharingSiegen.Contracts.Helper
|
||||
@using FoodsharingSiegen.Server.BaseClasses
|
||||
|
||||
@inherits FsBase
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@page "/users"
|
||||
@using FoodsharingSiegen.Contracts.Entity
|
||||
|
||||
@inherits FsBase
|
||||
@inherits FoodsharingSiegen.Server.BaseClasses.FsBase
|
||||
|
||||
@code {
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace FoodsharingSiegen.Shared.Helper
|
||||
/// <returns>The string</returns>
|
||||
public static string CreateToken(User user)
|
||||
{
|
||||
user.Password = "";
|
||||
var serializedUser = JsonSerializer.Serialize(user);
|
||||
var userClone = user.Clone();
|
||||
|
||||
userClone.Password = "";
|
||||
var serializedUser = JsonSerializer.Serialize(userClone);
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
@@ -57,18 +59,7 @@ namespace FoodsharingSiegen.Shared.Helper
|
||||
try
|
||||
{
|
||||
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
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
@@ -81,15 +72,15 @@ namespace FoodsharingSiegen.Shared.Helper
|
||||
ValidIssuer = Issuer
|
||||
}).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;
|
||||
}
|
||||
|
||||
return result.IsValid;
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user