1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-04 03:02:18 -05:00
bitwarden/src/Identity/Startup.cs
Alex Morask c0a4ba8de1
[AC-2965] Use OrganizationBillingService to purchase org when FF is on (#4737)
* Add PurchaseSubscription to OrganizationBillingService and call from OrganizationService.SignUpAsync when FF is on

* Run dotnet format

* Missed billing service DI for SCIM which uses the OrganizationService
2024-09-06 10:24:05 -04:00

244 lines
8.8 KiB
C#

using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using AspNetCoreRateLimit;
using Bit.Core;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Billing.Extensions;
using Bit.Core.Context;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.SecretsManager.Repositories.Noop;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Identity.Utilities;
using Bit.SharedWeb.Swagger;
using Bit.SharedWeb.Utilities;
using Duende.IdentityServer.Services;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.IdentityModel.Logging;
using Microsoft.OpenApi.Models;
namespace Bit.Identity;
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
Configuration = configuration;
Environment = env;
}
public IConfiguration Configuration { get; private set; }
public IWebHostEnvironment Environment { get; set; }
public void ConfigureServices(IServiceCollection services)
{
// Options
services.AddOptions();
// Settings
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
if (!globalSettings.SelfHosted)
{
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimitOptions"));
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
}
// Data Protection
services.AddCustomDataProtectionServices(Environment, globalSettings);
// Repositories
services.AddDatabaseRepositories(globalSettings);
// Context
services.AddScoped<ICurrentContext, CurrentContext>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Caching
services.AddMemoryCache();
services.AddDistributedCache(globalSettings);
// Mvc
services.AddMvc(config =>
{
config.Filters.Add(new ModelStateValidationFilterAttribute());
});
services.AddSwaggerGen(c =>
{
c.SchemaFilter<EnumSchemaFilter>();
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Bitwarden Identity", Version = "v1" });
});
if (!globalSettings.SelfHosted)
{
services.AddIpRateLimiting(globalSettings);
}
// Cookies
if (Environment.IsDevelopment())
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
options.OnAppendCookie = ctx =>
{
ctx.CookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
};
});
}
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
// Authentication
services
.AddDistributedIdentityServices()
.AddAuthentication()
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
.AddOpenIdConnect("sso", "Single Sign On", options =>
{
options.Authority = globalSettings.BaseServiceUri.InternalSso;
options.RequireHttpsMetadata = !Environment.IsDevelopment() &&
globalSettings.BaseServiceUri.InternalIdentity.StartsWith("https");
options.ClientId = "oidc-identity";
options.ClientSecret = globalSettings.OidcIdentityClientKey;
options.ResponseMode = "form_post";
options.SignInScheme = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
options.ResponseType = "code";
options.SaveTokens = false;
options.GetClaimsFromUserInfoEndpoint = true;
// Some browsers (safari) won't allow Secure cookies to be set on a http connection
options.CorrelationCookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.NonceCookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Events = new Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
// Pass domain_hint onto the sso idp
context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"];
context.ProtocolMessage.Parameters.Add("organizationId", context.Properties.Items["organizationId"]);
if (context.Properties.Items.ContainsKey("user_identifier"))
{
context.ProtocolMessage.SessionState = context.Properties.Items["user_identifier"];
}
if (context.Properties.Parameters.Count > 0 &&
context.Properties.Parameters.TryGetValue(SsoTokenable.TokenIdentifier, out var tokenValue))
{
var token = tokenValue?.ToString() ?? "";
context.ProtocolMessage.Parameters.Add(SsoTokenable.TokenIdentifier, token);
}
return Task.FromResult(0);
}
};
});
// IdentityServer
services.AddCustomIdentityServerServices(Environment, globalSettings);
// Identity
services.AddCustomIdentityServices(globalSettings);
// Services
services.AddBaseServices(globalSettings);
services.AddDefaultServices(globalSettings);
services.AddCoreLocalizationServices();
services.AddBillingOperations();
// TODO: Remove when OrganizationUser methods are moved out of OrganizationService, this noop dependency should
// TODO: no longer be required - see PM-1880
services.AddScoped<IServiceAccountRepository, NoopServiceAccountRepository>();
if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) &&
CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ApplicationCacheTopicName))
{
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
}
// HttpClients
services.AddHttpClient("InternalSso", client =>
{
client.BaseAddress = new Uri(globalSettings.BaseServiceUri.InternalSso);
});
}
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env,
IHostApplicationLifetime appLifetime,
GlobalSettings globalSettings,
ILogger<Startup> logger)
{
IdentityModelEventSource.ShowPII = true;
app.UseSerilog(env, appLifetime, globalSettings);
// Add general security headers
app.UseMiddleware<SecurityHeadersMiddleware>();
if (!env.IsDevelopment())
{
var uri = new Uri(globalSettings.BaseServiceUri.Identity);
app.Use(async (ctx, next) =>
{
ctx.RequestServices.GetRequiredService<IServerUrls>().Origin = $"{uri.Scheme}://{uri.Host}";
await next();
});
}
if (globalSettings.SelfHosted)
{
app.UsePathBase("/identity");
app.UseForwardedHeaders(globalSettings);
}
// Default Middleware
app.UseDefaultMiddleware(env, globalSettings);
if (!globalSettings.SelfHosted)
{
// Rate limiting
app.UseMiddleware<CustomIpRateLimitMiddleware>();
}
if (env.IsDevelopment())
{
app.UseSwagger();
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
}
// Add localization
app.UseCoreLocalization();
// Add static files to the request pipeline.
app.UseStaticFiles();
// Add routing
app.UseRouting();
// Add Cors
app.UseCors(policy => policy.SetIsOriginAllowed(o =>
CoreHelpers.IsCorsOriginAllowed(o, globalSettings) ||
// If development - allow requests from the Swagger UI so it can authorize
(Environment.IsDevelopment() && o == globalSettings.BaseServiceUri.Api))
.AllowAnyMethod().AllowAnyHeader().AllowCredentials());
// Add current context
app.UseMiddleware<CurrentContextMiddleware>();
// Add IdentityServer to the request pipeline.
app.UseIdentityServer();
// Add Mvc stuff
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
// Log startup
logger.LogInformation(Constants.BypassFiltersEventId, globalSettings.ProjectName + " started.");
}
}