< Summary

Information
Class: OzonOrdersWeb.Authorization.Seed.AuthorizationSeeder
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Authorization/Seed/AuthorizationSeeder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 154
Coverable lines: 154
Total lines: 251
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 60
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
SeedAsync()0%702260%
IsBusinessStudio2Page(...)0%600240%
EnsureUserRoleBusinessPagePermissionsAsync()0%110100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Authorization/Seed/AuthorizationSeeder.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Identity;
 2using Microsoft.EntityFrameworkCore;
 3using Microsoft.Extensions.Caching.Memory;
 4using Microsoft.Extensions.DependencyInjection;
 5using Microsoft.Extensions.Logging;
 6using Microsoft.Extensions.Options;
 7using OzonDomains.Models.Authorization;
 8using OzonRepositories.Context;
 9using OzonRepositories.Context.Identity;
 10using OzonOrdersWeb.Authorization;
 11using OzonOrdersWeb.Authorization.Discovery;
 12using Services.Authorization;
 13using Services.Authorization.Services;
 14
 15namespace OzonOrdersWeb.Authorization.Seed;
 16
 17/// <summary>
 18/// Инициализация системы авторизации при первом запуске: системные роли, администратор, реестр страниц/полей.
 19/// </summary>
 20public sealed class AuthorizationSeeder
 21{
 22    private readonly IServiceProvider _services;
 23    private readonly ILogger<AuthorizationSeeder> _logger;
 24    private readonly AppAuthorizationOptions _options;
 25
 026    public AuthorizationSeeder(IServiceProvider services, ILogger<AuthorizationSeeder> logger, IOptions<AppAuthorization
 27    {
 028        _services = services;
 029        _logger = logger;
 030        _options = options.Value;
 031    }
 32
 33    public async Task SeedAsync(CancellationToken ct = default)
 34    {
 035        using var scope = _services.CreateScope();
 036        var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
 37
 38        // Системные роли
 039        var superAdmin = await db.AppRoles.FirstOrDefaultAsync(r => r.Name == AuthorizationConstants.SuperAdminRoleName,
 040        if (superAdmin is null)
 41        {
 042            superAdmin = new AppRole
 043            {
 044                Name = AuthorizationConstants.SuperAdminRoleName,
 045                DisplayName = "Супер-администратор",
 046                Description = "Полный доступ ко всему приложению",
 047                IsSystem = true,
 048                IsActive = true
 049            };
 050            db.AppRoles.Add(superAdmin);
 51        }
 052        var userRole = await db.AppRoles.FirstOrDefaultAsync(r => r.Name == AuthorizationConstants.DefaultUserRoleName, 
 053        if (userRole is null)
 54        {
 055            userRole = new AppRole
 056            {
 057                Name = AuthorizationConstants.DefaultUserRoleName,
 058                DisplayName = "Пользователь",
 059                Description = "Базовая роль по умолчанию",
 060                IsSystem = true,
 061                IsActive = true
 062            };
 063            db.AppRoles.Add(userRole);
 64        }
 065        await db.SaveChangesAsync(ct);
 66
 67        // Создание администратора через Identity
 068        var userManager = scope.ServiceProvider.GetRequiredService<UserManager<CustomIdentityUser>>();
 069        var admin = await userManager.FindByEmailAsync(_options.SeedAdminEmail);
 070        if (admin is null)
 71        {
 072            admin = new CustomIdentityUser
 073            {
 074                UserName = _options.SeedAdminEmail,
 075                Email = _options.SeedAdminEmail,
 076                EmailConfirmed = true
 077            };
 078            var createResult = await userManager.CreateAsync(admin, _options.SeedAdminPassword);
 079            if (!createResult.Succeeded)
 80            {
 081                _logger.LogError("Не удалось создать администратора: {Errors}",
 082                    string.Join("; ", createResult.Errors.Select(e => e.Description)));
 83            }
 84            else
 85            {
 086                _logger.LogInformation("Создан администратор {Email}", _options.SeedAdminEmail);
 87            }
 88        }
 89
 090        if (admin is not null)
 91        {
 092            var link = await db.UserAppRoles.FirstOrDefaultAsync(u => u.UserId == admin.Id && u.AppRoleId == superAdmin.
 093            if (link is null)
 94            {
 095                db.UserAppRoles.Add(new UserAppRole
 096                {
 097                    UserId = admin.Id,
 098                    AppRoleId = superAdmin.Id,
 099                    AssignedBy = "system"
 0100                });
 0101                await db.SaveChangesAsync(ct);
 102            }
 103        }
 104
 105        // Автоматическая миграция: все пользователи с Identity-ролью Admin получают AppRole SuperAdmin.
 106        try
 107        {
 0108            var identityAdmins = await userManager.GetUsersInRoleAsync("Admin");
 0109            foreach (var u in identityAdmins)
 110            {
 0111                var exists = await db.UserAppRoles.AnyAsync(x => x.UserId == u.Id && x.AppRoleId == superAdmin.Id, ct);
 0112                if (!exists)
 113                {
 0114                    db.UserAppRoles.Add(new UserAppRole
 0115                    {
 0116                        UserId = u.Id,
 0117                        AppRoleId = superAdmin.Id,
 0118                        AssignedBy = "system-migration"
 0119                    });
 120                }
 0121            }
 0122            await db.SaveChangesAsync(ct);
 0123        }
 0124        catch (Exception ex)
 125        {
 0126            _logger.LogWarning(ex, "Не удалось смигрировать Identity Admin → SuperAdmin AppRole");
 0127        }
 128
 129        // Авто-синхронизация страниц
 130        try
 131        {
 0132            var pageDiscovery = scope.ServiceProvider.GetRequiredService<IPageDiscoveryService>();
 0133            var added = await pageDiscovery.SyncAllAsync(ct);
 0134            if (added > 0) _logger.LogInformation("Авто-зарегистрировано страниц: {Added}", added);
 0135            var fromViews = await pageDiscovery.RefreshDisplayNamesFromViewsAsync(ct);
 0136            if (fromViews > 0) _logger.LogInformation("Заголовки страниц (DisplayName) обновлены из ViewData в .cshtml: 
 0137            var cache = scope.ServiceProvider.GetService<IMemoryCache>();
 0138            var h = await Studio2AppPageHierarchySync.EnsureAsync(db, cache, ct);
 0139            if (h > 0) _logger.LogInformation("Обновлена иерархия/оформление страниц Studio2 (транзакции, заказы): {H}",
 0140        }
 0141        catch (Exception ex)
 142        {
 0143            _logger.LogWarning(ex, "Не удалось выполнить авто-обнаружение страниц");
 0144        }
 145
 146        // Авто-синхронизация полей
 147        try
 148        {
 0149            var fieldDiscovery = scope.ServiceProvider.GetRequiredService<IFieldDiscoveryService>();
 0150            var added = await fieldDiscovery.SyncAllAsync(ct);
 0151            if (added > 0) _logger.LogInformation("Авто-зарегистрировано полей: {Added}", added);
 0152        }
 0153        catch (Exception ex)
 154        {
 0155            _logger.LogWarning(ex, "Не удалось выполнить авто-обнаружение полей");
 0156        }
 157
 158        // Реестр колонок списка заказов (как на главной / транзакциях)
 159        try
 160        {
 0161            var n = await OrderListFieldSync.EnsureInDatabaseAsync(db, ct);
 0162            if (n > 0) _logger.LogInformation("Синхронизировано колонок OrderList: {N}", n);
 0163        }
 0164        catch (Exception ex)
 165        {
 0166            _logger.LogWarning(ex, "Не удалось синхронизировать колонки OrderList");
 0167        }
 168
 169        // Права по умолчанию: роль User — заказы, транзакции/проведения и связанные экраны Studio2
 170        try
 171        {
 0172            await EnsureUserRoleBusinessPagePermissionsAsync(db, scope.ServiceProvider, ct);
 0173        }
 0174        catch (Exception ex)
 175        {
 0176            _logger.LogWarning(ex, "Не удалось выдать роли User права на страницы Studio2 (заказы/транзакции)");
 0177        }
 0178    }
 179
 180    /// <summary>
 181    /// Страницы области Studio2, относящиеся к заказам, транзакциям (проведениям) и справочникам вокруг них.
 182    /// Исключаем только настройки доступа к «старым» наборам в подпапке Admin.
 183    /// </summary>
 184    private static bool IsBusinessStudio2Page(AppPage p)
 185    {
 0186        if (!string.Equals(p.Area, "Studio2", StringComparison.OrdinalIgnoreCase))
 0187            return false;
 0188        var c = p.Controller ?? string.Empty;
 0189        if (c.Equals("AccessProfiles", StringComparison.OrdinalIgnoreCase) ||
 0190            c.Equals("DataScopeSets", StringComparison.OrdinalIgnoreCase))
 0191            return false;
 0192        if (c.Contains("Order", StringComparison.OrdinalIgnoreCase))
 0193            return true;
 0194        if (c.Contains("Transaction", StringComparison.OrdinalIgnoreCase))
 0195            return true;
 0196        if (c.Equals("Cart", StringComparison.OrdinalIgnoreCase) ||
 0197            c.Equals("Percentage", StringComparison.OrdinalIgnoreCase) ||
 0198            c.Equals("Profile", StringComparison.OrdinalIgnoreCase) ||
 0199            c.Equals("TransactionFiles", StringComparison.OrdinalIgnoreCase) ||
 0200            c.Equals("TransactionTimeTrackingReport", StringComparison.OrdinalIgnoreCase))
 0201            return true;
 202        // Проценка API-поставщиков на странице транзакции (ArmtekPrice, ArmtekByPrice, AvdPrice, BergPrice, MotexPrice,
 0203        if (c.EndsWith("Price", StringComparison.OrdinalIgnoreCase))
 0204            return true;
 0205        return false;
 206    }
 207
 208    private async Task EnsureUserRoleBusinessPagePermissionsAsync(
 209        ApplicationDbContext db, IServiceProvider scope, CancellationToken ct)
 210    {
 0211        var userRole = await db.AppRoles.AsNoTracking()
 0212            .FirstOrDefaultAsync(r => r.Name == AuthorizationConstants.DefaultUserRoleName, ct);
 0213        if (userRole is null) return;
 214
 0215        var pages = await db.AppPages.AsNoTracking()
 0216            .Where(p => p.IsActive)
 0217            .ToListAsync(ct);
 0218        var targetPages = pages.Where(IsBusinessStudio2Page).ToList();
 0219        if (targetPages.Count == 0) return;
 220
 0221        var pageIds = targetPages.Select(p => p.Id).ToList();
 0222        var have = await db.Set<RolePagePermission>()
 0223            .Where(rp => rp.AppRoleId == userRole.Id && pageIds.Contains(rp.AppPageId))
 0224            .Select(rp => rp.AppPageId)
 0225            .ToListAsync(ct);
 0226        var haveSet = have.ToHashSet();
 227
 0228        var added = 0;
 0229        foreach (var p in targetPages)
 230        {
 0231            if (haveSet.Contains(p.Id)) continue;
 0232            db.Add(new RolePagePermission
 0233            {
 0234                AppRoleId = userRole.Id,
 0235                AppPageId = p.Id,
 0236                CanView = true,
 0237                CanCreate = true,
 0238                CanEdit = true,
 0239                CanDelete = true,
 0240                CanExport = true,
 0241            });
 0242            added++;
 243        }
 244
 0245        if (added <= 0) return;
 0246        await db.SaveChangesAsync(ct);
 0247        var perm = scope.GetRequiredService<IPermissionService>();
 0248        perm.InvalidateAll();
 0249        _logger.LogInformation("Роль User: выданы права на {Count} новых страниц (Studio2: заказы/транзакции/связанное).
 0250    }
 251}