/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { services.AddMapster(options => { options.Default.IgnoreNonMapped(true); options.Default.IgnoreNullValues(true); options.AllowImplicitDestinationInheritance = true; options.AllowImplicitSourceInheritance = true; options.Default.UseDestinationValue(member => member.SetterModifier == AccessModifier.None && member.Type.IsGenericType && member.Type.GetGenericTypeDefinition() == typeof(ICollection <>)); }); services.Configure <JsonSerializerOptions>(options => { options.IgnoreNullValues = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:IgnoreNullValues"]) ? Boolean.Parse(this.Configuration["Serialization:Json:IgnoreNullValues"]) : false; options.PropertyNameCaseInsensitive = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) ? Boolean.Parse(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) : false; options.PropertyNamingPolicy = this.Configuration["Serialization:Json:PropertyNamingPolicy"] == "CamelCase" ? JsonNamingPolicy.CamelCase : null; options.WriteIndented = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:WriteIndented"]) ? Boolean.Parse(this.Configuration["Serialization:Json:WriteIndented"]) : false; options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(new Int32ToStringJsonConverter()); }); services.Configure <Core.Http.Configuration.AuthClientOptions>(this.Configuration.GetSection("Keycloak")); services.Configure <Core.Http.Configuration.OpenIdConnectOptions>(this.Configuration.GetSection("Keycloak:OpenIdConnect")); services.Configure <Keycloak.Configuration.KeycloakOptions>(this.Configuration.GetSection("Keycloak")); services.Configure <Pims.Dal.PimsOptions>(this.Configuration.GetSection("Pims")); services.AddOptions(); services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:IgnoreNullValues"]) ? Boolean.Parse(this.Configuration["Serialization:Json:IgnoreNullValues"]) : false; options.JsonSerializerOptions.PropertyNameCaseInsensitive = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) ? Boolean.Parse(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) : false; options.JsonSerializerOptions.PropertyNamingPolicy = this.Configuration["Serialization:Json:PropertyNamingPolicy"] == "CamelCase" ? JsonNamingPolicy.CamelCase : null; options.JsonSerializerOptions.WriteIndented = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:WriteIndented"]) ? Boolean.Parse(this.Configuration["Serialization:Json:WriteIndented"]) : false; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.JsonSerializerOptions.Converters.Add(new Int32ToStringJsonConverter()); }); services.AddMvcCore() .AddJsonOptions(options => { options.JsonSerializerOptions.IgnoreNullValues = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:IgnoreNullValues"]) ? Boolean.Parse(this.Configuration["Serialization:Json:IgnoreNullValues"]) : false; options.JsonSerializerOptions.PropertyNameCaseInsensitive = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) ? Boolean.Parse(this.Configuration["Serialization:Json:PropertyNameCaseInsensitive"]) : false; options.JsonSerializerOptions.PropertyNamingPolicy = this.Configuration["Serialization:Json:PropertyNamingPolicy"] == "CamelCase" ? JsonNamingPolicy.CamelCase : null; options.JsonSerializerOptions.WriteIndented = !String.IsNullOrWhiteSpace(this.Configuration["Serialization:Json:WriteIndented"]) ? Boolean.Parse(this.Configuration["Serialization:Json:WriteIndented"]) : false; options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); options.JsonSerializerOptions.Converters.Add(new Int32ToStringJsonConverter()); }); services.AddRouting(options => { options.ConstraintMap.Add("pid", typeof(PidConstraint)); }); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { var key = Encoding.ASCII.GetBytes(Configuration["Keycloak:Secret"]); options.RequireHttpsMetadata = false; options.Authority = Configuration["Keycloak:Authority"]; options.Audience = Configuration["Keycloak:Audience"]; options.SaveToken = true; options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuerSigningKey = true, ValidateIssuer = false, ValidateAudience = false }; if (key.Length > 0) { options.TokenValidationParameters.IssuerSigningKey = new SymmetricSecurityKey(key); } options.Events = new JwtBearerEvents() { OnTokenValidated = context => { return(Task.CompletedTask); }, OnAuthenticationFailed = context => { context.NoResult(); context.Response.StatusCode = 401; throw context.Exception; }, OnForbidden = context => { return(Task.CompletedTask); } }; }); services.AddAuthorization(options => { options.AddPolicy("Administrator", policy => policy.Requirements.Add(new RealmAccessRoleRequirement("administrator"))); }); var cs = Configuration.GetConnectionString("PIMS"); var builder = new SqlConnectionStringBuilder(cs); var pwd = Configuration["DB_PASSWORD"]; if (!String.IsNullOrEmpty(pwd)) { builder.Password = pwd; } services.AddDbContext <PimsContext>(options => { var sql = options.UseSqlServer(builder.ConnectionString); if (!this.Environment.IsProduction()) { var debugLoggerFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); // NOSONAR sql.UseLoggerFactory(debugLoggerFactory); options.EnableSensitiveDataLogging(); } }); services.AddHttpClient(); services.AddPimsServices(); services.AddPimsKeycloakService(); services.AddGeocoderService(this.Configuration.GetSection("Geocoder")); // TODO: Determine if a default value could be used instead. services.AddChesService(this.Configuration.GetSection("Ches")); services.AddNotificationsService(); services.AddSingleton <IAuthorizationHandler, RealmAccessRoleHandler>(); services.AddTransient <IClaimsTransformation, KeycloakClaimTransformer>(); services.AddHttpContextAccessor(); services.AddTransient <ClaimsPrincipal>(s => s.GetService <IHttpContextAccessor>().HttpContext.User); services.AddScoped <IProxyRequestClient, ProxyRequestClient>(); services.AddScoped <IOpenIdConnectRequestClient, OpenIdConnectRequestClient>(); services.AddHealthChecks() .AddCheck("liveliness", () => HealthCheckResult.Healthy()) .AddSqlServer(builder.ConnectionString, tags: new[] { "services" }); services.AddApiVersioning(options => { options.ReportApiVersions = true; options.AssumeDefaultVersionWhenUnspecified = true; options.ApiVersionReader = new HeaderApiVersionReader("api-version"); // options.DefaultApiVersion = new ApiVersion(1, 0); }); services.AddVersionedApiExplorer(options => { // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service // note: the specified format code will format the version as "'v'major[.minor][-status]" options.GroupNameFormat = "'v'VVV"; // note: this option is only necessary when versioning by url segment. the SubstitutionFormat // can also be used to control the format of the API version in route templates options.SubstituteApiVersionInUrl = true; }); services.AddTransient <IConfigureOptions <SwaggerGenOptions>, Helpers.Swagger.ConfigureSwaggerOptions>(); services.AddSwaggerGen(options => { options.EnableAnnotations(true); options.CustomSchemaIds(o => o.FullName); options.OperationFilter <Helpers.Swagger.SwaggerDefaultValues>(); options.DocumentFilter <Helpers.Swagger.SwaggerDocumentFilter>(); options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Name = "Authorization", In = ParameterLocation.Header, Description = "Please enter into field the word 'Bearer' following by space and JWT", Type = SecuritySchemeType.ApiKey }); options.AddSecurityRequirement(new OpenApiSecurityRequirement() { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }, Scheme = "oauth2", Name = "Bearer", In = ParameterLocation.Header, }, new List <string>() } }); var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); options.IncludeXmlComments(xmlPath); }); services.Configure <ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.All; options.AllowedHosts = this.Configuration.GetValue <string>("AllowedHosts")?.Split(';').ToList <string>(); }); }