Enforce advertised body producer authority

This commit is contained in:
Meta Vens 2026-07-13 22:31:56 +01:00
commit 2306b470f1
3 changed files with 55 additions and 24 deletions

View file

@ -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)

View file

@ -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,

View file

@ -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": [