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,120 @@
|
||||
using FinalFactory.Rendezvous.Contracts;
|
||||
|
||||
namespace FinalFactory.Rendezvous.Server.Provisioning;
|
||||
|
||||
internal sealed class ProvisioningRuntime : IDisposable
|
||||
{
|
||||
private readonly IDisposable? _secretProviderLifetime;
|
||||
|
||||
private ProvisioningRuntime(
|
||||
GamePolicyRegistry policies,
|
||||
SigningKeyRing signingKeys,
|
||||
PrincipalCredentialService credentials,
|
||||
PublisherAuthorizationService publisherAuthorization,
|
||||
IDisposable? secretProviderLifetime)
|
||||
{
|
||||
Policies = policies;
|
||||
SigningKeys = signingKeys;
|
||||
Credentials = credentials;
|
||||
PublisherAuthorization = publisherAuthorization;
|
||||
_secretProviderLifetime = secretProviderLifetime;
|
||||
}
|
||||
|
||||
public GamePolicyRegistry Policies { get; }
|
||||
public SigningKeyRing SigningKeys { get; }
|
||||
public PrincipalCredentialService Credentials { get; }
|
||||
public PublisherAuthorizationService PublisherAuthorization { get; }
|
||||
|
||||
public static ProvisioningRuntime Create(
|
||||
ProvisioningOptions options,
|
||||
ISecretProvider secretProvider,
|
||||
DateTimeOffset now)
|
||||
{
|
||||
try
|
||||
{
|
||||
SigningKeyRing signingKeys = SigningKeyRing.Create(options.SigningKeys, secretProvider);
|
||||
try
|
||||
{
|
||||
if (!signingKeys.HasKeys
|
||||
|| !signingKeys.HasActiveSigningKey(now))
|
||||
{
|
||||
throw new ProvisioningConfigurationException(
|
||||
"At least one active signing key with available production key material is required.");
|
||||
}
|
||||
|
||||
GamePolicyRegistry policies = GamePolicyRegistry.Create(options.Games);
|
||||
if (!policies.HasEnabledPolicies)
|
||||
{
|
||||
throw new ProvisioningConfigurationException(
|
||||
"At least one enabled game/environment policy is required.");
|
||||
}
|
||||
|
||||
foreach (GamePolicy policy in policies.EnabledPolicies)
|
||||
{
|
||||
RequirePublisherKey(
|
||||
signingKeys,
|
||||
policy,
|
||||
PublisherTrustMode.ManagedDedicated,
|
||||
PrincipalCredentialKind.DedicatedPublisher,
|
||||
now);
|
||||
RequirePublisherKey(
|
||||
signingKeys,
|
||||
policy,
|
||||
PublisherTrustMode.PlayerGrant,
|
||||
PrincipalCredentialKind.PlayerHostGrant,
|
||||
now);
|
||||
}
|
||||
|
||||
PrincipalCredentialService credentials = new(
|
||||
options.Issuer,
|
||||
options.Audience,
|
||||
TimeSpan.FromSeconds(options.ClockSkewSeconds),
|
||||
signingKeys);
|
||||
PublisherAuthorizationService authorization = new(policies);
|
||||
return new ProvisioningRuntime(
|
||||
policies,
|
||||
signingKeys,
|
||||
credentials,
|
||||
authorization,
|
||||
secretProvider as IDisposable);
|
||||
}
|
||||
catch
|
||||
{
|
||||
signingKeys.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
(secretProvider as IDisposable)?.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SigningKeys.Dispose();
|
||||
_secretProviderLifetime?.Dispose();
|
||||
}
|
||||
|
||||
private static void RequirePublisherKey(
|
||||
SigningKeyRing signingKeys,
|
||||
GamePolicy policy,
|
||||
PublisherTrustMode trustMode,
|
||||
PrincipalCredentialKind credentialKind,
|
||||
DateTimeOffset now)
|
||||
{
|
||||
if (policy.AllowsPublisherTrust(trustMode)
|
||||
&& !signingKeys.HasActiveSigningKey(
|
||||
now,
|
||||
credentialKind,
|
||||
policy.GameId.ToString(),
|
||||
policy.EnvironmentId.ToString()))
|
||||
{
|
||||
throw new ProvisioningConfigurationException(
|
||||
$"Policy {policy.GameId}/{policy.EnvironmentId} has no active {credentialKind} key.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed record ProvisioningReadiness(bool IsReady);
|
||||
Reference in New Issue
Block a user