using System; using System.IO; using Blazorise.Bootstrap5; using Blazorise.Icons.FontAwesome; using Medallion.Threading; using Medallion.Threading.Redis; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.OpenApi.Models; using Acme.BookStore.Blazor.WebApp.Tiered.Client; using Acme.BookStore.Blazor.WebApp.Tiered.Client.Menus; using Acme.BookStore.Blazor.WebApp.Tiered.Components; using Acme.BookStore.Blazor.WebApp.Tiered.Menus; using Acme.BookStore.Localization; using Acme.BookStore.MultiTenancy; using StackExchange.Redis; using Volo.Abp; using Volo.Abp.AspNetCore.Authentication.OpenIdConnect; using Volo.Abp.AspNetCore.Components.Web; using Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme; using Volo.Abp.AspNetCore.Components.Server.LeptonXLiteTheme.Bundling; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; using Volo.Abp.AspNetCore.Mvc.Client; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite; using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; using Volo.Abp.Caching; using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.DistributedLocking; using Volo.Abp.Http.Client.IdentityModel.Web; using Volo.Abp.Identity.Blazor.Server; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; using Volo.Abp.Security.Claims; using Volo.Abp.SettingManagement.Blazor.Server; using Volo.Abp.Swashbuckle; using Volo.Abp.TenantManagement.Blazor.Server; using Volo.Abp.UI; using Volo.Abp.UI.Navigation; using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.VirtualFileSystem; namespace Acme.BookStore.Blazor.WebApp.Tiered; [DependsOn( typeof(BookStoreHttpApiClientModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpDistributedLockingModule), typeof(AbpAspNetCoreMvcClientModule), typeof(AbpAspNetCoreAuthenticationOpenIdConnectModule), typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpAspNetCoreComponentsServerLeptonXLiteThemeModule), typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule), typeof(AbpAutofacModule), typeof(AbpSwashbuckleModule), typeof(AbpAspNetCoreSerilogModule), typeof(AbpIdentityBlazorServerModule), typeof(AbpTenantManagementBlazorServerModule), typeof(AbpSettingManagementBlazorServerModule) )] public class BookStoreBlazorModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.PreConfigure(options => { options.AddAssemblyResource( typeof(BookStoreResource), typeof(BookStoreDomainSharedModule).Assembly, typeof(BookStoreApplicationContractsModule).Assembly, typeof(BookStoreBlazorModule).Assembly ); }); PreConfigure(options => { options.IsBlazorWebApp = true; }); } public override void ConfigureServices(ServiceConfigurationContext context) { var hostingEnvironment = context.Services.GetHostingEnvironment(); var configuration = context.Services.GetConfiguration(); // Add services to the container. context.Services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); ConfigureUrls(configuration); ConfigureCache(); ConfigureBundles(); ConfigureMultiTenancy(); ConfigureAuthentication(context, configuration); ConfigureAutoMapper(); ConfigureVirtualFileSystem(hostingEnvironment); ConfigureBlazorise(context); ConfigureRouter(context); ConfigureMenu(configuration); ConfigureDataProtection(context, configuration, hostingEnvironment); ConfigureDistributedLocking(context, configuration); ConfigureSwaggerServices(context.Services); } private void ConfigureUrls(IConfiguration configuration) { Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; }); } private void ConfigureCache() { Configure(options => { options.KeyPrefix = "BookStore:"; }); } private void ConfigureBundles() { Configure(options => { // MVC UI options.StyleBundles.Configure( LeptonXLiteThemeBundles.Styles.Global, bundle => { bundle.AddFiles("/global-styles.css"); } ); //BLAZOR UI options.StyleBundles.Configure( BlazorLeptonXLiteThemeBundles.Styles.Global, bundle => { bundle.AddFiles("/blazor-global-styles.css"); //You can remove the following line if you don't use Blazor CSS isolation for components bundle.AddFiles(new BundleFile("/Acme.BookStore.Blazor.WebApp.Tiered.Client.styles.css", true)); } ); }); } private void ConfigureMultiTenancy() { Configure(options => { options.IsEnabled = MultiTenancyConsts.IsEnabled; }); } private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies", options => { options.ExpireTimeSpan = TimeSpan.FromDays(365); options.IntrospectAccessToken(); }) .AddAbpOpenIdConnect("oidc", options => { options.Authority = configuration["AuthServer:Authority"]; options.RequireHttpsMetadata = configuration.GetValue("AuthServer:RequireHttpsMetadata"); options.ResponseType = OpenIdConnectResponseType.CodeIdToken; options.ClientId = configuration["AuthServer:ClientId"]; options.ClientSecret = configuration["AuthServer:ClientSecret"]; options.SaveTokens = true; options.GetClaimsFromUserInfoEndpoint = true; options.Scope.Add("roles"); options.Scope.Add("email"); options.Scope.Add("phone"); options.Scope.Add("BookStore"); }); /* * This configuration is used when the AuthServer is running on the internal network such as docker or k8s. * Configuring the redirecting URLs for internal network and the web * The login and the logout URLs are configured to redirect to the AuthServer real DNS for browser. * The token acquired and validated from the the internal network AuthServer URL. */ if (configuration.GetValue("AuthServer:IsContainerized")) { context.Services.Configure("oidc", options => { options.TokenValidationParameters.ValidIssuers = new[] { configuration["AuthServer:MetaAddress"]!.EnsureEndsWith('/'), configuration["AuthServer:Authority"]!.EnsureEndsWith('/') }; options.MetadataAddress = configuration["AuthServer:MetaAddress"]!.EnsureEndsWith('/') + ".well-known/openid-configuration"; var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider; options.Events.OnRedirectToIdentityProvider = async ctx => { // Intercept the redirection so the browser navigates to the right URL in your host ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/authorize"; if (previousOnRedirectToIdentityProvider != null) { await previousOnRedirectToIdentityProvider(ctx); } }; var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut; options.Events.OnRedirectToIdentityProviderForSignOut = async ctx => { // Intercept the redirection for signout so the browser navigates to the right URL in your host ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/logout"; if (previousOnRedirectToIdentityProviderForSignOut != null) { await previousOnRedirectToIdentityProviderForSignOut(ctx); } }; }); } context.Services.Configure(options => { options.IsDynamicClaimsEnabled = true; }); } private void ConfigureVirtualFileSystem(IWebHostEnvironment hostingEnvironment) { if (hostingEnvironment.IsDevelopment()) { Configure(options => { options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Acme.BookStore.Domain.Shared")); options.FileSets.ReplaceEmbeddedByPhysical(Path.Combine(hostingEnvironment.ContentRootPath, $"..{Path.DirectorySeparatorChar}Acme.BookStore.Application.Contracts")); options.FileSets.ReplaceEmbeddedByPhysical(hostingEnvironment.ContentRootPath); }); } } private void ConfigureBlazorise(ServiceConfigurationContext context) { context.Services .AddBootstrap5Providers() .AddFontAwesomeIcons(); } private void ConfigureMenu(IConfiguration configuration) { Configure(options => { options.MenuContributors.Add(new BookStoreMenuContributor(configuration)); }); Configure(options => { options.Contributors.Add(new BookStoreToolbarContributor()); }); } private void ConfigureRouter(ServiceConfigurationContext context) { Configure(options => { options.AppAssembly = typeof(BookStoreBlazorModule).Assembly; options.AdditionalAssemblies.Add(typeof(BookStoreBlazorClientModule).Assembly); }); } private void ConfigureAutoMapper() { Configure(options => { options.AddMaps(); }); } private void ConfigureSwaggerServices(IServiceCollection services) { services.AddAbpSwaggerGen( options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "BookStore API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); } ); } private void ConfigureDataProtection( ServiceConfigurationContext context, IConfiguration configuration, IWebHostEnvironment hostingEnvironment) { var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("BookStore"); if (!hostingEnvironment.IsDevelopment()) { var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "BookStore-Protection-Keys"); } } private void ConfigureDistributedLocking( ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddSingleton(sp => { var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); return new RedisDistributedSynchronizationProvider(connection.GetDatabase()); }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) { var env = context.GetEnvironment(); var app = context.GetApplicationBuilder(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAbpRequestLocalization(); if (!env.IsDevelopment()) { app.UseErrorPage(); } app.UseCorrelationId(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); if (MultiTenancyConsts.IsEnabled) { app.UseMultiTenancy(); } app.UseDynamicClaims(); app.UseAntiforgery(); app.UseAuthorization(); app.UseSwagger(); app.UseAbpSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "BookStore API"); }); app.UseAbpSerilogEnrichers(); app.UseConfiguredEndpoints(builder => { builder.MapRazorComponents() .AddInteractiveServerRenderMode() .AddInteractiveWebAssemblyRenderMode() .AddAdditionalAssemblies(builder.ServiceProvider.GetRequiredService>().Value.AdditionalAssemblies.ToArray()); }); } }