114 lines
4.1 KiB
C#
114 lines
4.1 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Web;
|
|
using Authinator.Backend.Utils;
|
|
using Isopoh.Cryptography.Argon2;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
|
|
namespace Authinator.Backend.Database.Tables;
|
|
|
|
public class User {
|
|
public int Id { get; set; }
|
|
public int Iteration { get; set; }
|
|
public string Reference { get; set; } = null!;
|
|
public string? Username { get; set; }
|
|
public string? Password { get; set; }
|
|
public string? Email { get; set; }
|
|
|
|
public List<Group> Groups { get; set; } = new();
|
|
|
|
public bool IsSignupComplete => !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password) && !string.IsNullOrEmpty(Email);
|
|
|
|
public string GetAuthToken() => $"auth:{Id}:{Iteration}:{GetAuthTokenExpiry()}:".HmacWithMessage(ConfigCache.HmacSecret);
|
|
|
|
private static long GetAuthTokenExpiry() => DateTimeOffset.UtcNow.Add(ConfigCache.AuthTokenLifetime).ToUnixTimeSeconds();
|
|
|
|
public string GetSignupToken() => $"register:{Id}:".HmacWithMessage(ConfigCache.HmacSecret);
|
|
|
|
public string GetPwResetToken() => $"reset:{Id}:{Iteration}:{GetPwResetTokenExpiry()}:".HmacWithMessage(ConfigCache.HmacSecret);
|
|
|
|
private static long GetPwResetTokenExpiry() => DateTimeOffset.UtcNow.Add(ConfigCache.PwResetTokenLifetime).ToUnixTimeSeconds();
|
|
|
|
[SuppressMessage("ReSharper.DPA", "DPA0001: Memory allocation issues")]
|
|
public bool ValidatePassword(string password) => Argon2.Verify(Password ?? string.Empty, password);
|
|
|
|
[SuppressMessage("ReSharper.DPA", "DPA0001: Memory allocation issues")]
|
|
public void SetPassword(string password) => Password = Argon2.Hash(password);
|
|
|
|
public class Configuration : IEntityTypeConfiguration<User> {
|
|
public void Configure(EntityTypeBuilder<User> user) {
|
|
user.ToTable("Users");
|
|
user.HasKey(p => p.Id);
|
|
|
|
user.Property(b => b.Iteration).HasColumnName("Iteration").HasDefaultValue(0);
|
|
user.Property(b => b.Reference).HasColumnName("Reference").IsRequired();
|
|
user.Property(b => b.Username).HasColumnName("Username");
|
|
user.Property(b => b.Password).HasColumnName("Password");
|
|
user.Property(b => b.Email).HasColumnName("Email");
|
|
|
|
user.HasMany(p => p.Groups).WithMany();
|
|
}
|
|
}
|
|
|
|
private bool Equals(User other) => Id == other.Id;
|
|
|
|
public override bool Equals(object? obj) {
|
|
if (ReferenceEquals(null, obj))
|
|
return false;
|
|
if (ReferenceEquals(this, obj))
|
|
return true;
|
|
|
|
return obj.GetType() == GetType() && Equals((User)obj);
|
|
}
|
|
|
|
public override int GetHashCode() => Id;
|
|
|
|
public static bool operator ==(User? left, User? right) => Equals(left, right);
|
|
|
|
public static bool operator !=(User? left, User? right) => !Equals(left, right);
|
|
}
|
|
|
|
public static class UserUtils {
|
|
public static User? ValidateAuthToken(this DbSet<User> users, string token) {
|
|
if (string.IsNullOrWhiteSpace(token) || !token.StartsWith("auth:") || token.Split(":").Length != 5)
|
|
return null;
|
|
|
|
var index = token.LastIndexOf(":", StringComparison.Ordinal) + 1;
|
|
var hmac = token[index..].FixUrlEncodedBase64();
|
|
var message = token[..index];
|
|
|
|
if (message.Hmac(ConfigCache.HmacSecret) != hmac)
|
|
return null;
|
|
|
|
var expiry = long.Parse(message.Split(":")[3]);
|
|
if (DateTimeOffset.FromUnixTimeSeconds(expiry) < DateTimeOffset.UtcNow)
|
|
return null;
|
|
|
|
var userId = int.Parse(message.Split(":")[1]);
|
|
var userIteration = int.Parse(message.Split(":")[2]);
|
|
|
|
return users.Include(p => p.Groups).FirstOrDefault(p => p.Id == userId && p.Iteration == userIteration);
|
|
}
|
|
|
|
public static bool ValidateResetToken(this User user, string token) {
|
|
if (string.IsNullOrWhiteSpace(token) || !token.StartsWith("reset:") || token.Split(":").Length != 5)
|
|
return false;
|
|
|
|
var index = token.LastIndexOf(":", StringComparison.Ordinal) + 1;
|
|
var hmac = token[index..].FixUrlEncodedBase64();
|
|
var message = token[..index];
|
|
|
|
if (message.Hmac(ConfigCache.HmacSecret) != hmac)
|
|
return false;
|
|
|
|
var expiry = long.Parse(message.Split(":")[3]);
|
|
if (DateTimeOffset.FromUnixTimeSeconds(expiry) < DateTimeOffset.UtcNow)
|
|
return false;
|
|
|
|
var userId = int.Parse(message.Split(":")[1]);
|
|
var userIteration = int.Parse(message.Split(":")[2]);
|
|
|
|
return user.Id == userId && user.Iteration == userIteration;
|
|
}
|
|
}
|