From 961ed872664b83bed788b402be0758d012dde16d Mon Sep 17 00:00:00 2001 From: Wvader <34067397+wvader@users.noreply.github.com> Date: Thu, 29 Sep 2022 00:37:24 +0100 Subject: [PATCH] Before remove session func --- .../Configuration/BlueWestConnectionString.cs | 13 - BlueWest.Api/Startup.cs | 16 +- BlueWest.Api/StartupExtensions.cs | 21 -- .../AuthSettings.cs | 0 .../Configuration/BlueWestConnectionString.cs | 13 + .../Session/SessionExtensions.cs | 18 ++ BlueWest.Data.Auth/Users/Auth/AuthManager.cs | 1 + BlueWest.Startup/BlueWest.Startup.csproj | 9 + BlueWest.Startup/Class1.cs | 278 ++++++++++++++++++ BlueWest.Views/BlueWest.Views.csproj | 5 + BlueWest.Views/Controllers/AuthController.cs | 10 +- BlueWest.Views/Program.cs | 89 ++---- BlueWest.Views/Startup.cs | 100 +++++++ BlueWest.Views/StartupExtensions.cs | 246 ++++++++++++++++ BlueWest.Views/Views/Auth/Index.cshtml | 6 + BlueWest.Views/Views/Jobs/Index.cshtml | 2 +- BlueWest.Views/appsettings.json | 17 +- BlueWest.Views/config.json | 4 + 18 files changed, 739 insertions(+), 109 deletions(-) delete mode 100644 BlueWest.Api/Configuration/BlueWestConnectionString.cs rename {BlueWest.Api/Configuration => BlueWest.Data.Auth}/AuthSettings.cs (100%) create mode 100644 BlueWest.Data.Auth/Configuration/BlueWestConnectionString.cs create mode 100644 BlueWest.Data.Auth/Session/SessionExtensions.cs create mode 100644 BlueWest.Startup/BlueWest.Startup.csproj create mode 100644 BlueWest.Startup/Class1.cs create mode 100644 BlueWest.Views/Startup.cs create mode 100644 BlueWest.Views/StartupExtensions.cs create mode 100644 BlueWest.Views/Views/Auth/Index.cshtml create mode 100644 BlueWest.Views/config.json diff --git a/BlueWest.Api/Configuration/BlueWestConnectionString.cs b/BlueWest.Api/Configuration/BlueWestConnectionString.cs deleted file mode 100644 index 9be68d1..0000000 --- a/BlueWest.Api/Configuration/BlueWestConnectionString.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace BlueWest.WebApi.Configuration -{ - internal class BlueWestConnectionString - { - public string Redis { get; set; } - public string MySql { get; set; } - } - internal class ConnectionStringDocker : BlueWestConnectionString { } - internal class ConnectionStringNoDocker : BlueWestConnectionString { } - - -} - diff --git a/BlueWest.Api/Startup.cs b/BlueWest.Api/Startup.cs index eb1419d..2047125 100644 --- a/BlueWest.Api/Startup.cs +++ b/BlueWest.Api/Startup.cs @@ -141,7 +141,6 @@ namespace BlueWest.WebApi .AddJsonFile("config.json") .Build(); - var allowedDatabase = configuration["database"]; services .AddSingleton(); @@ -150,21 +149,8 @@ namespace BlueWest.WebApi services.AddAuthServerServices( _configuration, _environment, configuration); services.AddScoped(); + services.PrepareMySqlDatabasePool(_configuration, _environment, configuration); - switch (allowedDatabase) - { - case "mysql": - services.PrepareMySqlDatabasePool(_configuration, _environment, configuration); - break; - - case "sqlite": - services.PrepareSqlLiteDatabasePool(_configuration, _environment); - break; - - default: - services.PrepareMySqlDatabasePool(_configuration, _environment, configuration); - break; - } } diff --git a/BlueWest.Api/StartupExtensions.cs b/BlueWest.Api/StartupExtensions.cs index 7f59c3d..667685b 100644 --- a/BlueWest.Api/StartupExtensions.cs +++ b/BlueWest.Api/StartupExtensions.cs @@ -120,28 +120,7 @@ namespace BlueWest.WebApi .AddDbContextPool(options => options.GetMySqlSettings(configuration, configurationRoot, environment)); } - - /// - /// Setup database Contexts - /// - /// - /// - /// - /// - public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection, - IConfiguration configuration, IWebHostEnvironment environment) - { - var sqliteConString = "Data Source=BlueWest.Api.db"; - return serviceCollection - .AddDbContextPool(options => options.UseSqlite(sqliteConString)) - .AddDbContextPool(options => options.UseSqlite(sqliteConString)) - .AddDbContextPool(options => options.UseSqlite(sqliteConString)) - .AddDbContextPool(options => options.UseSqlite(sqliteConString)) - .AddDbContextPool(options => options.UseSqlite(sqliteConString)); - - } - internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot) { diff --git a/BlueWest.Api/Configuration/AuthSettings.cs b/BlueWest.Data.Auth/AuthSettings.cs similarity index 100% rename from BlueWest.Api/Configuration/AuthSettings.cs rename to BlueWest.Data.Auth/AuthSettings.cs diff --git a/BlueWest.Data.Auth/Configuration/BlueWestConnectionString.cs b/BlueWest.Data.Auth/Configuration/BlueWestConnectionString.cs new file mode 100644 index 0000000..9d3813a --- /dev/null +++ b/BlueWest.Data.Auth/Configuration/BlueWestConnectionString.cs @@ -0,0 +1,13 @@ +namespace BlueWest.WebApi.Configuration +{ + public class BlueWestConnectionString + { + public string Redis { get; set; } + public string MySql { get; set; } + } + public class ConnectionStringDocker : BlueWestConnectionString { } + public class ConnectionStringNoDocker : BlueWestConnectionString { } + + +} + diff --git a/BlueWest.Data.Auth/Session/SessionExtensions.cs b/BlueWest.Data.Auth/Session/SessionExtensions.cs new file mode 100644 index 0000000..e37b022 --- /dev/null +++ b/BlueWest.Data.Auth/Session/SessionExtensions.cs @@ -0,0 +1,18 @@ +using System.Text.Json; +using Microsoft.AspNetCore.Http; + +namespace BlueWest.WebApi.Session; + +public static class SessionExtensions +{ + public static void Set(this ISession session, string key, T value) where T: class + { + session.SetString(key, JsonSerializer.Serialize(value)); + } + + public static T Get(this ISession session, string key) where T: class + { + var value = session.GetString(key); + return value == null ? default : JsonSerializer.Deserialize(value); + } +} \ No newline at end of file diff --git a/BlueWest.Data.Auth/Users/Auth/AuthManager.cs b/BlueWest.Data.Auth/Users/Auth/AuthManager.cs index 0d3b8db..7cf8669 100644 --- a/BlueWest.Data.Auth/Users/Auth/AuthManager.cs +++ b/BlueWest.Data.Auth/Users/Auth/AuthManager.cs @@ -1,3 +1,4 @@ +using System; using System.Security.Claims; using BlueWest.Cryptography; using BlueWest.Data.Application; diff --git a/BlueWest.Startup/BlueWest.Startup.csproj b/BlueWest.Startup/BlueWest.Startup.csproj new file mode 100644 index 0000000..eb2460e --- /dev/null +++ b/BlueWest.Startup/BlueWest.Startup.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/BlueWest.Startup/Class1.cs b/BlueWest.Startup/Class1.cs new file mode 100644 index 0000000..f1237b6 --- /dev/null +++ b/BlueWest.Startup/Class1.cs @@ -0,0 +1,278 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using BlueWest.Domain; +using BlueWest.Domain; +using BlueWest.Cryptography; +using BlueWest.Data.Application.Users; +using BlueWest.Data.Auth; +using BlueWest.Data.Auth.Context.Users; +using BlueWest.WebApi.Configuration; +using BlueWest.WebApi.Context.Users; +using BlueWest.WebApi.Session; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.Tokens; +using Redis.OM; + +namespace BlueWest.Startyp +{ + /// + /// Startup Extensions + /// + public static class StartupExtensions + { + private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build)); + + private static BlueWestConnectionString GetConnectionString(this IConfigurationRoot configurationRoot) + { + // Docker / No-Docker + var startupMode = configurationRoot["mode"]; + + if (startupMode == "docker") + { + var config = configurationRoot.Get(); + return config; + } + else + { + var config = configurationRoot.Get(); + return config; + } + + return null; + } + + /// + /// Get MYSQL Connection String + /// + /// + /// + /// + private static DbContextOptionsBuilder GetMySqlSettings( + this DbContextOptionsBuilder optionsBuilder, + IConfiguration configuration, + IConfigurationRoot configurationRoot, + IWebHostEnvironment environment) + { + var sqlVersion = GetMySqlServerVersion(8, 0, 11); + + // Docker / No-Docker + + string mySqlConnectionString = configurationRoot.GetConnectionString().MySql; + + if (mySqlConnectionString == string.Empty) + { + throw new InvalidOperationException("Fatal error: MySQL Connection string is empty."); + } + + + optionsBuilder + .UseMySql( + mySqlConnectionString, + sqlVersion) + .UseMySql(sqlVersion, + builder => + { + builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null); + }); + + // The following three options help with debugging, but should + // be changed or removed for production. + if (environment.IsDevelopment()) + { + optionsBuilder + .LogTo(Console.WriteLine, LogLevel.Information) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + } + + return optionsBuilder; + } + + /// + /// Setup database Contexts + /// + /// + /// + /// + /// + public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection, + IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot) + { + return serviceCollection + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)); + } + + /// + /// Setup database Contexts + /// + /// + /// + /// + /// + public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection, + IConfiguration configuration, IWebHostEnvironment environment) + { + var sqliteConString = "Data Source=BlueWest.Api.db"; + + return serviceCollection + .AddDbContextPool(options => options.UseSqlite(sqliteConString)) + .AddDbContextPool(options => options.UseSqlite(sqliteConString)) + .AddDbContextPool(options => options.UseSqlite(sqliteConString)) + .AddDbContextPool(options => options.UseSqlite(sqliteConString)) + .AddDbContextPool(options => options.UseSqlite(sqliteConString)); + + } + + internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot) + { + + var connectionString = configurationRoot.GetConnectionString(); + + if (connectionString == null) + { + throw new InvalidOperationException("Redis connection string is empty"); + } + + services + .AddSingleton(new RedisConnectionProvider(connectionString.Redis)) + .AddScoped() + .AddScoped() + .AddHostedService() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + + // Database Context and Swagger + + + // Register the ConfigurationBuilder instance of AuthSettings + var authSettings = configuration.GetSection(nameof(AuthSettings)); + services.Configure(authSettings); + var signingKey = new SymmetricSecurityKey + (Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)])); + + // jwt wire up + // Get options from app settings + var jwtAppSettingOptions = configuration + .GetSection(nameof(JwtIssuerOptions)); + + // Configure JwtIssuerOptions + services.Configure(options => + { + options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; + options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)]; + options.SigningCredentials = new SigningCredentials + (signingKey, SecurityAlgorithms.HmacSha256); + }); + + var tokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)], + + ValidateAudience = true, + ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)], + + ValidateIssuerSigningKey = true, + IssuerSigningKey = signingKey, + + RequireExpirationTime = false, + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero + }; + + services.AddAuthentication(options => + { + + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddCookie(options => + { + options.Cookie.SameSite = SameSiteMode.Lax; + options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; + options.Cookie.MaxAge = SessionConstants.DefaultSessionMaxAge; + options.LoginPath = "/api/auth/logincookie"; + options.LogoutPath = "/api/auth/logout"; + }) + .AddJwtBearer(configureOptions => + { + configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; + configureOptions.TokenValidationParameters = tokenValidationParameters; + configureOptions.SaveToken = true; + + configureOptions.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Add("Token-Expired", "true"); + } + + return Task.CompletedTask; + }, + }; + }); + + + // api user claim policy + services.AddAuthorization(options => + { + options.AddPolicy(SessionConstants.ApiNamePolicy, + policy => policy.RequireClaim(Data.Auth.Context.Users.Constants.JwtClaimIdentifiers.Rol, + Data.Auth.Context.Users.Constants.JwtClaims.ApiAccess)); + + }); + + // add identity + var identityBuilder = services.AddIdentityCore(o => + { + o.User.RequireUniqueEmail = true; + + // configure identity options + o.Password.RequireDigit = false; + o.Password.RequireLowercase = false; + o.Password.RequireUppercase = false; + o.Password.RequireNonAlphanumeric = false; + o.Password.RequiredLength = 6; + }) + .AddUserManager() + .AddUserStore(); + + identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services); + identityBuilder + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + return services; + } + + } +} + + diff --git a/BlueWest.Views/BlueWest.Views.csproj b/BlueWest.Views/BlueWest.Views.csproj index 8d45718..e4a465c 100644 --- a/BlueWest.Views/BlueWest.Views.csproj +++ b/BlueWest.Views/BlueWest.Views.csproj @@ -11,6 +11,7 @@ + @@ -97,4 +98,8 @@ + + + + diff --git a/BlueWest.Views/Controllers/AuthController.cs b/BlueWest.Views/Controllers/AuthController.cs index 95c9255..4bc4d27 100644 --- a/BlueWest.Views/Controllers/AuthController.cs +++ b/BlueWest.Views/Controllers/AuthController.cs @@ -1,10 +1,18 @@ +using BlueWest.Views.Utils; using Microsoft.AspNetCore.Mvc; namespace BlueWest.Views.Controllers { + [System.Web.Mvc.Route("/login")] + public class AuthController : Controller { - + + public IActionResult Index() + { + this.HandleGlobalization(); + return View(); + } } } diff --git a/BlueWest.Views/Program.cs b/BlueWest.Views/Program.cs index 8fde92b..d32f983 100644 --- a/BlueWest.Views/Program.cs +++ b/BlueWest.Views/Program.cs @@ -1,66 +1,41 @@ -using BlueWest.Views.Utils; -using System.Globalization; -using BlueWest.Data.Auth; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Localization; -using Microsoft.Extensions.Options; -var builder = WebApplication.CreateBuilder(args); +using BlueWest.Views; -builder.Services.AddSingleton(); -// Add services to the container. - -builder.Services.Configure(options => +namespace BlueWest.WebApi { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = SameSiteMode.None; -}); -builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie(cookieOptions => { - cookieOptions.LoginPath = "/"; -}); + /// + /// Entry point of the application. + /// + public class Program + { -builder.Services.AddLogging(builder => -{ - builder.AddSimpleConsole(); -}); + /// + /// Host Interface of the application. + /// + public static IHost MainHost { get; private set; } + + /// + /// Entry point of the application + /// + /// Command line arguments. + public static void Main(string[] args) + { + MainHost = CreateHostBuilder(args) + .UseContentRoot(Directory.GetCurrentDirectory()) + .Build(); -builder.Services.AddSession(options => -{ - options.Cookie.Domain = SessionConstants.CookieDomain; - options.Cookie.HttpOnly = true; -}); -builder.Services.AddControllersWithViews(); + MainHost.Run(); -var app = builder.Build(); + + } -// Configure the HTTP request pipeline. -if (!app.Environment.IsDevelopment()) -{ - app.UseExceptionHandler("/Home/Error"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); + private static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } } - -app.UseHttpsRedirection(); -app.UseStaticFiles(); - -app.UseRouting(); - -app.UseRequestLocalization(options => -{ - var supportedCultures = CultureInfo.GetCultures(CultureTypes.AllCultures); - options.DefaultRequestCulture = new RequestCulture("en-GB"); - options.SupportedCultures = supportedCultures; - options.SupportedUICultures = supportedCultures; -}); - -app.UseAuthorization(); - -app.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - -app.Run(); \ No newline at end of file diff --git a/BlueWest.Views/Startup.cs b/BlueWest.Views/Startup.cs new file mode 100644 index 0000000..cc3ed93 --- /dev/null +++ b/BlueWest.Views/Startup.cs @@ -0,0 +1,100 @@ +using System.Globalization; +using BlueWest.Data.Auth; +using BlueWest.Views.Utils; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Localization; + +namespace BlueWest.Views; + +public class Startup +{ + private readonly IConfiguration _configuration; + private readonly IWebHostEnvironment _environment; + + /// + /// Startup ctor + /// + public Startup(IConfiguration configuration, IWebHostEnvironment hostEnvironment) + { + _configuration = configuration; + _environment = hostEnvironment; + } + + + /// + /// Configure Services + /// + /// Dependency injection + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); +// Add services to the container. + + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddJsonFile("config.json") + .Build(); + + services.Configure(options => + { + // This lambda determines whether user consent for non-essential cookies is needed for a given request. + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = SameSiteMode.None; + }); + + services.AddLogging(builder => + { + builder.AddSimpleConsole(); + }); + + services.AddSession(options => + { + options.Cookie.Domain = SessionConstants.CookieDomain; + options.Cookie.HttpOnly = true; + }); + services.AddControllersWithViews(x => x.EnableEndpointRouting = false); + + services.AddSession(options => options.IdleTimeout = TimeSpan.FromHours(8)); + + + services.AddAuthServerServices(_configuration, _environment, configuration); + services.PrepareMySqlDatabasePool(_configuration, _environment, configuration); + + } + + /// + /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + /// + /// Object with the necessary data to configure an application's request pipeline + /// Provides information about the web hosting environment an application is running in. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + +// Configure the HTTP request pipeline. + if (!env.IsDevelopment()) + { + app.UseExceptionHandler("/Home/Error"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + } + + app.UseHttpsRedirection(); + app.UseStaticFiles(); + + app.UseRouting(); + + app.UseRequestLocalization(options => + { + var supportedCultures = CultureInfo.GetCultures(CultureTypes.AllCultures); + options.DefaultRequestCulture = new RequestCulture("en-GB"); + options.SupportedCultures = supportedCultures; + options.SupportedUICultures = supportedCultures; + }); + + app.UseAuthentication(); + app.UseAuthorization(); + app.UseSession(); + app.UseMvcWithDefaultRoute(); + + + } +} \ No newline at end of file diff --git a/BlueWest.Views/StartupExtensions.cs b/BlueWest.Views/StartupExtensions.cs new file mode 100644 index 0000000..79cbcac --- /dev/null +++ b/BlueWest.Views/StartupExtensions.cs @@ -0,0 +1,246 @@ +using System.Text; +using BlueWest.Cryptography; +using BlueWest.Data.Application.Users; +using BlueWest.Data.Auth; +using BlueWest.Data.Auth.Context.Users; +using BlueWest.Domain; +using BlueWest.WebApi.Configuration; +using BlueWest.WebApi.Context.Users; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using Redis.OM; + +namespace BlueWest.Views; + +public static class StartupExtensions +{ + private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build)); + + + private static BlueWestConnectionString GetConnectionString(this IConfigurationRoot configurationRoot) + { + // Docker / No-Docker + var startupMode = configurationRoot["mode"]; + + if (startupMode == "docker") + { + var config = configurationRoot.Get(); + return config; + } + else + { + var config = configurationRoot.Get(); + return config; + } + + return null; + } + internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot) + { + + var connectionString = configurationRoot.GetConnectionString(); + + if (connectionString == null) + { + throw new InvalidOperationException("Redis connection string is empty"); + } + + services.AddDistributedRedisCache(options => + { + options.Configuration = connectionString.Redis; + + }); + + services + .AddSingleton(new RedisConnectionProvider(connectionString.Redis)) + .AddScoped() + .AddScoped() + .AddHostedService() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + + // Database Context and Swagger + + + // Register the ConfigurationBuilder instance of AuthSettings + var authSettings = configuration.GetSection(nameof(AuthSettings)); + services.Configure(authSettings); + var signingKey = new SymmetricSecurityKey + (Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)])); + + // jwt wire up + // Get options from app settings + var jwtAppSettingOptions = configuration + .GetSection(nameof(JwtIssuerOptions)); + + // Configure JwtIssuerOptions + services.Configure(options => + { + options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; + options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)]; + options.SigningCredentials = new SigningCredentials + (signingKey, SecurityAlgorithms.HmacSha256); + }); + + var tokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)], + + ValidateAudience = true, + ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)], + + ValidateIssuerSigningKey = true, + IssuerSigningKey = signingKey, + + RequireExpirationTime = false, + ValidateLifetime = true, + ClockSkew = TimeSpan.Zero + }; + + services.AddAuthentication(options => + { + + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddCookie(options => + { + options.LoginPath = "/"; + options.LogoutPath = "/logout"; + }) + .AddJwtBearer(configureOptions => + { + configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; + configureOptions.TokenValidationParameters = tokenValidationParameters; + configureOptions.SaveToken = true; + + configureOptions.Events = new JwtBearerEvents + { + OnAuthenticationFailed = context => + { + if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) + { + context.Response.Headers.Add("Token-Expired", "true"); + } + + return Task.CompletedTask; + }, + }; + }); + + + // api user claim policy + services.AddAuthorization(options => + { + options.AddPolicy(SessionConstants.ApiNamePolicy, + policy => policy.RequireClaim(Data.Auth.Context.Users.Constants.JwtClaimIdentifiers.Rol, + Data.Auth.Context.Users.Constants.JwtClaims.ApiAccess)); + + }); + + // add identity + var identityBuilder = services.AddIdentityCore(o => + { + o.User.RequireUniqueEmail = true; + + // configure identity options + o.Password.RequireDigit = false; + o.Password.RequireLowercase = false; + o.Password.RequireUppercase = false; + o.Password.RequireNonAlphanumeric = false; + o.Password.RequiredLength = 6; + }) + .AddUserManager() + .AddUserStore(); + + identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services); + identityBuilder + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + return services; + } + + + /// + /// Get MYSQL Connection String + /// + /// + /// + /// + private static DbContextOptionsBuilder GetMySqlSettings( + this DbContextOptionsBuilder optionsBuilder, + IConfiguration configuration, + IConfigurationRoot configurationRoot, + IWebHostEnvironment environment) + { + var sqlVersion = GetMySqlServerVersion(8, 0, 11); + + // Docker / No-Docker + + string mySqlConnectionString = configurationRoot.GetConnectionString().MySql; + + if (mySqlConnectionString == string.Empty) + { + throw new InvalidOperationException("Fatal error: MySQL Connection string is empty."); + } + + + optionsBuilder + .UseMySql( + mySqlConnectionString, + sqlVersion) + .UseMySql(sqlVersion, + builder => + { + builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null); + }); + + // The following three options help with debugging, but should + // be changed or removed for production. + if (environment.IsDevelopment()) + { + optionsBuilder + .LogTo(Console.WriteLine, LogLevel.Information) + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + } + + return optionsBuilder; + } + + /// + /// Setup database Contexts + /// + /// + /// + /// + /// + public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection, + IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot) + { + return serviceCollection + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)) + .AddDbContextPool(options => + options.GetMySqlSettings(configuration, configurationRoot, environment)); + } + + +} \ No newline at end of file diff --git a/BlueWest.Views/Views/Auth/Index.cshtml b/BlueWest.Views/Views/Auth/Index.cshtml new file mode 100644 index 0000000..1ef105b --- /dev/null +++ b/BlueWest.Views/Views/Auth/Index.cshtml @@ -0,0 +1,6 @@ +@model BlueWest.Data.Auth.Context.Users.LoginRequest +@using (Html.BeginForm()){ + @Html.LabelFor(x => x.Password, "Password") + @Html.PasswordFor(x => x.Password); + @Html.NameFor(x => x.Email); +} \ No newline at end of file diff --git a/BlueWest.Views/Views/Jobs/Index.cshtml b/BlueWest.Views/Views/Jobs/Index.cshtml index 15108b4..479cee9 100644 --- a/BlueWest.Views/Views/Jobs/Index.cshtml +++ b/BlueWest.Views/Views/Jobs/Index.cshtml @@ -3,7 +3,7 @@ }
-

Data Module

+

Jobs Module

diff --git a/BlueWest.Views/appsettings.json b/BlueWest.Views/appsettings.json index 10f68b8..0a4a6fc 100644 --- a/BlueWest.Views/appsettings.json +++ b/BlueWest.Views/appsettings.json @@ -5,5 +5,20 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStringDocker": { + "MySQL": "server=db;user=blueuser;password=dXjw127124dJ;database=bluedb;", + "Redis": "redis://redisinstance:6379" + }, + "ConnectionStringNoDocker": { + "MySQL": "server=localhost;user=blueuser;password=dXjw127124dJ;database=bluedb;", + "Redis": "redis://localhost:6379" + }, + "AuthSettings": { + "SecretKey": "iJWHDmHLpUA283sqsfhqGbMRdRj1PVkH" + }, + "JwtIssuerOptions": { + "Issuer": "SomeIssuer", + "Audience": "http://localhost:5000" + } } diff --git a/BlueWest.Views/config.json b/BlueWest.Views/config.json new file mode 100644 index 0000000..4cf70fd --- /dev/null +++ b/BlueWest.Views/config.json @@ -0,0 +1,4 @@ +{ + "mode": "no-docker", + "database": "mysql" +} \ No newline at end of file