Enforce advertised body producer authority
This commit is contained in:
parent
d0aa50d31f
commit
2306b470f1
3 changed files with 55 additions and 24 deletions
|
|
@ -286,7 +286,7 @@ namespace GameCult.Eve.UnityScene
|
|||
|
||||
private CultMeshBodyPublicationResolver CreateBodyResolver()
|
||||
{
|
||||
var producerId = RequireAdvertisedBodyProducerId(_advertisement);
|
||||
var producerIds = RequireAdvertisedBodyProducerIds(_advertisement);
|
||||
var mappedRoot = Path.GetDirectoryName(_replicaPath) ?? ".";
|
||||
return new CultMeshBodyPublicationResolver(new CultMeshBodyTransportService(
|
||||
new ICultMeshBodyTransportAdapter[]
|
||||
|
|
@ -295,20 +295,27 @@ namespace GameCult.Eve.UnityScene
|
|||
new CultMeshMappedBodyAdapter(mappedRoot),
|
||||
new CultMeshNetworkBodyAdapter(ReadNetworkBody)
|
||||
},
|
||||
(candidateProducerId, _) => IsAdvertisedBodyProducer(producerId, candidateProducerId)));
|
||||
(candidateProducerId, _) => IsAdvertisedBodyProducer(producerIds, candidateProducerId)));
|
||||
}
|
||||
|
||||
private static bool IsAdvertisedBodyProducer(string advertisedProducerId, string candidateProducerId) =>
|
||||
string.Equals(candidateProducerId, advertisedProducerId, StringComparison.Ordinal);
|
||||
private static bool IsAdvertisedBodyProducer(
|
||||
IReadOnlyList<string> advertisedProducerIds,
|
||||
string candidateProducerId) =>
|
||||
advertisedProducerIds.Any(producerId =>
|
||||
string.Equals(candidateProducerId, producerId, StringComparison.Ordinal));
|
||||
|
||||
private static string RequireAdvertisedBodyProducerId(EveProviderAdvertisementDocument? advertisement)
|
||||
private static IReadOnlyList<string> RequireAdvertisedBodyProducerIds(EveProviderAdvertisementDocument? advertisement)
|
||||
{
|
||||
if (advertisement == null)
|
||||
throw new InvalidOperationException("The Eve provider advertisement must be resolved before body transport authorization.");
|
||||
if (string.IsNullOrWhiteSpace(advertisement.ServiceId))
|
||||
var producerIds = advertisement.AuthorizedBodyProducerIds
|
||||
.Where(producerId => !string.IsNullOrWhiteSpace(producerId))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
if (producerIds.Length == 0)
|
||||
throw new InvalidOperationException(
|
||||
$"Eve provider '{advertisement.ProviderId}' does not advertise a service identity for body production.");
|
||||
return advertisement.ServiceId;
|
||||
$"Eve provider '{advertisement.ProviderId}' does not advertise an authorized body producer.");
|
||||
return producerIds;
|
||||
}
|
||||
|
||||
private byte[] ReadNetworkBody(CultMeshBodyDescriptor descriptor)
|
||||
|
|
|
|||
|
|
@ -103,36 +103,56 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
}
|
||||
|
||||
[Test]
|
||||
public void LiveTransportAuthorizesBodiesWithAdvertisedServiceIdentity()
|
||||
public void LiveTransportAuthorizesOnlyExplicitlyAdvertisedBodyProducers()
|
||||
{
|
||||
var advertisement = ProviderAdvertisement("aetheria", "aetheria.daemon");
|
||||
var advertisement = ProviderAdvertisement("aetheria", "aetheria-daemon-17", "aetheria.daemon");
|
||||
var method = typeof(EveUnityCultMeshLiveProviderTransport)
|
||||
.GetMethod("RequireAdvertisedBodyProducerId", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
.GetMethod("RequireAdvertisedBodyProducerIds", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
var producerId = (string)method.Invoke(null, new object?[] { advertisement })!;
|
||||
var producerIds = (IReadOnlyList<string>)method.Invoke(null, new object?[] { advertisement })!;
|
||||
|
||||
Assert.That(producerId, Is.EqualTo("aetheria.daemon"));
|
||||
Assert.That(producerId, Is.Not.EqualTo(advertisement.ProviderId));
|
||||
Assert.That(producerIds, Is.EqualTo(new[] { "aetheria.daemon" }));
|
||||
Assert.That(producerIds, Does.Not.Contain(advertisement.ProviderId));
|
||||
Assert.That(producerIds, Does.Not.Contain(advertisement.ServiceId));
|
||||
|
||||
var authorizes = typeof(EveUnityCultMeshLiveProviderTransport)
|
||||
.GetMethod("IsAdvertisedBodyProducer", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerId, "aetheria.daemon" }), Is.True);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerId, "aetheria" }), Is.False);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerId, "impostor" }), Is.False);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria.daemon" }), Is.True);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria" }), Is.False);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "aetheria-daemon-17" }), Is.False);
|
||||
Assert.That(authorizes.Invoke(null, new object[] { producerIds, "impostor" }), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LiveTransportRejectsAdvertisementWithoutBodyProducerIdentity()
|
||||
{
|
||||
var advertisement = ProviderAdvertisement("aetheria", "");
|
||||
var advertisement = ProviderAdvertisement("aetheria", "aetheria-daemon-17");
|
||||
var method = typeof(EveUnityCultMeshLiveProviderTransport)
|
||||
.GetMethod("RequireAdvertisedBodyProducerId", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
.GetMethod("RequireAdvertisedBodyProducerIds", BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
var error = Assert.Throws<TargetInvocationException>(() =>
|
||||
method.Invoke(null, new object?[] { advertisement }));
|
||||
|
||||
Assert.That(error!.InnerException, Is.TypeOf<InvalidOperationException>());
|
||||
StringAssert.Contains("does not advertise a service identity", error.InnerException!.Message);
|
||||
StringAssert.Contains("does not advertise an authorized body producer", error.InnerException!.Message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ProviderAdvertisementRoundTripPreservesBodyProducerAuthority()
|
||||
{
|
||||
var advertisement = ProviderAdvertisement(
|
||||
"aetheria",
|
||||
"aetheria-daemon-17",
|
||||
"aetheria.daemon",
|
||||
"aetheria.asset-publisher");
|
||||
|
||||
var bytes = MessagePack.MessagePackSerializer.Serialize(advertisement);
|
||||
var decoded = MessagePack.MessagePackSerializer.Deserialize<EveProviderAdvertisementDocument>(bytes);
|
||||
|
||||
Assert.That(decoded.ProviderId, Is.EqualTo("aetheria"));
|
||||
Assert.That(decoded.ServiceId, Is.EqualTo("aetheria-daemon-17"));
|
||||
Assert.That(decoded.AuthorizedBodyProducerIds,
|
||||
Is.EqualTo(new[] { "aetheria.daemon", "aetheria.asset-publisher" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -2055,7 +2075,10 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
}
|
||||
};
|
||||
|
||||
private static EveProviderAdvertisementDocument ProviderAdvertisement(string providerId, string serviceId) =>
|
||||
private static EveProviderAdvertisementDocument ProviderAdvertisement(
|
||||
string providerId,
|
||||
string serviceId,
|
||||
params string[] authorizedBodyProducerIds) =>
|
||||
new EveProviderAdvertisementDocument(
|
||||
providerId,
|
||||
serviceId,
|
||||
|
|
@ -2068,7 +2091,8 @@ namespace GameCult.Eve.UnityScene.Tests
|
|||
Array.Empty<string>(),
|
||||
Array.Empty<EveProviderWitness>(),
|
||||
Array.Empty<EveAdvertisedSurface>(),
|
||||
Array.Empty<EveAdvertisedCommand>());
|
||||
Array.Empty<EveAdvertisedCommand>(),
|
||||
authorizedBodyProducerIds);
|
||||
|
||||
private static CultMeshBodyPublicationDocument BodyPublication(
|
||||
EveEntitySoaViewDocument view,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"name": "org.gamecult.eve.unity-scene",
|
||||
"displayName": "Eve Unity Scene Lowering",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.5",
|
||||
"unity": "2021.3",
|
||||
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
|
||||
"dependencies": {
|
||||
"org.gamecult.cultlib": "1.0.10",
|
||||
"org.gamecult.eve.plugin-fields": "0.1.1",
|
||||
"org.gamecult.eve.surface": "0.2.0",
|
||||
"org.gamecult.eve.surface": "0.2.1",
|
||||
"com.unity.mathematics": "1.3.2"
|
||||
},
|
||||
"keywords": [
|
||||
|
|
|
|||
Reference in a new issue