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();
+}