diff --git a/lib/Logitar.Identity.Core/ApiKeys/ApiKey.cs b/lib/Logitar.Identity.Core/ApiKeys/ApiKey.cs index 2974a8c..fba5d8d 100644 --- a/lib/Logitar.Identity.Core/ApiKeys/ApiKey.cs +++ b/lib/Logitar.Identity.Core/ApiKeys/ApiKey.cs @@ -19,6 +19,14 @@ public class ApiKey : AggregateRoot /// Gets the identifier of the API key. /// public new ApiKeyId Id => new(base.Id); + /// + /// Gets the tenant identifier of the API key. + /// + public TenantId? TenantId => Id.TenantId; + /// + /// Gets the entity identifier of the API key. This identifier is unique within the tenant. + /// + public EntityId? EntityId => Id.EntityId; /// /// The display name of the API key. @@ -108,13 +116,13 @@ public DateTime? ExpiresOn /// /// The role to be added. /// The actor identifier. + /// The role and API key tenant identifiers do not match. public void AddRole(Role role, ActorId actorId = default) { - //The role and API key tenant identifiers do not match. - //if (role.TenantId != TenantId) - //{ - // throw new TenantMismatchException(TenantId, role.TenantId); - //} // TODO(fpion): implement + if (role.TenantId != TenantId) + { + throw new TenantMismatchException(TenantId, role.TenantId); + } if (!HasRole(role)) { diff --git a/lib/Logitar.Identity.Core/ApiKeys/ApiKeyId.cs b/lib/Logitar.Identity.Core/ApiKeys/ApiKeyId.cs index b09346b..3dc1365 100644 --- a/lib/Logitar.Identity.Core/ApiKeys/ApiKeyId.cs +++ b/lib/Logitar.Identity.Core/ApiKeys/ApiKeyId.cs @@ -16,21 +16,33 @@ public readonly struct ApiKeyId /// public string Value => StreamId.Value; + /// + /// Gets the tenant identifier. + /// + public TenantId? TenantId { get; } + /// + /// Gets the entity identifier. + /// + public EntityId EntityId { get; } + /// /// Initializes a new instance of the struct. /// - /// A Guid value. - public ApiKeyId(Guid value) + /// The tenant identifier. + /// The entity identifier. + public ApiKeyId(TenantId? tenantId, Guid entityId) : this(tenantId, Convert.ToBase64String(entityId.ToByteArray()).ToUriSafeBase64()) { - StreamId = new StreamId(value); } /// /// Initializes a new instance of the struct. /// - /// A string value. - public ApiKeyId(string value) + /// The tenant identifier. + /// The entity identifier. + public ApiKeyId(TenantId? tenantId, string entityId) { - StreamId = new StreamId(value); + TenantId = tenantId; + EntityId = new(entityId); + StreamId = new(tenantId.HasValue ? $"{tenantId}:{entityId}" : entityId); } /// /// Initializes a new instance of the struct. @@ -44,14 +56,9 @@ public ApiKeyId(StreamId streamId) /// /// Randomly generates a new API key identifier. /// + /// The tenant identifier. /// The generated identifier. - public static ApiKeyId NewId() => new(StreamId.NewId()); - - /// - /// Converts the identifier to a . The conversion will fail if the identifier has not been created from a . - /// - /// The resulting Guid. - public Guid ToGuid() => StreamId.ToGuid(); + public static ApiKeyId NewId(TenantId? tenantId = null) => new(tenantId, Guid.NewGuid()); /// /// Returns a value indicating whether or not the specified identifiers are equal. diff --git a/lib/Logitar.Identity.Core/ApiKeys/ApiKeyIsExpiredException.cs b/lib/Logitar.Identity.Core/ApiKeys/ApiKeyIsExpiredException.cs index d0f4677..a5ab227 100644 --- a/lib/Logitar.Identity.Core/ApiKeys/ApiKeyIsExpiredException.cs +++ b/lib/Logitar.Identity.Core/ApiKeys/ApiKeyIsExpiredException.cs @@ -13,10 +13,10 @@ public class ApiKeyIsExpiredException : InvalidCredentialsException /// /// Gets the identifier of the expired API key. /// - public ApiKeyId ApiKeyId // TODO(fpion): do we really want this? + public string ApiKeyId { - get => new((string)Data[nameof(ApiKeyId)]!); - private set => Data[nameof(ApiKeyId)] = value.Value; + get => (string)Data[nameof(ApiKeyId)]!; + private set => Data[nameof(ApiKeyId)] = value; } /// @@ -25,7 +25,7 @@ public class ApiKeyIsExpiredException : InvalidCredentialsException /// The API key that is expired. public ApiKeyIsExpiredException(ApiKey apiKey) : base(BuildMessage(apiKey)) { - ApiKeyId = apiKey.Id; + ApiKeyId = apiKey.Id.Value; } /// @@ -34,6 +34,6 @@ public ApiKeyIsExpiredException(ApiKey apiKey) : base(BuildMessage(apiKey)) /// The API key that is expired. /// The exception message. private static string BuildMessage(ApiKey apiKey) => new ErrorMessageBuilder(ErrorMessage) - .AddData(nameof(ApiKeyId), apiKey.Id.Value) + .AddData(nameof(ApiKeyId), apiKey.Id) .Build(); } diff --git a/lib/Logitar.Identity.Core/ApiKeys/IncorrectApiKeySecretException.cs b/lib/Logitar.Identity.Core/ApiKeys/IncorrectApiKeySecretException.cs index 807ef41..70392ec 100644 --- a/lib/Logitar.Identity.Core/ApiKeys/IncorrectApiKeySecretException.cs +++ b/lib/Logitar.Identity.Core/ApiKeys/IncorrectApiKeySecretException.cs @@ -13,10 +13,10 @@ public class IncorrectApiKeySecretException : InvalidCredentialsException /// /// Gets or sets the identifier of the API key. /// - public ApiKeyId ApiKeyId // TODO(fpion): do we really want this? + public string ApiKeyId { - get => new((string)Data[nameof(ApiKeyId)]!); - private set => Data[nameof(ApiKeyId)] = value.Value; + get => (string)Data[nameof(ApiKeyId)]!; + private set => Data[nameof(ApiKeyId)] = value; } /// /// Gets or sets the attempted secret. @@ -35,7 +35,7 @@ public string AttemptedSecret public IncorrectApiKeySecretException(ApiKey apiKey, string attemptedSecret) : base(BuildMessage(apiKey, attemptedSecret)) { - ApiKeyId = apiKey.Id; + ApiKeyId = apiKey.Id.Value; AttemptedSecret = attemptedSecret; } @@ -46,7 +46,7 @@ public IncorrectApiKeySecretException(ApiKey apiKey, string attemptedSecret) /// The attempted secret. /// The exception message. private static string BuildMessage(ApiKey apiKey, string attemptedSecret) => new ErrorMessageBuilder(ErrorMessage) - .AddData(nameof(ApiKeyId), apiKey.Id.Value) + .AddData(nameof(ApiKeyId), apiKey.Id) .AddData(nameof(AttemptedSecret), attemptedSecret) .Build(); } diff --git a/lib/Logitar.Identity.Core/TenantMismatchException.cs b/lib/Logitar.Identity.Core/TenantMismatchException.cs new file mode 100644 index 0000000..816e077 --- /dev/null +++ b/lib/Logitar.Identity.Core/TenantMismatchException.cs @@ -0,0 +1,52 @@ +namespace Logitar.Identity.Core; + +/// +/// The exception raised when an association is made between two entities in different tenants. +/// +public class TenantMismatchException : Exception +{ + /// + /// A generic error message for this exception. + /// + private const string ErrorMessage = "The specified tenant identifier was not expected."; + + /// + /// Gets or sets the expected tenant identifier. + /// + public string? ExpectedTenantId + { + get => (string?)Data[nameof(ExpectedTenantId)]; + private set => Data[nameof(ExpectedTenantId)] = value; + } + /// + /// Gets or sets the actual tenant identifier. + /// + public string? ActualTenantId + { + get => (string?)Data[nameof(ActualTenantId)]; + private set => Data[nameof(ActualTenantId)] = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The expected tenant identifier. + /// The actual tenant identifier. + public TenantMismatchException(TenantId? expected, TenantId? actual) + : base(BuildMessage(expected, actual)) + { + ExpectedTenantId = expected?.Value; + ActualTenantId = actual?.Value; + } + + /// + /// Builds the exception message. + /// + /// The expected tenant identifier. + /// The actual tenant identifier. + /// The exception message. + private static string BuildMessage(TenantId? expected, TenantId? actual) => new ErrorMessageBuilder(ErrorMessage) + .AddData(nameof(ExpectedTenantId), expected, "") + .AddData(nameof(ActualTenantId), actual, "") + .Build(); +}