Before remove session func
This commit is contained in:
parent
81abab2cbe
commit
961ed87266
|
@ -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 { }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -141,7 +141,6 @@ namespace BlueWest.WebApi
|
||||||
.AddJsonFile("config.json")
|
.AddJsonFile("config.json")
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var allowedDatabase = configuration["database"];
|
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddSingleton<EventManager>();
|
.AddSingleton<EventManager>();
|
||||||
|
@ -150,21 +149,8 @@ namespace BlueWest.WebApi
|
||||||
services.AddAuthServerServices( _configuration, _environment, configuration);
|
services.AddAuthServerServices( _configuration, _environment, configuration);
|
||||||
services.AddScoped<ExchangeInterface>();
|
services.AddScoped<ExchangeInterface>();
|
||||||
|
|
||||||
|
|
||||||
switch (allowedDatabase)
|
|
||||||
{
|
|
||||||
case "mysql":
|
|
||||||
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
||||||
break;
|
|
||||||
|
|
||||||
case "sqlite":
|
|
||||||
services.PrepareSqlLiteDatabasePool(_configuration, _environment);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
services.PrepareMySqlDatabasePool(_configuration, _environment, configuration);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,27 +121,6 @@ namespace BlueWest.WebApi
|
||||||
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setup database Contexts
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceCollection"></param>
|
|
||||||
/// <param name="configuration"></param>
|
|
||||||
/// <param name="environment"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection,
|
|
||||||
IConfiguration configuration, IWebHostEnvironment environment)
|
|
||||||
{
|
|
||||||
var sqliteConString = "Data Source=BlueWest.Api.db";
|
|
||||||
|
|
||||||
return serviceCollection
|
|
||||||
.AddDbContextPool<UserDbContext>(options => options.UseSqlite(sqliteConString))
|
|
||||||
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
|
|
||||||
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
|
|
||||||
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
|
|
||||||
.AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -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 { }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace BlueWest.WebApi.Session;
|
||||||
|
|
||||||
|
public static class SessionExtensions
|
||||||
|
{
|
||||||
|
public static void Set<T>(this ISession session, string key, T value) where T: class
|
||||||
|
{
|
||||||
|
session.SetString(key, JsonSerializer.Serialize(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Get<T>(this ISession session, string key) where T: class
|
||||||
|
{
|
||||||
|
var value = session.GetString(key);
|
||||||
|
return value == null ? default : JsonSerializer.Deserialize<T>(value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using BlueWest.Cryptography;
|
using BlueWest.Cryptography;
|
||||||
using BlueWest.Data.Application;
|
using BlueWest.Data.Application;
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Startup Extensions
|
||||||
|
/// </summary>
|
||||||
|
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<ConnectionStringDocker>();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var config = configurationRoot.Get<ConnectionStringNoDocker>();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get MYSQL Connection String
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="optionsBuilder"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="environment"></param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setup database Contexts
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceCollection"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="environment"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||||
|
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||||
|
{
|
||||||
|
return serviceCollection
|
||||||
|
.AddDbContextPool<UserDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<CountryDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<FinanceDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<CompanyDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setup database Contexts
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceCollection"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="environment"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection,
|
||||||
|
IConfiguration configuration, IWebHostEnvironment environment)
|
||||||
|
{
|
||||||
|
var sqliteConString = "Data Source=BlueWest.Api.db";
|
||||||
|
|
||||||
|
return serviceCollection
|
||||||
|
.AddDbContextPool<UserDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
|
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
|
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
|
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
|
||||||
|
.AddDbContextPool<ApplicationUserDbContext>(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<IJwtTokenHandler, JwtTokenHandler>()
|
||||||
|
.AddScoped<IJwtFactory, JwtFactory>()
|
||||||
|
.AddHostedService<SessionManager>()
|
||||||
|
.AddSingleton<ISessionCache, SessionManager>()
|
||||||
|
.AddScoped<UserRepository>()
|
||||||
|
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||||
|
.AddScoped<IAuthManager, AuthManager>()
|
||||||
|
.AddScoped<IHasher, Hasher>();
|
||||||
|
|
||||||
|
// Database Context and Swagger
|
||||||
|
|
||||||
|
|
||||||
|
// Register the ConfigurationBuilder instance of AuthSettings
|
||||||
|
var authSettings = configuration.GetSection(nameof(AuthSettings));
|
||||||
|
services.Configure<AuthSettings>(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<JwtIssuerOptions>(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<ApplicationUser>(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<ApplicationUserManager>()
|
||||||
|
.AddUserStore<UserRepository>();
|
||||||
|
|
||||||
|
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
|
||||||
|
identityBuilder
|
||||||
|
.AddEntityFrameworkStores<ApplicationUserDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.9" />
|
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.8" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
|
||||||
<PackageReference Include="Redis.OM" Version="0.2.2" />
|
<PackageReference Include="Redis.OM" Version="0.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -97,4 +98,8 @@
|
||||||
<Compile Remove="Controllers\DataController.cs" />
|
<Compile Remove="Controllers\DataController.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Pages" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
|
using BlueWest.Views.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace BlueWest.Views.Controllers
|
namespace BlueWest.Views.Controllers
|
||||||
{
|
{
|
||||||
|
[System.Web.Mvc.Route("/login")]
|
||||||
|
|
||||||
public class AuthController : Controller
|
public class AuthController : Controller
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
this.HandleGlobalization();
|
||||||
|
return View();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<LayoutCache>();
|
namespace BlueWest.WebApi
|
||||||
// Add services to the container.
|
|
||||||
|
|
||||||
builder.Services.Configure<CookiePolicyOptions>(options =>
|
|
||||||
{
|
{
|
||||||
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
|
/// <summary>
|
||||||
options.CheckConsentNeeded = context => true;
|
/// Entry point of the application.
|
||||||
options.MinimumSameSitePolicy = SameSiteMode.None;
|
/// </summary>
|
||||||
});
|
public class Program
|
||||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
{
|
||||||
.AddCookie(cookieOptions => {
|
|
||||||
cookieOptions.LoginPath = "/";
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Services.AddLogging(builder =>
|
/// <summary>
|
||||||
{
|
/// Host Interface of the application.
|
||||||
builder.AddSimpleConsole();
|
/// </summary>
|
||||||
});
|
public static IHost MainHost { get; private set; }
|
||||||
|
|
||||||
builder.Services.AddSession(options =>
|
/// <summary>
|
||||||
{
|
/// Entry point of the application
|
||||||
options.Cookie.Domain = SessionConstants.CookieDomain;
|
/// </summary>
|
||||||
options.Cookie.HttpOnly = true;
|
/// <param name="args">Command line arguments.</param>
|
||||||
});
|
public static void Main(string[] args)
|
||||||
builder.Services.AddControllersWithViews();
|
{
|
||||||
|
MainHost = CreateHostBuilder(args)
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.Build();
|
||||||
|
|
||||||
var app = builder.Build();
|
MainHost.Run();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (!app.Environment.IsDevelopment())
|
}
|
||||||
{
|
|
||||||
app.UseExceptionHandler("/Home/Error");
|
private static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
Host.CreateDefaultBuilder(args)
|
||||||
app.UseHsts();
|
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Startup ctor
|
||||||
|
/// </summary>
|
||||||
|
public Startup(IConfiguration configuration, IWebHostEnvironment hostEnvironment)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_environment = hostEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configure Services
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">Dependency injection</param>
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddSingleton<LayoutCache>();
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
IConfigurationRoot configuration = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("config.json")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
services.Configure<CookiePolicyOptions>(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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="app">Object with the necessary data to configure an application's request pipeline</param>
|
||||||
|
/// <param name="env">Provides information about the web hosting environment an application is running in.</param>
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<ConnectionStringDocker>();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var config = configurationRoot.Get<ConnectionStringNoDocker>();
|
||||||
|
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<IJwtTokenHandler, JwtTokenHandler>()
|
||||||
|
.AddScoped<IJwtFactory, JwtFactory>()
|
||||||
|
.AddHostedService<SessionManager>()
|
||||||
|
.AddSingleton<ISessionCache, SessionManager>()
|
||||||
|
.AddScoped<UserRepository>()
|
||||||
|
.AddScoped<IUserManager, ApplicationUserManager>()
|
||||||
|
.AddScoped<IAuthManager, AuthManager>()
|
||||||
|
.AddScoped<IHasher, Hasher>();
|
||||||
|
|
||||||
|
// Database Context and Swagger
|
||||||
|
|
||||||
|
|
||||||
|
// Register the ConfigurationBuilder instance of AuthSettings
|
||||||
|
var authSettings = configuration.GetSection(nameof(AuthSettings));
|
||||||
|
services.Configure<AuthSettings>(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<JwtIssuerOptions>(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<ApplicationUser>(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<ApplicationUserManager>()
|
||||||
|
.AddUserStore<UserRepository>();
|
||||||
|
|
||||||
|
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
|
||||||
|
identityBuilder
|
||||||
|
.AddEntityFrameworkStores<ApplicationUserDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get MYSQL Connection String
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="optionsBuilder"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="environment"></param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Setup database Contexts
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceCollection"></param>
|
||||||
|
/// <param name="configuration"></param>
|
||||||
|
/// <param name="environment"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
|
||||||
|
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
|
||||||
|
{
|
||||||
|
return serviceCollection
|
||||||
|
.AddDbContextPool<UserDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<CountryDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<FinanceDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<CompanyDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment))
|
||||||
|
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
||||||
|
options.GetMySqlSettings(configuration, configurationRoot, environment));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">Data Module</h1>
|
<h1 class="display-4">Jobs Module</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,5 +5,20 @@
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"mode": "no-docker",
|
||||||
|
"database": "mysql"
|
||||||
|
}
|
Loading…
Reference in New Issue