PhoneToolMX/PhoneToolMX/Program.cs
2023-10-21 13:46:56 -07:00

162 lines
6 KiB
C#

using PhoneToolMX.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.CookiePolicy;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using NuGet.Packaging;
using PhoneToolMX.Models;
using PhoneToolMX.Services;
using System.Net;
using System.Security.Authentication;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddDbContext<PTMXContext>(
options => options.UseNpgsql(builder.Configuration.GetConnectionString("DbConnection"),
b => b.MigrationsAssembly("PhoneToolMX.Models")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
if (!builder.Environment.IsDevelopment()) {
builder.Host.UseSystemd();
}
builder.Services.AddIdentityCore<User>(opts =>
{
opts.ClaimsIdentity.UserIdClaimType = "sub";
opts.ClaimsIdentity.UserNameClaimType = "preferred_username";
opts.ClaimsIdentity.EmailClaimType = "email";
})
.AddRoles<Role>()
.AddRoleManager<RoleManager<Role>>()
.AddSignInManager<SignInManager<User>>()
.AddUserManager<UserManager<User>>()
.AddEntityFrameworkStores<PTMXContext>();
var proxyConfig = builder.Configuration.GetSection("Proxies");
if (proxyConfig?.GetSection("TrustedProxies")?.Get<IList<string>>() is {} trustedProxies) {
builder.Services.Configure<ForwardedHeadersOptions>(opts =>
{
opts.KnownProxies.AddRange(trustedProxies.Select(IPAddress.Parse));
opts.ForwardedHeaders = ForwardedHeaders.All;
});
}
// Using OIDC
builder.Services.AddAuthentication(opts =>
{
opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opts.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect((OpenIdConnectOptions opts) => {
// pull from appsettings
var oidcConfig = builder.Configuration.GetRequiredSection("OpenIdConnect");
opts.ClientId = oidcConfig.GetValue<string>("ClientId");
opts.ClientSecret = oidcConfig.GetValue<string>("ClientSecret");
opts.MetadataAddress = oidcConfig.GetValue<string>("MetadataUrl");
opts.Authority = oidcConfig.GetValue<string>("Authority");
opts.GetClaimsFromUserInfoEndpoint = true;
opts.SaveTokens = false;
opts.ResponseType = "code";
opts.Scope.Add("openid");
opts.Events = new OpenIdConnectEvents
{
OnUserInformationReceived = async ctx =>
{
// Create the user in UserManager for future authz
var _userManager = ctx.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
var curUser = ctx.Principal;
var user = new User
{
Id = curUser?.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.NameIdentifier))
?.Value,
UserName = curUser?.Claims.FirstOrDefault(c => c.Type.Equals("preferred_username"))?.Value,
Email = curUser?.Claims.FirstOrDefault(c => c.Type.Equals("email"))
?.Value,
EmailConfirmed = true, // always confirmed (or rather, expect the IdP to confirm it)
Phones = null, // no phones or extensions by default
Extensions = null,
};
if (user.Id == null) {
throw new InvalidCredentialException($"Missing required OIDC claim \"{ClaimTypes.NameIdentifier}\"");
}
if (await _userManager.FindByIdAsync(user.Id) == null) {
var res = await _userManager.CreateAsync(user);
if (res.Succeeded == false) {
throw new InvalidCredentialException($"Creating identity failed: {res.Errors.FirstOrDefault()!.Description}");
}
}
}
};
// if dev, disable secure
if (!builder.Environment.IsDevelopment()) return;
opts.NonceCookie.SecurePolicy = CookieSecurePolicy.None;
opts.NonceCookie.SameSite = SameSiteMode.Unspecified;
opts.CorrelationCookie.SecurePolicy = CookieSecurePolicy.None;
opts.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
});
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("RequireAdmin",
policy => policy.RequireRole("Administrator"));
});
// AMI for PJSIP notifications
if (builder.Configuration.GetSection("ami").Exists()) {
builder.Services.AddSingleton<IAsteriskManager, AsteriskManager>();
}
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseCookiePolicy(new CookiePolicyOptions
{
HttpOnly = HttpOnlyPolicy.Always,
MinimumSameSitePolicy = SameSiteMode.Strict,
Secure = CookieSecurePolicy.Always,
});
app.UseHttpsRedirection();
app.UseForwardedHeaders();
} else {
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
using (var scope = app.Services.CreateScope()) {
var services = scope.ServiceProvider;
var context = services.GetRequiredService<PTMXContext>();
context.Database.EnsureCreated();
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}")
.RequireAuthorization();
app.Run();