From b63e56f72144636e8ca5efb32e5261598b9e3b5e Mon Sep 17 00:00:00 2001 From: sophia chen Date: Thu, 11 Jun 2026 15:30:46 +1000 Subject: [PATCH 1/5] feat(auth): add Role.CLAUDE_ACCESS for machine service-account access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces a new fine-grained role for Claude (and similar AI service accounts) to authenticate against uid2-admin via the Okta custom scope uid2.admin.claude-access. The role is intentionally more restrictive than MAINTAINER — it grants read access to all non-reveal endpoints plus add-only POST access, with no update, delete, or reveal operations. - Add CLAUDE_ACCESS to OktaCustomScope, wiring uid2.admin.claude-access to the new Role.CLAUDE_ACCESS from uid2-shared 11.4.21-alpha-349-SNAPSHOT - Add Role.CLAUDE_ACCESS to all applicable auth.handle() calls across 13 service files and GetClientSideKeypairsBySite annotation - Reveal endpoints (GET /api/client/reveal, GET /api/operator/reveal) explicitly excluded; enclave/add and service/add also excluded CLAUDE_ACCESS is a strict subset of MAINTAINER: every endpoint it can reach, MAINTAINER can also reach. Refs: UID2-7271 Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 21 ++++++++++++++++++- .../com/uid2/admin/auth/OktaCustomScope.java | 1 + .../api/cstg/GetClientSideKeypairsBySite.java | 2 +- .../admin/vertx/service/ClientKeyService.java | 12 +++++------ .../service/ClientSideKeypairService.java | 6 +++--- .../service/CloudEncryptionKeyService.java | 4 ++-- .../admin/vertx/service/EnclaveIdService.java | 4 ++-- .../vertx/service/EncryptionKeyService.java | 6 +++--- .../vertx/service/JobDispatcherService.java | 4 ++-- .../admin/vertx/service/KeyAclService.java | 2 +- .../vertx/service/OperatorKeyService.java | 7 +++---- .../vertx/service/PartnerConfigService.java | 2 +- .../uid2/admin/vertx/service/SaltService.java | 2 +- .../vertx/service/ServiceLinkService.java | 4 ++-- .../admin/vertx/service/ServiceService.java | 4 ++-- .../admin/vertx/service/SharingService.java | 10 ++++----- .../uid2/admin/vertx/service/SiteService.java | 6 +++--- 17 files changed, 58 insertions(+), 39 deletions(-) diff --git a/pom.xml b/pom.xml index cea33e997..731a40380 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 1.12.2 5.11.2 - 11.1.91 + 11.4.21-alpha-349-SNAPSHOT 0.5.10 ${project.version} @@ -150,6 +150,18 @@ 5.12.0 test + + com.google.code.gson + gson + 2.12.1 + compile + + + org.projectlombok + lombok + 1.18.34 + true + @@ -162,6 +174,13 @@ 21 21 21 + + + org.projectlombok + lombok + 1.18.34 + + diff --git a/src/main/java/com/uid2/admin/auth/OktaCustomScope.java b/src/main/java/com/uid2/admin/auth/OktaCustomScope.java index c60a08141..512a26c08 100644 --- a/src/main/java/com/uid2/admin/auth/OktaCustomScope.java +++ b/src/main/java/com/uid2/admin/auth/OktaCustomScope.java @@ -12,6 +12,7 @@ public enum OktaCustomScope { SITE_SYNC("uid2.admin.site-sync", Role.PRIVATE_OPERATOR_SYNC), METRICS_EXPORT("uid2.admin.metrics-export", Role.METRICS_EXPORT), ENCLAVE_REGISTRAR("uid2.admin.enclave-registrar", Role.ENCLAVE_REGISTRAR), + CLAUDE_ACCESS("uid2.admin.claude-access", Role.CLAUDE_ACCESS), INVALID("invalid", Role.UNKNOWN); private final String name; private final Role role; diff --git a/src/main/java/com/uid2/admin/vertx/api/cstg/GetClientSideKeypairsBySite.java b/src/main/java/com/uid2/admin/vertx/api/cstg/GetClientSideKeypairsBySite.java index ee9f2005c..49968132a 100644 --- a/src/main/java/com/uid2/admin/vertx/api/cstg/GetClientSideKeypairsBySite.java +++ b/src/main/java/com/uid2/admin/vertx/api/cstg/GetClientSideKeypairsBySite.java @@ -27,7 +27,7 @@ public GetClientSideKeypairsBySite(IKeypairManager keypairManager) { @Path("/sites/:siteId/client-side-keypairs") @Method(ApiMethod.GET) - @Roles({Role.MAINTAINER, Role.SHARING_PORTAL}) + @Roles({Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS}) public Handler getHandler() { return UrlParameterProviders.provideSiteId(this::handleGetClientSideKeys); } diff --git a/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java b/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java index e13aa3d9b..4f6864ee9 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java @@ -69,7 +69,7 @@ public ClientKeyService(JsonObject config, @Override public void setupRoutes(Router router) { router.get(API_CLIENT_METADATA.toString()).handler( - auth.handle(this::handleClientMetadata, Role.MAINTAINER)); + auth.handle(this::handleClientMetadata, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_CLIENT_REWRITE_METADATA.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleRewriteMetadata(ctx); @@ -77,16 +77,16 @@ public void setupRoutes(Router router) { }, Role.PRIVILEGED)); router.get(API_CLIENT_LIST.toString()).handler( - auth.handle(this::handleClientList, Role.MAINTAINER, Role.METRICS_EXPORT)); + auth.handle(this::handleClientList, Role.MAINTAINER, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); router.get(API_CLIENT_LIST_SITEID.toString()).handler( - auth.handle(this::handleClientListBySite, Role.MAINTAINER, Role.SHARING_PORTAL)); + auth.handle(this::handleClientListBySite, Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.get(API_CLIENT_KEYID.toString()).handler( - auth.handle(this::handleClientByKeyId, Role.MAINTAINER, Role.SHARING_PORTAL)); + auth.handle(this::handleClientByKeyId, Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.get(API_CLIENT_CONTACT.toString()).handler( - auth.handle(this::handleClientByContact, Role.MAINTAINER, Role.SHARING_PORTAL)); + auth.handle(this::handleClientByContact, Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.get(API_CLIENT_REVEAL.toString()).handler( auth.handle(this::handleClientReveal, Role.PRIVILEGED)); @@ -95,7 +95,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleClientAdd(ctx); } - }, new AuditParams(List.of("name", "roles", "site_id"), Collections.emptyList()), Role.MAINTAINER, Role.SHARING_PORTAL)); + }, new AuditParams(List.of("name", "roles", "site_id"), Collections.emptyList()), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.post(API_CLIENT_DEL.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java b/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java index 4d2715b50..fc73648de 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java @@ -70,7 +70,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleAddKeypair(ctx); } - }, new AuditParams(Collections.emptyList(), List.of("site_id", "name", "contact", "disabled")), Role.MAINTAINER, Role.SHARING_PORTAL)); + }, new AuditParams(Collections.emptyList(), List.of("site_id", "name", "contact", "disabled")), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.post(API_CLIENT_SIDE_KEYPAIRS_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleUpdateKeypair(ctx); @@ -82,9 +82,9 @@ public void setupRoutes(Router router) { } }, new AuditParams(Collections.emptyList(), List.of("subscription_id")), Role.PRIVILEGED, Role.SHARING_PORTAL)); router.get(API_CLIENT_SIDE_KEYPAIRS_LIST.toString()).handler( - auth.handle(this::handleListAllKeypairs, Role.MAINTAINER, Role.METRICS_EXPORT)); + auth.handle(this::handleListAllKeypairs, Role.MAINTAINER, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); router.get(API_CLIENT_SIDE_KEYPAIRS_SUBSCRIPTIONID.toString()).handler( - auth.handle(this::handleListKeypair, Role.MAINTAINER) + auth.handle(this::handleListKeypair, Role.MAINTAINER, Role.CLAUDE_ACCESS) ); } diff --git a/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java b/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java index 10df2a86d..de081f3c8 100644 --- a/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java @@ -36,10 +36,10 @@ public CloudEncryptionKeyService( @Override public void setupRoutes(Router router) { router.get(Endpoints.CLOUD_ENCRYPTION_KEY_METADATA.toString()).handler( - auth.handle(this::handleMetadata, Role.MAINTAINER)); + auth.handle(this::handleMetadata, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.get(Endpoints.CLOUD_ENCRYPTION_KEY_LIST.toString()).handler( - auth.handle(this::handleList, Role.MAINTAINER) + auth.handle(this::handleList, Role.MAINTAINER, Role.CLAUDE_ACCESS) ); router.post(Endpoints.CLOUD_ENCRYPTION_KEY_ROTATE.toString()).handler( diff --git a/src/main/java/com/uid2/admin/vertx/service/EnclaveIdService.java b/src/main/java/com/uid2/admin/vertx/service/EnclaveIdService.java index 9e4859ee9..bd013a08d 100644 --- a/src/main/java/com/uid2/admin/vertx/service/EnclaveIdService.java +++ b/src/main/java/com/uid2/admin/vertx/service/EnclaveIdService.java @@ -49,9 +49,9 @@ public EnclaveIdService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { router.get(API_ENCLAVE_METADATA.toString()).handler( - auth.handle(this::handleEnclaveMetadata, Role.MAINTAINER)); + auth.handle(this::handleEnclaveMetadata, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.get(API_ENCLAVE_LIST.toString()).handler( - auth.handle(this::handleEnclaveList, Role.MAINTAINER)); + auth.handle(this::handleEnclaveList, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_ENCLAVE_ADD.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { 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 a07551b71..f8d7695a4 100644 --- a/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java @@ -125,11 +125,11 @@ public EncryptionKeyService(JsonObject config, @Override public void setupRoutes(Router router) { router.get(API_KEY_LIST.toString()).handler( - auth.handle(this::handleKeyList, Role.MAINTAINER)); + auth.handle(this::handleKeyList, Role.MAINTAINER, Role.CLAUDE_ACCESS)); if(enableKeysets) { router.get(API_KEY_LIST_KEYSET_KEYS.toString()).handler( - auth.handle(this::handleKeysetKeyList, Role.MAINTAINER)); + auth.handle(this::handleKeysetKeyList, Role.MAINTAINER, Role.CLAUDE_ACCESS)); } router.post(API_KEY_REWRITE_METADATA.toString()).blockingHandler(auth.handle((ctx) -> { @@ -148,7 +148,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleAddSiteKey(ctx); } - }, new AuditParams(List.of("site_id", "activates_in_seconds"), Collections.emptyList()), Role.MAINTAINER)); + }, new AuditParams(List.of("site_id", "activates_in_seconds"), Collections.emptyList()), Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_KEY_ROTATE_SITE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/JobDispatcherService.java b/src/main/java/com/uid2/admin/vertx/service/JobDispatcherService.java index 51156200d..80a221d2e 100644 --- a/src/main/java/com/uid2/admin/vertx/service/JobDispatcherService.java +++ b/src/main/java/com/uid2/admin/vertx/service/JobDispatcherService.java @@ -30,7 +30,7 @@ public void setupRoutes(Router router) { } }, //can be other role - Role.MAINTAINER)); + Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.get(API_JOB_DISPATCHER_JOB_QUEUE.toString()).blockingHandler(auth.handle((ctx) -> { try { @@ -40,6 +40,6 @@ public void setupRoutes(Router router) { } }, //can be other role - Role.MAINTAINER)); + Role.MAINTAINER, Role.CLAUDE_ACCESS)); } } diff --git a/src/main/java/com/uid2/admin/vertx/service/KeyAclService.java b/src/main/java/com/uid2/admin/vertx/service/KeyAclService.java index bc8685942..a7a9b6ccb 100644 --- a/src/main/java/com/uid2/admin/vertx/service/KeyAclService.java +++ b/src/main/java/com/uid2/admin/vertx/service/KeyAclService.java @@ -51,7 +51,7 @@ public KeyAclService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { router.get(API_KEYS_ACL_LIST.toString()).handler( - auth.handle(this::handleKeyAclList, Role.MAINTAINER)); + auth.handle(this::handleKeyAclList, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_KEYS_ACL_REWRITE_METADATA.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java b/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java index 542df2b8f..8237d1c7b 100644 --- a/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java @@ -75,9 +75,9 @@ public OperatorKeyService(JsonObject config, @Override public void setupRoutes(Router router) { router.get(API_OPERATOR_METADATA.toString()).handler( - auth.handle(this::handleOperatorMetadata, Role.MAINTAINER)); + auth.handle(this::handleOperatorMetadata, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.get(API_OPERATOR_LIST.toString()).handler( - auth.handle(this::handleOperatorList, Role.MAINTAINER, Role.METRICS_EXPORT)); + auth.handle(this::handleOperatorList, Role.MAINTAINER, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); router.get(API_OPERATOR_REVEAL.toString()).handler( auth.handle(this::handleOperatorReveal, new AuditParams(List.of("name"), Collections.emptyList()), Role.MAINTAINER)); @@ -85,7 +85,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleOperatorAdd(ctx); } - }, new AuditParams(List.of("name", "protocol", "site_id", "operator_type", "roles"), Collections.emptyList()), Role.MAINTAINER)); + }, new AuditParams(List.of("name", "protocol", "site_id", "operator_type", "roles"), Collections.emptyList()), Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_OPERATOR_DEL.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { @@ -381,7 +381,6 @@ private void handleOperatorUpdate(RoutingContext rc) { ResponseUtil.error(rc, 404, "operator name not found"); return; } - boolean siteIdChanged = false; if (!rc.queryParam("site_id").isEmpty()) { final Site site = RequestUtil.getSiteFromParam(rc, "site_id", this.siteProvider); diff --git a/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java b/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java index c95c4877d..52a462de6 100644 --- a/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java +++ b/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java @@ -37,7 +37,7 @@ public PartnerConfigService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { router.get(API_PARTNER_CONFIG_GET.toString()).handler( - auth.handle(this::handlePartnerConfigGet, Role.MAINTAINER)); + auth.handle(this::handlePartnerConfigGet, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_PARTNER_CONFIG_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handlePartnerConfigUpdate(ctx); diff --git a/src/main/java/com/uid2/admin/vertx/service/SaltService.java b/src/main/java/com/uid2/admin/vertx/service/SaltService.java index 527f19c04..3aed57a06 100644 --- a/src/main/java/com/uid2/admin/vertx/service/SaltService.java +++ b/src/main/java/com/uid2/admin/vertx/service/SaltService.java @@ -65,7 +65,7 @@ public SaltService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { router.get(API_SALT_SNAPSHOTS.toString()).handler( - auth.handle(this::handleSaltSnapshots, Role.MAINTAINER)); + auth.handle(this::handleSaltSnapshots, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_SALT_REBUILD.toString()).blockingHandler(auth.handle(ctx -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java b/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java index 44dee73a3..945b018e5 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java @@ -50,12 +50,12 @@ public ServiceLinkService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { - router.get(API_SERVICE_LINK_LIST.toString()).handler(auth.handle(this::handleServiceLinkList, Role.MAINTAINER, Role.METRICS_EXPORT)); + router.get(API_SERVICE_LINK_LIST.toString()).handler(auth.handle(this::handleServiceLinkList, Role.MAINTAINER, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); router.post(API_SERVICE_LINK_ADD.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleServiceLinkAdd(ctx); } - }, new AuditParams(Collections.emptyList(), List.of("link_id", "service_id", "site_id", "name", "roles")), Role.MAINTAINER)); + }, new AuditParams(Collections.emptyList(), List.of("link_id", "service_id", "site_id", "name", "roles")), Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_SERVICE_LINK_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleServiceLinkUpdate(ctx); diff --git a/src/main/java/com/uid2/admin/vertx/service/ServiceService.java b/src/main/java/com/uid2/admin/vertx/service/ServiceService.java index 2f989d09f..e10917188 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ServiceService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ServiceService.java @@ -52,8 +52,8 @@ public ServiceService(AdminAuthMiddleware auth, @Override public void setupRoutes(Router router) { - router.get(API_SERVICE_LIST.toString()).handler(auth.handle(this::handleServiceListAll, Role.MAINTAINER, Role.METRICS_EXPORT)); - router.get(API_SERVICE_LIST_SERVICE_ID.toString()).handler(auth.handle(this::handleServiceList, Role.MAINTAINER)); + router.get(API_SERVICE_LIST.toString()).handler(auth.handle(this::handleServiceListAll, Role.MAINTAINER, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); + router.get(API_SERVICE_LIST_SERVICE_ID.toString()).handler(auth.handle(this::handleServiceList, Role.MAINTAINER, Role.CLAUDE_ACCESS)); router.post(API_SERVICE_ADD.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleServiceAdd(ctx); 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 e202c4598..c300c662d 100644 --- a/src/main/java/com/uid2/admin/vertx/service/SharingService.java +++ b/src/main/java/com/uid2/admin/vertx/service/SharingService.java @@ -61,26 +61,26 @@ public SharingService(AdminAuthMiddleware auth, public void setupRoutes(Router router) { if(!enableKeysets) return; router.get(API_SHARING_LISTS.toString()).handler( - auth.handle(this::handleListAllAllowedSites, Role.MAINTAINER, Role.SHARING_PORTAL, Role.METRICS_EXPORT) + auth.handle(this::handleListAllAllowedSites, Role.MAINTAINER, Role.SHARING_PORTAL, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS) ); router.get(API_SHARING_LIST_SITEID.toString()).handler( - auth.handle(this::handleListAllowedSites, Role.MAINTAINER, Role.SHARING_PORTAL) + auth.handle(this::handleListAllowedSites, Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS) ); router.post(API_SHARING_LIST_SITEID.toString()).handler( auth.handle(this::handleSetAllowedSites, new AuditParams(Collections.emptyList(), List.of("hash", "allowed_sites", "allowed_types")), Role.MAINTAINER, Role.SHARING_PORTAL) ); router.get(API_SHARING_KEYSETS.toString()).handler( - auth.handle(this::handleListAllKeysets, Role.MAINTAINER) + auth.handle(this::handleListAllKeysets, Role.MAINTAINER, Role.CLAUDE_ACCESS) ); router.post(API_SHARING_KEYSET.toString()).handler( auth.handle(this::handleSetKeyset, new AuditParams(Collections.emptyList(), List.of("site_id", "name", "allowed_sites", "allowed_types")), Role.MAINTAINER) ); router.get(API_SHARING_KEYSET_KEYSETID.toString()).handler( - auth.handle(this::handleListKeyset, Role.MAINTAINER) + auth.handle(this::handleListKeyset, Role.MAINTAINER, Role.CLAUDE_ACCESS) ); router.get(API_SHARING_KEYSETS_RELATED.toString()).handler( - auth.handle(this::handleListAllKeysetsRelated, Role.MAINTAINER) + auth.handle(this::handleListAllKeysetsRelated, Role.MAINTAINER, Role.CLAUDE_ACCESS) ); } diff --git a/src/main/java/com/uid2/admin/vertx/service/SiteService.java b/src/main/java/com/uid2/admin/vertx/service/SiteService.java index 957380d07..5bafaeb40 100644 --- a/src/main/java/com/uid2/admin/vertx/service/SiteService.java +++ b/src/main/java/com/uid2/admin/vertx/service/SiteService.java @@ -62,14 +62,14 @@ public void setupRoutes(Router router) { }, Role.PRIVILEGED)); router.get(API_SITE_LIST.toString()).handler( - auth.handle(this::handleSiteList, Role.MAINTAINER, Role.SHARING_PORTAL, Role.METRICS_EXPORT)); + auth.handle(this::handleSiteList, Role.MAINTAINER, Role.SHARING_PORTAL, Role.METRICS_EXPORT, Role.CLAUDE_ACCESS)); router.get(API_SITE_SITEID.toString()).handler( - auth.handle(this::handleSiteById, Role.MAINTAINER, Role.SHARING_PORTAL)); + auth.handle(this::handleSiteById, Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.post(API_SITE_ADD.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleSiteAdd(ctx); } - }, new AuditParams(List.of("name", "enable", "types", "description"), List.of("domain_names", "app_names")), Role.MAINTAINER, Role.SHARING_PORTAL)); + }, new AuditParams(List.of("name", "enable", "types", "description"), List.of("domain_names", "app_names")), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); router.post(API_SITE_ENABLE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleSiteEnable(ctx); From 3e3c1f7ef1518c62a20ec6d96de252ebe35df7bd Mon Sep 17 00:00:00 2001 From: sophia chen Date: Thu, 11 Jun 2026 16:52:31 +1000 Subject: [PATCH 2/5] refactor(auth): restrict CLAUDE_ACCESS to read-only endpoints Remove Role.CLAUDE_ACCESS from all add POST endpoints (client/add, client_side_keypairs/add, key/add, operator/add, service_link/add, site/add). CLAUDE_ACCESS is now read-only. Refs: UID2-7271 Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/uid2/admin/vertx/service/ClientKeyService.java | 2 +- .../com/uid2/admin/vertx/service/ClientSideKeypairService.java | 2 +- .../java/com/uid2/admin/vertx/service/EncryptionKeyService.java | 2 +- .../java/com/uid2/admin/vertx/service/OperatorKeyService.java | 2 +- .../java/com/uid2/admin/vertx/service/ServiceLinkService.java | 2 +- src/main/java/com/uid2/admin/vertx/service/SiteService.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java b/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java index 4f6864ee9..01502d170 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ClientKeyService.java @@ -95,7 +95,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleClientAdd(ctx); } - }, new AuditParams(List.of("name", "roles", "site_id"), Collections.emptyList()), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); + }, new AuditParams(List.of("name", "roles", "site_id"), Collections.emptyList()), Role.MAINTAINER, Role.SHARING_PORTAL)); router.post(API_CLIENT_DEL.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java b/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java index fc73648de..2f8c896d6 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ClientSideKeypairService.java @@ -70,7 +70,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleAddKeypair(ctx); } - }, new AuditParams(Collections.emptyList(), List.of("site_id", "name", "contact", "disabled")), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); + }, new AuditParams(Collections.emptyList(), List.of("site_id", "name", "contact", "disabled")), Role.MAINTAINER, Role.SHARING_PORTAL)); router.post(API_CLIENT_SIDE_KEYPAIRS_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleUpdateKeypair(ctx); 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 f8d7695a4..993836064 100644 --- a/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/EncryptionKeyService.java @@ -148,7 +148,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleAddSiteKey(ctx); } - }, new AuditParams(List.of("site_id", "activates_in_seconds"), Collections.emptyList()), Role.MAINTAINER, Role.CLAUDE_ACCESS)); + }, new AuditParams(List.of("site_id", "activates_in_seconds"), Collections.emptyList()), Role.MAINTAINER)); router.post(API_KEY_ROTATE_SITE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java b/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java index 8237d1c7b..8ef80f8ec 100644 --- a/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java +++ b/src/main/java/com/uid2/admin/vertx/service/OperatorKeyService.java @@ -85,7 +85,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleOperatorAdd(ctx); } - }, new AuditParams(List.of("name", "protocol", "site_id", "operator_type", "roles"), Collections.emptyList()), Role.MAINTAINER, Role.CLAUDE_ACCESS)); + }, new AuditParams(List.of("name", "protocol", "site_id", "operator_type", "roles"), Collections.emptyList()), Role.MAINTAINER)); router.post(API_OPERATOR_DEL.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { diff --git a/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java b/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java index 945b018e5..68d905b20 100644 --- a/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java +++ b/src/main/java/com/uid2/admin/vertx/service/ServiceLinkService.java @@ -55,7 +55,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleServiceLinkAdd(ctx); } - }, new AuditParams(Collections.emptyList(), List.of("link_id", "service_id", "site_id", "name", "roles")), Role.MAINTAINER, Role.CLAUDE_ACCESS)); + }, new AuditParams(Collections.emptyList(), List.of("link_id", "service_id", "site_id", "name", "roles")), Role.MAINTAINER)); router.post(API_SERVICE_LINK_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleServiceLinkUpdate(ctx); diff --git a/src/main/java/com/uid2/admin/vertx/service/SiteService.java b/src/main/java/com/uid2/admin/vertx/service/SiteService.java index 5bafaeb40..7b67f46ba 100644 --- a/src/main/java/com/uid2/admin/vertx/service/SiteService.java +++ b/src/main/java/com/uid2/admin/vertx/service/SiteService.java @@ -69,7 +69,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handleSiteAdd(ctx); } - }, new AuditParams(List.of("name", "enable", "types", "description"), List.of("domain_names", "app_names")), Role.MAINTAINER, Role.SHARING_PORTAL, Role.CLAUDE_ACCESS)); + }, new AuditParams(List.of("name", "enable", "types", "description"), List.of("domain_names", "app_names")), Role.MAINTAINER, Role.SHARING_PORTAL)); router.post(API_SITE_ENABLE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handleSiteEnable(ctx); From 60b782789e8b399d264b16aed1b28ed584c52ee8 Mon Sep 17 00:00:00 2001 From: sophia chen Date: Thu, 11 Jun 2026 16:56:51 +1000 Subject: [PATCH 3/5] chore: remove accidentally added gson and lombok dependencies from pom.xml Co-Authored-By: Claude Sonnet 4.6 --- pom.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pom.xml b/pom.xml index b95253e42..5c8ad7062 100644 --- a/pom.xml +++ b/pom.xml @@ -186,18 +186,6 @@ 5.12.0 test - - com.google.code.gson - gson - 2.12.1 - compile - - - org.projectlombok - lombok - 1.18.34 - true - From d4d787da667f564c53ac2df377f686a0163404fe Mon Sep 17 00:00:00 2001 From: sophia chen Date: Thu, 11 Jun 2026 17:03:26 +1000 Subject: [PATCH 4/5] fix(auth): remove CLAUDE_ACCESS from POST /api/partner_config/add Missed in the read-only restriction pass. Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/uid2/admin/vertx/service/PartnerConfigService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java b/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java index 53a792d2a..10995efe5 100644 --- a/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java +++ b/src/main/java/com/uid2/admin/vertx/service/PartnerConfigService.java @@ -52,7 +52,7 @@ public void setupRoutes(Router router) { synchronized (writeLock) { this.handlePartnerConfigAdd(ctx); } - }, new AuditParams(Collections.emptyList(), List.of("name")), Role.MAINTAINER, Role.CLAUDE_ACCESS)); + }, new AuditParams(Collections.emptyList(), List.of("name")), Role.MAINTAINER)); router.put(API_PARTNER_CONFIG_UPDATE.toString()).blockingHandler(auth.handle((ctx) -> { synchronized (writeLock) { this.handlePartnerConfigUpdate(ctx); From 32a678cf5e8d58decfb97fd722ce7847b954d804 Mon Sep 17 00:00:00 2001 From: sophia chen Date: Thu, 11 Jun 2026 17:06:03 +1000 Subject: [PATCH 5/5] fix(test): update RouterConfigurationTest to expect CLAUDE_ACCESS on GetClientSideKeypairsBySite Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/uid2/admin/v2Router/RouterConfigurationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/uid2/admin/v2Router/RouterConfigurationTest.java b/src/test/java/com/uid2/admin/v2Router/RouterConfigurationTest.java index 9740c328a..55c80a64e 100644 --- a/src/test/java/com/uid2/admin/v2Router/RouterConfigurationTest.java +++ b/src/test/java/com/uid2/admin/v2Router/RouterConfigurationTest.java @@ -51,7 +51,7 @@ public void WhenANonBlockingRouteProviderIsUsed_ItIsRegisteredCorrectly() { router.setupSubRouter(vertxMock, routerMock); verify(routeMock).handler(handlerMock); - verify(authMiddlewareMock).handle(any(), eq(Role.MAINTAINER), eq(Role.SHARING_PORTAL)); + verify(authMiddlewareMock).handle(any(), eq(Role.MAINTAINER), eq(Role.SHARING_PORTAL), eq(Role.CLAUDE_ACCESS)); } } }