From 208ea99a42afdd37e9451177e477d3646f1119ac Mon Sep 17 00:00:00 2001 From: Andre Beging Date: Mon, 11 Apr 2022 13:05:15 +0200 Subject: [PATCH] Password encryption, Claim groups --- FoodsharingSiegen.Contracts/Entity/User.cs | 6 +- .../FoodsharingSiegen.Contracts.csproj | 2 +- FoodsharingSiegen.Contracts/Helper/Cryptor.cs | 105 +++++++++++++ .../Helper/EntityExtensions.cs | 38 +++++ FoodsharingSiegen.Server/Auth/AuthService.cs | 42 ++++-- .../Auth/TokenAuthStateProvider.cs | 2 +- .../FoodsharingSiegen.Server.csproj | 1 + FoodsharingSiegen.Server/Pages/FsBase.cs | 41 +++++ .../Pages/Prospects.razor | 9 +- FoodsharingSiegen.Server/Pages/Users.razor | 6 +- .../FoodsharingSiegen.Shared.csproj | 4 + FoodsharingSiegen.Shared/Helper/AuthHelper.cs | 142 +++++------------- 12 files changed, 273 insertions(+), 125 deletions(-) create mode 100644 FoodsharingSiegen.Contracts/Helper/Cryptor.cs create mode 100644 FoodsharingSiegen.Contracts/Helper/EntityExtensions.cs create mode 100644 FoodsharingSiegen.Server/Pages/FsBase.cs diff --git a/FoodsharingSiegen.Contracts/Entity/User.cs b/FoodsharingSiegen.Contracts/Entity/User.cs index 24bea47..e93600a 100644 --- a/FoodsharingSiegen.Contracts/Entity/User.cs +++ b/FoodsharingSiegen.Contracts/Entity/User.cs @@ -1,5 +1,5 @@ using System.ComponentModel.DataAnnotations.Schema; -using FoodsharingSiegen.Shared.Helper; +using FoodsharingSiegen.Contracts.Helper; namespace FoodsharingSiegen.Contracts.Entity { @@ -33,8 +33,8 @@ namespace FoodsharingSiegen.Contracts.Entity [NotMapped] public string Password { - get => AuthHelper.TryDecrypt(EncryptedPassword, out var password) ? password : string.Empty; - set => EncryptedPassword = AuthHelper.Encrypt(value); + get => Cryptor.TryDecrypt(EncryptedPassword, out var password) ? password : string.Empty; + set => EncryptedPassword = Cryptor.Encrypt(value); } #endregion diff --git a/FoodsharingSiegen.Contracts/FoodsharingSiegen.Contracts.csproj b/FoodsharingSiegen.Contracts/FoodsharingSiegen.Contracts.csproj index 78e054a..445c813 100644 --- a/FoodsharingSiegen.Contracts/FoodsharingSiegen.Contracts.csproj +++ b/FoodsharingSiegen.Contracts/FoodsharingSiegen.Contracts.csproj @@ -7,7 +7,7 @@ - + diff --git a/FoodsharingSiegen.Contracts/Helper/Cryptor.cs b/FoodsharingSiegen.Contracts/Helper/Cryptor.cs new file mode 100644 index 0000000..7023e2c --- /dev/null +++ b/FoodsharingSiegen.Contracts/Helper/Cryptor.cs @@ -0,0 +1,105 @@ +using System.Security.Cryptography; +using System.Text; +using Microsoft.IdentityModel.Tokens; + +namespace FoodsharingSiegen.Contracts.Helper +{ + public static class Cryptor + { + /// + /// The signing key + /// + private const string SigningKey = "2uasw2§$%1nd47n9s43&%Zs3529s23&/%AW"; + + #region Public Method TryDecrypt + + /// + /// Decrypts the crypted text (a. beging, 04.04.2022) + /// + /// The crypted text + /// + /// The string + 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 + + /// + /// Encrypts the plain text (a. beging, 04.04.2022) + /// + /// The plain text + /// The string + 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 + + /// + /// Gets the signing key (a. beging, 04.04.2022) + /// + /// The security key + public static SecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey)); + + #endregion + + #region Private Method CreateAlgorithm + + /// + /// Creates the algorithm using the specified triple des (a. beging, 04.04.2022) + /// + /// The triple des + 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 + } +} \ No newline at end of file diff --git a/FoodsharingSiegen.Contracts/Helper/EntityExtensions.cs b/FoodsharingSiegen.Contracts/Helper/EntityExtensions.cs new file mode 100644 index 0000000..8fec741 --- /dev/null +++ b/FoodsharingSiegen.Contracts/Helper/EntityExtensions.cs @@ -0,0 +1,38 @@ +using FoodsharingSiegen.Contracts.Entity; + +namespace FoodsharingSiegen.Contracts.Helper +{ + /// + /// The entity extensions class (a. beging, 08.04.2022) + /// + public static class EntityExtensions + { + #region Public Method IsAdmin + + /// + /// Describes whether is admin + /// + /// The user + /// The bool + public static bool IsAdmin(this User user) => user.Type == UserType.Admin; + + #endregion + + #region Public Method IsInGroup + + /// + /// Describes whether is in group + /// + /// The user + /// The groups + /// The bool + 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 + } +} \ No newline at end of file diff --git a/FoodsharingSiegen.Server/Auth/AuthService.cs b/FoodsharingSiegen.Server/Auth/AuthService.cs index eb4cc06..4409f92 100644 --- a/FoodsharingSiegen.Server/Auth/AuthService.cs +++ b/FoodsharingSiegen.Server/Auth/AuthService.cs @@ -1,14 +1,12 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; using FoodsharingSiegen.Contracts; using FoodsharingSiegen.Contracts.Entity; +using FoodsharingSiegen.Contracts.Helper; using FoodsharingSiegen.Server.Data; using FoodsharingSiegen.Server.Data.Service; using FoodsharingSiegen.Server.Service; using FoodsharingSiegen.Shared.Helper; using Microsoft.AspNetCore.Components.Authorization; using Microsoft.EntityFrameworkCore; -using Microsoft.IdentityModel.Tokens; namespace FoodsharingSiegen.Server.Auth { @@ -23,7 +21,7 @@ namespace FoodsharingSiegen.Server.Auth /// /// Gets or sets the value of the user (ab) /// - public User? User { get; set; } + public User? User => _user; #endregion @@ -39,6 +37,11 @@ namespace FoodsharingSiegen.Server.Auth /// private readonly LocalStorageService _localStorageService; + /// + /// The user + /// + private User? _user; + #endregion #region Setup/Teardown @@ -49,7 +52,10 @@ namespace FoodsharingSiegen.Server.Auth /// The context /// The local storage service /// The authentication state provider - public AuthService(FsContext context, LocalStorageService localStorageService, AuthenticationStateProvider authenticationStateProvider) : base(context) + public AuthService( + FsContext context, + LocalStorageService localStorageService, + AuthenticationStateProvider authenticationStateProvider) : base(context) { _localStorageService = localStorageService; _authenticationStateProvider = authenticationStateProvider; @@ -57,6 +63,22 @@ namespace FoodsharingSiegen.Server.Auth #endregion + #region Public Method Initialize + + /// + /// Initializes this instance (a. beging, 11.04.2022) + /// + public async Task Initialize() + { + if (_user != null) return; + + var token = await _localStorageService.GetItem(StorageKeys.TokenKey); + if (AuthHelper.ValidateToken(token, out var user) && user != null) + _user = user; + } + + #endregion + #region Public Method Login /// @@ -87,13 +109,13 @@ namespace FoodsharingSiegen.Server.Auth #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); return new OperationResult(); @@ -115,7 +137,7 @@ namespace FoodsharingSiegen.Server.Auth try { await _localStorageService.RemoveItem(StorageKeys.TokenKey); - User = null; + _user = null; ((TokenAuthStateProvider) _authenticationStateProvider).MarkUserAsLoggedOut(); return new OperationResult(); } diff --git a/FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs b/FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs index 59eade6..2c84edf 100644 --- a/FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs +++ b/FoodsharingSiegen.Server/Auth/TokenAuthStateProvider.cs @@ -38,7 +38,7 @@ namespace FoodsharingSiegen.Server.Service public override async Task GetAuthenticationStateAsync() { var token = await _localStorageService.GetItem(StorageKeys.TokenKey); - var tokenValid = await AuthHelper.ValidateToken(token); + var tokenValid = AuthHelper.ValidateToken(token, out _); var identity = new ClaimsIdentity(); if (tokenValid) diff --git a/FoodsharingSiegen.Server/FoodsharingSiegen.Server.csproj b/FoodsharingSiegen.Server/FoodsharingSiegen.Server.csproj index 2fd10f9..a76b7ed 100644 --- a/FoodsharingSiegen.Server/FoodsharingSiegen.Server.csproj +++ b/FoodsharingSiegen.Server/FoodsharingSiegen.Server.csproj @@ -8,6 +8,7 @@ + diff --git a/FoodsharingSiegen.Server/Pages/FsBase.cs b/FoodsharingSiegen.Server/Pages/FsBase.cs new file mode 100644 index 0000000..a291f96 --- /dev/null +++ b/FoodsharingSiegen.Server/Pages/FsBase.cs @@ -0,0 +1,41 @@ +using FoodsharingSiegen.Contracts.Entity; +using FoodsharingSiegen.Server.Auth; +using Microsoft.AspNetCore.Components; + +namespace FoodsharingSiegen.Server.Pages +{ + /// + /// The fs base class (a. beging, 08.04.2022) + /// + /// + public class FsBase : ComponentBase + { + #region Dependencies (Injected) + + /// + /// Gets or sets the value of the auth service (ab) + /// + [Inject] private AuthService? AuthService { get; set; } + + #endregion + + + #region Override OnInitializedAsync + + /// + /// Ons the initialized (a. beging, 11.04.2022) + /// + protected override async Task OnInitializedAsync() + { + await AuthService!.Initialize(); + await base.OnInitializedAsync(); + } + + #endregion + + /// + /// Gets the value of the current user (ab) + /// + protected User CurrentUser => AuthService?.User ?? new User(); + } +} \ No newline at end of file diff --git a/FoodsharingSiegen.Server/Pages/Prospects.razor b/FoodsharingSiegen.Server/Pages/Prospects.razor index db48493..2df9f58 100644 --- a/FoodsharingSiegen.Server/Pages/Prospects.razor +++ b/FoodsharingSiegen.Server/Pages/Prospects.razor @@ -4,12 +4,19 @@ @using FoodsharingSiegen.Server.Dialogs @using FoodsharingSiegen.Server.Controls @using FoodsharingSiegen.Contracts.Entity +@using FoodsharingSiegen.Contracts.Helper + +@inherits FsBase Einarbeitungen

Aktuelle Einarbeitungen

- + @{ var activeProspects = ProspectList?.Where(x => x.Interactions.All(i => i.Type != InteractionType.Complete)); diff --git a/FoodsharingSiegen.Server/Pages/Users.razor b/FoodsharingSiegen.Server/Pages/Users.razor index 7e7bc6a..e545426 100644 --- a/FoodsharingSiegen.Server/Pages/Users.razor +++ b/FoodsharingSiegen.Server/Pages/Users.razor @@ -2,6 +2,8 @@ @page "/users" @using FoodsharingSiegen.Contracts.Entity +@inherits FsBase + @code { private RenderFragment PopupTitleTemplate(PopupTitleContext value) @@ -24,7 +26,7 @@

Benutzerverwaltung Admin

- +
diff --git a/FoodsharingSiegen.Shared/FoodsharingSiegen.Shared.csproj b/FoodsharingSiegen.Shared/FoodsharingSiegen.Shared.csproj index 2d1eb67..d794571 100644 --- a/FoodsharingSiegen.Shared/FoodsharingSiegen.Shared.csproj +++ b/FoodsharingSiegen.Shared/FoodsharingSiegen.Shared.csproj @@ -10,4 +10,8 @@
+ + + + diff --git a/FoodsharingSiegen.Shared/Helper/AuthHelper.cs b/FoodsharingSiegen.Shared/Helper/AuthHelper.cs index fcc7285..ceed536 100644 --- a/FoodsharingSiegen.Shared/Helper/AuthHelper.cs +++ b/FoodsharingSiegen.Shared/Helper/AuthHelper.cs @@ -1,7 +1,8 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; -using System.Security.Cryptography; -using System.Text; +using System.Text.Json; +using FoodsharingSiegen.Contracts.Entity; +using FoodsharingSiegen.Contracts.Helper; using Microsoft.IdentityModel.Tokens; namespace FoodsharingSiegen.Shared.Helper @@ -16,22 +17,23 @@ namespace FoodsharingSiegen.Shared.Helper /// /// Creates the token using the specified user id (a. beging, 04.04.2022) /// - /// The user id /// The string - public static string CreateToken(Guid userId) + public static string CreateToken(User user) { + user.Password = ""; + var serializedUser = JsonSerializer.Serialize(user); + var tokenHandler = new JwtSecurityTokenHandler(); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { - new Claim(ClaimTypes.NameIdentifier, userId.ToString()), - new Claim("CanDoShit","yes") + new Claim(ClaimTypes.UserData, serializedUser) }), Expires = DateTime.UtcNow.AddDays(30), Issuer = Issuer, Audience = Audience, - SigningCredentials = new SigningCredentials(GetSigningKey(), SecurityAlgorithms.HmacSha256Signature) + SigningCredentials = new SigningCredentials(Cryptor.GetSigningKey(), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); @@ -39,92 +41,52 @@ namespace FoodsharingSiegen.Shared.Helper } #endregion - - #region Public Method Decrypt - - /// - /// Decrypts the crypted text (a. beging, 04.04.2022) - /// - /// The crypted text - /// - /// The string - 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 - - /// - /// Encrypts the plain text (a. beging, 04.04.2022) - /// - /// The plain text - /// The string - 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 /// /// Validates the token using the specified token (a. beging, 04.04.2022) /// /// The token + /// /// A task containing the bool - public static async Task ValidateToken(string? token) + public static bool ValidateToken(string? token, out User? user) { + user = null; + try { var tokenHandler = new JwtSecurityTokenHandler(); - var result = await tokenHandler.ValidateTokenAsync(token, new TokenValidationParameters + tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, - IssuerSigningKey = GetSigningKey(), + IssuerSigningKey = Cryptor.GetSigningKey(), ValidateAudience = true, ValidAudience = Audience, ValidateIssuer = true, 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(jsonObj.ToString()); + if (user != null) user.Password = string.Empty; + } + return result.IsValid; } catch (Exception e) @@ -135,40 +97,6 @@ namespace FoodsharingSiegen.Shared.Helper #endregion - #region Private Method CreateAlgorithm - - /// - /// Creates the algorithm using the specified triple des (a. beging, 04.04.2022) - /// - /// The triple des - 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 - - /// - /// Gets the signing key (a. beging, 04.04.2022) - /// - /// The security key - private static SecurityKey GetSigningKey() => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SigningKey)); - - #endregion - - /// - /// The signing key - /// - private const string SigningKey = "2uasw2§$%1nd47n9s43&%Zs3529s23&/%AW"; /// /// The audience