Password encryption, Claim groups

This commit is contained in:
Andre Beging
2022-04-11 13:05:15 +02:00
parent c553047369
commit 208ea99a42
12 changed files with 273 additions and 125 deletions

View File

@@ -1,5 +1,5 @@
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using FoodsharingSiegen.Shared.Helper; using FoodsharingSiegen.Contracts.Helper;
namespace FoodsharingSiegen.Contracts.Entity namespace FoodsharingSiegen.Contracts.Entity
{ {
@@ -33,8 +33,8 @@ namespace FoodsharingSiegen.Contracts.Entity
[NotMapped] [NotMapped]
public string Password public string Password
{ {
get => AuthHelper.TryDecrypt(EncryptedPassword, out var password) ? password : string.Empty; get => Cryptor.TryDecrypt(EncryptedPassword, out var password) ? password : string.Empty;
set => EncryptedPassword = AuthHelper.Encrypt(value); set => EncryptedPassword = Cryptor.Encrypt(value);
} }
#endregion #endregion

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FoodsharingSiegen.Shared\FoodsharingSiegen.Shared.csproj" /> <PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.16.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,105 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;
namespace FoodsharingSiegen.Contracts.Helper
{
public static class Cryptor
{
/// <summary>
/// The signing key
/// </summary>
private const string SigningKey = "2uasw2§$%1nd47n9s43&%Zs3529s23&/%AW";
#region Public Method TryDecrypt
/// <summary>
/// Decrypts the crypted text (a. beging, 04.04.2022)
/// </summary>
/// <param name="cryptedText">The crypted text</param>
/// <param name="plainText"></param>
/// <returns>The string</returns>
public static bool TryDecrypt(string cryptedText, out string plainText)
{
plainText = string.Empty;
try
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Convert.FromBase64String(cryptedText);
var cTransform = tripleDes.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
plainText = Encoding.UTF8.GetString(resultArray);
return true;
}
catch (Exception e)
{
return false;
}
}
#endregion
#region Public Method Encrypt
/// <summary>
/// Encrypts the plain text (a. beging, 04.04.2022)
/// </summary>
/// <param name="plainText">The plain text</param>
/// <returns>The string</returns>
public static string Encrypt(string plainText)
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Encoding.UTF8.GetBytes(plainText );
var cTransform = tripleDes.CreateEncryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
#endregion
#region Private Method GetSigningKey
/// <summary>
/// Gets the signing key (a. beging, 04.04.2022)
/// </summary>
/// <returns>The security key</returns>
public static SecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey));
#endregion
#region Private Method CreateAlgorithm
/// <summary>
/// Creates the algorithm using the specified triple des (a. beging, 04.04.2022)
/// </summary>
/// <param name="tripleDes">The triple des</param>
private static void CreateAlgorithm(out TripleDES tripleDes)
{
var md5 = MD5.Create();
var keyArray = md5.ComputeHash(Encoding.UTF8.GetBytes(SigningKey));
md5.Clear();
tripleDes = TripleDES.Create();
tripleDes.Key = keyArray;
tripleDes.Mode = CipherMode.ECB;
tripleDes.Padding = PaddingMode.PKCS7;
}
#endregion
}
}

View File

@@ -0,0 +1,38 @@
using FoodsharingSiegen.Contracts.Entity;
namespace FoodsharingSiegen.Contracts.Helper
{
/// <summary>
/// The entity extensions class (a. beging, 08.04.2022)
/// </summary>
public static class EntityExtensions
{
#region Public Method IsAdmin
/// <summary>
/// Describes whether is admin
/// </summary>
/// <param name="user">The user</param>
/// <returns>The bool</returns>
public static bool IsAdmin(this User user) => user.Type == UserType.Admin;
#endregion
#region Public Method IsInGroup
/// <summary>
/// Describes whether is in group
/// </summary>
/// <param name="user">The user</param>
/// <param name="groups">The groups</param>
/// <returns>The bool</returns>
public static bool IsInGroup(this User user, params UserGroup[] groups)
{
if (user.Type == UserType.Admin) return true;
if (groups.Any(x => user.GroupsList.Contains(x))) return true;
return false;
}
#endregion
}
}

View File

