CodeLiturgy.Dashboard/BlueWest.Api/StartupExtensions.cs

276 lines
11 KiB
C#
Raw Normal View History

2022-08-13 06:35:36 +03:00
using System;
2022-09-10 00:33:17 +03:00
using System.Text;
using System.Threading.Tasks;
2022-09-19 05:50:15 +03:00
using BlueWest.Domain;
using BlueWest.Domain;
2022-09-10 00:33:17 +03:00
using BlueWest.Cryptography;
2022-09-19 05:50:15 +03:00
using BlueWest.WebApi.Configuration;
2022-09-10 00:33:17 +03:00
using BlueWest.WebApi.Context.Users;
2022-09-18 04:00:24 +03:00
using BlueWest.WebApi.Session;
2022-09-10 00:33:17 +03:00
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
2022-08-19 19:47:35 +03:00
using Microsoft.AspNetCore.Hosting;
2022-09-17 22:13:35 +03:00
using Microsoft.AspNetCore.Http;
2022-09-10 00:33:17 +03:00
using Microsoft.AspNetCore.Identity;
2022-08-13 06:35:36 +03:00
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
2022-08-19 19:47:35 +03:00
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
2022-08-19 06:18:50 +03:00
using Microsoft.Extensions.Logging;
2022-09-10 00:33:17 +03:00
using Microsoft.IdentityModel.Tokens;
2022-09-18 04:00:24 +03:00
using Redis.OM;
2022-08-13 06:35:36 +03:00
2022-08-19 19:47:35 +03:00
namespace BlueWest.WebApi
2022-08-13 06:35:36 +03:00
{
2022-08-19 19:47:35 +03:00
/// <summary>
/// Startup Extensions
/// </summary>
public static class StartupExtensions
2022-08-13 06:35:36 +03:00
{
2022-09-06 07:54:48 +03:00
private static MySqlServerVersion GetMySqlServerVersion(int major, int minor, int build) => new (new Version(major, minor, build));
2022-09-18 04:17:37 +03:00
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;
}
2022-09-06 07:54:48 +03:00
2022-08-19 19:47:35 +03:00
/// <summary>
/// Get MYSQL Connection String
/// </summary>
/// <param name="optionsBuilder"></param>
/// <param name="configuration"></param>
2022-08-20 05:47:32 +03:00
/// <param name="environment"></param>
private static DbContextOptionsBuilder GetMySqlSettings(
2022-08-19 19:47:35 +03:00
this DbContextOptionsBuilder optionsBuilder,
IConfiguration configuration,
2022-09-18 04:00:24 +03:00
IConfigurationRoot configurationRoot,
2022-08-19 19:47:35 +03:00
IWebHostEnvironment environment)
{
2022-09-07 20:26:28 +03:00
var sqlVersion = GetMySqlServerVersion(8, 0, 11);
2022-09-18 04:00:24 +03:00
// Docker / No-Docker
2022-09-18 04:17:37 +03:00
string mySqlConnectionString = configurationRoot.GetConnectionString().MySql;
2022-09-18 04:00:24 +03:00
if (mySqlConnectionString == string.Empty)
{
throw new InvalidOperationException("Fatal error: MySQL Connection string is empty.");
}
2022-09-06 07:54:48 +03:00
optionsBuilder
2022-09-07 20:26:28 +03:00
.UseMySql(
2022-09-18 04:00:24 +03:00
mySqlConnectionString,
2022-09-07 20:26:28 +03:00
sqlVersion)
.UseMySql(sqlVersion,
builder =>
{
builder.EnableRetryOnFailure(6, TimeSpan.FromSeconds(3), null);
});
2022-08-20 05:47:32 +03:00
2022-08-19 06:18:50 +03:00
// The following three options help with debugging, but should
// be changed or removed for production.
2022-08-19 19:47:35 +03:00
if (environment.IsDevelopment())
{
optionsBuilder
2022-08-19 19:47:35 +03:00
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
2022-08-20 05:47:32 +03:00
return optionsBuilder;
2022-08-19 19:47:35 +03:00
}
/// <summary>
/// Setup database Contexts
/// </summary>
/// <param name="serviceCollection"></param>
/// <param name="configuration"></param>
2022-08-20 05:47:32 +03:00
/// <param name="environment"></param>
2022-08-19 19:47:35 +03:00
/// <returns></returns>
2022-09-07 20:26:28 +03:00
public static IServiceCollection PrepareMySqlDatabasePool(this IServiceCollection serviceCollection,
2022-09-18 04:00:24 +03:00
IConfiguration configuration, IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
2022-08-19 19:47:35 +03:00
{
return serviceCollection
2022-09-18 04:00:24 +03:00
.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))
2022-09-17 22:13:35 +03:00
.AddDbContextPool<ApplicationUserDbContext>(options =>
2022-09-18 04:00:24 +03:00
options.GetMySqlSettings(configuration, configurationRoot, environment));
2022-08-19 19:47:35 +03:00
}
2022-09-06 07:54:48 +03:00
/// <summary>
/// Setup database Contexts
/// </summary>
/// <param name="serviceCollection"></param>
/// <param name="configuration"></param>
/// <param name="environment"></param>
/// <returns></returns>
2022-09-07 20:26:28 +03:00
public static IServiceCollection PrepareSqlLiteDatabasePool(this IServiceCollection serviceCollection,
2022-09-06 07:54:48 +03:00
IConfiguration configuration, IWebHostEnvironment environment)
{
2022-09-08 06:15:44 +03:00
var sqliteConString = "Data Source=BlueWest.Api.db";
2022-09-10 07:12:03 +03:00
2022-09-06 07:54:48 +03:00
return serviceCollection
2022-09-08 06:15:44 +03:00
.AddDbContextPool<UserDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<CountryDbContext>(options => options.UseSqlite(sqliteConString))
.AddDbContextPool<FinanceDbContext>(options => options.UseSqlite(sqliteConString))
2022-09-10 07:28:41 +03:00
.AddDbContextPool<CompanyDbContext>(options => options.UseSqlite(sqliteConString))
2022-09-18 04:00:24 +03:00
.AddDbContextPool<ApplicationUserDbContext>(options => options.UseSqlite(sqliteConString));
2022-09-17 22:13:35 +03:00
2022-09-06 07:54:48 +03:00
}
2022-09-18 04:17:37 +03:00
internal static IServiceCollection AddAuthServerServices(this IServiceCollection services, IConfiguration configuration , IWebHostEnvironment environment, IConfigurationRoot configurationRoot)
2022-09-10 00:33:17 +03:00
{
2022-09-10 07:12:03 +03:00
2022-09-18 04:17:37 +03:00
var connectionString = configurationRoot.GetConnectionString();
if (connectionString == null)
{
throw new InvalidOperationException("Redis connection string is empty");
}
2022-09-18 04:00:24 +03:00
services
2022-09-18 04:17:37 +03:00
.AddSingleton(new RedisConnectionProvider(connectionString.Redis))
2022-09-18 04:00:24 +03:00
.AddScoped<IJwtTokenHandler, JwtTokenHandler>()
2022-09-17 22:13:35 +03:00
.AddScoped<IJwtFactory, JwtFactory>()
2022-09-19 05:50:15 +03:00
.AddHostedService<SessionManager>()
.AddSingleton<ISessionCache, SessionManager>()
2022-09-17 22:13:35 +03:00
.AddScoped<UserRepository>()
2022-09-11 01:22:04 +03:00
.AddScoped<IUserManager, ApplicationUserManager>()
2022-09-10 07:12:03 +03:00
.AddScoped<IAuthManager, AuthManager>()
.AddScoped<IHasher, Hasher>();
2022-09-10 00:33:17 +03:00
// Database Context and Swagger
// Register the ConfigurationBuilder instance of AuthSettings
2022-09-10 07:12:03 +03:00
var authSettings = configuration.GetSection(nameof(AuthSettings));
2022-09-10 00:33:17 +03:00
services.Configure<AuthSettings>(authSettings);
var signingKey = new SymmetricSecurityKey
(Encoding.ASCII.GetBytes(authSettings[nameof(AuthSettings.SecretKey)]));
// jwt wire up
// Get options from app settings
2022-09-10 07:12:03 +03:00
var jwtAppSettingOptions = configuration
2022-09-10 00:33:17 +03:00
.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 =>
{
2022-09-17 22:13:35 +03:00
2022-09-10 00:33:17 +03:00
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
2022-09-17 22:13:35 +03:00
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
2022-09-19 05:50:15 +03:00
options.Cookie.MaxAge = SessionConstants.DefaultSessionMaxAge;
2022-09-12 17:57:37 +03:00
options.LoginPath = "/api/auth/logincookie";
2022-09-10 00:33:17 +03:00
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;
2022-09-19 05:50:15 +03:00
},
2022-09-10 00:33:17 +03:00
};
});
// api user claim policy
services.AddAuthorization(options =>
{
2022-09-18 04:00:24 +03:00
options.AddPolicy(SessionConstants.ApiNamePolicy,
2022-09-12 17:57:37 +03:00
policy => policy.RequireClaim(Context.Users.Constants.JwtClaimIdentifiers.Rol,
Context.Users.Constants.JwtClaims.ApiAccess));
2022-09-10 00:33:17 +03:00
});
// add identity
2022-09-10 07:12:03 +03:00
var identityBuilder = services.AddIdentityCore<ApplicationUser>(o =>
2022-09-10 00:33:17 +03:00
{
2022-09-17 22:13:35 +03:00
o.User.RequireUniqueEmail = true;
2022-09-10 00:33:17 +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-09-17 22:13:35 +03:00
})
.AddUserManager<ApplicationUserManager>()
.AddUserStore<UserRepository>();
2022-09-10 00:33:17 +03:00
2022-09-10 07:12:03 +03:00
identityBuilder = new IdentityBuilder(identityBuilder.UserType, typeof(ApplicationRole), identityBuilder.Services);
identityBuilder
.AddEntityFrameworkStores<ApplicationUserDbContext>()
.AddDefaultTokenProviders();
2022-09-10 00:33:17 +03:00
2022-09-10 07:12:03 +03:00
return services;
2022-09-10 00:33:17 +03:00
}
2022-08-13 06:35:36 +03:00
}
2022-09-18 04:00:24 +03:00
}