using System.Reflection; using System.Text; using FindAndDrive.API.Filters; using FindAndDrive.API.Middleware; using FindAndDrive.Domain.Helpers; using FindAndDrive.Domain.Interfaces.Gateways; using FindAndDrive.Domain.Interfaces.Providers; using FindAndDrive.Domain.Interfaces.Repositories; using FindAndDrive.Domain.Interfaces.Services; using FindAndDrive.Domain.Models.DTO; using FindAndDrive.Domain.Models.Entities; using FindAndDrive.Domain.Models.Settings; using FindAndDrive.Domain.Providers; using FindAndDrive.Domain.Services; using FindAndDrive.Gateway.Experian; using FindAndDrive.Gateway.TransUnion; using FindAndDrive.Persistence; using FindAndDrive.Persistence.Repository; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; namespace FindAndDrive.API; public class Startup { private IConfiguration Configuration; public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { services.AddLogging(); services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle services.AddEndpointsApiExplorer(); services.AddSwaggerGen(options => { var xmlPath = AppContext.BaseDirectory; options.IncludeXmlComments(Path.Combine(xmlPath, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml")); options.IncludeXmlComments(Path.Combine(xmlPath, $"{Assembly.GetExecutingAssembly().GetName().Name.Split('.').First()}.Domain.xml")); // Add custom types options.MapType(() => new OpenApiSchema {Type = "number", Format = "decimal"}); options.MapType(() => new OpenApiSchema {Type = "number", Format = "decimal?"}); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme." }); options.OperationFilter(); }); var securitySettings = Configuration.GetSection("SecuritySettings").Get(); var experianSettings = Configuration.GetSection("ExperianSettings").Get(); var transUnionSettings = Configuration.GetSection("TransUnionSettings").Get(); services.Configure(Configuration.GetSection("SecuritySettings")); services.Configure(Configuration.GetSection("ExperianSettings")); services.Configure(Configuration.GetSection("TransUnionSettings")); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(x => { x.RequireHttpsMetadata = false; x.SaveToken = false; // NB set this to true, when going to prod. x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, ValidIssuer = securitySettings.Issuer, ValidAudience = securitySettings.Audience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securitySettings.Key)), ValidateIssuer = false, // NB set this to true, when going to prod. ValidateAudience = false // NB set this to true, when going to prod. }; }); services.AddAuthorization(options => { var permissionsEnums = Enum.GetValues(typeof(Permissions)).Cast(); foreach (var permission in permissionsEnums) { var policyName = permission.ToString(); options.AddPolicy(policyName, policy => policy.RequireAssertion(context => { var permissionsList = context.User.Claims.Where(f => f.Type == "Permissions") .Select(s => s.Value) .ToList(); if (permissionsList is null || !permissionsList.Any()) return false; return permissionsList.Contains(policyName); }) ); } }); var defaultConnection = Configuration.GetConnectionString("DefaultConnection"); services.AddDbContext(options => options.UseSqlServer(defaultConnection, serverOptions => { serverOptions.MigrationsAssembly(typeof(DefaultContext).Assembly.FullName); })); // Dependency Injection services.AddScoped(); #region Services services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); #endregion #region Repositories services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); #endregion #region Providers services.AddScoped(); #endregion #region Gateways var experianHttpClient = new HttpClient(); experianHttpClient.BaseAddress = new Uri(experianSettings.BaseUrl); services.AddTransient( provider => new ExperianGateway(experianHttpClient, provider.GetService>(), provider.GetService>())); var transUnionHttpClient = new HttpClient(); transUnionHttpClient.BaseAddress = new Uri(transUnionSettings.BaseUrl); services.AddTransient( provider => new TransUnionGateway(transUnionHttpClient, provider.GetService>())); #endregion } private static void ApplyMigration(IServiceScope scope) { var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); var seedData = scope.ServiceProvider.GetRequiredService(); var dbRoles = db.Roles.ToList().ToDictionary(s => s.RoleName); var seedRoles = seedData.GetRoles(); foreach (var role in seedRoles) { if (dbRoles.TryGetValue(role.RoleName, out var dbRole)) { dbRole.Permissions = role.Permissions; dbRole.ModifiedAt = role.ModifiedAt; } else { db.Roles.Add(role); } } db.SaveChanges(); } private static void CreateInitialClient(IServiceScope scope) { var db = scope.ServiceProvider.GetRequiredService(); var clientExist = db.Clients.Any(a => a.ClientName == "Admin"); if (clientExist) return; var dateProvider = scope.ServiceProvider.GetRequiredService(); var adminRole = db.Roles.First(f => f.RoleName == "SystemAdmin"); var clientId = Guid.NewGuid(); var initialClient = new Client { Active = true, CreatedAt = dateProvider.GetSouthAfricanDateTime(), ClientId = clientId, ClientName = "Admin" }; var apiKeySecret = ApiKeyHelper.GenerateApiKey(); var initialKey = new ApiKey { Revoked = false, CreatedAt = dateProvider.GetSouthAfricanDateTime(), ClientId = clientId, RoleId = adminRole.RoleId, KeySecretHash = ApiKeyHelper.HashString(apiKeySecret), }; db.Clients.Add(initialClient); db.ApiKeys.Add(initialKey); db.SaveChanges(); Console.WriteLine($"Client Id: {clientId}"); Console.WriteLine($"Client Name: {initialClient.ClientName}"); Console.WriteLine($"ApiKey Id: {initialKey.ApiKeyId}"); Console.WriteLine($"ApiKey Secret: {apiKeySecret}"); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // SWAGGER SWASHBUCKLE SETUP app.UseSwagger(); app.UseSwaggerUI(options => { options.DocumentTitle = "FindAndDrive Api"; options.SwaggerEndpoint("/swagger/v1/swagger.json", "FindAndDrive Api"); options.DefaultModelExpandDepth(2); options.EnableFilter(); }); app.UseRouting(); app.UseMiddleware(); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); // Apply migrations using var scope = app.ApplicationServices.CreateScope(); ApplyMigration(scope); // Create client if no client exists with the name Admin CreateInitialClient(scope); } }