yavsc/src/Yavsc/Extensions/HostingExtensions.cs

420 lines
17 KiB
C#

using System.Globalization;
10 months ago
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Util.Store;
10 months ago
using IdentityServer8;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
10 months ago
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Yavsc.Abstract.Workflow;
using Yavsc.Billing;
using Yavsc.Helpers;
using Yavsc.Interface;
using Yavsc.Models;
using Yavsc.Models.Billing;
using Yavsc.Models.Haircut;
using Yavsc.Models.Workflow;
using Yavsc.Services;
using Yavsc.Settings;
1 year ago
using Yavsc.ViewModels.Auth;
namespace Yavsc.Extensions;
internal static class HostingExtensions
{
10 months ago
public static IApplicationBuilder ConfigureFileServerApp(this IApplicationBuilder app,
bool enableDirectoryBrowsing = false)
{
10 months ago
var userFilesDirInfo = new DirectoryInfo(Config.SiteSetup.Blog);
AbstractFileSystemHelpers.UserFilesDirName = userFilesDirInfo.FullName;
10 months ago
if (!userFilesDirInfo.Exists) userFilesDirInfo.Create();
10 months ago
Config.UserFilesOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(AbstractFileSystemHelpers.UserFilesDirName),
RequestPath = PathString.FromUriComponent(Constants.UserFilesPath),
EnableDirectoryBrowsing = enableDirectoryBrowsing,
};
Config.UserFilesOptions.EnableDefaultFiles = true;
Config.UserFilesOptions.StaticFileOptions.ServeUnknownFileTypes = true;
var avatarsDirInfo = new DirectoryInfo(Config.SiteSetup.Avatars);
if (!avatarsDirInfo.Exists) avatarsDirInfo.Create();
Config.AvatarsDirName = avatarsDirInfo.FullName;
Config.AvatarsOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(Config.AvatarsDirName),
RequestPath = PathString.FromUriComponent(Constants.AvatarsPath),
EnableDirectoryBrowsing = enableDirectoryBrowsing
};
10 months ago
var gitdirinfo = new DirectoryInfo(Config.SiteSetup.GitRepository);
Config.GitDirName = gitdirinfo.FullName;
if (!gitdirinfo.Exists) gitdirinfo.Create();
Config.GitOptions = new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(Config.GitDirName),
RequestPath = PathString.FromUriComponent(Constants.GitPath),
EnableDirectoryBrowsing = enableDirectoryBrowsing,
};
Config.GitOptions.DefaultFilesOptions.DefaultFileNames.Add("index.md");
Config.GitOptions.StaticFileOptions.ServeUnknownFileTypes = true;
10 months ago
app.UseFileServer(Config.UserFilesOptions);
10 months ago
app.UseFileServer(Config.AvatarsOptions);
10 months ago
app.UseFileServer(Config.GitOptions);
app.UseStaticFiles();
return app;
}
public static void ConfigureWorkflow()
10 months ago
{
foreach (var a in System.AppDomain.CurrentDomain.GetAssemblies())
{
10 months ago
foreach (var c in a.GetTypes())
{
10 months ago
if (c.IsClass && !c.IsAbstract &&
c.GetInterface("ISpecializationSettings") != null)
{
10 months ago
Config.ProfileTypes.Add(c);
}
}
10 months ago
}
10 months ago
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
{
foreach (var attr in propertyInfo.CustomAttributes)
{
10 months ago
// something like a DbSet?
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
{
10 months ago
BillingService.UserSettings.Add(propertyInfo);
}
}
}
10 months ago
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) =>
{
10 months ago
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
return query;
}));
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IDecidableQuery>
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
}
public static void RegisterBilling<T>(string code, Func<ApplicationDbContext, long, IDecidableQuery> getter) where T : IBillable
{
BillingService.Billing.Add(code, getter);
BillingService.GlobalBillingMap.Add(typeof(T).Name, code);
}
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
10 months ago
var siteSection = builder.Configuration.GetSection("Site");
var smtpSection = builder.Configuration.GetSection("Smtp");
var paypalSection = builder.Configuration.GetSection("Authentication:PayPal");
// OAuth2AppSettings
var googleAuthSettings = builder.Configuration.GetSection("Authentication:Google");
string? googleClientFile = builder.Configuration["Authentication:Google:GoogleWebClientJson"];
string? googleServiceAccountJsonFile = builder.Configuration["Authentication:Google:GoogleServiceAccountJson"];
if (googleClientFile != null)
{
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
}
if (googleServiceAccountJsonFile != null)
{
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
}
string? googleClientId = builder.Configuration["Authentication:Google:ClientId"];
string? googleClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
2 years ago
var services = builder.Services;
_ = services.AddControllersWithViews()
.AddNewtonsoftJson();
10 months ago
LoadGoogleConfig(builder.Configuration);
services.Configure<SiteSettings>(siteSection);
services.Configure<SmtpSettings>(smtpSection);
services.Configure<PayPalSettings>(paypalSection);
1 year ago
services.Configure<GoogleAuthSettings>(googleAuthSettings);
10 months ago
services.AddRazorPages();
2 years ago
services.AddSignalR(o =>
{
o.EnableDetailedErrors = true;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
10 months ago
services
.AddAuthorization(options =>
{
options.AddPolicy("ApiScope", policy =>
{
policy
.RequireAuthenticatedUser()
.RequireClaim("scope", "api1");
});
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
10 months ago
var identityServerBuilder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
// see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryClients(Config.Clients)
10 months ago
.AddInMemoryApiScopes(Config.ApiScopes)
.AddAspNetIdentity<ApplicationUser>()
;
10 months ago
if (builder.Environment.IsDevelopment())
{
identityServerBuilder.AddDeveloperSigningCredential();
}
else
{
var key = builder.Configuration["YOUR-KEY-NAME"];
var pfxBytes = Convert.FromBase64String(key);
var cert = new X509Certificate2(pfxBytes, (string)null, X509KeyStorageFlags.MachineKeySet);
identityServerBuilder.AddSigningCredential(cert);
}
services.AddSession();
10 months ago
// TODO .AddServerSideSessionStore<YavscServerSideSessionStore>()
10 months ago
10 months ago
var authenticationBuilder = services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:5001";
options.TokenValidationParameters =
new() { ValidateAudience = false };
});
10 months ago
authenticationBuilder.AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
// register your IdentityServer with Google at https://console.developers.google.com
// enable the Google+ API
// set the redirect URI to https://localhost:5001/signin-google
options.ClientId = googleClientId;
options.ClientSecret = googleClientSecret;
});
services.Configure<RequestLocalizationOptions>(options =>
{
CultureInfo[] supportedCultures = new[]
{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("pt")
10 months ago
};
10 months ago
CultureInfo[] supportedUICultures = new[]
{
new CultureInfo("fr"),
new CultureInfo("en"),
new CultureInfo("pt")
10 months ago
};
10 months ago
// You must explicitly state which cultures your application supports.
// These are the cultures the app supports for formatting numbers, dates, etc.
options.SupportedCultures = supportedCultures;
10 months ago
// These are the cultures the app supports for UI strings, i.e. we have localized resources for.
options.SupportedUICultures = supportedUICultures;
10 months ago
options.RequestCultureProviders = new List<IRequestCultureProvider>
{
new QueryStringRequestCultureProvider { Options = options },
new CookieRequestCultureProvider { Options = options, CookieName="ASPNET_CULTURE" },
new AcceptLanguageHeaderRequestCultureProvider { Options = options }
10 months ago
};
});
10 months ago
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder =>
{
10 months ago
_ = builder.WithOrigins("*");
});
10 months ago
});
10 months ago
// Add the system clock service
_ = services.AddSingleton<ISystemClock, SystemClock>();
10 months ago
_ = services.AddSingleton<IConnexionManager, HubConnectionManager>();
_ = services.AddSingleton<ILiveProcessor, LiveProcessor>();
_ = services.AddTransient<IFileSystemAuthManager, FileSystemAuthManager>();
10 months ago
services.AddMvc(config =>
{
/* var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy)); */
config.Filters.Add(new ProducesAttribute("application/json"));
// config.ModelBinders.Insert(0,new MyDateTimeModelBinder());
// config.ModelBinders.Insert(0,new MyDecimalModelBinder());
config.EnableEndpointRouting = true;
}).AddFormatterMappings(
config => config.SetMediaTypeMappingForFormat("text/pdf",
new MediaTypeHeaderValue("text/pdf"))
).AddFormatterMappings(
config => config.SetMediaTypeMappingForFormat("text/x-tex",
new MediaTypeHeaderValue("text/x-tex"))
)
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix,
options =>
{
options.ResourcesPath = "Resources";
}).AddDataAnnotationsLocalization();
_ = services.AddTransient<ITrueEmailSender, MailSender>();
_ = services.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, MailSender>();
_ = services.AddTransient<IYavscMessageSender, YavscMessageSender>();
_ = services.AddTransient<IBillingService, BillingService>();
_ = services.AddTransient<IDataStore, FileDataStore>((sp) => new FileDataStore("googledatastore", false));
_ = services.AddTransient<ICalendarManager, CalendarManager>();
10 months ago
// TODO for SMS: services.AddTransient<ISmsSender, AuthMessageSender>();
10 months ago
_ = services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
var dataDir = new DirectoryInfo(builder.Configuration["Site:DataDir"]);
// Add session related services.
10 months ago
services.AddDataProtection().PersistKeysToFileSystem(dataDir);
services.AddAuthorization(options =>
{
10 months ago
options.AddPolicy("ApiScope", policy =>
{
policy
.RequireAuthenticatedUser()
.RequireClaim("scope", "scope2");
});
10 months ago
options.AddPolicy("AdministratorOnly", policy =>
{
10 months ago
_ = policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName);
});
10 months ago
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
options.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes("Bearer")
.RequireAuthenticatedUser().Build());
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
options.AddPolicy("IsTheAuthor", policy =>
policy.Requirements.Add(new EditPermission()));
});
services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
1 year ago
return builder.Build();
}
public static WebApplication ConfigurePipeline(this WebApplication app)
10 months ago
{
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
10 months ago
else
{
app.UseExceptionHandler("/Home/Error");
}
10 months ago
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
10 months ago
app.MapGet("/api/me", (HttpContext context) =>
new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value }))
).RequireAuthorization("ApiScope");
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages()
.RequireAuthorization();
2 years ago
app.MapHub<ChatHub>("/chatHub");
app.MapAreaControllerRoute("api", "api", "~/api/{controller}/{action}/{id?}");
ConfigureWorkflow();
var services = app.Services;
ILoggerFactory loggerFactory = services.GetRequiredService<ILoggerFactory>();
var siteSettings = services.GetRequiredService<IOptions<SiteSettings>>();
var smtpSettings = services.GetRequiredService<IOptions<SmtpSettings>>();
var payPalSettings = services.GetRequiredService<IOptions<PayPalSettings>>();
var googleAuthSettings = services.GetRequiredService<IOptions<GoogleAuthSettings>>();
var authorizationService = services.GetRequiredService<IAuthorizationService>();
var localization = services.GetRequiredService<IStringLocalizer<YavscLocalization>>();
10 months ago
Startup.Configure(app, siteSettings, smtpSettings, authorizationService,
payPalSettings, googleAuthSettings, localization, loggerFactory,
10 months ago
app.Environment.EnvironmentName);
app.ConfigureFileServerApp();
10 months ago
return app;
}
10 months ago
static void LoadGoogleConfig(IConfigurationRoot configuration)
{
string? googleClientFile = configuration["Authentication:Google:GoogleWebClientJson"];
string? googleServiceAccountJsonFile = configuration["Authentication:Google:GoogleServiceAccountJson"];
if (googleClientFile != null)
{
Config.GoogleWebClientConfiguration = new ConfigurationBuilder().AddJsonFile(googleClientFile).Build();
}
if (googleServiceAccountJsonFile != null)
{
FileInfo safile = new FileInfo(googleServiceAccountJsonFile);
Config.GServiceAccount = JsonConvert.DeserializeObject<GoogleServiceAccount>(safile.OpenText().ReadToEnd());
}
}
}