264 lines
10 KiB
C#
264 lines
10 KiB
C#
using line_gestao_api.Models;
|
|
using line_gestao_api.Services;
|
|
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace line_gestao_api.Data;
|
|
|
|
public class SeedOptions
|
|
{
|
|
public bool Enabled { get; set; } = true;
|
|
public string AdminMasterName { get; set; } = "System Admin";
|
|
public string? AdminMasterEmail { get; set; } = "sysadmin@linegestao.local";
|
|
public string? AdminMasterPassword { get; set; } = "DevSysAdmin123!";
|
|
public bool ReapplyAdminCredentialsOnStartup { get; set; } = false;
|
|
}
|
|
|
|
public static class SeedData
|
|
{
|
|
public static async Task EnsureSeedDataAsync(IServiceProvider services)
|
|
{
|
|
using var scope = services.CreateScope();
|
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
|
|
var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole<Guid>>>();
|
|
var tenantProvider = scope.ServiceProvider.GetRequiredService<ITenantProvider>();
|
|
var options = scope.ServiceProvider.GetRequiredService<IOptions<SeedOptions>>().Value;
|
|
|
|
if (db.Database.IsRelational())
|
|
{
|
|
await db.Database.MigrateAsync();
|
|
}
|
|
else
|
|
{
|
|
await db.Database.EnsureCreatedAsync();
|
|
}
|
|
|
|
// Mantem o contrato de roles atualizado em todos os ambientes, inclusive
|
|
// quando o seed de usuario master estiver desabilitado.
|
|
var roles = AppRoles.All;
|
|
foreach (var role in roles)
|
|
{
|
|
if (!await roleManager.RoleExistsAsync(role))
|
|
{
|
|
var roleResult = await roleManager.CreateAsync(new IdentityRole<Guid>(role));
|
|
EnsureIdentitySucceeded(roleResult, $"Falha ao criar role '{role}'.");
|
|
}
|
|
}
|
|
|
|
await MigrateLegacyRolesAsync(db, roleManager);
|
|
|
|
if (!options.Enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var systemTenantId = SystemTenantConstants.SystemTenantId;
|
|
var systemTenant = await db.Tenants.FirstOrDefaultAsync(t => t.Id == systemTenantId);
|
|
if (systemTenant == null)
|
|
{
|
|
systemTenant = new Tenant
|
|
{
|
|
Id = systemTenantId,
|
|
NomeOficial = SystemTenantConstants.SystemTenantNomeOficial,
|
|
IsSystem = true,
|
|
Ativo = true,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
db.Tenants.Add(systemTenant);
|
|
}
|
|
else
|
|
{
|
|
systemTenant.NomeOficial = SystemTenantConstants.SystemTenantNomeOficial;
|
|
systemTenant.IsSystem = true;
|
|
systemTenant.Ativo = true;
|
|
}
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var emailFromEnv = Environment.GetEnvironmentVariable("SYSADMIN_EMAIL")
|
|
?? Environment.GetEnvironmentVariable("ADMIN_MASTER_EMAIL");
|
|
var passwordFromEnv = Environment.GetEnvironmentVariable("SYSADMIN_PASSWORD")
|
|
?? Environment.GetEnvironmentVariable("ADMIN_MASTER_PASSWORD");
|
|
|
|
var adminMasterEmail = (emailFromEnv ?? options.AdminMasterEmail ?? string.Empty).Trim().ToLowerInvariant();
|
|
var adminMasterPassword = passwordFromEnv ?? options.AdminMasterPassword ?? string.Empty;
|
|
|
|
if (string.IsNullOrWhiteSpace(adminMasterEmail) || string.IsNullOrWhiteSpace(adminMasterPassword))
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Credenciais do sysadmin ausentes. Defina SYSADMIN_EMAIL e SYSADMIN_PASSWORD (ou Seed:AdminMasterEmail/Seed:AdminMasterPassword).");
|
|
}
|
|
|
|
var normalizedEmail = userManager.NormalizeEmail(adminMasterEmail);
|
|
|
|
var previousTenant = tenantProvider.TenantId;
|
|
tenantProvider.SetTenantId(systemTenantId);
|
|
|
|
try
|
|
{
|
|
var existingAdminMaster = await userManager.Users
|
|
.IgnoreQueryFilters()
|
|
.FirstOrDefaultAsync(u => u.TenantId == systemTenantId && u.NormalizedEmail == normalizedEmail);
|
|
|
|
if (existingAdminMaster == null)
|
|
{
|
|
var adminMaster = new ApplicationUser
|
|
{
|
|
Name = options.AdminMasterName,
|
|
Email = adminMasterEmail,
|
|
UserName = adminMasterEmail,
|
|
TenantId = systemTenantId,
|
|
EmailConfirmed = true,
|
|
IsActive = true,
|
|
LockoutEnabled = true
|
|
};
|
|
|
|
var createResult = await userManager.CreateAsync(adminMaster, adminMasterPassword);
|
|
EnsureIdentitySucceeded(createResult, "Falha ao criar usuário sysadmin.");
|
|
|
|
var addRoleResult = await userManager.AddToRoleAsync(adminMaster, SystemTenantConstants.SystemRole);
|
|
EnsureIdentitySucceeded(addRoleResult, "Falha ao associar role sysadmin ao usuário inicial.");
|
|
}
|
|
else
|
|
{
|
|
existingAdminMaster.Name = options.AdminMasterName;
|
|
existingAdminMaster.Email = adminMasterEmail;
|
|
existingAdminMaster.UserName = adminMasterEmail;
|
|
existingAdminMaster.EmailConfirmed = true;
|
|
existingAdminMaster.IsActive = true;
|
|
existingAdminMaster.LockoutEnabled = true;
|
|
|
|
var updateResult = await userManager.UpdateAsync(existingAdminMaster);
|
|
EnsureIdentitySucceeded(updateResult, "Falha ao atualizar usuário sysadmin.");
|
|
|
|
if (options.ReapplyAdminCredentialsOnStartup)
|
|
{
|
|
await userManager.SetLockoutEndDateAsync(existingAdminMaster, null);
|
|
await userManager.ResetAccessFailedCountAsync(existingAdminMaster);
|
|
|
|
var resetToken = await userManager.GeneratePasswordResetTokenAsync(existingAdminMaster);
|
|
var resetPasswordResult = await userManager.ResetPasswordAsync(existingAdminMaster, resetToken, adminMasterPassword);
|
|
if (!resetPasswordResult.Succeeded)
|
|
{
|
|
var removePasswordResult = await userManager.RemovePasswordAsync(existingAdminMaster);
|
|
if (removePasswordResult.Succeeded)
|
|
{
|
|
var addPasswordResult = await userManager.AddPasswordAsync(existingAdminMaster, adminMasterPassword);
|
|
EnsureIdentitySucceeded(addPasswordResult, "Falha ao definir senha do sysadmin.");
|
|
}
|
|
else
|
|
{
|
|
var addPasswordResult = await userManager.AddPasswordAsync(existingAdminMaster, adminMasterPassword);
|
|
EnsureIdentitySucceeded(addPasswordResult, "Falha ao definir senha do sysadmin.");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!await userManager.IsInRoleAsync(existingAdminMaster, SystemTenantConstants.SystemRole))
|
|
{
|
|
var addRoleResult = await userManager.AddToRoleAsync(existingAdminMaster, SystemTenantConstants.SystemRole);
|
|
EnsureIdentitySucceeded(addRoleResult, "Falha ao associar role sysadmin ao usuário inicial.");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
tenantProvider.SetTenantId(previousTenant);
|
|
}
|
|
}
|
|
|
|
private static void EnsureIdentitySucceeded(IdentityResult result, string message)
|
|
{
|
|
if (result.Succeeded)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var details = string.Join("; ", result.Errors.Select(e => e.Description));
|
|
throw new InvalidOperationException($"{message} Detalhes: {details}");
|
|
}
|
|
|
|
private static async Task MigrateLegacyRolesAsync(AppDbContext db, RoleManager<IdentityRole<Guid>> roleManager)
|
|
{
|
|
await MigrateLegacyRoleAsync(db, roleManager, "admin_master", AppRoles.SysAdmin);
|
|
await MigrateLegacyRoleAsync(db, roleManager, "admin", AppRoles.SysAdmin);
|
|
await MigrateLegacyRoleAsync(db, roleManager, "leitura", AppRoles.Cliente);
|
|
await MigrateLegacyRoleAsync(db, roleManager, "operador", AppRoles.Cliente);
|
|
}
|
|
|
|
private static async Task MigrateLegacyRoleAsync(
|
|
AppDbContext db,
|
|
RoleManager<IdentityRole<Guid>> roleManager,
|
|
string legacyRole,
|
|
string newRole)
|
|
{
|
|
var legacyRoleId = await roleManager.Roles
|
|
.Where(r => r.Name == legacyRole)
|
|
.Select(r => (Guid?)r.Id)
|
|
.FirstOrDefaultAsync();
|
|
if (!legacyRoleId.HasValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var newRoleId = await roleManager.Roles
|
|
.Where(r => r.Name == newRole)
|
|
.Select(r => (Guid?)r.Id)
|
|
.FirstOrDefaultAsync();
|
|
if (!newRoleId.HasValue)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var legacyUserIds = await db.UserRoles
|
|
.Where(ur => ur.RoleId == legacyRoleId.Value)
|
|
.Select(ur => ur.UserId)
|
|
.Distinct()
|
|
.ToListAsync();
|
|
if (legacyUserIds.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var alreadyInNewRole = await db.UserRoles
|
|
.Where(ur => ur.RoleId == newRoleId.Value && legacyUserIds.Contains(ur.UserId))
|
|
.Select(ur => ur.UserId)
|
|
.ToListAsync();
|
|
var existingSet = alreadyInNewRole.ToHashSet();
|
|
|
|
foreach (var userId in legacyUserIds)
|
|
{
|
|
if (!existingSet.Contains(userId))
|
|
{
|
|
db.UserRoles.Add(new IdentityUserRole<Guid>
|
|
{
|
|
UserId = userId,
|
|
RoleId = newRoleId.Value
|
|
});
|
|
}
|
|
}
|
|
|
|
var legacyAssignments = await db.UserRoles
|
|
.Where(ur => ur.RoleId == legacyRoleId.Value)
|
|
.ToListAsync();
|
|
if (legacyAssignments.Count > 0)
|
|
{
|
|
db.UserRoles.RemoveRange(legacyAssignments);
|
|
}
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
var legacyRoleStillUsed = await db.UserRoles.AnyAsync(ur => ur.RoleId == legacyRoleId.Value);
|
|
if (!legacyRoleStillUsed)
|
|
{
|
|
var legacyRoleEntity = await roleManager.Roles.FirstOrDefaultAsync(r => r.Id == legacyRoleId.Value);
|
|
if (legacyRoleEntity != null)
|
|
{
|
|
await roleManager.DeleteAsync(legacyRoleEntity);
|
|
}
|
|
}
|
|
}
|
|
}
|