98 lines
3.9 KiB
C#
98 lines
3.9 KiB
C#
using System.Reflection;
|
|
using FinalFactory.Rendezvous.Contracts;
|
|
using FinalFactory.Rendezvous.Server.Provisioning;
|
|
|
|
namespace FinalFactory.Rendezvous.Tests.Provisioning;
|
|
|
|
public sealed class ProvisioningSecurityTests
|
|
{
|
|
[Fact]
|
|
public void StartupFailsClearlyWhenKeyMaterialIsAbsent()
|
|
{
|
|
ProvisioningOptions options = ProvisioningTestData.CreateOptions(
|
|
ProvisioningTestData.CreateKey(secretReference: "missing-production-secret"));
|
|
using DictionarySecretProvider empty = ProvisioningTestData.CreateSecrets();
|
|
|
|
ProvisioningConfigurationException exception = Assert.Throws<ProvisioningConfigurationException>(
|
|
() => ProvisioningRuntime.Create(options, empty, ProvisioningTestData.Now));
|
|
|
|
Assert.Contains("key-1", exception.Message, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("missing-production-secret", exception.Message, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void StartupRequiresAnActivePublisherKeyForEveryEnabledPolicy()
|
|
{
|
|
ProvisioningOptions options = ProvisioningTestData.CreateOptions();
|
|
options.Games.Add(ProvisioningTestData.CreatePolicy("unscouted", "production"));
|
|
using DictionarySecretProvider secrets = ProvisioningTestData.CreateSecrets("secret-1");
|
|
|
|
ProvisioningConfigurationException exception = Assert.Throws<ProvisioningConfigurationException>(
|
|
() => ProvisioningRuntime.Create(options, secrets, ProvisioningTestData.Now));
|
|
|
|
Assert.Contains("unscouted/production", exception.Message, StringComparison.Ordinal);
|
|
Assert.Contains("key", exception.Message, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void SecretMaterialCredentialsAndKeysAreRedactedFromDiagnostics()
|
|
{
|
|
byte[] knownSecret = Enumerable.Range(1, 32).Select(static value => (byte)value).ToArray();
|
|
string encodedSecret = Convert.ToBase64String(knownSecret);
|
|
using SecretMaterial material = new(knownSecret);
|
|
using DictionarySecretProvider secrets = new(new Dictionary<string, byte[]>
|
|
{
|
|
["secret-1"] = knownSecret,
|
|
});
|
|
using SigningKeyRing keys = SigningKeyRing.Create(
|
|
[ProvisioningTestData.CreateKey()],
|
|
secrets);
|
|
PrincipalCredentialService service = new(
|
|
"final-factory-rendezvous",
|
|
"rendezvous-service",
|
|
TimeSpan.FromSeconds(30),
|
|
keys);
|
|
string token = service.Issue(
|
|
ProvisioningTestData.CreateDedicatedPublisher(),
|
|
ProvisioningTestData.Now);
|
|
CredentialValidationResult result = service.Validate(token, ProvisioningTestData.Now);
|
|
|
|
string diagnostics = string.Join(
|
|
'|',
|
|
material,
|
|
secrets,
|
|
keys,
|
|
service,
|
|
result);
|
|
Assert.DoesNotContain(encodedSecret, diagnostics, StringComparison.Ordinal);
|
|
Assert.DoesNotContain(token, diagnostics, StringComparison.Ordinal);
|
|
Assert.Contains("redacted", diagnostics, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
[Fact]
|
|
public void PublicClientAndContractSurfacesContainNoProvisioningSecrets()
|
|
{
|
|
Type[] publicTypes = typeof(GameId).Assembly.GetExportedTypes()
|
|
.Concat(Assembly.Load("FinalFactory.Rendezvous.Client").GetExportedTypes())
|
|
.ToArray();
|
|
string[] forbiddenTerms =
|
|
[
|
|
"GameSecret",
|
|
"SigningKey",
|
|
"KeyMaterial",
|
|
"PublisherCredential",
|
|
"SecretProvider",
|
|
];
|
|
|
|
foreach (Type type in publicTypes)
|
|
{
|
|
IEnumerable<string> names = type
|
|
.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
|
|
.Select(static member => member.Name)
|
|
.Append(type.Name);
|
|
Assert.DoesNotContain(names, name => forbiddenTerms.Any(term =>
|
|
name.Contains(term, StringComparison.OrdinalIgnoreCase)));
|
|
}
|
|
}
|
|
}
|