Sube capa Infrastructure, Solution y archivos de configuracion del Backend

This commit is contained in:
FrancoRu
2026-09-18 13:36:47 -03:00
parent 2eb4eeef03
commit 1ef9a0bd03
205 changed files with 77651 additions and 0 deletions
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Identity;
using System;
namespace Infrastructure.Identity;
/// <summary>
/// Identity user for authentication, kept separate from domain entities.
/// </summary>
public class ApplicationUser : IdentityUser<Guid>
{
/// <summary>
/// Opaque refresh token stored server-side.
/// </summary>
public string? RefreshToken { get; set; }
/// <summary>
/// UTC expiry for the stored refresh token.
/// </summary>
public DateTime? RefreshTokenExpiryTime { get; set; }
/// <summary>
/// The Id of the OWNER who registered this account, when it was created by an owner rather than an admin.
/// </summary>
public Guid? CreatedByOwnerId { get; set; }
public DateTime DateCreated { get; set; } = DateTime.UtcNow;
public bool MustChangePassword { get; set; }
}
@@ -0,0 +1,49 @@
using Domain.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using System;
namespace Infrastructure.Identity;
/// <summary>
/// EF Core context for ASP.NET Core Identity tables, generating AspNetUsers, AspNetRoles, and related tables.
/// </summary>
public class IdentityAppDbContext(DbContextOptions<IdentityAppDbContext> options)
: IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>(options)
{
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>()
.HasIndex(u => u.NormalizedEmail)
.IsUnique();
SeedRoles(builder);
}
private static void SeedRoles(ModelBuilder builder)
{
// Seed values 3 and 4 are intentionally skipped because the former TournamentManager and TeamManager roles were removed; Guest keeps its original seed value 5 so its role Id stays stable.
builder.Entity<IdentityRole<Guid>>().HasData(
CreateRole(1, UserRoleType.ADMIN),
CreateRole(2, UserRoleType.OWNER),
CreateRole(5, UserRoleType.GUEST)
);
}
private static IdentityRole<Guid> CreateRole(int seed, UserRoleType roleType)
{
string name = roleType.ToRoleName();
return new IdentityRole<Guid>
{
Id = new Guid($"00000000-0000-0000-0000-{seed:D12}"),
Name = name,
NormalizedName = name.ToUpperInvariant(),
ConcurrencyStamp = seed.ToString()
};
}
}
@@ -0,0 +1,78 @@
using Application.Utils.Constants.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Linq;
namespace Infrastructure.Identity;
/// <summary>
/// Provides a design-time instance of IdentityAppDbContext for EF Core CLI tools.
/// </summary>
public sealed class IdentityAppDbContextFactory : IDesignTimeDbContextFactory<IdentityAppDbContext>
{
/// <summary>
/// Builds configuration the same way WebApplication.CreateBuilder does at runtime, with later appsettings files overriding earlier ones.
/// </summary>
public IdentityAppDbContext CreateDbContext(string[] args)
{
string basePath = ResolveBasePath();
string aspNetEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")
?? "Development";
ConfigurationBuilder builder = new();
builder.SetBasePath(basePath);
builder.AddJsonFile("appsettings.json", optional: true);
builder.AddJsonFile($"appsettings.{aspNetEnv}.json", optional: true);
if (Directory.Exists(basePath))
{
string?[] extras = Directory
.GetFiles(basePath, "appsettings.*.json")
.Select(Path.GetFileName)
.Where(f => !string.Equals(
f, $"appsettings.{aspNetEnv}.json",
StringComparison.OrdinalIgnoreCase))
.ToArray();
foreach (string? file in extras)
{
builder.AddJsonFile(file!, optional: true);
}
}
IConfigurationRoot config = builder.Build();
string connectionString = config.GetConnectionString(ConfigurationKeys.DbConnection)
?? throw new InvalidOperationException(
$"Connection string '{ConfigurationKeys.DbConnection}' not found.\n" +
$"Searched in: {basePath}\n" +
$"Files loaded: appsettings.json, appsettings.{aspNetEnv}.json + all appsettings.*.json");
DbContextOptionsBuilder<IdentityAppDbContext> optionsBuilder = new();
optionsBuilder.UseNpgsql(connectionString);
return new IdentityAppDbContext(optionsBuilder.Options);
}
private static string ResolveBasePath()
{
string current = Directory.GetCurrentDirectory();
string[] candidates =
[
current,
Path.Combine(current, "API"),
Path.Combine(current, "..", "API"),
];
string? match = Array.Find(candidates, path => File.Exists(Path.Combine(path, "appsettings.json")));
return match is null ? current : Path.GetFullPath(match);
}
}
@@ -0,0 +1,401 @@
using Application.DTOs.Auth.Request;
using Application.DTOs.Auth.Response;
using Application.Interfaces.Services;
using Application.Utils.Constants;
using Application.Utils.Constants.Auth;
using Application.Utils.Constants.Configuration;
using Domain.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace Infrastructure.Identity;
/// <summary>
/// Infrastructure implementation of IAuthenticationService backed by ASP.NET Core Identity.
/// </summary>
public sealed class IdentityAuthenticationService(
UserManager<ApplicationUser> userManager,
IAuthService authService,
IEmailService emailService,
IConfiguration configuration) : IAuthenticationService
{
private const string MagicLinkPurpose = "MagicLink";
private const int RefreshExpiryDays = 7;
private static readonly Dictionary<string, HashSet<string>> _creationPolicy =
new(StringComparer.OrdinalIgnoreCase)
{
[UserRoleType.ADMIN.ToRoleName()] = new(StringComparer.OrdinalIgnoreCase)
{
UserRoleType.ADMIN.ToRoleName(),
UserRoleType.OWNER.ToRoleName()
},
[UserRoleType.OWNER.ToRoleName()] = new(StringComparer.OrdinalIgnoreCase)
{
UserRoleType.ADMIN.ToRoleName(),
UserRoleType.OWNER.ToRoleName()
}
};
/// <inheritdoc/>
public async Task<RegisterUserResponse> RegisterAsync(
RegisterUserRequest request, string callerRole, Guid callerId, CancellationToken ct = default)
{
EnforceCreationPolicy(callerRole, request.Role);
ApplicationUser? existing = await userManager.FindByEmailAsync(request.Email);
if (existing is not null)
{
throw new InvalidOperationException(ErrorMessages.Auth.EmailAlreadyExists);
}
ApplicationUser user = new()
{
UserName = request.Username,
Email = request.Email,
EmailConfirmed = true,
PhoneNumber = request.Phone,
CreatedByOwnerId = IsOwner(callerRole) ? callerId : null,
MustChangePassword = true
};
string temporaryPassword = GenerateTemporaryPassword();
IdentityResult result = await userManager.CreateAsync(user, temporaryPassword);
if (!result.Succeeded)
{
string errors = string.Join(" | ", result.Errors.Select(e => e.Description));
throw new InvalidOperationException(ErrorMessages.Auth.UserCreationFailed(errors));
}
await userManager.AddToRoleAsync(user, request.Role.ToUpperInvariant());
string token = await userManager.GeneratePasswordResetTokenAsync(user);
string frontendUrl = configuration[ConfigurationKeys.Frontend.PasswordResetUrl]
?? throw new InvalidOperationException(
ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl));
string setPasswordLink =
$"{frontendUrl}" +
$"?email={Uri.EscapeDataString(user.Email!)}" +
$"&token={Uri.EscapeDataString(token)}";
await emailService.SendWelcomeSetPasswordAsync(user.Email!, user.UserName!, setPasswordLink, ct);
return new RegisterUserResponse(
user.Id, user.Email!, user.UserName!, request.Role.ToUpperInvariant(), user.PhoneNumber);
}
/// <inheritdoc/>
public async Task<InviteUserResponse> InviteUserAsync(
InviteUserRequest request, string callerRole, Guid callerId, CancellationToken ct = default)
{
EnforceCreationPolicy(callerRole, request.Role);
ApplicationUser? existing = await userManager.FindByEmailAsync(request.Email);
if (existing is not null)
{
throw new InvalidOperationException(ErrorMessages.Auth.EmailAlreadyExists);
}
// No password is set for an invited account; UserName mirrors the email since Identity requires a unique UserName, and MustChangePassword keeps the account gated until activation completes.
ApplicationUser user = new()
{
UserName = request.Email,
Email = request.Email,
EmailConfirmed = true,
PhoneNumber = request.Phone,
CreatedByOwnerId = IsOwner(callerRole) ? callerId : null,
MustChangePassword = true
};
IdentityResult result = await userManager.CreateAsync(user);
if (!result.Succeeded)
{
string errors = string.Join(" | ", result.Errors.Select(e => e.Description));
throw new InvalidOperationException(ErrorMessages.Auth.UserCreationFailed(errors));
}
await userManager.AddToRoleAsync(user, request.Role.ToUpperInvariant());
// The activation token is a standard Identity password-reset token, which works on a passwordless account because it derives from the SecurityStamp rather than an existing password, and it is later verified by ActivateAccountAsync's ResetPasswordAsync call.
string token = await userManager.GeneratePasswordResetTokenAsync(user);
string activationLink = BuildTokenLink(user.Email!, token, activation: true);
await emailService.SendWelcomeSetPasswordAsync(user.Email!, user.UserName!, activationLink, ct);
return new InviteUserResponse(user.Id, user.Email!, request.Role.ToUpperInvariant());
}
/// <inheritdoc/>
public async Task<TokenResponse> ActivateAccountAsync(
ActivateAccountRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.FindByEmailAsync(request.Email)
?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidPasswordResetRequest);
// ResetPasswordAsync sets the password hash whether or not one already existed, so it doubles as the first password set for an invited, passwordless account.
IdentityResult result = await userManager.ResetPasswordAsync(
user, request.Token, request.NewPassword);
if (!result.Succeeded)
{
throw new InvalidOperationException(
string.Join("; ", result.Errors.Select(e => e.Description)));
}
user.MustChangePassword = false;
await userManager.UpdateAsync(user);
IList<string> roles = await userManager.GetRolesAsync(user);
return await BuildTokenResponseAsync(user, roles, ct);
}
/// <inheritdoc/>
public async Task RequestPasswordResetAsync(
RequestPasswordResetRequest request, CancellationToken ct = default)
{
ApplicationUser? user = await userManager.FindByEmailAsync(request.Email);
// Silently no-op for an unknown email so the endpoint never reveals which addresses have accounts.
if (user is null)
{
return;
}
string token = await userManager.GeneratePasswordResetTokenAsync(user);
string resetLink = BuildTokenLink(user.Email!, token, activation: false);
await emailService.SendPasswordResetAsync(user.Email!, user.UserName!, resetLink, ct);
}
/// <inheritdoc/>
public async Task<TokenResponse> LoginAsync(LogInUserRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.FindByEmailAsync(request.Email)
?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidCredentials);
if (!await userManager.CheckPasswordAsync(user, request.Password))
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidCredentials);
}
if (await userManager.IsLockedOutAsync(user))
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.AccountDeactivated);
}
// The role model is now just the two password-based operator accounts; the former TeamManager-only magic-link gate was removed along with that role.
IList<string> roles = await userManager.GetRolesAsync(user);
return await BuildTokenResponseAsync(user, roles, ct);
}
/// <inheritdoc/>
public async Task<MagicLinkResponse> RequestMagicLinkAsync(
MagicLinkRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.FindByEmailAsync(request.Email)
?? throw new KeyNotFoundException(ErrorMessages.Auth.NoAccountForEmail);
// Magic-link was the TeamManager-only auth path; that role no longer exists and the flow is deferred, so the role gate was dropped but the token flow itself is left in place for future use.
string token = await userManager.GenerateUserTokenAsync(
user, TokenOptions.DefaultEmailProvider, MagicLinkPurpose);
string magicLink =
$"/api/auth/magic-link/login" +
$"?email={Uri.EscapeDataString(user.Email!)}" +
$"&token={Uri.EscapeDataString(token)}";
return new MagicLinkResponse("Magic link generated. Check your email.", magicLink);
}
/// <inheritdoc/>
public async Task<TokenResponse> MagicLinkLoginAsync(
MagicLinkLoginRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.FindByEmailAsync(request.Email)
?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidMagicLink);
bool valid = await userManager.VerifyUserTokenAsync(
user, TokenOptions.DefaultEmailProvider, MagicLinkPurpose, request.Token);
if (!valid)
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.MagicLinkAlreadyUsed);
}
if (await userManager.IsLockedOutAsync(user))
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.AccountDeactivated);
}
IList<string> roles = await userManager.GetRolesAsync(user);
return await BuildTokenResponseAsync(user, roles, ct);
}
/// <inheritdoc/>
public async Task<TokenResponse> ConfirmPasswordResetAsync(
PasswordResetConfirmRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.FindByEmailAsync(request.Email)
?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidPasswordResetRequest);
IdentityResult result = await userManager.ResetPasswordAsync(
user, request.Token, request.NewPassword);
if (!result.Succeeded)
{
throw new InvalidOperationException(
string.Join("; ", result.Errors.Select(e => e.Description)));
}
user.MustChangePassword = false;
await userManager.UpdateAsync(user);
IList<string> roles = await userManager.GetRolesAsync(user);
return await BuildTokenResponseAsync(user, roles, ct);
}
/// <inheritdoc/>
public async Task<TokenResponse> GuestAsync(CancellationToken ct = default)
{
Claim[] claims = [new(ClaimTypes.Role, UserRoleType.GUEST.ToRoleName())];
TokenResponse response = await authService.GenerateJwtTokenAsync(claims, ct);
return new TokenResponse(response.AccessToken, response.ExpiresIn, refreshToken: null);
}
/// <inheritdoc/>
public async Task<TokenResponse> RefreshAsync(
RefreshTokenRequest request, CancellationToken ct = default)
{
ApplicationUser user = await userManager.Users
.FirstOrDefaultAsync(u => u.RefreshToken == request.RefreshToken, ct)
?? throw new UnauthorizedAccessException(ErrorMessages.Auth.InvalidRefreshToken);
if (user.RefreshTokenExpiryTime <= DateTime.UtcNow)
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.RefreshTokenExpired);
}
IList<string> roles = await userManager.GetRolesAsync(user);
return await BuildTokenResponseAsync(user, roles, ct);
}
/// <inheritdoc/>
public async Task LogoutAsync(Guid userId, CancellationToken ct = default)
{
ApplicationUser? user = await userManager.FindByIdAsync(userId.ToString());
if (user is not null)
{
user.RefreshToken = null;
user.RefreshTokenExpiryTime = null;
await userManager.UpdateAsync(user);
}
}
private async Task<TokenResponse> BuildTokenResponseAsync(
ApplicationUser user, IList<string> roles, CancellationToken ct)
{
List<Claim> claims =
[
new(ClaimTypes.NameIdentifier, user.Id.ToString()),
new(ClaimTypes.Email, user.Email!),
.. roles.Select(r => new Claim(ClaimTypes.Role, r))
];
if (user.MustChangePassword)
{
claims.Add(new Claim(CustomClaimTypes.MustChangePassword, "true"));
}
TokenResponse response = await authService.GenerateJwtTokenAsync(claims, ct);
user.RefreshToken = response.RefreshToken;
user.RefreshTokenExpiryTime = DateTime.UtcNow.AddDays(RefreshExpiryDays);
await userManager.UpdateAsync(user);
return response;
}
/// <summary>
/// Builds an email link that carries an Identity token, using ActivationUrl for activation links and PasswordResetUrl otherwise.
/// </summary>
private string BuildTokenLink(string email, string token, bool activation)
{
string baseUrl = activation
? configuration[ConfigurationKeys.Frontend.ActivationUrl]
?? configuration[ConfigurationKeys.Frontend.PasswordResetUrl]
?? throw new InvalidOperationException(
ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl))
: configuration[ConfigurationKeys.Frontend.PasswordResetUrl]
?? throw new InvalidOperationException(
ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl));
return $"{baseUrl}" +
$"?email={Uri.EscapeDataString(email)}" +
$"&token={Uri.EscapeDataString(token)}";
}
private static string GenerateTemporaryPassword(int length = 16)
{
if (length < 8)
{
length = 8;
}
const string upper = "ABCDEFGHJKLMNPQRSTUVWXYZ";
const string lower = "abcdefghijkmnopqrstuvwxyz";
const string digits = "23456789";
const string special = "!@#$%^&*_-+=?";
string all = upper + lower + digits + special;
char[] chars = new char[length];
chars[0] = upper[RandomNumberGenerator.GetInt32(upper.Length)];
chars[1] = lower[RandomNumberGenerator.GetInt32(lower.Length)];
chars[2] = digits[RandomNumberGenerator.GetInt32(digits.Length)];
chars[3] = special[RandomNumberGenerator.GetInt32(special.Length)];
for (int i = 4; i < chars.Length; i++)
{
chars[i] = all[RandomNumberGenerator.GetInt32(all.Length)];
}
for (int i = chars.Length - 1; i > 0; i--)
{
int j = RandomNumberGenerator.GetInt32(i + 1);
(chars[i], chars[j]) = (chars[j], chars[i]);
}
return new string(chars);
}
private static void EnforceCreationPolicy(string callerRole, string targetRole)
{
bool allowed = _creationPolicy.TryGetValue(callerRole, out HashSet<string>? permitted)
&& permitted.Contains(targetRole);
if (!allowed)
{
throw new UnauthorizedAccessException(ErrorMessages.Auth.RoleNotAllowedToCreate(callerRole, targetRole));
}
}
private static bool IsOwner(string role)
{
return role.Equals(UserRoleType.OWNER.ToRoleName(), StringComparison.OrdinalIgnoreCase);
}
}
@@ -0,0 +1,65 @@
using Application.Utils.Constants.Configuration;
using Domain.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;
namespace Infrastructure.Identity;
/// <summary>
/// Seeds the initial admin user into the Identity database at startup.
/// </summary>
public sealed class IdentitySeeder(
UserManager<ApplicationUser> userManager,
IConfiguration configuration,
ILogger<IdentitySeeder> logger)
{
public async Task SeedAsync()
{
await SeedAdminAsync();
}
private async Task SeedAdminAsync()
{
string? email = configuration[ConfigurationKeys.AdminUser.Email];
string? password = configuration[ConfigurationKeys.AdminUser.Password];
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
{
logger.LogWarning(
"AdminUser:Email or AdminUser:Password not configured — skipping admin seeding.");
return;
}
if (await userManager.FindByEmailAsync(email) is not null)
{
logger.LogInformation("Admin user '{Email}' already exists — skipping.", email);
return;
}
ApplicationUser admin = new()
{
UserName = email,
Email = email,
EmailConfirmed = true,
};
IdentityResult result = await userManager.CreateAsync(admin, password);
if (!result.Succeeded)
{
string errors = string.Join(", ", result.Errors.Select(e => e.Description));
logger.LogError("Failed to create admin user '{Email}': {Errors}", email, errors);
return;
}
await userManager.AddToRoleAsync(admin, UserRoleType.ADMIN.ToRoleName());
logger.LogInformation("Admin user '{Email}' created and assigned role ADMIN.", email);
}
}
@@ -0,0 +1,397 @@
using Application.DTOs.Abstract.Request;
using Application.DTOs.Abstract.Response;
using Application.DTOs.User.Request;
using Application.DTOs.User.Response;
using Application.Interfaces.Services;
using Application.Utils.Constants;
using Application.Utils.Constants.Configuration;
using Domain.Enums;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Infrastructure.Identity;
/// <summary>
/// Identity-backed implementation of IUserManagementService that enforces all access-control rules, keeping controllers thin.
/// </summary>
public sealed class IdentityUserManagementService(
UserManager<ApplicationUser> userManager,
IdentityAppDbContext identityDbContext,
IEmailService emailService,
IConfiguration configuration) : IUserManagementService
{
/// <inheritdoc/>
public async Task<PaginatedResponse<UserResponse>> GetAllAsync(
string callerRole, Guid callerId,
UserFilteredRequest filter,
CancellationToken ct = default)
{
IQueryable<ApplicationUser> query = ResolveVisibleUsersQuery(callerRole);
if (!string.IsNullOrWhiteSpace(filter.UserName))
{
query = query.Where(u => u.UserName != null &&
u.UserName.ToLower().Contains(filter.UserName.ToLower()));
}
if (!string.IsNullOrWhiteSpace(filter.Email))
{
query = query.Where(u => u.Email != null &&
u.Email.ToLower().Contains(filter.Email.ToLower()));
}
if (!string.IsNullOrWhiteSpace(filter.PhoneNumber))
{
query = query.Where(u => u.PhoneNumber != null &&
u.PhoneNumber.Contains(filter.PhoneNumber));
}
if (filter.Role.HasValue)
{
string roleName = filter.Role.Value.ToRoleName();
IQueryable<Guid> userIdsWithRole =
from ur in identityDbContext.UserRoles
join r in identityDbContext.Roles on ur.RoleId equals r.Id
where r.Name == roleName
select ur.UserId;
query = query.Where(u => userIdsWithRole.Contains(u.Id));
}
int totalCount = await query.CountAsync(ct);
query = filter.OrderBy?.ToLower() switch
{
"username" => filter.Order == SortOrder.Descending
? query.OrderByDescending(u => u.UserName)
: query.OrderBy(u => u.UserName),
"email" => filter.Order == SortOrder.Descending
? query.OrderByDescending(u => u.Email)
: query.OrderBy(u => u.Email),
_ => filter.Order == SortOrder.Descending
? query.OrderByDescending(u => u.DateCreated)
: query.OrderBy(u => u.DateCreated),
};
int skip = (filter.PageNumber - 1) * filter.PageSize;
List<ApplicationUser> users = await query.Skip(skip).Take(filter.PageSize).ToListAsync(ct);
IReadOnlyList<UserResponse> items = await MapManyAsync(users);
return new PaginatedResponse<UserResponse>
{
Items = items,
Page = filter.PageNumber,
PageSize = filter.PageSize,
TotalCount = totalCount,
};
}
/// <inheritdoc/>
public async Task<UserResponse> GetByIdAsync(
string callerRole, Guid callerId, Guid userId, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceReadAccess(user, callerRole, callerId);
return await MapOneAsync(user);
}
/// <inheritdoc/>
public async Task<UserResponse> UpdateAsync(
string callerRole, Guid callerId, Guid userId,
UpdateUserRequest request, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceWriteAccess(user, callerRole, callerId);
if (!string.IsNullOrWhiteSpace(request.Username))
{
ThrowIfFailed(await userManager.SetUserNameAsync(user, request.Username));
}
if (!string.IsNullOrWhiteSpace(request.Email))
{
ThrowIfFailed(await userManager.SetEmailAsync(user, request.Email));
}
if (request.Phone is not null)
{
ThrowIfFailed(await userManager.SetPhoneNumberAsync(user, request.Phone));
}
if (request.Role.HasValue)
{
await ChangeRoleAsync(user, request.Role.Value, callerRole, callerId);
}
return await MapOneAsync(user);
}
/// <inheritdoc/>
public async Task ChangePasswordAsync(
string callerRole, Guid callerId, Guid userId,
ChangePasswordRequest request, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceWriteAccess(user, callerRole, callerId);
bool isSelfUpdate = user.Id == callerId;
if (isSelfUpdate)
{
if (string.IsNullOrWhiteSpace(request.CurrentPassword))
{
throw new ArgumentException(ErrorMessages.User.CurrentPasswordRequired);
}
ThrowIfFailed(await userManager.ChangePasswordAsync(
user, request.CurrentPassword, request.NewPassword));
}
else
{
ThrowIfFailed(await userManager.RemovePasswordAsync(user));
ThrowIfFailed(await userManager.AddPasswordAsync(user, request.NewPassword));
}
user.MustChangePassword = false;
ThrowIfFailed(await userManager.UpdateAsync(user));
}
/// <summary>
/// Generates a password reset token and flags the account so MustChangePasswordMiddleware blocks access until the reset completes.
/// </summary>
public async Task<ResetPasswordResponse> ResetPasswordAsync(
string callerRole, Guid callerId, Guid userId, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceResetPasswordAccess(callerRole);
string token = await userManager.GeneratePasswordResetTokenAsync(user);
user.MustChangePassword = true;
ThrowIfFailed(await userManager.UpdateAsync(user));
string frontendUrl = configuration[ConfigurationKeys.Frontend.PasswordResetUrl]
?? throw new InvalidOperationException(
ErrorMessages.Configuration.KeyNotConfigured(ConfigurationKeys.Frontend.PasswordResetUrl));
string resetLink =
$"{frontendUrl}" +
$"?email={Uri.EscapeDataString(user.Email!)}" +
$"&token={Uri.EscapeDataString(token)}";
await emailService.SendPasswordResetAsync(user.Email!, user.UserName!, resetLink, ct);
return new ResetPasswordResponse(user.Id);
}
/// <inheritdoc/>
public async Task DeleteAsync(
string callerRole, Guid callerId, Guid userId, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceDeleteAccess(callerRole);
ThrowIfFailed(await userManager.DeleteAsync(user));
}
/// <inheritdoc/>
public async Task<UserResponse> SetActiveAsync(
string callerRole, Guid callerId, Guid userId, bool isActive, CancellationToken ct = default)
{
ApplicationUser user = await FindOrThrowAsync(userId);
EnforceDeleteAccess(callerRole);
if (user.Id == callerId)
{
throw new InvalidOperationException(ErrorMessages.User.CannotChangeOwnActiveState);
}
if (isActive)
{
ThrowIfFailed(await userManager.SetLockoutEndDateAsync(user, null));
}
else
{
ThrowIfFailed(await userManager.SetLockoutEnabledAsync(user, true));
ThrowIfFailed(await userManager.SetLockoutEndDateAsync(user, DateTimeOffset.MaxValue));
}
return await MapOneAsync(user);
}
private static void EnforceReadAccess(ApplicationUser target, string callerRole, Guid callerId)
{
if (IsAdmin(callerRole) || IsOwner(callerRole))
{
return;
}
if (target.Id == callerId)
{
return;
}
throw new UnauthorizedAccessException(ErrorMessages.Auth.AccessDenied);
}
/// <summary>
/// Write access currently follows the exact same rule as read access, kept as its own method for a future divergence.
/// </summary>
private static void EnforceWriteAccess(ApplicationUser target, string callerRole, Guid callerId)
{
EnforceReadAccess(target, callerRole, callerId);
}
private static void EnforceResetPasswordAccess(string callerRole)
{
if (IsAdmin(callerRole) || IsOwner(callerRole))
{
return;
}
throw new UnauthorizedAccessException(ErrorMessages.User.PasswordResetRestricted);
}
private static void EnforceDeleteAccess(string callerRole)
{
if (IsAdmin(callerRole) || IsOwner(callerRole))
{
return;
}
throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToDelete);
}
/// <summary>
/// Which roles each caller role may assign to another user, kept as separate dictionary entries per role for a future divergence.
/// </summary>
private static readonly Dictionary<string, HashSet<string>> _roleChangePolicy =
new(StringComparer.OrdinalIgnoreCase)
{
[Roles.Admin] = new(StringComparer.OrdinalIgnoreCase)
{
Roles.Admin, Roles.Owner
},
[Roles.Owner] = new(StringComparer.OrdinalIgnoreCase)
{
Roles.Admin, Roles.Owner
}
};
/// <summary>
/// Replaces the target user's role with newRole so they end up in exactly one role, never accumulating multiple.
/// </summary>
private async Task ChangeRoleAsync(
ApplicationUser user, UserRoleType newRole, string callerRole, Guid callerId)
{
if (!Enum.IsDefined(newRole))
{
throw new ArgumentException(ErrorMessages.User.InvalidRole(newRole));
}
string targetRoleName = newRole.ToRoleName();
EnforceRoleChangeAccess(user, callerRole, callerId, targetRoleName);
IList<string> currentRoles = await userManager.GetRolesAsync(user);
if (currentRoles.Count > 0)
{
ThrowIfFailed(await userManager.RemoveFromRolesAsync(user, currentRoles));
}
ThrowIfFailed(await userManager.AddToRoleAsync(user, targetRoleName));
}
/// <summary>
/// A user may never change their own role, since that is the one path that could let someone grant themselves more privilege.
/// </summary>
private static void EnforceRoleChangeAccess(
ApplicationUser target, string callerRole, Guid callerId, string targetRoleName)
{
if (target.Id == callerId)
{
throw new InvalidOperationException(ErrorMessages.User.CannotChangeOwnRole);
}
bool hasJurisdictionOverTarget = IsAdmin(callerRole) || IsOwner(callerRole);
if (!hasJurisdictionOverTarget)
{
throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToChangeRole);
}
bool allowedToAssignTargetRole =
_roleChangePolicy.TryGetValue(callerRole, out HashSet<string>? permittedRoles)
&& permittedRoles.Contains(targetRoleName);
if (!allowedToAssignTargetRole)
{
throw new UnauthorizedAccessException(
ErrorMessages.User.RoleNotAllowedToAssign(callerRole, targetRoleName));
}
}
private async Task<UserResponse> MapOneAsync(ApplicationUser user)
{
IList<string> roles = await userManager.GetRolesAsync(user);
string role = roles.FirstOrDefault() ?? string.Empty;
bool isActive = !(user.LockoutEnd.HasValue && user.LockoutEnd.Value > DateTimeOffset.UtcNow);
return new UserResponse(user.Id, user.Email!, user.UserName!, role,
user.PhoneNumber, user.CreatedByOwnerId, isActive);
}
private async Task<IReadOnlyList<UserResponse>> MapManyAsync(IEnumerable<ApplicationUser> users)
{
List<UserResponse> results = [];
foreach (ApplicationUser user in users)
{
results.Add(await MapOneAsync(user));
}
return results;
}
private async Task<ApplicationUser> FindOrThrowAsync(Guid userId)
{
ApplicationUser? user = await userManager.FindByIdAsync(userId.ToString());
return user ?? throw new KeyNotFoundException(ErrorMessages.User.NotFound(userId.ToString()));
}
private static void ThrowIfFailed(IdentityResult result)
{
if (!result.Succeeded)
{
throw new InvalidOperationException(
string.Join("; ", result.Errors.Select(e => e.Description)));
}
}
private IQueryable<ApplicationUser> ResolveVisibleUsersQuery(string callerRole)
{
return IsAdmin(callerRole) || IsOwner(callerRole)
? userManager.Users
: throw new UnauthorizedAccessException(ErrorMessages.User.InsufficientPermissionsToListUsers);
}
private static bool IsAdmin(string role)
{
return role == Roles.Admin;
}
private static bool IsOwner(string role)
{
return role == Roles.Owner;
}
}
@@ -0,0 +1,319 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260315043537_InitialIdentityMigration")]
partial class InitialIdentityMigration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000003"),
ConcurrencyStamp = "3",
Name = "TOURNAMENT_MANAGER",
NormalizedName = "TOURNAMENT_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000004"),
ConcurrencyStamp = "4",
Name = "TEAM_MANAGER",
NormalizedName = "TEAM_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,240 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using System;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace Infrastructure.Identity.Migrations;
/// <inheritdoc />
public partial class InitialIdentityMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
RefreshToken = table.Column<string>(type: "text", nullable: true),
RefreshTokenExpiryTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
UserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "boolean", nullable: false),
PasswordHash = table.Column<string>(type: "text", nullable: true),
SecurityStamp = table.Column<string>(type: "text", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "text", nullable: true),
PhoneNumber = table.Column<string>(type: "text", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "boolean", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "boolean", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
LockoutEnabled = table.Column<bool>(type: "boolean", nullable: false),
AccessFailedCount = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
RoleId = table.Column<Guid>(type: "uuid", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
ClaimType = table.Column<string>(type: "text", nullable: true),
ClaimValue = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "text", nullable: false),
ProviderKey = table.Column<string>(type: "text", nullable: false),
ProviderDisplayName = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<Guid>(type: "uuid", nullable: false),
RoleId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<Guid>(type: "uuid", nullable: false),
LoginProvider = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false),
Value = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.InsertData(
table: "AspNetRoles",
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
{ new Guid("00000000-0000-0000-0000-000000000001"), "1", "ADMIN", "ADMIN" },
{ new Guid("00000000-0000-0000-0000-000000000002"), "2", "OWNER", "OWNER" },
{ new Guid("00000000-0000-0000-0000-000000000003"), "3", "TOURNAMENT_MANAGER", "TOURNAMENT_MANAGER" },
{ new Guid("00000000-0000-0000-0000-000000000004"), "4", "TEAM_MANAGER", "TEAM_MANAGER" },
{ new Guid("00000000-0000-0000-0000-000000000005"), "5", "GUEST", "GUEST" }
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
@@ -0,0 +1,322 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260316030250_changesPropsForUsers")]
partial class changesPropsForUsers
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000003"),
ConcurrencyStamp = "3",
Name = "TOURNAMENT_MANAGER",
NormalizedName = "TOURNAMENT_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000004"),
ConcurrencyStamp = "4",
Name = "TEAM_MANAGER",
NormalizedName = "TEAM_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
#nullable disable
namespace Infrastructure.Identity.Migrations;
/// <inheritdoc />
public partial class changesPropsForUsers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "CreatedByOwnerId",
table: "AspNetUsers",
type: "uuid",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CreatedByOwnerId",
table: "AspNetUsers");
}
}
@@ -0,0 +1,325 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260316031655_addDateCreatedForUsers")]
partial class addDateCreatedForUsers
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000003"),
ConcurrencyStamp = "3",
Name = "TOURNAMENT_MANAGER",
NormalizedName = "TOURNAMENT_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000004"),
ConcurrencyStamp = "4",
Name = "TEAM_MANAGER",
NormalizedName = "TEAM_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,30 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
#nullable disable
namespace Infrastructure.Identity.Migrations;
/// <inheritdoc />
public partial class addDateCreatedForUsers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "DateCreated",
table: "AspNetUsers",
type: "timestamp with time zone",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DateCreated",
table: "AspNetUsers");
}
}
@@ -0,0 +1,328 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260316042032_addMustChangePassword")]
partial class addMustChangePassword
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<bool>("MustChangePassword")
.HasColumnType("boolean");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000003"),
ConcurrencyStamp = "3",
Name = "TOURNAMENT_MANAGER",
NormalizedName = "TOURNAMENT_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000004"),
ConcurrencyStamp = "4",
Name = "TEAM_MANAGER",
NormalizedName = "TEAM_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Infrastructure.Identity.Migrations;
/// <inheritdoc />
public partial class addMustChangePassword : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "MustChangePassword",
table: "AspNetUsers",
type: "boolean",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "MustChangePassword",
table: "AspNetUsers");
}
}
@@ -0,0 +1,329 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260326215327_AddNewPropsForIdentity")]
partial class AddNewPropsForIdentity
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.11")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<bool>("MustChangePassword")
.HasColumnType("boolean");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.IsUnique()
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000003"),
ConcurrencyStamp = "3",
Name = "TOURNAMENT_MANAGER",
NormalizedName = "TOURNAMENT_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000004"),
ConcurrencyStamp = "4",
Name = "TEAM_MANAGER",
NormalizedName = "TEAM_MANAGER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,36 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Infrastructure.Identity.Migrations;
/// <inheritdoc />
public partial class AddNewPropsForIdentity : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "EmailIndex",
table: "AspNetUsers");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "EmailIndex",
table: "AspNetUsers");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
}
}
@@ -0,0 +1,315 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
[Migration("20260828021949_RemoveTournamentAndTeamManagerRoles")]
partial class RemoveTournamentAndTeamManagerRoles
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.30")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<bool>("MustChangePassword")
.HasColumnType("boolean");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.IsUnique()
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,40 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace Infrastructure.Identity.Migrations
{
/// <inheritdoc />
public partial class RemoveTournamentAndTeamManagerRoles : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DeleteData(
table: "AspNetRoles",
keyColumn: "Id",
keyValue: new Guid("00000000-0000-0000-0000-000000000003"));
migrationBuilder.DeleteData(
table: "AspNetRoles",
keyColumn: "Id",
keyValue: new Guid("00000000-0000-0000-0000-000000000004"));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.InsertData(
table: "AspNetRoles",
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
{ new Guid("00000000-0000-0000-0000-000000000003"), "3", "TOURNAMENT_MANAGER", "TOURNAMENT_MANAGER" },
{ new Guid("00000000-0000-0000-0000-000000000004"), "4", "TEAM_MANAGER", "TEAM_MANAGER" }
});
}
}
}
@@ -0,0 +1,312 @@
// <auto-generated />
using System;
using Infrastructure.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Infrastructure.Identity.Migrations
{
[DbContext(typeof(IdentityAppDbContext))]
partial class IdentityAppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.30")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<Guid?>("CreatedByOwnerId")
.HasColumnType("uuid");
b.Property<DateTime>("DateCreated")
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<bool>("MustChangePassword")
.HasColumnType("boolean");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("RefreshToken")
.HasColumnType("text");
b.Property<DateTime?>("RefreshTokenExpiryTime")
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.IsUnique()
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
b.HasData(
new
{
Id = new Guid("00000000-0000-0000-0000-000000000001"),
ConcurrencyStamp = "1",
Name = "ADMIN",
NormalizedName = "ADMIN"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000002"),
ConcurrencyStamp = "2",
Name = "OWNER",
NormalizedName = "OWNER"
},
new
{
Id = new Guid("00000000-0000-0000-0000-000000000005"),
ConcurrencyStamp = "5",
Name = "GUEST",
NormalizedName = "GUEST"
});
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<Guid>("RoleId")
.HasColumnType("uuid");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
{
b.HasOne("Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}