162 lines
6 KiB
C#
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();
|