2022-09-29 02:37:24 +03:00
|
|
|
using System.Text;
|
|
|
|
using BlueWest.Cryptography;
|
|
|
|
using BlueWest.Data.Application.Users;
|
|
|
|
using BlueWest.Data.Auth;
|
|
|
|
using BlueWest.Data.Auth.Context.Users;
|
2022-10-30 19:48:24 +03:00
|
|
|
using CodeLiturgy.Domain;
|
2022-09-29 02:37:24 +03:00
|
|
|
using BlueWest.WebApi.Context.Users;
|
2022-10-30 19:48:24 +03:00
|
|
|
using CodeLiturgy.Views.Utils;
|
2022-09-29 02:37:24 +03:00
|
|
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using Microsoft.IdentityModel.Tokens;
|
|
|
|
using Redis.OM;
|
|
|
|
|
2022-10-30 19:48:24 +03:00
|
|
|
namespace CodeLiturgy.Views;
|
2022-09-29 02:37:24 +03:00
|
|
|
|
|
|
|
public static class StartupExtensions
|
|
|
|
{
|
2022-10-27 20:13:02 +03:00
|
|
|
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) =>
|
|
|
|
new(new Version(major, minor, build));
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
private static string GetConnectionString(this IConfiguration configurationRoot, string db)
|
2022-09-29 02:37:24 +03:00
|
|
|
{
|
|
|
|
var startupMode = configurationRoot["mode"];
|
2022-10-27 20:13:02 +03:00
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(startupMode) && startupMode == "docker")
|
2022-09-29 02:37:24 +03:00
|
|
|
{
|
2022-10-27 20:13:02 +03:00
|
|
|
var config = configurationRoot.GetSection("ConnectionStringDocker")[db];
|
2022-09-29 02:37:24 +03:00
|
|
|
return config;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-10-27 20:13:02 +03:00
|
|
|
return configurationRoot.GetSection("ConnectionStringNoDocker")[db] ?? string.Empty;
|
2022-09-29 02:37:24 +03:00
|
|
|
}
|
2022-10-27 20:13:02 +03:00
|
|
|
}
|
|
|
|
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
private static bool IsDockerMode(this IConfiguration config)
|
|
|
|
{
|
|
|
|
var startupMode = config["mode"];
|
|
|
|
return startupMode == "docker";
|
2022-09-29 02:37:24 +03:00
|
|
|
}
|
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services,
|
|
|
|
IConfiguration configuration, IWebHostEnvironment environment)
|
|
|
|
{
|
|
|
|
var connectionString = configuration.GetConnectionString("Redis");
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
if (string.IsNullOrEmpty(connectionString))
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException("Redis connection string is empty");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
services.AddSession(options =>
|
|
|
|
{
|
|
|
|
options.Cookie.Domain = SessionConstants.CookieDomain;
|
|
|
|
options.Cookie.HttpOnly = true;
|
|
|
|
options.IdleTimeout = TimeSpan.FromHours(8);
|
|
|
|
});
|
|
|
|
|
|
|
|
services
|
|
|
|
.AddSingleton(new RedisConnectionProvider(connectionString))
|
|
|
|
.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);
|
|
|
|
});
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
var tokenValidationParameters = new TokenValidationParameters
|
|
|
|
{
|
|
|
|
ValidateIssuer = true,
|
|
|
|
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
ValidateAudience = true,
|
|
|
|
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
ValidateIssuerSigningKey = true,
|
|
|
|
IssuerSigningKey = signingKey,
|
|
|
|
|
|
|
|
RequireExpirationTime = false,
|
|
|
|
ValidateLifetime = true,
|
|
|
|
ClockSkew = TimeSpan.Zero
|
|
|
|
};
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
services.AddAuthentication(options =>
|
2022-09-29 02:37:24 +03:00
|
|
|
{
|
2022-10-27 20:13:02 +03:00
|
|
|
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
|
|
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
|
|
|
|
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
|
|
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
|
|
})
|
|
|
|
.AddCookie(options =>
|
2022-09-29 02:37:24 +03:00
|
|
|
{
|
2022-10-27 20:13:02 +03:00
|
|
|
options.LoginPath = Routes.AuthLoginRoute;
|
|
|
|
options.LogoutPath = Routes.AuthLogoutRoute;
|
2022-09-29 02:37:24 +03:00
|
|
|
});
|
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
|
|
|
|
// api user claim policy
|
|
|
|
services.AddAuthorization(options =>
|
|
|
|
{
|
|
|
|
options.AddPolicy(SessionConstants.ApiNamePolicy,
|
2022-10-30 19:48:24 +03:00
|
|
|
policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol,
|
|
|
|
Constants.JwtClaims.ApiAccess));
|
2022-10-27 20:13:02 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// add identity
|
|
|
|
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o =>
|
2022-09-29 02:37:24 +03:00
|
|
|
{
|
|
|
|
o.User.RequireUniqueEmail = true;
|
2022-10-27 20:13:02 +03:00
|
|
|
|
2022-09-29 02:37:24 +03:00
|
|
|
// configure identity options
|
|
|
|
o.Password.RequireDigit = false;
|
|
|
|
o.Password.RequireLowercase = false;
|
|
|
|
o.Password.RequireUppercase = false;
|
|
|
|
o.Password.RequireNonAlphanumeric = false;
|
|
|
|
o.Password.RequiredLength = 6;
|
|
|
|
})
|
2022-10-27 20:13:02 +03:00
|
|
|
.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,
|
|
|
|
IWebHostEnvironment environment)
|
|
|
|
{
|
|
|
|
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
|
|
|
|
|
|
|
|
// Docker / No-Docker
|
2022-09-29 02:37:24 +03:00
|
|
|
|
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
var mySqlConnectionString = configuration.GetConnectionString("MySQL");
|
|
|
|
|
|
|
|
if (mySqlConnectionString == string.Empty)
|
|
|
|
{
|
|
|
|
throw new InvalidOperationException("MySQL Connection string appears to be empty.");
|
2022-09-29 02:37:24 +03:00
|
|
|
}
|
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
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();
|
|
|
|
}
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
return optionsBuilder;
|
|
|
|
}
|
2022-09-29 02:37:24 +03:00
|
|
|
|
2022-10-27 20:13:02 +03:00
|
|
|
/// <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)
|
|
|
|
{
|
|
|
|
return serviceCollection
|
|
|
|
.AddDbContextPool<UserDbContext>(options =>
|
|
|
|
options.GetMySqlSettings(configuration, environment))
|
|
|
|
.AddDbContextPool<CountryDbContext>(options =>
|
|
|
|
options.GetMySqlSettings(configuration, environment))
|
|
|
|
.AddDbContextPool<ApplicationUserDbContext>(options =>
|
|
|
|
options.GetMySqlSettings(configuration, environment));
|
|
|
|
}
|
2022-09-29 02:37:24 +03:00
|
|
|
}
|