@@ -1,14 +1,12 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using FoodsharingSiegen.Contracts; using FoodsharingSiegen.Contracts;
using FoodsharingSiegen.Contracts.Entity; using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Contracts.Helper;
using FoodsharingSiegen.Server.Data; using FoodsharingSiegen.Server.Data;
using FoodsharingSiegen.Server.Data.Service; using FoodsharingSiegen.Server.Data.Service;
using FoodsharingSiegen.Server.Service; using FoodsharingSiegen.Server.Service;
using FoodsharingSiegen.Shared.Helper; using FoodsharingSiegen.Shared.Helper;
using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
namespace FoodsharingSiegen.Server.Auth namespace FoodsharingSiegen.Server.Auth
{ {
@@ -23,7 +21,7 @@ namespace FoodsharingSiegen.Server.Auth
/// <summary> /// <summary>
/// Gets or sets the value of the user (ab) /// Gets or sets the value of the user (ab)
/// </summary> /// </summary>
public User? User { get; set; } public User? User => _user;
#endregion #endregion
@@ -39,6 +37,11 @@ namespace FoodsharingSiegen.Server.Auth
/// </summary> /// </summary>
private readonly LocalStorageService _localStorageService; private readonly LocalStorageService _localStorageService;
/// <summary>
/// The user
/// </summary>
private User? _user;
#endregion #endregion
#region Setup/Teardown #region Setup/Teardown
@@ -49,7 +52,10 @@ namespace FoodsharingSiegen.Server.Auth
/// <param name="context">The context</param> /// <param name="context">The context</param>
/// <param name="localStorageService">The local storage service</param> /// <param name="localStorageService">The local storage service</param>
/// <param name="authenticationStateProvider">The authentication state provider</param> /// <param name="authenticationStateProvider">The authentication state provider</param>
public AuthService(FsContext context, LocalStorageService localStorageService, AuthenticationStateProvider authenticationStateProvider) : base(context) public AuthService(
FsContext context,
LocalStorageService localStorageService,
AuthenticationStateProvider authenticationStateProvider) : base(context)
{ {
_localStorageService = localStorageService; _localStorageService = localStorageService;
_authenticationStateProvider = authenticationStateProvider; _authenticationStateProvider = authenticationStateProvider;
@@ -57,6 +63,22 @@ namespace FoodsharingSiegen.Server.Auth
#endregion #endregion
#region Public Method Initialize
/// <summary>
/// Initializes this instance (a. beging, 11.04.2022)
/// </summary>
public async Task Initialize()
{
if (_user != null) return;
var token = await _localStorageService.GetItem<string>(StorageKeys.TokenKey);
if (AuthHelper.ValidateToken(token, out var user) && user != null)
_user = user;
}
#endregion
#region Public Method Login #region Public Method Login
/// <summary> /// <summary>
@@ -87,13 +109,13 @@ namespace FoodsharingSiegen.Server.Auth
#endregion Ensure Admin #endregion Ensure Admin
var encryptedPassword = AuthHelper.Encrypt(password); var encryptedPassword = Cryptor.Encrypt(password);
User = await Context.Users.FirstOrDefaultAsync(x => x.Mail.ToLower() == mailAddress.ToLower() && x.EncryptedPassword == encryptedPassword); _user = await Context.Users.FirstOrDefaultAsync(x => x.Mail.ToLower() == mailAddress.ToLower() && x.EncryptedPassword == encryptedPassword);
if (User != null) if (_user != null)
{ {
var serializedToken = AuthHelper.CreateToken(User.Id); var serializedToken = AuthHelper.CreateToken(_user);
await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken); await _localStorageService.SetItem(StorageKeys.TokenKey, serializedToken);
return new OperationResult(); return new OperationResult();
@@ -115,7 +137,7 @@ namespace FoodsharingSiegen.Server.Auth
try try
{ {
await _localStorageService.RemoveItem(StorageKeys.TokenKey); await _localStorageService.RemoveItem(StorageKeys.TokenKey);
User = null; _user = null;
((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut(); ((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut();
return new OperationResult(); return new OperationResult();
} }

View File

@@ -38,7 +38,7 @@ 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 = await AuthHelper.ValidateToken(token); var tokenValid = AuthHelper.ValidateToken(token, out _);
var identity = new ClaimsIdentity(); var identity = new ClaimsIdentity();
if (tokenValid) if (tokenValid)

View File

@@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\FoodsharingSiegen.Contracts\FoodsharingSiegen.Contracts.csproj" /> <ProjectReference Include="..\FoodsharingSiegen.Contracts\FoodsharingSiegen.Contracts.csproj" />
<ProjectReference Include="..\FoodsharingSiegen.Shared\FoodsharingSiegen.Shared.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -0,0 +1,41 @@
using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Server.Auth;
using Microsoft.AspNetCore.Components;
namespace FoodsharingSiegen.Server.Pages
{
/// <summary>
/// The fs base class (a. beging, 08.04.2022)
/// </summary>
/// <seealso cref="ComponentBase"/>
public class FsBase : ComponentBase
{
#region Dependencies (Injected)
/// <summary>
/// Gets or sets the value of the auth service (ab)
/// </summary>
[Inject] private AuthService? AuthService { get; set; }
#endregion
#region Override OnInitializedAsync
/// <summary>
/// Ons the initialized (a. beging, 11.04.2022)
/// </summary>
protected override async Task OnInitializedAsync()
{
await AuthService!.Initialize();
await base.OnInitializedAsync();
}
#endregion
/// <summary>
/// Gets the value of the current user (ab)
/// </summary>
protected User CurrentUser => AuthService?.User ?? new User();
}
}

View File

@@ -4,12 +4,19 @@
@using FoodsharingSiegen.Server.Dialogs @using FoodsharingSiegen.Server.Dialogs
@using FoodsharingSiegen.Server.Controls @using FoodsharingSiegen.Server.Controls
@using FoodsharingSiegen.Contracts.Entity @using FoodsharingSiegen.Contracts.Entity
@using FoodsharingSiegen.Contracts.Helper
@inherits FsBase
<PageTitle>Einarbeitungen</PageTitle> <PageTitle>Einarbeitungen</PageTitle>
<h4>Aktuelle Einarbeitungen</h4> <h4>Aktuelle Einarbeitungen</h4>
<Button Color="Color.Primary" Clicked="() => ProspectModal.Show()">Hinzufügen</Button> <Button
Color="Color.Primary"
Clicked="() => ProspectModal.Show()"
Visibility="@(CurrentUser.IsInGroup(UserGroup.WelcomeTeam, UserGroup.Ambassador) ? Visibility.Default : Visibility.Invisible)"
>Hinzufügen</Button>
@{ @{
var activeProspects = ProspectList?.Where(x => x.Interactions.All(i => i.Type != InteractionType.Complete)); var activeProspects = ProspectList?.Where(x => x.Interactions.All(i => i.Type != InteractionType.Complete));

View File

@@ -2,6 +2,8 @@
@page "/users" @page "/users"
@using FoodsharingSiegen.Contracts.Entity @using FoodsharingSiegen.Contracts.Entity
@inherits FsBase
@code { @code {
private RenderFragment PopupTitleTemplate(PopupTitleContext<User> value) private RenderFragment PopupTitleTemplate(PopupTitleContext<User> value)
@@ -24,7 +26,7 @@
<h2>Benutzerverwaltung <span style="font-size: .5em; line-height: 0;">Admin</span></h2> <h2>Benutzerverwaltung <span style="font-size: .5em; line-height: 0;">Admin</span></h2>
<div class="my-2"> <div class="my-2">
<Button Color="Color.Primary" Disabled="@(SelectedUser == null)">Passwort setzen</Button> <Button Color="Color.Primary" Disabled="@(SelectedUser == null)"><i class="fa-solid fa-key"></i>&nbsp;setzen</Button>
</div> </div>
<DataGrid TItem="User" <DataGrid TItem="User"
@@ -35,7 +37,7 @@
PopupTitleTemplate="PopupTitleTemplate" PopupTitleTemplate="PopupTitleTemplate"
RowInserted="RowInserted" RowInserted="RowInserted"
RowUpdated="RowUpdated" RowUpdated="RowUpdated"
SelectedRow="SelectedUser" @bind-SelectedRow="SelectedUser"
RowDoubleClicked="arg => UserDataGrid.Edit(arg.Item)" RowDoubleClicked="arg => UserDataGrid.Edit(arg.Item)"
Editable Editable
Responsive> Responsive>

View File

@@ -10,4 +10,8 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.16.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.16.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\FoodsharingSiegen.Contracts\FoodsharingSiegen.Contracts.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,7 +1,8 @@
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Cryptography; using System.Text.Json;
using System.Text; using FoodsharingSiegen.Contracts.Entity;
using FoodsharingSiegen.Contracts.Helper;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
namespace FoodsharingSiegen.Shared.Helper namespace FoodsharingSiegen.Shared.Helper
@@ -16,22 +17,23 @@ namespace FoodsharingSiegen.Shared.Helper
/// <summary> /// <summary>
/// Creates the token using the specified user id (a. beging, 04.04.2022) /// Creates the token using the specified user id (a. beging, 04.04.2022)
/// </summary> /// </summary>
/// <param name="userId">The user id</param>
/// <returns>The string</returns> /// <returns>The string</returns>
public static string CreateToken(Guid userId) public static string CreateToken(User user)
{ {
user.Password = "";
var serializedUser = JsonSerializer.Serialize(user);
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor var tokenDescriptor = new SecurityTokenDescriptor
{ {
Subject = new ClaimsIdentity(new[] Subject = new ClaimsIdentity(new[]
{ {
new Claim(ClaimTypes.NameIdentifier, userId.ToString()), new Claim(ClaimTypes.UserData, serializedUser)
new Claim("CanDoShit","yes")
}), }),
Expires = DateTime.UtcNow.AddDays(30), Expires = DateTime.UtcNow.AddDays(30),
Issuer = Issuer, Issuer = Issuer,
Audience = Audience, Audience = Audience,
SigningCredentials = new SigningCredentials(GetSigningKey(), SecurityAlgorithms.HmacSha256Signature) SigningCredentials = new SigningCredentials(Cryptor.GetSigningKey(), SecurityAlgorithms.HmacSha256Signature)
}; };
var token = tokenHandler.CreateToken(tokenDescriptor); var token = tokenHandler.CreateToken(tokenDescriptor);
@@ -40,90 +42,50 @@ namespace FoodsharingSiegen.Shared.Helper
#endregion #endregion
#region Public Method Decrypt
/// <summary>
/// Decrypts the crypted text (a. beging, 04.04.2022)
/// </summary>
/// <param name="cryptedText">The crypted text</param>
/// <param name="plainText"></param>
/// <returns>The string</returns>
public static bool TryDecrypt(string cryptedText, out string plainText)
{
plainText = string.Empty;
try
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Convert.FromBase64String(cryptedText);
var cTransform = tripleDes.CreateDecryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
plainText = Encoding.UTF8.GetString(resultArray);
return true;
}
catch (Exception e)
{
return false;
}
}
#endregion
#region Public Method Encrypt
/// <summary>
/// Encrypts the plain text (a. beging, 04.04.2022)
/// </summary>
/// <param name="plainText">The plain text</param>
/// <returns>The string</returns>
public static string Encrypt(string plainText)
{
CreateAlgorithm(out var tripleDes);
var toEncryptArray = Encoding.UTF8.GetBytes(plainText );
var cTransform = tripleDes.CreateEncryptor();
var resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
tripleDes.Clear();
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
}
#endregion
#region Public Method ValidateToken #region Public Method ValidateToken
/// <summary> /// <summary>
/// Validates the token using the specified token (a. beging, 04.04.2022) /// Validates the token using the specified token (a. beging, 04.04.2022)
/// </summary> /// </summary>
/// <param name="token">The token</param> /// <param name="token">The token</param>
/// <param name="user"></param>
/// <returns>A task containing the bool</returns> /// <returns>A task containing the bool</returns>
public static async Task<bool> ValidateToken(string? token) public static bool ValidateToken(string? token, out User? user)
{ {
user = null;
try try
{ {
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var result = await tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters tokenHandler.ValidateToken(token, new TokenValidationParameters
{ {
ValidateIssuerSigningKey = true, ValidateIssuerSigningKey = true,
IssuerSigningKey = GetSigningKey(), IssuerSigningKey = Cryptor.GetSigningKey(),
ValidateAudience = true, ValidateAudience = true,
ValidAudience = Audience, ValidAudience = Audience,
ValidateIssuer = true, ValidateIssuer = true,
ValidIssuer = Issuer ValidIssuer = Issuer
}); }, out var stuff);
var result = tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = Cryptor.GetSigningKey(),
ValidateAudience = true,
ValidAudience = Audience,
ValidateIssuer = true,
ValidIssuer = Issuer
}).Result;
if (result.Claims.TryGetValue(ClaimTypes.UserData, out var jsonObj))
{
user = JsonSerializer.Deserialize<User>(jsonObj.ToString());
if (user != null) user.Password = string.Empty;
}
return result.IsValid; return result.IsValid;
} }
@@ -135,40 +97,6 @@ namespace FoodsharingSiegen.Shared.Helper
#endregion #endregion
#region Private Method CreateAlgorithm
/// <summary>
/// Creates the algorithm using the specified triple des (a. beging, 04.04.2022)
/// </summary>
/// <param name="tripleDes">The triple des</param>
private static void CreateAlgorithm(out TripleDES tripleDes)
{
var md5 = MD5.Create();
var keyArray = md5.ComputeHash(Encoding.UTF8.GetBytes(SigningKey));
md5.Clear();
tripleDes = TripleDES.Create();
tripleDes.Key = keyArray;
tripleDes.Mode = CipherMode.ECB;
tripleDes.Padding = PaddingMode.PKCS7;
}
#endregion
#region Private Method GetSigningKey
/// <summary>
/// Gets the signing key (a. beging, 04.04.2022)
/// </summary>
/// <returns>The security key</returns>
private static SecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey));
#endregion
/// <summary>
/// The signing key
/// </summary>
private const string SigningKey = "2uasw2§$%1nd47n9s43&%Zs3529s23&/%AW";
/// <summary> /// <summary>
/// The audience /// The audience