CodeLiturgy.Dashboard/BlueWest.Api/StartupExtensions.cs

239 lines
10 KiB
C#

using System;
using System.Text;
using System.Threading.Tasks;
using BlueWest.Cryptography;
using BlueWest.Data;
using BlueWest.WebApi.Context.Users;
using BlueWest.WebApi.EF;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
namespace BlueWest.WebApi
{
/// <summary>
/// Startup Extensions
/// </summary>
public static class StartupExtensions
{
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build));
/// <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);
optionsBuilder
.UseMySql(
configuration.GetConnectionString("DockerMySQL"),
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)
{
return serviceCollection
.AddDbContextPool<UserDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<CountryDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<FinanceDbContext>(options => options.GetMySqlSettings(configuration, environment))
.AddDbContextPool<CompanyDbContext>(options => options.GetMySqlSettings(configuration, 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));
}
public static void AddAuthServerServices(this IServiceCollection services, string origins, IConfiguration _configuration)
{
services.AddScoped<IJwtTokenHandler, JwtTokenHandler>();
services.AddScoped<IJwtFactory, JwtFactory>();
// User management
services
.AddIdentityCore<ApplicationUser>(opt => { opt.User.RequireUniqueEmail = true; })
.AddEntityFrameworkStores<UserDbContext>()
.AddUserManager<UserManager>()
.AddUserStore<UserRepository>();
// Database Context and Swagger
services.TryAddSingleton<ISystemClock, SystemClock>();
// Registering 'services' and Authentication, Cookies, JWT
services
.AddScoped<IUsersRepo, UserRepository>()
.AddScoped<IUserManager, UserManager>() // So it gets successfully registered in UserManager
.AddScoped<IAuthManager, AuthManager>()
.AddScoped<IHasher, Hasher>();
// 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 = "/api/auth/login2";
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("ApiUser",
policy => policy.RequireClaim(Constants.JwtClaimIdentifiers.Rol,
Constants.JwtClaims.ApiAccess));
});
// add identity
var identityBuilder = services.AddIdentityCore<User>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(IdentityRole), identityBuilder.Services);
identityBuilder.AddEntityFrameworkStores<UserDbContext>().AddDefaultTokenProviders();
}
public static void ConfigureApiWithUsers(this IApplicationBuilder app, IWebHostEnvironment env, string origins)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseSwagger()
.UseSwaggerUI(config => { config.SwaggerEndpoint("/swagger/v1/swagger.json", "Commands And Snippets API"); })
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseCors(origins)
.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
}