< Summary

Information
Class: Anichron.API.Services.RegistrationValidator
Assembly: Anichron.API
File(s): /home/runner/work/anichron/anichron/src/Anichron.API/Services/RegistrationValidator.cs
Tag: 228_36821334185
Line coverage
100%
Covered lines: 39
Uncovered lines: 0
Coverable lines: 39
Total lines: 76
Line coverage: 100%
Branch coverage
100%
Covered branches: 20
Total branches: 20
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ValidateIdentity(...)100%88100%
ValidateAsync(...)100%22100%
ValidatePasswordAsync(...)100%11100%
ValidatePasswordRulesAsync()100%88100%
IsValidEmail(...)100%22100%

File(s)

/home/runner/work/anichron/anichron/src/Anichron.API/Services/RegistrationValidator.cs

#LineLine coverage
 1using Anichron.API.Settings;
 2using Microsoft.Extensions.Options;
 3using System.Net.Mail;
 4
 5namespace Anichron.API.Services;
 6
 7public interface IRegistrationValidator
 8{
 9    AuthError? ValidateIdentity(string username, string email);
 10    Task<AuthError?> ValidateAsync(string username, string email, string password, CancellationToken ct);
 11    Task<AuthError?> ValidatePasswordAsync(string password, CancellationToken ct);
 12}
 13
 4214public sealed class RegistrationValidator(
 4215    IOptions<PasswordPolicy> passwordPolicyOptions,
 4216    IOptions<UsernamePolicy> usernamePolicyOptions,
 4217    IPwnedPasswordClient pwnedClient) : IRegistrationValidator
 18{
 4219    private readonly PasswordPolicy passwordPolicy = passwordPolicyOptions.Value;
 4220    private readonly UsernamePolicy usernamePolicy = usernamePolicyOptions.Value;
 21
 22    public AuthError? ValidateIdentity(string username, string email)
 3423    {
 3424        if (username.Length < usernamePolicy.MinLength
 3425            || username.Length > usernamePolicy.MaxLength
 3426            || !UsernamePolicy.AllowedCharacters().IsMatch(username))
 1127        {
 1128            return AuthError.InvalidUsername;
 29        }
 30
 2331        if (!IsValidEmail(email))
 1032            return AuthError.InvalidEmail;
 33
 1334        return null;
 3435    }
 36
 37    public Task<AuthError?> ValidateAsync(string username, string email, string password, CancellationToken ct)
 2238        => ValidateIdentity(username, email) is { } identityError
 2239            ? Task.FromResult<AuthError?>(identityError)
 2240            : ValidatePasswordRulesAsync(password, ct);
 41
 42    public Task<AuthError?> ValidatePasswordAsync(string password, CancellationToken ct)
 843        => ValidatePasswordRulesAsync(password, ct);
 44
 45    private async Task<AuthError?> ValidatePasswordRulesAsync(string password, CancellationToken ct)
 1846    {
 1847        if (password.Length < passwordPolicy.MinLength)
 448            return AuthError.PasswordTooShort;
 49
 1450        if (password.Length > passwordPolicy.MaxLength)
 251            return AuthError.PasswordTooLong;
 52
 53        // IDE0046 suppressed: collapsing an async condition into a ternary reduces readability
 54#pragma warning disable IDE0046
 1255        if (passwordPolicy.CheckPwnedPasswords && await pwnedClient.IsPwnedAsync(password, ct))
 56#pragma warning restore IDE0046
 257            return AuthError.PasswordPwned;
 58
 859        return null;
 1660    }
 61
 62    private static bool IsValidEmail(string email)
 2363    {
 2364        if (email.Length > AppDefaults.Email.MaxLength)
 165            return false;
 66        try
 2267        {
 2268            var mailAddress = new MailAddress(email.Trim());
 1469            return mailAddress.Address.Equals(email.Trim(), StringComparison.OrdinalIgnoreCase);
 70        }
 871        catch (Exception ex) when (ex is FormatException or ArgumentException)
 872        {
 873            return false;
 74        }
 2375    }
 76}