diff options
| author | ivar <i@oiee.no> | 2025-10-19 23:41:23 +0200 |
|---|---|---|
| committer | ivar <i@oiee.no> | 2025-10-19 23:41:23 +0200 |
| commit | 3f4c0720e1e3421431e7baa20882a4a4512a7fab (patch) | |
| tree | 734ca81d7d0841d8863e3f523ebba14c282dc681 /src/Utilities | |
| download | fagprove-3f4c0720e1e3421431e7baa20882a4a4512a7fab.tar.xz fagprove-3f4c0720e1e3421431e7baa20882a4a4512a7fab.zip | |
Diffstat (limited to 'src/Utilities')
| -rw-r--r-- | src/Utilities/ClaimExtensions.cs | 16 | ||||
| -rw-r--r-- | src/Utilities/Config.cs | 28 | ||||
| -rw-r--r-- | src/Utilities/Cryptography.cs | 26 | ||||
| -rw-r--r-- | src/Utilities/Email.cs | 56 | ||||
| -rw-r--r-- | src/Utilities/Helpers.cs | 19 | ||||
| -rw-r--r-- | src/Utilities/PasswordHasher.cs | 18 | ||||
| -rw-r--r-- | src/Utilities/ServicesCollectionExtensions.cs | 62 | ||||
| -rw-r--r-- | src/Utilities/Validators.cs | 50 |
8 files changed, 275 insertions, 0 deletions
diff --git a/src/Utilities/ClaimExtensions.cs b/src/Utilities/ClaimExtensions.cs new file mode 100644 index 0000000..f8becc2 --- /dev/null +++ b/src/Utilities/ClaimExtensions.cs @@ -0,0 +1,16 @@ +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; + +namespace IOL.Fagprove.Utilities +{ + public static class ClaimExtensions + { + public static string GetClaimValue(this IPrincipal currentPrincipal, string key) + { + var identity = currentPrincipal.Identity as ClaimsIdentity; + var claim = identity?.Claims.FirstOrDefault(c => c.Type == key); + return claim?.Value; + } + } +}
\ No newline at end of file diff --git a/src/Utilities/Config.cs b/src/Utilities/Config.cs new file mode 100644 index 0000000..79f7b58 --- /dev/null +++ b/src/Utilities/Config.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.CookiePolicy; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace IOL.Fagprove.Utilities +{ + public static class Config + { + public static string GetConnectionString(this IConfiguration config) + { + var databaseName = config.GetValue<string>("MYSQL_DB", "fagprove"); + var port = config.GetValue<string>("MYSQL_PORT", "3306"); + var host = config.GetValue<string>("MYSQL_HOST", "localhost"); + var user = config.GetValue<string>("MYSQL_USER", "root"); + var password = config.GetValue<string>("MYSQL_PASSWORD", "okpassword10"); + var res = $"server={host};port={port};user={user};password={password};database={databaseName}"; + return res; + } + + public static CookiePolicyOptions CookiePolicyOptions => new CookiePolicyOptions + { + MinimumSameSitePolicy = SameSiteMode.Lax, + HttpOnly = HttpOnlyPolicy.Always, + Secure = CookieSecurePolicy.SameAsRequest + }; + } +}
\ No newline at end of file diff --git a/src/Utilities/Cryptography.cs b/src/Utilities/Cryptography.cs new file mode 100644 index 0000000..cdf4984 --- /dev/null +++ b/src/Utilities/Cryptography.cs @@ -0,0 +1,26 @@ +using System.Security.Cryptography; +using System.Text; + +namespace IOL.Fagprove.Utilities +{ + public class Cryptography + { + public static string RandomString(int length = 12) + { + var chars = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); + var data = new byte[length]; + using (var crypto = new RNGCryptoServiceProvider()) + { + crypto.GetBytes(data); + } + + var result = new StringBuilder(length); + foreach (var b in data) + { + result.Append(chars[b % (chars.Length)]); + } + + return result.ToString(); + } + } +}
\ No newline at end of file diff --git a/src/Utilities/Email.cs b/src/Utilities/Email.cs new file mode 100644 index 0000000..248db32 --- /dev/null +++ b/src/Utilities/Email.cs @@ -0,0 +1,56 @@ +using System; +using System.Net.Mail; +using System.Net; + +namespace IOL.Fagprove.Utilities +{ + public class Email + { + public string Recepient { get; set; } + public string Message { get; set; } + public string Title { get; set; } + public bool Valid { get; private set; } + public bool Sent { get; private set; } + + public bool Send() + { + if (!Validate()) return false; + var client = new SmtpClient("smtp.server.com", 587) + { + EnableSsl = true, + Credentials = new NetworkCredential("user", "pass") + }; + var from = new MailAddress("bot@bottesen.no", "Hal", System.Text.Encoding.UTF8); + var to = new MailAddress(Recepient); + var message = new MailMessage(from, to) + { + Body = Message, + BodyEncoding = System.Text.Encoding.UTF8, + Subject = Title, + SubjectEncoding = System.Text.Encoding.UTF8 + }; +#if DEBUG + Console.WriteLine(Message); +#else + client.Send(message); +#endif + Sent = true; + return true; + } + + private bool Validate() + { + if (Recepient.IsMissing() + || Title.IsMissing() + || Message.IsMissing() + || !Recepient.IsEmail()) + { + Valid = false; + return false; + } + + Valid = true; + return true; + } + } +}
\ No newline at end of file diff --git a/src/Utilities/Helpers.cs b/src/Utilities/Helpers.cs new file mode 100644 index 0000000..ba9b648 --- /dev/null +++ b/src/Utilities/Helpers.cs @@ -0,0 +1,19 @@ +using System; +using IOL.Fagprove.Data.Enums; + +namespace IOL.Fagprove.Utilities +{ + public static class Helpers + { + public static Guid ToGuid(this string input) + { + return input.IsGuid() ? new Guid(input) : default; + } + + public static UserRole ToUserRole(this string input) + { + var isEnum = Enum.TryParse(input, out UserRole role); + return isEnum ? role : default; + } + } +}
\ No newline at end of file diff --git a/src/Utilities/PasswordHasher.cs b/src/Utilities/PasswordHasher.cs new file mode 100644 index 0000000..213e03d --- /dev/null +++ b/src/Utilities/PasswordHasher.cs @@ -0,0 +1,18 @@ +using IOL.Fagprove.Data.Models; +using Microsoft.AspNetCore.Identity; + +namespace IOL.Fagprove.Utilities +{ + public static class PasswordHasher + { + public static string HashPassword(string password) { + var hasher = new PasswordHasher<User>(); + return hasher.HashPassword(null, password); + } + + public static bool PasswordMatches(string hash, string password) { + var hasher = new PasswordHasher<User>(); + return hasher.VerifyHashedPassword(null, hash, password) == PasswordVerificationResult.Success; + } + } +}
\ No newline at end of file diff --git a/src/Utilities/ServicesCollectionExtensions.cs b/src/Utilities/ServicesCollectionExtensions.cs new file mode 100644 index 0000000..0e3b05e --- /dev/null +++ b/src/Utilities/ServicesCollectionExtensions.cs @@ -0,0 +1,62 @@ +using System; +using System.Security.Claims; +using IOL.Fagprove.Data; +using IOL.Fagprove.Data.Enums; +using IOL.Fagprove.Services; +using IOL.Fagprove.Services.Interfaces; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Redis; + +namespace IOL.Fagprove.Utilities +{ + public static class ServicesCollectionExtensions + { + public static void AddServices(this IServiceCollection services) + { + services.AddScoped<IUserService, UserService>(); + services.AddScoped<IAppReservationService, AppReservationService>(); + } + + public static void AddAppDbContext(this IServiceCollection services, IConfiguration config) + { + services.AddDbContext<AppDbContext>(options => + { + options.UseMySql(config.GetConnectionString(), + builder => builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(60), null)); +#if DEBUG + options.EnableSensitiveDataLogging(); +#endif + }); + } + + public static void AddDataProtectionWithRedis(this IServiceCollection services, IConfiguration config) + { + var accessKey = config.GetValue<string>("REDIS_KEY"); + if (!accessKey.IsPresent()) return; + var redis = ConnectionMultiplexer.Connect( + $"aredis.cache.net:6380,password={accessKey},ssl=True,abortConnect=False"); + services.AddDataProtection().PersistKeysToStackExchangeRedis(redis, "IOL.Fagprove.DataProtectionKeys"); + } + + public static void AddInternalUserPolicies(this IServiceCollection services) + { + services.Configure<AuthorizationOptions>(options => + { + options.AddPolicy(UserRole.Administrator.ToString(), policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim(ClaimTypes.Role, UserRole.Administrator.ToString()); + }); + options.AddPolicy(UserRole.Basic.ToString(), policy => + { + policy.RequireAuthenticatedUser(); + policy.RequireClaim(ClaimTypes.Role, new string[] {UserRole.Basic.ToString()}); + }); + }); + } + } +}
\ No newline at end of file diff --git a/src/Utilities/Validators.cs b/src/Utilities/Validators.cs new file mode 100644 index 0000000..e49d49a --- /dev/null +++ b/src/Utilities/Validators.cs @@ -0,0 +1,50 @@ +using System; +using System.Text.RegularExpressions; + +namespace IOL.Fagprove.Utilities +{ + public static class Validators + { + public static bool IsEmail(this string email) + { + try + { + const string pattern = + @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$"; + var regex = new Regex(pattern); + var match = regex.Match(email); + return match.Success; + } + catch + { + return false; + } + } + + public static bool IsMissing(this string value) + { + return string.IsNullOrWhiteSpace(value); + } + + public static bool IsPresent(this string value) + { + return !value.IsMissing(); + } + + public static bool IsNumeric(this string value) + { + var r = new Regex(@"[0-9]"); + return r.IsMatch(value); + } + + public static bool IsInt(this string value) + { + return int.TryParse(value, out _); + } + + public static bool IsGuid(this string value) + { + return Guid.TryParse(value, out _); + } + } +}
\ No newline at end of file |
