diff --git a/pom.xml b/pom.xml index a6e03eb5..6d003d43 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.uid2 uid2-admin - 6.13.0 + 6.13.1-alpha-227-SNAPSHOT UTF-8 diff --git a/src/main/java/com/uid2/admin/managers/KeysetManager.java b/src/main/java/com/uid2/admin/managers/KeysetManager.java index 17b03576..89011064 100644 --- a/src/main/java/com/uid2/admin/managers/KeysetManager.java +++ b/src/main/java/com/uid2/admin/managers/KeysetManager.java @@ -53,7 +53,7 @@ public static Integer getMaxKeyset(Map keysets) { return max(Collections.max(keysets.keySet()), 3); } - public static AdminKeyset createDefaultKeyset(int siteId, int keysetId) { + public static AdminKeyset createKeysetSharedWithDsps(int siteId, int keysetId) { String name = ""; //only set if both siteId and keysetId match our expectation according to the requirements @@ -67,8 +67,8 @@ else if(siteId == Const.Data.RefreshKeySiteId && keysetId == Const.Data.RefreshK else if(siteId == Const.Data.AdvertisingTokenSiteId && keysetId == Const.Data.FallbackPublisherKeysetId) { name = FallbackPublisherKeysetName; } - return new AdminKeyset(keysetId, siteId, name, null, Instant.now().getEpochSecond(), - true, true, new HashSet<>()); + return new AdminKeyset(keysetId, siteId, name, new HashSet<>(), Instant.now().getEpochSecond(), + true, true, new HashSet<>(Set.of(ClientType.DSP))); } public static Keyset adminKeysetToKeyset(AdminKeyset adminKeyset, Map> siteIdsByType) { @@ -138,7 +138,7 @@ private AdminKeyset createAndAddDefaultKeyset(Integer siteId) throws Exception{ this.keysetProvider.loadContent(); int newKeysetId = getNextKeysetId(); - AdminKeyset newKeyset = KeysetManager.createDefaultKeyset(siteId, newKeysetId); + AdminKeyset newKeyset = KeysetManager.createKeysetSharedWithDsps(siteId, newKeysetId); addOrReplaceKeyset(newKeyset); return newKeyset; } diff --git a/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java b/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java index a07551b7..530b9929 100644 --- a/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java @@ -548,7 +548,7 @@ else if(siteId == Const.Data.RefreshKeySiteId) { else if(siteId == Const.Data.AdvertisingTokenSiteId) { newKeysetId = Const.Data.FallbackPublisherKeysetId; } - keyset = createDefaultKeyset(siteId, newKeysetId); + keyset = createKeysetSharedWithDsps(siteId, newKeysetId); currentKeysets.put(newKeysetId, keyset); keysetStoreWriter.upload(currentKeysets, null); } diff --git a/src/main/java/com/uid2/admin/vertx/service/SharingService.java b/src/main/java/com/uid2/admin/vertx/service/SharingService.java index e202c459..37f6ecc0 100644 --- a/src/main/java/com/uid2/admin/vertx/service/SharingService.java +++ b/src/main/java/com/uid2/admin/vertx/service/SharingService.java @@ -380,7 +380,7 @@ private void handleSetAllowedSites(RoutingContext rc) { AdminKeyset newKeyset = setAdminKeyset(rc, allowedSites, allowedTypes, siteId, keysetId, name); if(newKeyset == null) return; JsonObject jo = new JsonObject(); - jo.put("allowed_sites", allowedSites); + jo.put("allowed_sites", newKeyset.getAllowedSites()); jo.put("allowed_types", newKeyset.getAllowedTypes()); jo.put("hash", newKeyset.hashCode()); @@ -430,7 +430,7 @@ private AdminKeyset setAdminKeyset(RoutingContext rc, JsonArray allowedSites, Js .boxed() .collect(Collectors.toSet()); } else { - newlist = null; + newlist = new HashSet<>(); } Set newAllowedTypes = null; diff --git a/src/test/java/com/uid2/admin/managers/KeysetManagerTest.java b/src/test/java/com/uid2/admin/managers/KeysetManagerTest.java index 5b52a982..8d33b2a0 100644 --- a/src/test/java/com/uid2/admin/managers/KeysetManagerTest.java +++ b/src/test/java/com/uid2/admin/managers/KeysetManagerTest.java @@ -58,16 +58,16 @@ public void testAddOrReplaceKeyset() throws Exception{ keysetKeyManager, true); Map keysets = new HashMap() {{ - put(1, KeysetManager.createDefaultKeyset(3, 1)); - put(2, KeysetManager.createDefaultKeyset(4, 2)); - put(3, KeysetManager.createDefaultKeyset(5, 3)); - put(4, KeysetManager.createDefaultKeyset(6, 4)); + put(1, KeysetManager.createKeysetSharedWithDsps(3, 1)); + put(2, KeysetManager.createKeysetSharedWithDsps(4, 2)); + put(3, KeysetManager.createKeysetSharedWithDsps(5, 3)); + put(4, KeysetManager.createKeysetSharedWithDsps(6, 4)); }}; setKeysets(keysets); final int keysetId = 5; // add new keyset - AdminKeyset keyset1 = KeysetManager.createDefaultKeyset(7, keysetId); + AdminKeyset keyset1 = KeysetManager.createKeysetSharedWithDsps(7, keysetId); keysetManager.addOrReplaceKeyset(keyset1); assertTrue(keysets.containsKey(5)); assertTrue(keyset1.equals(keysets.get(5))); @@ -102,7 +102,7 @@ public void createsKeysetWhenNoneExists() throws Exception { @Test public void doesNotCreateKeysetWhenOneExists() throws Exception { - final AdminKeyset keyset = KeysetManager.createDefaultKeyset(1, 1); + final AdminKeyset keyset = KeysetManager.createKeysetSharedWithDsps(1, 1); final HashMap keysets = new HashMap<>(); keysets.put(1, keyset); @@ -135,10 +135,10 @@ public void testCreateKeysetForClient() throws Exception { keysetKeyManager, true); Map keysets = new HashMap() {{ - put(1, KeysetManager.createDefaultKeyset(3, 1)); - put(2, KeysetManager.createDefaultKeyset(4, 2)); - put(3, KeysetManager.createDefaultKeyset(5, 3)); - put(4, KeysetManager.createDefaultKeyset(6, 4)); + put(1, KeysetManager.createKeysetSharedWithDsps(3, 1)); + put(2, KeysetManager.createKeysetSharedWithDsps(4, 2)); + put(3, KeysetManager.createKeysetSharedWithDsps(5, 3)); + put(4, KeysetManager.createKeysetSharedWithDsps(6, 4)); }}; setKeysets(keysets); @@ -149,12 +149,13 @@ public void testCreateKeysetForClient() throws Exception { assertTrue(sharerKeyset.equals(returnedKeyset)); assertEquals(sharerKeyset.getAllowedSites(), Set.of()); - // Generator makes a null list + // Generator makes an empty allowed_sites list with default allowed_types [DSP] ClientKey generator = new ClientKey("", "", "", "", "", Instant.now(), Set.of(Role.GENERATOR), 8, false, "key-id-8"); returnedKeyset = keysetManager.createKeysetForClient(generator); AdminKeyset generatorKeyset = keysets.get(returnedKeyset.getKeysetId()); assertTrue(generatorKeyset.equals(returnedKeyset)); - assertNull(generatorKeyset.getAllowedSites()); + assertEquals(Set.of(), generatorKeyset.getAllowedSites()); + assertEquals(Set.of(ClientType.DSP), generatorKeyset.getAllowedTypes()); // Generator takes priority of sharer ClientKey sharerGenerator = new ClientKey("", "", "", "", "", Instant.now(), Set.of(Role.SHARER, Role.GENERATOR), 9, false, "key-id-9"); @@ -162,7 +163,8 @@ public void testCreateKeysetForClient() throws Exception { returnedKeyset = keysetManager.createKeysetForClient(sharerGenerator); AdminKeyset bothKeyset = keysets.get(returnedKeyset.getKeysetId()); assertTrue(bothKeyset.equals(returnedKeyset)); - assertNull(bothKeyset.getAllowedSites()); + assertEquals(Set.of(), bothKeyset.getAllowedSites()); + assertEquals(Set.of(ClientType.DSP), bothKeyset.getAllowedTypes()); // If keyset already exists none gets added returnedKeyset = keysetManager.createKeysetForClient(sharer); @@ -172,6 +174,30 @@ public void testCreateKeysetForClient() throws Exception { assertEquals(7, keysets.keySet().size()); } + @Test + public void createKeysetForSite_newKeyset_hasNonNullEmptyAllowedSitesAndDspType() throws Exception { + setKeysets(new HashMap<>()); + KeysetManager keysetManager = new KeysetManager(keysetProvider, keysetStoreWriter, keysetKeyManager, true); + + AdminKeyset created = keysetManager.createKeysetForSite(99); + + assertNotNull(created.getAllowedSites()); + assertTrue(created.getAllowedSites().isEmpty()); + assertTrue(created.getAllowedTypes().contains(ClientType.DSP)); + } + + @Test + public void createAndAddKeyset_emptyAllowedSites_nullAllowedTypes_hasEmptySitesAndNullAllowedTypes() + throws Exception { + setKeysets(new HashMap<>()); + KeysetManager keysetManager = new KeysetManager(keysetProvider, keysetStoreWriter, keysetKeyManager, true); + + AdminKeyset created = keysetManager.createAndAddKeyset(42, new HashSet<>(), null); + + assertNotNull(created.getAllowedSites()); + assertTrue(created.getAllowedSites().isEmpty()); + assertNull(created.getAllowedTypes()); + } @Test public void testLookUpKeyset() { @@ -228,33 +254,33 @@ public void testGetMaxKeyset() { public void testKeysetNameCreation() { //expected cases of special keysets when site id and keyset id match our expectations - AdminKeyset keyset = createDefaultKeyset(-1, -1); + AdminKeyset keyset = createKeysetSharedWithDsps(-1, -1); assertEquals(keyset.getName(), KeysetManager.MasterKeysetName); - keyset = createDefaultKeyset(-2, -2); + keyset = createKeysetSharedWithDsps(-2, -2); assertEquals(keyset.getName(), KeysetManager.RefreshKeysetName); - keyset = createDefaultKeyset(2, 2); + keyset = createKeysetSharedWithDsps(2, 2); assertEquals(keyset.getName(), KeysetManager.FallbackPublisherKeysetName); //only site id matches but keyset id aren't the same as what we expected - keyset = createDefaultKeyset(-1, 3); + keyset = createKeysetSharedWithDsps(-1, 3); assertEquals(keyset.getName(), ""); - keyset = createDefaultKeyset(-2, 34); + keyset = createKeysetSharedWithDsps(-2, 34); assertEquals(keyset.getName(), ""); - keyset = createDefaultKeyset(2, 56); + keyset = createKeysetSharedWithDsps(2, 56); assertEquals(keyset.getName(), ""); //only keyset id matches but site Id aren't the same as what we expected - keyset = createDefaultKeyset(-5, 1); + keyset = createKeysetSharedWithDsps(-5, 1); assertEquals(keyset.getName(), ""); - keyset = createDefaultKeyset(-3, 2); + keyset = createKeysetSharedWithDsps(-3, 2); assertEquals(keyset.getName(), ""); - keyset = createDefaultKeyset(20, 3); + keyset = createKeysetSharedWithDsps(20, 3); assertEquals(keyset.getName(), ""); //for any other normal keyset creation - keyset = createDefaultKeyset(6, 7); + keyset = createKeysetSharedWithDsps(6, 7); assertEquals(keyset.getName(), ""); - keyset = createDefaultKeyset(9, 23); + keyset = createKeysetSharedWithDsps(9, 23); assertEquals(keyset.getName(), ""); } diff --git a/src/test/java/com/uid2/admin/vertx/ClientKeyServiceTest.java b/src/test/java/com/uid2/admin/vertx/ClientKeyServiceTest.java index 6f321641..9ea3798a 100644 --- a/src/test/java/com/uid2/admin/vertx/ClientKeyServiceTest.java +++ b/src/test/java/com/uid2/admin/vertx/ClientKeyServiceTest.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.uid2.admin.auth.AdminKeyset; import com.uid2.admin.auth.RevealedKey; import com.uid2.admin.legacy.LegacyClientKey; import com.uid2.admin.managers.KeysetManager; @@ -10,6 +11,7 @@ import com.uid2.admin.vertx.test.ServiceTestBase; import com.uid2.shared.auth.ClientKey; import com.uid2.shared.auth.Role; +import com.uid2.shared.model.ClientType; import com.uid2.shared.model.Site; import com.uid2.shared.util.Mapper; import io.vertx.core.Vertx; @@ -21,6 +23,10 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; @@ -97,6 +103,94 @@ public void clientAdd(Vertx vertx, VertxTestContext testContext) { }); } + @Test + public void clientAddWithSharer_createsKeysetWithEmptyAllowedSitesAndTypes(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + Map keysetsById = new HashMap<>(); + setAdminKeysets(keysetsById); + + post(vertx, testContext, "api/client/add?name=test_sharer&roles=sharer&site_id=999", "", response -> { + assertEquals(200, response.statusCode()); + + Optional keysetForSite = + keysetsById.values().stream().filter(k -> k.getSiteId() == 999).findFirst(); + assertTrue(keysetForSite.isPresent()); + AdminKeyset keyset = keysetForSite.get(); + assertTrue(keyset.getAllowedSites().isEmpty()); + assertTrue(keyset.getAllowedTypes().isEmpty()); + + verify(keysetKeyManager).addKeysetKey(keyset.getKeysetId()); + verify(clientKeyStoreWriter).upload(collectionOfSize(1), isNull()); + testContext.completeNow(); + }); + } + + @Test + public void clientAddWithGenerator_createsKeysetWithEmptyAllowedSitesAndDspAllowedType(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + Map keysetsById = new HashMap<>(); + setAdminKeysets(keysetsById); + + post(vertx, testContext, "api/client/add?name=test_generator&roles=generator&site_id=999", "", response -> { + assertEquals(200, response.statusCode()); + + Optional keysetForSite = + keysetsById.values().stream().filter(k -> k.getSiteId() == 999).findFirst(); + assertTrue(keysetForSite.isPresent()); + AdminKeyset keyset = keysetForSite.get(); + assertTrue(keyset.getAllowedSites().isEmpty()); + assertEquals(Set.of(ClientType.DSP), keyset.getAllowedTypes()); + + verify(keysetKeyManager).addKeysetKey(keyset.getKeysetId()); + verify(clientKeyStoreWriter).upload(collectionOfSize(1), isNull()); + testContext.completeNow(); + }); + } + + @Test + public void clientAddWithMapper_onAdvertiserTypedSite_doesNotCreateKeyset(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + setSites(new Site(999, "test_site", true, Set.of(ClientType.ADVERTISER), Collections.emptySet())); + + Map keysetsById = new HashMap<>(); + setAdminKeysets(keysetsById); + + post(vertx, testContext, "api/client/add?name=test_mapper_adv&roles=mapper&site_id=999", "", response -> { + assertEquals(200, response.statusCode()); + + assertTrue( + keysetsById.values().stream().noneMatch(k -> k.getSiteId() == 999), + "KeysetManager.createKeysetForClient returns null for MAPPER-only keys"); + + verifyNoInteractions(keysetKeyManager); + verify(clientKeyStoreWriter).upload(collectionOfSize(1), isNull()); + testContext.completeNow(); + }); + } + + @Test + public void clientAddWithMapper_onDataProviderTypedSite_doesNotCreateKeyset(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + setSites(new Site(999, "test_site", true, Set.of(ClientType.DATA_PROVIDER), Collections.emptySet())); + + Map keysetsById = new HashMap<>(); + setAdminKeysets(keysetsById); + + post(vertx, testContext, "api/client/add?name=test_mapper_dp&roles=mapper&site_id=999", "", response -> { + assertEquals(200, response.statusCode()); + + assertTrue( + keysetsById.values().stream().noneMatch(k -> k.getSiteId() == 999), + "KeysetManager.createKeysetForClient returns null for MAPPER-only keys"); + + verifyNoInteractions(keysetKeyManager); + verify(clientKeyStoreWriter).upload(collectionOfSize(1), isNull()); + testContext.completeNow(); + }); + } + @ParameterizedTest @MethodSource("createSiteKeyIfNoneExistsTestData") public void clientAddCreatesSiteKeyIfNoneExists(Set roles, boolean siteKeyShouldBeCreatedIfNoneExists, Vertx vertx, VertxTestContext testContext) { diff --git a/src/test/java/com/uid2/admin/vertx/ClientSideKeypairServiceTest.java b/src/test/java/com/uid2/admin/vertx/ClientSideKeypairServiceTest.java index 88c0cfe7..771146e6 100644 --- a/src/test/java/com/uid2/admin/vertx/ClientSideKeypairServiceTest.java +++ b/src/test/java/com/uid2/admin/vertx/ClientSideKeypairServiceTest.java @@ -1,5 +1,6 @@ package com.uid2.admin.vertx; +import com.uid2.admin.auth.AdminKeyset; import com.uid2.admin.secret.SecureKeypairGenerator; import com.uid2.admin.store.Clock; import com.uid2.admin.vertx.service.ClientSideKeypairService; @@ -7,6 +8,7 @@ import com.uid2.admin.vertx.test.ServiceTestBase; import com.uid2.shared.auth.Role; import com.uid2.shared.model.ClientSideKeypair; +import com.uid2.shared.model.ClientType; import com.uid2.shared.model.Site; import io.vertx.core.Vertx; import io.vertx.core.json.JsonArray; @@ -17,13 +19,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.mockito.ArgumentCaptor; - import java.time.Instant; import java.util.*; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; public class ClientSideKeypairServiceTest extends ServiceTestBase { @@ -253,18 +254,14 @@ void addKeypair(Vertx vertx, VertxTestContext testContext) throws Exception { setKeypairs(new ArrayList<>(expectedKeypairs.values())); setSites(new Site(123, "test", true)); + Map adminKeysets = new HashMap<>(); + setAdminKeysets(adminKeysets); + JsonObject jo = new JsonObject(); jo.put("site_id", 123); jo.put("contact", "email@email.com"); post(vertx, testContext, "api/client_side_keypairs/add", jo.encode(), response -> { - final ArgumentCaptor siteId = ArgumentCaptor.forClass(Integer.class); - try { - verify(this.keysetManager).createKeysetForSite(siteId.capture()); - } catch (Exception e) { - fail(e); - } - assertEquals(123, siteId.getValue()); assertEquals(200, response.statusCode()); JsonObject resp = response.bodyAsJsonObject(); assertEquals(123, resp.getInteger("site_id")); @@ -276,6 +273,46 @@ void addKeypair(Vertx vertx, VertxTestContext testContext) throws Exception { assertEquals("UID2-X-L-", resp.getString("public_key").substring(0, 9)); assertEquals(KEY_CREATE_TIME_IN_SECONDS, resp.getLong("created")); assertEquals(false, resp.getBoolean("disabled")); + AdminKeyset keyset = adminKeysets.values().stream() + .filter(k -> k.getSiteId() == 123) + .findFirst() + .orElseThrow(); + assertTrue(keyset.getAllowedSites().isEmpty()); + assertEquals(Set.of(ClientType.DSP), keyset.getAllowedTypes()); + assertTrue(keyset.isDefault()); + verify(keysetKeyManager).addKeysetKey(keyset.getKeysetId()); + verify(adminKeysetWriter).upload(any(), isNull()); + verify(keypairStoreWriter, times(1)).upload(any(), isNull()); + testContext.completeNow(); + }); + } + + @Test + void addKeypair_createsKeysetForSiteWhenNoneExists(Vertx vertx, VertxTestContext testContext) throws Exception { + fakeAuth(Role.MAINTAINER); + setSites(new Site(456, "csp_keyset_site", true)); + setKeypairs(new ArrayList<>()); + + Map adminKeysets = new HashMap<>(); + setAdminKeysets(adminKeysets); + + JsonObject jo = new JsonObject(); + jo.put("site_id", 456); + jo.put("contact", "keyset-check@example.com"); + + post(vertx, testContext, "api/client_side_keypairs/add", jo.encode(), response -> { + assertEquals(200, response.statusCode()); + + AdminKeyset keyset = adminKeysets.values().stream() + .filter(k -> k.getSiteId() == 456) + .findFirst() + .orElseThrow(); + assertTrue(keyset.getAllowedSites().isEmpty()); + assertEquals(Set.of(ClientType.DSP), keyset.getAllowedTypes()); + assertTrue(keyset.isDefault()); + + verify(keysetKeyManager).addKeysetKey(keyset.getKeysetId()); + verify(adminKeysetWriter).upload(any(), isNull()); verify(keypairStoreWriter, times(1)).upload(any(), isNull()); testContext.completeNow(); }); diff --git a/src/test/java/com/uid2/admin/vertx/EncryptionKeyServiceTest.java b/src/test/java/com/uid2/admin/vertx/EncryptionKeyServiceTest.java index 6a839cdb..f16b6cff 100644 --- a/src/test/java/com/uid2/admin/vertx/EncryptionKeyServiceTest.java +++ b/src/test/java/com/uid2/admin/vertx/EncryptionKeyServiceTest.java @@ -7,6 +7,7 @@ import com.uid2.admin.vertx.test.ServiceTestBase; import com.uid2.shared.Const; import com.uid2.shared.auth.Role; +import com.uid2.shared.model.ClientType; import com.uid2.shared.model.EncryptionKey; import com.uid2.shared.model.KeysetKey; import io.vertx.core.Vertx; @@ -150,7 +151,7 @@ void addSiteKeyAddsKeysetAndKey() throws Exception { setKeysetKeys(123); final EncryptionKey key = keyService.addSiteKey(5); - AdminKeyset expected = new AdminKeyset(4, 5, "", null, Instant.now().getEpochSecond(), true, true, new HashSet<>()); + AdminKeyset expected = new AdminKeyset(4, 5, "", new HashSet<>(), Instant.now().getEpochSecond(), true, true, Set.of(ClientType.DSP)); assertNotNull(keysets.get(4)); assertEquals(expected, keysets.get(4)); verify(keysetKeyStoreWriter).upload(collectionOfSize(1), eq(124)); diff --git a/src/test/java/com/uid2/admin/vertx/SharingServiceTest.java b/src/test/java/com/uid2/admin/vertx/SharingServiceTest.java index ee6758d7..963d7162 100644 --- a/src/test/java/com/uid2/admin/vertx/SharingServiceTest.java +++ b/src/test/java/com/uid2/admin/vertx/SharingServiceTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; @@ -26,8 +27,7 @@ import java.util.stream.Stream; import static com.uid2.admin.vertx.Endpoints.API_SHARING_KEYSETS_RELATED; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class SharingServiceTest extends ServiceTestBase { @@ -52,7 +52,7 @@ private void compareKeysetTypeListToResult(AdminKeyset keyset, JsonArray actualL .collect(Collectors.toSet()); assertEquals(keyset.getAllowedTypes(), actualSet); } - + private void mockSiteExistence(Integer... sites){ for(Integer site : sites) { doReturn(new Site(site, "test-name", true, null)).when(siteProvider).getSite(site); @@ -250,6 +250,37 @@ void listSiteSetNew(Vertx vertx, VertxTestContext testContext) { }); } + @Test + void listSiteSetNewAllowedSitesMissingInRequest(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.SHARING_PORTAL); + + Map keysets = new HashMap() {{ + put(3, new AdminKeyset(3, 5, "test", Set.of(4, 6, 7), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(4, new AdminKeyset(4, 7, "test", Set.of(12), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(5, new AdminKeyset(5, 4, "test", Set.of(5), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + }}; + + setAdminKeysets(keysets); + mockSiteExistence(5, 7, 4, 8); + + String body = " {\n" + + " \"hash\": 0\n" + + " }"; + + post(vertx, testContext, "api/sharing/list/8", body, response -> { + assertEquals(200, response.statusCode()); + + AdminKeyset expected = new AdminKeyset(6, 8, "", Set.of(), Instant.now().getEpochSecond(), true, true, new HashSet<>()); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(6).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(6).getAllowedTypes()); + + verify(keysetKeyManager).addKeysetKey(6); + testContext.completeNow(); + }); + } + @Test void listSiteSetNewWithType(Vertx vertx, VertxTestContext testContext) { fakeAuth(Role.SHARING_PORTAL); @@ -1112,8 +1143,9 @@ void KeysetSetNewDisallowMultipleForSite(Vertx vertx, VertxTestContext testConte }); } + // `allowed_sites = null` is no longer allowed in the admin UI. This test is retained to verify normalization: if `allowed_sites` is null or missing, it should be converted to an empty set.` @Test - void KeysetSetNewNullAllowedSites(Vertx vertx, VertxTestContext testContext) { + void KeysetSetNewAllowedSitesMissingInRequest(Vertx vertx, VertxTestContext testContext) { fakeAuth(Role.MAINTAINER); mockSiteExistence(5, 3); @@ -1131,15 +1163,20 @@ void KeysetSetNewNullAllowedSites(Vertx vertx, VertxTestContext testContext) { post(vertx, testContext, "api/sharing/keyset", body, response -> { assertEquals(200, response.statusCode()); - AdminKeyset expected = new AdminKeyset(2, 1, "test", null, Instant.now().getEpochSecond(), true, true, new HashSet<>()); - assertEquals(null, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + // Next keyset id is max(existing ids, 3) + 1 => 4 when only keyset 1 exists + int newKeysetId = 4; + AdminKeyset expected = new AdminKeyset(newKeysetId, 3, "", new HashSet<>(), Instant.now().getEpochSecond(), true, true, new HashSet<>()); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(newKeysetId).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(newKeysetId).getAllowedTypes()); testContext.completeNow(); }); } @Test - void KeysetSetNewExplicitlyNullAllowedSites(Vertx vertx, VertxTestContext testContext) { + void KeysetSetUsingNewExplicitlyNullAllowedSitesInput(Vertx vertx, VertxTestContext testContext) { fakeAuth(Role.MAINTAINER); mockSiteExistence(5, 3); @@ -1158,8 +1195,77 @@ void KeysetSetNewExplicitlyNullAllowedSites(Vertx vertx, VertxTestContext testCo post(vertx, testContext, "api/sharing/keyset", body, response -> { assertEquals(200, response.statusCode()); - AdminKeyset expected = new AdminKeyset(2, 1, "test", null, Instant.now().getEpochSecond(), true, true, new HashSet<>()); - assertEquals(null, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + int newKeysetId = 4; + AdminKeyset expected = new AdminKeyset(newKeysetId, 3, "", new HashSet<>(), Instant.now().getEpochSecond(), true, true, new HashSet<>()); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(newKeysetId).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(newKeysetId).getAllowedTypes()); + + testContext.completeNow(); + }); + } + + @Test + void KeysetSetAllowedSitesMissingWithNonEmptyAllowedTypesInput(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + mockSiteExistence(5, 3); + + Map keysets = new HashMap() {{ + put(1, new AdminKeyset(1, 5, "test", Set.of(4, 6, 7), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + }}; + + setAdminKeysets(keysets); + + String body = " {\n" + + " \"site_id\": 3,\n" + + " \"allowed_types\": [ \"ADVERTISER\" ]\n" + + " }"; + + post(vertx, testContext, "api/sharing/keyset", body, response -> { + assertEquals(200, response.statusCode()); + + int newKeysetId = 4; + AdminKeyset expected = new AdminKeyset(newKeysetId, 3, "", new HashSet<>(), Instant.now().getEpochSecond(), true, true, + Set.of(ClientType.ADVERTISER)); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(newKeysetId).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(newKeysetId).getAllowedTypes()); + + testContext.completeNow(); + }); + } + + @Test + void KeysetSetWithExplicitNullAllowedSitesAndNonEmptyAllowedTypesInput(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + mockSiteExistence(5, 3); + + Map keysets = new HashMap() {{ + put(1, new AdminKeyset(1, 5, "test", Set.of(4, 6, 7), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + }}; + + setAdminKeysets(keysets); + + String body = " {\n" + + " \"site_id\": 3,\n" + + " \"allowed_sites\": null,\n" + + " \"allowed_types\": [ \"ADVERTISER\" ]\n" + + " }"; + + post(vertx, testContext, "api/sharing/keyset", body, response -> { + assertEquals(200, response.statusCode()); + + int newKeysetId = 4; + AdminKeyset expected = new AdminKeyset(newKeysetId, 3, "", new HashSet<>(), Instant.now().getEpochSecond(), true, true, + Set.of(ClientType.ADVERTISER)); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(newKeysetId).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(newKeysetId).getAllowedTypes()); testContext.completeNow(); }); @@ -1184,8 +1290,11 @@ void KeysetSetUpdateNullAllowedSites(Vertx vertx, VertxTestContext testContext) post(vertx, testContext, "api/sharing/keyset", body, response -> { assertEquals(200, response.statusCode()); - AdminKeyset expected = new AdminKeyset(1, 5, "test", null, Instant.now().getEpochSecond(), true, true, new HashSet<>()); - assertEquals(null, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + AdminKeyset expected = new AdminKeyset(1, 5, "test", Set.of(), Instant.now().getEpochSecond(), true, true, new HashSet<>()); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(1).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(1).getAllowedTypes()); testContext.completeNow(); }); @@ -1211,8 +1320,11 @@ void KeysetSetUpdateExplicitlyNullAllowedSites(Vertx vertx, VertxTestContext tes post(vertx, testContext, "api/sharing/keyset", body, response -> { assertEquals(200, response.statusCode()); - AdminKeyset expected = new AdminKeyset(1, 5, "test", null, Instant.now().getEpochSecond(), true, true, new HashSet<>()); - assertEquals(null, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + AdminKeyset expected = new AdminKeyset(1, 5, "test", Set.of(), Instant.now().getEpochSecond(), true, true, new HashSet<>()); + compareKeysetListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_sites")); + compareKeysetTypeListToResult(expected, response.bodyAsJsonObject().getJsonArray("allowed_types")); + assertEquals(expected.getAllowedSites(), keysets.get(1).getAllowedSites()); + assertEquals(expected.getAllowedTypes(), keysets.get(1).getAllowedTypes()); testContext.completeNow(); }); @@ -1446,4 +1558,64 @@ void RelatedKeysetSetsWithoutIdReader(Vertx vertx, VertxTestContext testContext) testContext.completeNow(); }); } + + @ParameterizedTest + @EnumSource(value = ClientType.class, names = {"ADVERTISER", "DATA_PROVIDER"}) + void keysetSetNewViaHttp_forAdvertiserOrDataProvider_neverCallsCreateKeysetSharedWithDsps( + ClientType sharingType, Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + Map keysets = new HashMap<>() {{ + put(3, new AdminKeyset(3, 5, "test", Set.of(4, 6, 7), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(4, new AdminKeyset(4, 7, "test", Set.of(12), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(5, new AdminKeyset(5, 4, "test", Set.of(5), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + }}; + setAdminKeysets(keysets); + mockSiteExistence(5, 7, 4, 8); + + String body = "{\n" + + " \"site_id\": 8,\n" + + " \"allowed_types\": [\"" + sharingType.name() + "\"]\n" + + "}"; + + post(vertx, testContext, "api/sharing/keyset", body, response -> { + assertEquals(200, response.statusCode()); + JsonArray allowedTypesJson = response.bodyAsJsonObject().getJsonArray("allowed_types"); + compareKeysetTypeListToResult( + new AdminKeyset(6, 8, "", Set.of(), Instant.now().getEpochSecond(), true, true, Set.of(sharingType)), + allowedTypesJson); + assertEquals(1, allowedTypesJson.size()); + testContext.completeNow(); + }); + } + + @ParameterizedTest + @EnumSource(value = ClientType.class, names = {"ADVERTISER", "DATA_PROVIDER"}) + void listSiteSetNewViaHttp_forAdvertiserOrDataProvider_neverCallsCreateKeysetSharedWithDsps( + ClientType sharingType, Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.SHARING_PORTAL); + + Map keysets = new HashMap<>() {{ + put(3, new AdminKeyset(3, 5, "test", Set.of(4, 6, 7), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(4, new AdminKeyset(4, 7, "test", Set.of(12), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + put(5, new AdminKeyset(5, 4, "test", Set.of(5), Instant.now().getEpochSecond(), true, true, new HashSet<>())); + }}; + setAdminKeysets(keysets); + mockSiteExistence(5, 7, 4, 8); + + String body = "{\n" + + " \"allowed_types\": [\"" + sharingType.name() + "\"],\n" + + " \"hash\": 0\n" + + "}"; + + post(vertx, testContext, "api/sharing/list/8", body, response -> { + assertEquals(200, response.statusCode()); + JsonArray allowedTypesJson = response.bodyAsJsonObject().getJsonArray("allowed_types"); + compareKeysetTypeListToResult( + new AdminKeyset(6, 8, "", Set.of(), Instant.now().getEpochSecond(), true, true, Set.of(sharingType)), + allowedTypesJson); + assertEquals(1, allowedTypesJson.size()); + testContext.completeNow(); + }); + } } diff --git a/webroot/adm/keyset.html b/webroot/adm/keyset.html index 6ad5413c..c1633c09 100644 --- a/webroot/adm/keyset.html +++ b/webroot/adm/keyset.html @@ -202,20 +202,6 @@

UID2 Env - Keyset Access Management

keyset_id: parseInt(inputs.keysetId) }) } - }, - { - id: 'resetAllowedSitesToNull', - title: 'Reset Allowed Sites To Null', - role: 'maintainer', - inputs: [keysetIdInput], - apiCall: { - method: 'POST', - url: '/api/sharing/keyset', - getPayload: (inputs) => ({ - allowed_sites: null, - keyset_id: parseInt(inputs.keysetId) - }) - } } ] };