Обходной лист (#12)
Tests / backend-test (push) Successful in 1m38s
Tests / frontend-test (push) Successful in 47s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/12
This commit was merged in pull request #12.
This commit is contained in:
2026-08-17 05:43:52 +00:00
parent d076f4e98d
commit 48ede1420d
37 changed files with 1351 additions and 435 deletions
@@ -4,9 +4,6 @@ namespace Tnsu.ApprovalRoute.Api.Auth;
public static class AuthSchemes
{
/// <summary>Локальная разработка и тесты — Entra не настроен (см. AuthSetup).</summary>
public const string Dev = "Dev";
/// <summary>Токен пользователя (делегированный) — обычная работа админки.</summary>
public const string Bearer = JwtBearerDefaults.AuthenticationScheme;
@@ -20,11 +20,11 @@ public static class AuthSetup
var tenantId = configuration["Entra:TenantId"];
var audience = configuration["Entra:Audience"];
if (!string.IsNullOrWhiteSpace(tenantId) && !string.IsNullOrWhiteSpace(audience))
AddEntraAuth(services, configuration, tenantId, audience);
else
AddDevAuth(services);
if (string.IsNullOrWhiteSpace(tenantId) || string.IsNullOrWhiteSpace(audience))
throw new InvalidOperationException(
"Entra:TenantId и Entra:Audience обязательны — локального Dev-режима без них больше нет.");
AddEntraAuth(services, configuration, tenantId, audience);
return services;
}
@@ -125,26 +125,4 @@ public static class AuthSetup
options.DefaultPolicy = options.GetPolicy(AuthPolicies.Any)!;
});
}
private static void AddDevAuth(IServiceCollection services)
{
services.AddAuthentication(AuthSchemes.Dev)
.AddScheme<AuthenticationSchemeOptions, DevAuthHandler>(AuthSchemes.Dev, _ => { });
// Те же имена политик, но поверх Dev-схемы: иначе контроллер с
// [Authorize(Policy = ...)] падал бы локально с «политика не найдена».
services.AddAuthorization(options =>
{
foreach (var name in new[] { AuthPolicies.Delegated, AuthPolicies.AppOnly, AuthPolicies.Any })
{
options.AddPolicy(name, policy =>
{
policy.AuthenticationSchemes.Add(AuthSchemes.Dev);
policy.RequireAuthenticatedUser();
});
}
options.DefaultPolicy = options.GetPolicy(AuthPolicies.Any)!;
});
}
}
@@ -1,26 +0,0 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
namespace Tnsu.ApprovalRoute.Api.Auth;
public sealed class DevAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
IConfiguration configuration)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var email = Request.Headers["X-Dev-User-Email"].FirstOrDefault()
?? configuration["Dev:DefaultUserEmail"]
?? "dev@tnsu.kz";
var identity = new ClaimsIdentity([new Claim(ClaimTypes.Email, email)], AuthSchemes.Dev);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), AuthSchemes.Dev);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
@@ -1,8 +1,4 @@
{
"Entra": {
"TenantId": "",
"Audience": ""
},
"ConnectionStrings": {
"Postgres": "Host=localhost;Port=5435;Database=tnsu_approval_route;Username=tnsu_approval_route;Password=tnsu_approval_route"
}
@@ -13,9 +13,6 @@
"Audience": "9232d9e2-9807-4753-808f-07250e5474b0",
"AllowedAppIds": []
},
"Dev": {
"DefaultUserEmail": "dev@tnsu.kz"
},
"Cors": {
"AllowedOrigins": [
"https://tnsukz.sharepoint.com",
@@ -34,6 +31,7 @@
"ClientId": "9232d9e2-9807-4753-808f-07250e5474b0",
"ClientSecret": "",
"Scope": "",
"DirectorTansuEmail": "a.samayeva@tnsu.kz"
"DirectorTansuEmail": "a.samayeva@tnsu.kz",
"ManagingDirectorTansuEmail": "nkim@tnsu.kz"
}
}