Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ Each entry starts with a **plain-language summary** (what changed, in
everyday words) before any technical detail — written so someone outside
engineering can understand what shipped and why it matters.

## [0.8.3] - 2026-08-31

**In plain terms:** global admins now have full access to every repo by
default — no more having to ask themselves "please grant me reader
access to the repo I just created, from the other account I happen to be
logged in as".

### Changed

- **Global admins get implicit `admin` access to every repo.** A user with
`isGlobalAdmin=true` no longer needs an explicit per-repo role grant
to read, write, merge, or check the log of any repo. They were already
able to bypass the check on the role-management endpoints (the
endpoints that let them grant themselves access); the same bypass now
applies uniformly to the data endpoints (`log`, `push`, `pull`,
`merge`, etc.) that the role-management endpoints are supposed to
protect. This removes a real-world chicken-and-egg where the operator
who provisioned a repo couldn't access it after switching to a
different (e.g. human) account for daily use.

## [0.8.2] - 2026-08-31

**In plain terms:** `deltix repo create` (and any operation that internally
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "deltix-server",
"version": "0.8.2",
"version": "0.8.3",
"private": true,
"license": "BUSL-1.1",
"type": "module",
Expand Down
29 changes: 19 additions & 10 deletions src/contexts/versioning/versioning.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,26 @@ async function requireRepoRole(
return username;
}
const role = await authService.getRepoRole(username, repoId);
if (!role || ROLE_RANK[role] < ROLE_RANK[minimumRole]) {
return c.json(
{
error: new RepoAccessDeniedError(
`User ${username} lacks ${minimumRole} access to repo ${repoId}`,
).message,
},
403,
);
if (role && ROLE_RANK[role] >= ROLE_RANK[minimumRole]) {
return { username, role };
}
return { username, role };
// Global admins implicit-admin every repo by definition: otherwise they
// would have to grant themselves access to every repo they own, which is
// a chicken-and-egg that the role-management helper (which they already
// can use) is supposed to bypass. Without this, a freshly-provisioned
// repo is invisible to its own creator when the creator logs in with a
// different account than the one that created it.
if (await authService.isGlobalAdmin(username)) {
return { username, role: 'admin' as RepoRole };
}
return c.json(
{
error: new RepoAccessDeniedError(
`User ${username} lacks ${minimumRole} access to repo ${repoId}`,
).message,
},
403,
);
}

/**
Expand Down
14 changes: 9 additions & 5 deletions tests/smoke/versioning-orphan-repo-backfill-boot.smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,19 @@ describe('orphaned repo admin backfill boot smoke test (real subprocess, real HT
expect(res.status).toBe(200);
});

it('does not grant the global admin implicit data access to an already-governed repo they were never assigned a role on', async () => {
it('grants the global admin implicit data access to an already-governed repo they were never assigned a role on', async () => {
const res = await fetch(
`http://127.0.0.1:${httpPort}/api/v1/versioning/repos/already-governed/branches`,
{ headers: { authorization: `Bearer ${accessToken}` } },
);

// Reading repo *data* (branches, commits, diffs, etc.) still requires an
// actual per-repo role -- global admin only unlocks role management,
// never becomes an implicit reader/writer/admin for repo data itself.
expect(res.status).toBe(403);
// Reading repo *data* (branches, commits, diffs, etc.) is implicit for
// any user with isGlobalAdmin=true. Otherwise an operator who provisioned
// a repo from one account and then switched to a different (e.g.
// human-named) account for daily use would be locked out of their own
// data. Global admins already had the bypass on role-management endpoints
// (POST /repos/:id/roles) -- the same bypass now uniformly applies to the
// data endpoints those endpoints are supposed to protect.
expect(res.status).toBe(200);
});
});