sync public allowlist from private myoffice

This commit is contained in:
myoffice-sync
2026-08-05 10:36:17 +00:00
parent 6f7fd61ee9
commit 874796c860
720 changed files with 2841 additions and 69563 deletions
@@ -1,133 +0,0 @@
namespace MyOffice.Web.Identity.Repositories;
using System.Security.Cryptography;
using Domain;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
/// <summary>
/// Identity V3 hasher for new passwords; still verifies the legacy
/// (16-byte salt + 20-byte PBKDF2-SHA1 @ 100k) format and signals rehash.
/// </summary>
public class PasswordHasher : IPasswordHasher<ApplicationUser<Guid>>
{
private const int LegacySaltSize = 16;
private const int LegacyHashSize = 20;
private const int LegacyIterations = 100_000;
private const int LegacyPayloadSize = LegacySaltSize + LegacyHashSize;
private readonly PasswordHasher<ApplicationUser<Guid>> _identityHasher = new();
public string HashPassword(ApplicationUser<Guid> user, string password) =>
_identityHasher.HashPassword(user, password);
public PasswordVerificationResult VerifyHashedPassword(
ApplicationUser<Guid> user,
string hashedPassword,
string providedPassword
)
{
if (string.IsNullOrEmpty(hashedPassword) || providedPassword == null)
{
return PasswordVerificationResult.Failed;
}
// Prefer modern Identity format when payload is not the legacy 36-byte blob.
if (!IsLegacyPayload(hashedPassword))
{
return _identityHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
}
if (VerifyLegacyHash(providedPassword, hashedPassword))
{
return PasswordVerificationResult.SuccessRehashNeeded;
}
return PasswordVerificationResult.Failed;
}
private static bool IsLegacyPayload(string hashedPassword)
{
try
{
var bytes = Convert.FromBase64String(hashedPassword);
return bytes.Length == LegacyPayloadSize;
}
catch (FormatException)
{
return false;
}
}
internal static string HashLegacyForTests(string password)
{
var salt = RandomNumberGenerator.GetBytes(LegacySaltSize);
var hash = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
LegacyIterations,
HashAlgorithmName.SHA1,
LegacyHashSize);
var payload = new byte[LegacyPayloadSize];
Buffer.BlockCopy(salt, 0, payload, 0, LegacySaltSize);
Buffer.BlockCopy(hash, 0, payload, LegacySaltSize, LegacyHashSize);
return Convert.ToBase64String(payload);
}
private static bool VerifyLegacyHash(string password, string passwordHash)
{
byte[] hashBytes;
try
{
hashBytes = Convert.FromBase64String(passwordHash);
}
catch (FormatException)
{
return false;
}
if (hashBytes.Length != LegacyPayloadSize)
{
return false;
}
var salt = hashBytes.AsSpan(0, LegacySaltSize);
var expected = hashBytes.AsSpan(LegacySaltSize, LegacyHashSize);
var actual = Rfc2898DeriveBytes.Pbkdf2(
password,
salt,
LegacyIterations,
HashAlgorithmName.SHA1,
LegacyHashSize);
return CryptographicOperations.FixedTimeEquals(expected, actual);
}
}
public class AppUserManager : UserManager<ApplicationUser<Guid>>
{
public AppUserManager(
IUserStore<ApplicationUser<Guid>> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser<Guid>> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser<Guid>>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser<Guid>>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser<Guid>>> logger) :
base(
store,
optionsAccessor,
passwordHasher,
userValidators,
passwordValidators,
keyNormalizer,
errors,
services,
logger
)
{
}
}
@@ -1,62 +0,0 @@
namespace MyOffice.Web.Identity.Repositories;
using Domain;
using Microsoft.AspNetCore.Identity;
public class RoleStore : IRoleStore<ApplicationRole>
{
public void Dispose()
{
}
public Task<IdentityResult> CreateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> DeleteAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleIdAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetRoleNameAsync(ApplicationRole role, string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(ApplicationRole role, string normalizedName,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<ApplicationRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<ApplicationRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
@@ -1,198 +0,0 @@
namespace MyOffice.Web.Identity.Repositories;
using Data.Models.Users;
using Data.Repositories.Users;
using Domain;
using Microsoft.AspNetCore.Identity;
public class UserStore :
IUserStore<ApplicationUser<Guid>>,
IUserPasswordStore<ApplicationUser<Guid>>,
IUserEmailStore<ApplicationUser<Guid>>
{
private readonly IUserRepository _userRepository;
public UserStore(
IUserRepository userRepository
)
{
_userRepository = userRepository;
}
public void Dispose()
{
}
public Task<string> GetUserIdAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Id.ToString());
}
public Task<string> GetUserNameAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.UserName);
}
public Task SetUserNameAsync(ApplicationUser<Guid> user, string userName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedUserNameAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(ApplicationUser<Guid> user, string normalizedName,
CancellationToken cancellationToken)
{
user.UserName = normalizedName;
return Task.FromResult(0);
}
public Task<IdentityResult> CreateAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
var result = _userRepository.AddUser(new User
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
CurrencyId = user.CurrencyId!,
IsEmailConfirmed = user.IsEmailConfirmed
});
return Task.FromResult(result == 1 ? IdentityResult.Success : IdentityResult.Failed());
}
public Task<IdentityResult> UpdateAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
var userDb = new User
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
CurrencyId = user.CurrencyId!,
IsEmailConfirmed = user.IsEmailConfirmed
};
var result = _userRepository.UpdateUser(userDb);
return Task.FromResult(result == 1 ? IdentityResult.Success : IdentityResult.Failed());
}
public Task<IdentityResult> DeleteAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public async Task<ApplicationUser<Guid>> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
var user = await _userRepository.GetUserAsync(Guid.Parse(userId));
if (user == null)
{
#pragma warning disable CS8603 // Possible null reference return.
return null;
#pragma warning restore CS8603 // Possible null reference return.
}
return new ApplicationUser<Guid>
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
CurrencyId = user.CurrencyId,
IsEmailConfirmed = user.IsEmailConfirmed
};
}
public async Task<ApplicationUser<Guid>> FindByNameAsync(string normalizedUserName,
CancellationToken cancellationToken)
{
var user = await _userRepository.GetByUserUserNameAsync(normalizedUserName);
if (user == null)
{
#pragma warning disable CS8603 // Possible null reference return.
return null;
#pragma warning restore CS8603 // Possible null reference return.
}
return new ApplicationUser<Guid>
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
CurrencyId = user.CurrencyId,
IsEmailConfirmed = user.IsEmailConfirmed
};
}
public Task SetPasswordHashAsync(ApplicationUser<Guid> user, string passwordHash,
CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task<string> GetPasswordHashAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
}
public Task SetEmailAsync(ApplicationUser<Guid> user, string email, CancellationToken cancellationToken)
{
user.Email = email;
return Task.CompletedTask;
}
public Task<string> GetEmailAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.IsEmailConfirmed);
}
public Task SetEmailConfirmedAsync(ApplicationUser<Guid> user, bool confirmed, CancellationToken cancellationToken)
{
user.IsEmailConfirmed = confirmed;
return Task.CompletedTask;
}
public Task<ApplicationUser<Guid>> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
{
return FindByNameAsync(normalizedEmail, cancellationToken);
}
public Task<string?> GetNormalizedEmailAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user?.Email);
}
public Task SetNormalizedEmailAsync(ApplicationUser<Guid> user, string normalizedEmail,
CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
}