diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 6a99dfc638..7b6b153151 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -58,6 +58,12 @@ being weakened by a later `member` grant. The release gate exercises this
ordering with a deterministic barrier for 100 iterations and separately covers
`shared`, `personal`, `none`, invalid-mode, and legacy-disabled behavior.
+Release `0.183.320` preserves these roles and membership-write semantics. It
+only requires the token policy to authorize the *queried* project before a
+`projectMembers?projectId=` collection is loaded. A selected-project request
+header cannot authorize reading another project's member list; both v1 and
+v2-beta use the same check.
+
## External identity type upgrades
`oidc_user` and `oidc_group` are built-in OpenID Connect identity types. An
diff --git a/README.md b/README.md
index 772c82ad04..9259ffaed6 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@ PastureStack is an independent community effort to preserve, audit, and moderniz
## Project status
The current public GitHub Release
-[`v0.183.319`](https://github.com/PastureStack/orchestration-engine/releases/tag/v0.183.319)
-produces engine version `0.183.319`. It retains the existing Java 25, Ubuntu
+[`v0.183.320`](https://github.com/PastureStack/orchestration-engine/releases/tag/v0.183.320)
+produces engine version `0.183.320`. It retains the existing Java 25, Ubuntu
26.04, Maven, Liquibase, MariaDB/MySQL, WebSocket, dependency, concurrency, and
runtime-hardening work from the maintained compatibility line. Release builds
consume the exact `5.7.4` runtime JAR published by
@@ -21,6 +21,14 @@ product identity and provenance are carried by the artifact name, metadata,
SBOM, and release evidence. Provenance and scope are documented in
[`third-party/HAZELCAST.md`](third-party/HAZELCAST.md).
+Release `0.183.320` checks the requested project against the token's project
+access before loading a `projectMembers?projectId=` collection. This closes a
+cross-project read path when a valid `X-API-Project-Id` header and a different
+query `projectId` are supplied together. Both v1 and v2 API routes share the
+same resource manager; malformed project IDs fail without loading members.
+See [the release note](docs/releases/orchestration-engine-0.183.320.md) for
+scope and verification.
+
Release `0.183.319` makes shared Default membership reconciliation atomic. The
Engine now checks every authenticated direct and group identity and creates the
stable account membership while holding the same project lock, so a concurrent
@@ -241,7 +249,7 @@ The gate performs dependency-hygiene checks, builds every Maven module with JDK
To create the complete release archive after the gate passes:
```sh
-ENGINE_VERSION=0.183.319 bash scripts/build --release
+ENGINE_VERSION=0.183.320 bash scripts/build --release
bash scripts/check-release-artifact dist/artifacts/cattle.jar
```
diff --git a/code/framework/api-pub-sub-jetty/pom.xml b/code/framework/api-pub-sub-jetty/pom.xml
index 12e5bd18c4..d24a1cea02 100644
--- a/code/framework/api-pub-sub-jetty/pom.xml
+++ b/code/framework/api-pub-sub-jetty/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/api-pub-sub/pom.xml b/code/framework/api-pub-sub/pom.xml
index 6bd9c4e23c..423e3610a9 100644
--- a/code/framework/api-pub-sub/pom.xml
+++ b/code/framework/api-pub-sub/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/api/pom.xml b/code/framework/api/pom.xml
index 475441295a..15b89d3498 100644
--- a/code/framework/api/pom.xml
+++ b/code/framework/api/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/archaius/pom.xml b/code/framework/archaius/pom.xml
index 7a90707c8e..6153e082db 100644
--- a/code/framework/archaius/pom.xml
+++ b/code/framework/archaius/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-meta-parent
- 0.183.319
+ 0.183.320
../../meta-parent/pom.xml
diff --git a/code/framework/async/pom.xml b/code/framework/async/pom.xml
index b68648b6ce..f187c9b4e4 100644
--- a/code/framework/async/pom.xml
+++ b/code/framework/async/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/auditing/pom.xml b/code/framework/auditing/pom.xml
index 3ed5d25e1c..a51f8f58a4 100644
--- a/code/framework/auditing/pom.xml
+++ b/code/framework/auditing/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/db-loader/pom.xml b/code/framework/db-loader/pom.xml
index 1c49bcffcd..f82fb5f045 100644
--- a/code/framework/db-loader/pom.xml
+++ b/code/framework/db-loader/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/deferred/pom.xml b/code/framework/deferred/pom.xml
index 905d8ecec8..e19e2faa74 100644
--- a/code/framework/deferred/pom.xml
+++ b/code/framework/deferred/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/encryption/pom.xml b/code/framework/encryption/pom.xml
index 5ca140df01..821e131996 100644
--- a/code/framework/encryption/pom.xml
+++ b/code/framework/encryption/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/engine/pom.xml b/code/framework/engine/pom.xml
index b4c99a9dbe..57911d2b09 100644
--- a/code/framework/engine/pom.xml
+++ b/code/framework/engine/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/eventing/pom.xml b/code/framework/eventing/pom.xml
index 32cad5dd18..8a2fbc950e 100644
--- a/code/framework/eventing/pom.xml
+++ b/code/framework/eventing/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/events/pom.xml b/code/framework/events/pom.xml
index e1e16903f7..c769907bd2 100644
--- a/code/framework/events/pom.xml
+++ b/code/framework/events/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/extension-spring/pom.xml b/code/framework/extension-spring/pom.xml
index 288b5ee51c..83451903ba 100644
--- a/code/framework/extension-spring/pom.xml
+++ b/code/framework/extension-spring/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/extension/pom.xml b/code/framework/extension/pom.xml
index 1abdf49416..ff4cd0827d 100644
--- a/code/framework/extension/pom.xml
+++ b/code/framework/extension/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/java-server/pom.xml b/code/framework/java-server/pom.xml
index fad8fddfd8..488f2dd0de 100644
--- a/code/framework/java-server/pom.xml
+++ b/code/framework/java-server/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/jmx/pom.xml b/code/framework/jmx/pom.xml
index 95116e5680..3aa87480ec 100644
--- a/code/framework/jmx/pom.xml
+++ b/code/framework/jmx/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/jooq/pom.xml b/code/framework/jooq/pom.xml
index 2d9bdd5667..74134587e6 100644
--- a/code/framework/jooq/pom.xml
+++ b/code/framework/jooq/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/json/pom.xml b/code/framework/json/pom.xml
index 6341567ea6..e65194d810 100644
--- a/code/framework/json/pom.xml
+++ b/code/framework/json/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/launcher/pom.xml b/code/framework/launcher/pom.xml
index 188dcbeb08..0eff89bd18 100644
--- a/code/framework/launcher/pom.xml
+++ b/code/framework/launcher/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/lock/pom.xml b/code/framework/lock/pom.xml
index c00fec0fc8..92c446b020 100644
--- a/code/framework/lock/pom.xml
+++ b/code/framework/lock/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/logback/pom.xml b/code/framework/logback/pom.xml
index 2ce0ba387b..82cefb4b13 100644
--- a/code/framework/logback/pom.xml
+++ b/code/framework/logback/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-meta-parent
- 0.183.319
+ 0.183.320
../../meta-parent/pom.xml
diff --git a/code/framework/managed-context/pom.xml b/code/framework/managed-context/pom.xml
index 43ca9f9783..fd3e4d3927 100644
--- a/code/framework/managed-context/pom.xml
+++ b/code/framework/managed-context/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/metrics/pom.xml b/code/framework/metrics/pom.xml
index 06c0cd6690..df83a08fb8 100644
--- a/code/framework/metrics/pom.xml
+++ b/code/framework/metrics/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/module/pom.xml b/code/framework/module/pom.xml
index fe55af340c..2fe96f61a4 100644
--- a/code/framework/module/pom.xml
+++ b/code/framework/module/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/object/pom.xml b/code/framework/object/pom.xml
index 5acea849bd..bf52834a6d 100644
--- a/code/framework/object/pom.xml
+++ b/code/framework/object/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/pool/pom.xml b/code/framework/pool/pom.xml
index 800c0d7d69..d89806008a 100644
--- a/code/framework/pool/pom.xml
+++ b/code/framework/pool/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/resource-monitor/pom.xml b/code/framework/resource-monitor/pom.xml
index 81183b1301..ae2e95a754 100644
--- a/code/framework/resource-monitor/pom.xml
+++ b/code/framework/resource-monitor/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/schema/pom.xml b/code/framework/schema/pom.xml
index 4fab042159..5313832be2 100644
--- a/code/framework/schema/pom.xml
+++ b/code/framework/schema/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/server/pom.xml b/code/framework/server/pom.xml
index ee3e6c61cb..01cbb2da81 100644
--- a/code/framework/server/pom.xml
+++ b/code/framework/server/pom.xml
@@ -4,7 +4,7 @@
cattle-meta-parent
io.cattle
- 0.183.319
+ 0.183.320
../../meta-parent/pom.xml
diff --git a/code/framework/spring/pom.xml b/code/framework/spring/pom.xml
index eb1d8ac5b2..9a638b41ba 100644
--- a/code/framework/spring/pom.xml
+++ b/code/framework/spring/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/system-task/pom.xml b/code/framework/system-task/pom.xml
index c203fe7afb..6f1f649bba 100644
--- a/code/framework/system-task/pom.xml
+++ b/code/framework/system-task/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/token/pom.xml b/code/framework/token/pom.xml
index ca457dbc12..8ce86ee518 100644
--- a/code/framework/token/pom.xml
+++ b/code/framework/token/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/framework/utils/pom.xml b/code/framework/utils/pom.xml
index a8bb139d55..c994404084 100644
--- a/code/framework/utils/pom.xml
+++ b/code/framework/utils/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/agent-instance/pom.xml b/code/iaas/agent-instance/pom.xml
index 53fa56c556..f5d35a64d9 100644
--- a/code/iaas/agent-instance/pom.xml
+++ b/code/iaas/agent-instance/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/agent-server/pom.xml b/code/iaas/agent-server/pom.xml
index 781bd0469e..48c7920733 100644
--- a/code/iaas/agent-server/pom.xml
+++ b/code/iaas/agent-server/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/agent/pom.xml b/code/iaas/agent/pom.xml
index 04941d4007..f09d236a2b 100644
--- a/code/iaas/agent/pom.xml
+++ b/code/iaas/agent/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/allocator/pom.xml b/code/iaas/allocator/pom.xml
index 9c3749a38f..c614bfb7f7 100644
--- a/code/iaas/allocator/pom.xml
+++ b/code/iaas/allocator/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/api-logic/pom.xml b/code/iaas/api-logic/pom.xml
index 2ca937e0dc..88efa3526f 100644
--- a/code/iaas/api-logic/pom.xml
+++ b/code/iaas/api-logic/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/archaius-management/pom.xml b/code/iaas/archaius-management/pom.xml
index 68f531027b..af084de400 100644
--- a/code/iaas/archaius-management/pom.xml
+++ b/code/iaas/archaius-management/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/auth-logic/pom.xml b/code/iaas/auth-logic/pom.xml
index e5248e4cfb..7a222a436b 100644
--- a/code/iaas/auth-logic/pom.xml
+++ b/code/iaas/auth-logic/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.319
+ 0.183.320
../../parent/pom.xml
diff --git a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/projects/ProjectMemberResourceManager.java b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/projects/ProjectMemberResourceManager.java
index 0e3aeaf96a..0d2ae47713 100644
--- a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/projects/ProjectMemberResourceManager.java
+++ b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/projects/ProjectMemberResourceManager.java
@@ -65,10 +65,7 @@ protected Object listInternal(SchemaFactory schemaFactory, String type, Map members;
if (StringUtils.isNotEmpty(projectId)) {
- members = authDao.getActiveProjectMembers(Long.valueOf(projectId));
+ long requestedProjectId;
+ try {
+ requestedProjectId = Long.parseLong(projectId);
+ } catch (NumberFormatException e) {
+ throw new ClientVisibleException(ResponseCodes.NOT_FOUND);
+ }
+ requireProjectAccess(requestedProjectId, policy);
+ members = authDao.getActiveProjectMembers(requestedProjectId);
} else {
members = authDao.getActiveProjectMembers(policy.getAccountId());
}
@@ -90,6 +94,13 @@ protected Object listInternal(SchemaFactory schemaFactory, String type, Map identities = List.class.cast(manager.listInternal(null, "projectMember",
+ criteria("projectId", String.valueOf(OTHER_PROJECT_ID)), null));
+
+ assertEquals(1, memberLoads.get());
+ assertEquals(1, identities.size());
+ assertSame(expectedIdentity, identities.get(0));
+ }
+
+ @Test
+ public void rejectsMalformedProjectIdBeforeLoadingMembers() {
+ AtomicInteger memberLoads = new AtomicInteger();
+ ProjectMemberResourceManager manager = manager(true, memberLoads);
+
+ try {
+ manager.listInternal(null, "projectMember", criteria("projectId", "not-a-project-id"), null);
+ fail("Expected an invalid project ID to be hidden");
+ } catch (ClientVisibleException e) {
+ assertEquals(404, e.getStatus());
+ }
+
+ assertEquals(0, memberLoads.get());
+ }
+
+ @Test
+ public void directMemberLookupStillRejectsForeignProject() {
+ ProjectMemberResourceManager manager = manager(false, new AtomicInteger());
+
+ try {
+ manager.listInternal(null, "projectMember", criteria("id", String.valueOf(MEMBER_ID)), null);
+ fail("Expected a foreign project member to be hidden");
+ } catch (ClientVisibleException e) {
+ assertEquals(404, e.getStatus());
+ }
+ }
+
+ @Test
+ public void directMemberLookupStillAllowsAccessibleProject() {
+ ProjectMemberResourceManager manager = manager(true, new AtomicInteger());
+
+ List> identities = List.class.cast(manager.listInternal(null, "projectMember",
+ criteria("id", String.valueOf(MEMBER_ID)), null));
+
+ assertEquals(1, identities.size());
+ assertSame(expectedIdentity, identities.get(0));
+ }
+
+ private final Identity expectedIdentity = new Identity("rancher_id", "member-77");
+
+ private ProjectMemberResourceManager manager(boolean canAccess, AtomicInteger memberLoads) {
+ ProjectMemberRecord member = new ProjectMemberRecord();
+ member.setId(MEMBER_ID);
+ member.setProjectId(OTHER_PROJECT_ID);
+ member.setExternalIdType("rancher_id");
+ member.setExternalId("member-77");
+
+ AuthDao authDao = AuthDao.class.cast(Proxy.newProxyInstance(
+ AuthDao.class.getClassLoader(), new Class>[] {AuthDao.class},
+ (proxy, method, args) -> {
+ switch (method.getName()) {
+ case "hasAccessToProject":
+ assertEquals(OTHER_PROJECT_ID, args[0]);
+ assertEquals(TOKEN_ACCOUNT_ID, args[1]);
+ assertEquals(false, args[2]);
+ return canAccess;
+ case "getActiveProjectMembers":
+ assertEquals(OTHER_PROJECT_ID, args[0]);
+ memberLoads.incrementAndGet();
+ return Collections.singletonList(member);
+ case "getProjectMember":
+ assertEquals(MEMBER_ID, args[0]);
+ return member;
+ default:
+ throw new AssertionError("Unexpected AuthDao call: " + method.getName());
+ }
+ }));
+
+ ProjectMemberResourceManager manager = new ProjectMemberResourceManager();
+ manager.authDao = authDao;
+ manager.identityManager = new IdentityManager() {
+ @Override
+ public Identity projectMemberToIdentity(ProjectMember projectMember) {
+ return expectedIdentity;
+ }
+ };
+ return manager;
+ }
+
+ private Map