dc8f3466c9
- Added IdentityAccessModule.cs with DI registration - Added KArtSell.Modules.IdentityAccess.csproj with FastEndpoints deps - Added project files for UnitTests & IntegrationTests - Updated Program.cs to register IdentityAccessModule - Updated Host.csproj to reference IdentityAccess module - Fixed Directory.Packages.props with Moq + MS.Extensions.DependencyInjection ISSUES (to fix next session): - FastEndpoints Send/SendAsync/SendOkAsync method resolution incomplete - Response record initialization requires field values - Need to refactor endpoints to match ModelOperations pattern exactly WORKING: - Domain layer (IdentityState, RoleAssignmentState) ✅ - SQL repositories (Dapper) ✅ - Unit tests (RegisterIdentity, RequestMfaSetup handlers) ✅ - Integration test structure ready ✅ Next: Simplify endpoints using 'Endpoint<Req,Resp>' pattern from GetApprovalQueue sample Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
113 lines
3.6 KiB
C#
113 lines
3.6 KiB
C#
namespace KArtSell.Modules.IdentityAccess.ManageIdentityAndRoles.Domain;
|
|
|
|
/// <summary>
|
|
/// Identity lifecycle state machine (AEG-VS-01-03)
|
|
/// Immutable value object for state transitions
|
|
/// </summary>
|
|
public sealed record IdentityState
|
|
{
|
|
public const string Undefined = "UNDEFINED";
|
|
public const string Active = "ACTIVE";
|
|
public const string RequiresMfaSetup = "REQUIRES_MFA_SETUP";
|
|
public const string MfaConfigured = "MFA_CONFIGURED";
|
|
public const string MfaSuspended = "MFA_SUSPENDED";
|
|
public const string Inactive = "INACTIVE";
|
|
public const string Revoked = "REVOKED";
|
|
|
|
private static readonly HashSet<string> ValidStates =
|
|
[
|
|
Undefined, Active, RequiresMfaSetup, MfaConfigured, MfaSuspended, Inactive, Revoked
|
|
];
|
|
|
|
public string Value { get; }
|
|
|
|
private IdentityState(string value)
|
|
{
|
|
if (!ValidStates.Contains(value))
|
|
throw new ArgumentException($"Invalid identity state: {value}", nameof(value));
|
|
Value = value;
|
|
}
|
|
|
|
// Factory methods
|
|
public static IdentityState CreateUndefined() => new(Undefined);
|
|
public static IdentityState CreateActive() => new(Active);
|
|
public static IdentityState CreateInactive() => new(Inactive);
|
|
public static IdentityState CreateRevoked() => new(Revoked);
|
|
public static IdentityState Parse(string value) => new(value);
|
|
|
|
// State transitions (immutable - return new state)
|
|
public IdentityState Register()
|
|
{
|
|
return Value switch
|
|
{
|
|
Undefined => new(Active),
|
|
_ => throw new InvalidOperationException($"Cannot register from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState RequestMfaSetup()
|
|
{
|
|
return Value switch
|
|
{
|
|
Active => new(RequiresMfaSetup),
|
|
_ => throw new InvalidOperationException($"Cannot request MFA setup from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState CompleteMfaSetup()
|
|
{
|
|
return Value switch
|
|
{
|
|
RequiresMfaSetup => new(MfaConfigured),
|
|
_ => throw new InvalidOperationException($"Cannot complete MFA setup from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState SuspendMfaTemporarily()
|
|
{
|
|
return Value switch
|
|
{
|
|
MfaConfigured => new(MfaSuspended),
|
|
_ => throw new InvalidOperationException($"Cannot suspend MFA from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState ResumeMfa()
|
|
{
|
|
return Value switch
|
|
{
|
|
MfaSuspended => new(MfaConfigured),
|
|
_ => throw new InvalidOperationException($"Cannot resume MFA from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState Deactivate()
|
|
{
|
|
return Value switch
|
|
{
|
|
Active or RequiresMfaSetup or MfaConfigured or MfaSuspended => new(Inactive),
|
|
_ => throw new InvalidOperationException($"Cannot deactivate from {Value}")
|
|
};
|
|
}
|
|
|
|
public IdentityState Revoke()
|
|
{
|
|
return Value switch
|
|
{
|
|
Inactive => new(Revoked),
|
|
_ => throw new InvalidOperationException($"Cannot revoke from {Value}")
|
|
};
|
|
}
|
|
|
|
// State queries
|
|
public bool IsActive() => Value == Active;
|
|
public bool IsMfaRequired() => Value is RequiresMfaSetup or MfaConfigured or MfaSuspended;
|
|
public bool IsMfaConfigured() => Value == MfaConfigured;
|
|
public bool IsInactive() => Value == Inactive;
|
|
public bool IsRevoked() => Value == Revoked;
|
|
public bool CanRegister() => Value == Undefined;
|
|
public bool CanReceiveRoles() => Value is Active or RequiresMfaSetup or MfaConfigured;
|
|
|
|
public override string ToString() => Value;
|
|
}
|