using DevExpress.ExpressApp.Security; using DevExpress.ExpressApp.ApplicationBuilder; using DevExpress.ExpressApp.Blazor.ApplicationBuilder; using DevExpress.ExpressApp.Blazor.Services; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Components.Server.Circuits; using DurnyklyYol.Blazor.Server.Services; using DevExpress.Persistent.BaseImpl.PermissionPolicy; using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.OData; using DevExpress.ExpressApp.WebApi.Services; using DurnyklyYol.WebApi.JWT; using DevExpress.ExpressApp.Security.Authentication.ClientServer; using DurnyklyYol.Blazor.Server.OData; using Microsoft.AspNetCore.OData.Query.Validator; namespace DurnyklyYol.Blazor.Server; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddSingleton(typeof(Microsoft.AspNetCore.SignalR.HubConnectionHandler<>), typeof(ProxyHubConnectionHandler<>)); services.AddRazorPages(); services.AddServerSideBlazor(); services.AddHttpContextAccessor(); services.AddScoped(); services.AddScoped(); services.AddXaf(Configuration, builder => { builder.UseApplication(); builder.AddXafWebApi(webApiBuilder => { webApiBuilder.AddXpoServices(); webApiBuilder.ConfigureOptions(options => { // Make your business objects available in the Web API and generate the GET, POST, PUT, and DELETE HTTP methods for it. // options.BusinessObject(); options.BusinessObject(); }); }); builder.Modules .AddConditionalAppearance() .AddFileAttachments() .AddReports(options => { options.EnableInplaceReports = true; options.ReportDataType = typeof(DevExpress.Persistent.BaseImpl.ReportDataV2); options.ReportStoreMode = DevExpress.ExpressApp.ReportsV2.ReportStoreModes.XML; }) .AddValidation(options => { options.AllowValidationDetailsAccess = false; }) .AddViewVariants() .Add() .Add(); builder.ObjectSpaceProviders .AddSecuredXpo((serviceProvider, options) => { string connectionString = null; if(Configuration.GetConnectionString("ConnectionString") != null) { connectionString = Configuration.GetConnectionString("ConnectionString"); } #if EASYTEST if(Configuration.GetConnectionString("EasyTestConnectionString") != null) { connectionString = Configuration.GetConnectionString("EasyTestConnectionString"); } #endif ArgumentNullException.ThrowIfNull(connectionString); options.ConnectionString = connectionString; options.ThreadSafe = true; options.UseSharedDataStoreProvider = true; }) .AddNonPersistent(); builder.Security .UseIntegratedMode(options => { options.Lockout.Enabled = true; options.RoleType = typeof(PermissionPolicyRole); // ApplicationUser descends from PermissionPolicyUser and supports the OAuth authentication. For more information, refer to the following topic: https://docs.devexpress.com/eXpressAppFramework/402197 // If your application uses PermissionPolicyUser or a custom user type, set the UserType property as follows: options.UserType = typeof(DurnyklyYol.Module.BusinessObjects.ApplicationUser); // ApplicationUserLoginInfo is only necessary for applications that use the ApplicationUser user type. // If you use PermissionPolicyUser or a custom user type, comment out the following line: options.UserLoginInfoType = typeof(DurnyklyYol.Module.BusinessObjects.ApplicationUserLoginInfo); options.UseXpoPermissionsCaching(); options.Events.OnSecurityStrategyCreated += securityStrategy => { // Use the 'PermissionsReloadMode.NoCache' option to load the most recent permissions from the database once // for every Session instance when secured data is accessed through this instance for the first time. // Use the 'PermissionsReloadMode.CacheOnFirstAccess' option to reduce the number of database queries. // In this case, permission requests are loaded and cached when secured data is accessed for the first time // and used until the current user logs out. // See the following article for more details: https://docs.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.Security.SecurityStrategy.PermissionsReloadMode. ((SecurityStrategy)securityStrategy).PermissionsReloadMode = PermissionsReloadMode.NoCache; }; }) .AddPasswordAuthentication(options => { options.IsSupportChangePassword = true; }); }); var authentication = services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; }); authentication.AddCookie(options => { options.LoginPath = "/LoginPage"; }); authentication.AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters() { ValidateIssuerSigningKey = true, //ValidIssuer = Configuration["Authentication:Jwt:Issuer"], //ValidAudience = Configuration["Authentication:Jwt:Audience"], ValidateIssuer = false, ValidateAudience = false, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Authentication:Jwt:IssuerSigningKey"])) }; }); services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder( JwtBearerDefaults.AuthenticationScheme) .RequireAuthenticatedUser() .RequireXafAuthentication() .Build(); }); services .AddControllers() .AddOData((options, serviceProvider) => { options .AddRouteComponents("api/odata", new EdmModelBuilder(serviceProvider).GetEdmModel(), odataServices => { odataServices.AddSingleton(); }) .EnableQueryFeatures(100); }); services.AddSwaggerGen(c => { c.EnableAnnotations(); c.SwaggerDoc("v1", new OpenApiInfo { Title = "DurnyklyYol API", Version = "v1", Description = @"Use AddXafWebApi(options) in the DurnyklyYol.Blazor.Server\Startup.cs file to make Business Objects available in the Web API." }); c.AddSecurityDefinition("JWT", new OpenApiSecurityScheme() { Type = SecuritySchemeType.Http, Name = "Bearer", Scheme = "bearer", BearerFormat = "JWT", In = ParameterLocation.Header }); c.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme() { Reference = new OpenApiReference() { Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, Id = "JWT" } }, new string[0] }, }); }); services.Configure(o => { //The code below specifies that the naming of properties in an object serialized to JSON must always exactly match //the property names within the corresponding CLR type so that the property names are displayed correctly in the Swagger UI. //XPO is case-sensitive and requires this setting so that the example request data displayed by Swagger is always valid. //Comment this code out to revert to the default behavior. //See the following article for more information: https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions.propertynamingpolicy o.JsonSerializerOptions.PropertyNamingPolicy = null; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "DurnyklyYol WebApi v1"); }); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. To change this for production scenarios, see: https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseRequestLocalization(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseXaf(); app.UseEndpoints(endpoints => { endpoints.MapXafEndpoints(); endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); endpoints.MapControllers(); }); } }