< Summary

Information
Class: OzonOrdersWeb.Extensions.AuthorizationExtensions
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Extensions/AuthorizationExtensions.cs
Line coverage
41%
Covered lines: 36
Uncovered lines: 51
Coverable lines: 87
Total lines: 139
Line coverage: 41.3%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AddAppAuthorization(...)0%932043.37%
UseAppAuthorizationSeedAsync()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Extensions/AuthorizationExtensions.cs

#LineLine coverage
 1using System.Threading.RateLimiting;
 2using Microsoft.AspNetCore.Authentication;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Mvc;
 5using Microsoft.AspNetCore.RateLimiting;
 6using Microsoft.Extensions.DependencyInjection;
 7using Microsoft.Extensions.Options;
 8using OzonOrdersWeb.Areas.Admin.Services;
 9using OzonOrdersWeb.Authorization;
 10using OzonOrdersWeb.Authorization.Discovery;
 11using OzonOrdersWeb.Authorization.Seed;
 12using OzonRepositories.Authorization;
 13using OzonOrdersWeb.Observability;
 14using Services.Authorization;
 15using Services.Authorization.Services;
 16
 17namespace OzonOrdersWeb.Extensions;
 18
 19/// <summary>
 20/// Регистрация сервисов новой системы авторизации.
 21/// </summary>
 22public static class AuthorizationExtensions
 23{
 24    public static IServiceCollection AddAppAuthorization(this IServiceCollection services, IConfiguration configuration)
 25    {
 226        services.Configure<Services.Authorization.AppAuthorizationOptions>(configuration.GetSection(Services.Authorizati
 227        services.AddMemoryCache();
 228        services.AddHttpContextAccessor();
 29
 30        // Основные сервисы
 231        services.AddScoped<IPermissionService, PermissionService>();
 232        services.AddScoped<IMenuService, MenuService>();
 233        services.AddScoped<IDataScopeService, DataScopeService>();
 234        services.AddScoped<DataScopeFilterSummaryService>();
 235        services.AddScoped<IAuditService, AuditService>();
 236        services.AddScoped<IScopeFilterValuesService, ScopeFilterValuesService>();
 237        services.AddScoped<OzonRepositories.Authorization.IUserScopeProvider, CurrentUserScopeProvider>();
 38
 39        // Discovery + seed
 240        services.AddScoped<IPageDiscoveryService, PageDiscoveryService>();
 241        services.AddScoped<IFieldDiscoveryService, FieldDiscoveryService>();
 242        services.AddScoped<AuthorizationSeeder>();
 43
 44        // Policy + handler
 245        services.AddScoped<IAuthorizationHandler, PageAccessHandler>();
 246        services.AddAuthorization(options =>
 247        {
 048            options.AddPolicy(AuthorizationConstants.PolicyName, policy =>
 049                policy.Requirements.Add(new PageAccessRequirement()));
 250        });
 51
 52        // Claims transformation
 253        services.AddScoped<IClaimsTransformation, PermissionClaimsTransformation>();
 54
 55        // Глобальные фильтры + sanitizer
 256        services.Configure<MvcOptions>(options =>
 257        {
 058            options.Filters.Add<AutoPageAuthorizationFilter>();
 059            options.Filters.Add<FieldPermissionSanitizerFilter>();
 260        });
 261        services.AddScoped<AutoPageAuthorizationFilter>();
 262        services.AddScoped<FieldPermissionSanitizerFilter>();
 63
 264        services.AddSingleton<AuthRateLimiterPolicy>();
 65
 66        // Rate limiting: политика admin + политика auth (для Login.cshtml.cs)
 267        services.AddRateLimiter(options =>
 268        {
 069            options.RejectionStatusCode = 429;
 270
 071            options.AddPolicy(AdminRateLimitPolicy, httpCtx =>
 072            {
 073                var opts = httpCtx.RequestServices.GetRequiredService<IOptions<AppAuthorizationOptions>>().Value;
 074                // Матрица прав шлёт много AJAX подряд — лимит должен быть из конфига (по умолчанию высокий).
 075                var permitLimit = Math.Clamp(opts.AdminRateLimitPerMinute, 120, 10_000);
 076                var key = httpCtx.User?.Identity?.Name
 077                          ?? httpCtx.Connection.RemoteIpAddress?.ToString()
 078                          ?? "anonymous";
 079                return RateLimitPartition.GetSlidingWindowLimiter(key, _ => new SlidingWindowRateLimiterOptions
 080                {
 081                    PermitLimit = permitLimit,
 082                    SegmentsPerWindow = 6,
 083                    Window = TimeSpan.FromMinutes(1),
 084                    AutoReplenishment = true,
 085                    QueueLimit = 0,
 086                });
 087            });
 288
 289            // Политика для страницы входа и других форм аутентификации:
 290            // Строгая защита от брут-форса паролей для мутирующих запросов (POST),
 291            // с комфортным независимым лимитом для чтения/открытия страниц (GET/HEAD/OPTIONS).
 092            options.AddPolicy<string, AuthRateLimiterPolicy>(AuthRateLimitPolicy);
 293
 294            // Название организации по почте отдаётся без входа, поэтому перебор почт режем отдельным лимитом.
 095            options.AddPolicy(PartnerLookupRateLimitPolicy, httpCtx =>
 096            {
 097                var ip = httpCtx.Connection.RemoteIpAddress?.ToString() ?? "anonymous";
 098                return RateLimitPartition.GetFixedWindowLimiter($"partner-lookup:{ip}", _ => new FixedWindowRateLimiterO
 099                {
 0100                    PermitLimit = 10,
 0101                    Window = TimeSpan.FromMinutes(1),
 0102                    AutoReplenishment = true,
 0103                    QueueLimit = 0
 0104                });
 0105            });
 2106
 0107            options.AddPolicy(ClientDiagnosticsRateLimitPolicy, httpCtx =>
 0108            {
 0109                var configuredLimit = configuration.GetValue<int?>(
 0110                    $"{ObservabilityOptions.SectionName}:ClientEventsPerMinute") ?? 60;
 0111                var ip = httpCtx.Connection.RemoteIpAddress?.ToString() ?? "anonymous";
 0112                return RateLimitPartition.GetFixedWindowLimiter(ip, _ => new FixedWindowRateLimiterOptions
 0113                {
 0114                    PermitLimit = Math.Clamp(configuredLimit, 10, 600),
 0115                    Window = TimeSpan.FromMinutes(1),
 0116                    AutoReplenishment = true,
 0117                    QueueLimit = 0
 0118                });
 0119            });
 2120        });
 121
 2122        return services;
 123    }
 124
 125    public const string AdminRateLimitPolicy = "AdminRateLimit";
 126    public const string AuthRateLimitPolicy = "auth";
 127    public const string PartnerLookupRateLimitPolicy = "partner-lookup";
 128    public const string ClientDiagnosticsRateLimitPolicy = "client-diagnostics";
 129
 130    /// <summary>
 131    /// Выполнить seed после запуска приложения.
 132    /// </summary>
 133    public static async Task UseAppAuthorizationSeedAsync(this IApplicationBuilder app)
 134    {
 0135        using var scope = app.ApplicationServices.CreateScope();
 0136        var seeder = scope.ServiceProvider.GetRequiredService<AuthorizationSeeder>();
 0137        await seeder.SeedAsync();
 0138    }
 139}