feat: add tenant provisioning and key lifecycle (#5)
quality-gate / quality (push) Successful in 50s
quality-gate / quality (push) Successful in 50s
Closes #5
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
using FinalFactory.Rendezvous.Contracts;
|
||||
|
||||
namespace FinalFactory.Rendezvous.Server.Provisioning;
|
||||
|
||||
internal sealed class PublisherAuthorizationService(GamePolicyRegistry policies)
|
||||
{
|
||||
public PublisherAuthorizationResult Authorize(
|
||||
AuthenticatedPrincipal principal,
|
||||
GameId requestedGameId,
|
||||
EnvironmentId requestedEnvironmentId,
|
||||
RegionId requestedRegionId,
|
||||
uint requestedProtocolVersion,
|
||||
ListingVisibility requestedVisibility,
|
||||
IReadOnlyDictionary<string, string> requestedMetadata,
|
||||
DateTimeOffset now)
|
||||
{
|
||||
if (principal.ExpiresAt <= now)
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.PrincipalExpired);
|
||||
}
|
||||
|
||||
if (principal is not IPublisherPrincipal publisher)
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.NotPublisher);
|
||||
}
|
||||
|
||||
if (publisher.GameId != requestedGameId
|
||||
|| publisher.EnvironmentId != requestedEnvironmentId)
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.ScopeMismatch);
|
||||
}
|
||||
|
||||
if (!policies.TryGet(publisher.GameId, publisher.EnvironmentId, out GamePolicy? policy)
|
||||
|| policy is null)
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.PolicyNotFound);
|
||||
}
|
||||
|
||||
if (!policy.AllowsPublisherTrust(publisher.TrustMode))
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.TrustModeNotAllowed);
|
||||
}
|
||||
|
||||
if (!publisher.AllowedRegions.Contains(requestedRegionId)
|
||||
|| !policy.AllowsRegion(requestedRegionId))
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.RegionNotAllowed);
|
||||
}
|
||||
|
||||
if (!policy.AllowsProtocol(requestedProtocolVersion))
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.ProtocolNotAllowed);
|
||||
}
|
||||
|
||||
if (publisher.TrustMode == PublisherTrustMode.AnonymousUnlisted
|
||||
&& requestedVisibility != ListingVisibility.Unlisted)
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(
|
||||
PublisherAuthorizationError.AnonymousMustBeUnlisted);
|
||||
}
|
||||
|
||||
if (!policy.AllowsVisibility(requestedVisibility))
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.VisibilityNotAllowed);
|
||||
}
|
||||
|
||||
if (!policy.AllowsMetadata(requestedMetadata))
|
||||
{
|
||||
return PublisherAuthorizationResult.Denied(PublisherAuthorizationError.MetadataNotAllowed);
|
||||
}
|
||||
|
||||
return PublisherAuthorizationResult.Allowed(new AuthorizedPublisherContext(
|
||||
publisher.Subject,
|
||||
publisher.GameId,
|
||||
publisher.EnvironmentId,
|
||||
requestedRegionId,
|
||||
requestedProtocolVersion,
|
||||
requestedVisibility,
|
||||
publisher.TrustMode,
|
||||
policy));
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record AuthorizedPublisherContext(
|
||||
string Subject,
|
||||
GameId GameId,
|
||||
EnvironmentId EnvironmentId,
|
||||
RegionId RegionId,
|
||||
uint ProtocolVersion,
|
||||
ListingVisibility Visibility,
|
||||
PublisherTrustMode TrustMode,
|
||||
GamePolicy Policy);
|
||||
|
||||
internal readonly record struct PublisherAuthorizationResult(
|
||||
bool IsAllowed,
|
||||
PublisherAuthorizationError Error,
|
||||
AuthorizedPublisherContext? Context)
|
||||
{
|
||||
public static PublisherAuthorizationResult Allowed(AuthorizedPublisherContext context) =>
|
||||
new(true, PublisherAuthorizationError.None, context);
|
||||
|
||||
public static PublisherAuthorizationResult Denied(PublisherAuthorizationError error) =>
|
||||
new(false, error, null);
|
||||
}
|
||||
|
||||
internal enum PublisherAuthorizationError
|
||||
{
|
||||
None = 0,
|
||||
NotPublisher = 1,
|
||||
ScopeMismatch = 2,
|
||||
PolicyNotFound = 3,
|
||||
TrustModeNotAllowed = 4,
|
||||
RegionNotAllowed = 5,
|
||||
ProtocolNotAllowed = 6,
|
||||
AnonymousMustBeUnlisted = 7,
|
||||
VisibilityNotAllowed = 8,
|
||||
MetadataNotAllowed = 9,
|
||||
PrincipalExpired = 10,
|
||||
}
|
||||
Reference in New Issue
Block a user