Skip to content

Commit 7357dcc

Browse files
committed
fix(plugin-security): the platform-admin promotion WITHHELD warning says what 18.0 supports for a v17 database
The warning ended by pointing at the organization-ownership migration's id-to-name rewrite, which 18.0 refuses on every database. It now ends with the sentence the boot refusal carries. The withholding, the returned result and the log level are unchanged; a pin beside the unnamed-holder cases holds the text. Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK Co-authored-by: Claude <noreply@anthropic.com>
1 parent 73d4cc7 commit 7357dcc

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/plugin-security': patch
3+
---
4+
5+
fix(plugin-security): the platform-admin promotion WITHHELD warning says what 18.0 supports for a v17 database, not a migration 18.0 refuses
6+
7+
Clause-②: no
8+
9+
- **The boot warning "[security] platform-admin promotion WITHHELD: an unscoped grant exists whose permission-set name (sys_user_permission_set.permission_set) was never written"** used to end with a way on: "The organization-ownership migration's id-to-name rewrite (ADR-0131 D10) names it; the next pass after that reads it by name." 18.0 refuses that migration on every database, so the operator was sent to a refusal. The warning now ends with the sentence the boot refusal and the drop-gate refusal of `os migrate apply --allow-destructive` carry: "18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path."
10+
- **Unchanged:** the withholding itself (an unscoped grant that names no set is still read as an administrator who may already exist, and no second platform-admin grant is minted), the facts the warning states (which column was never written, that the grant predates it, that the set it holds can no longer be read), the log level and the `info` fallback when the logger has no `warn`, and the returned `admin_grant_unnamed` result.
11+
- **Removed from the warning:** the pointer to the organization-ownership migration's rewrite and the promise that the next pass reads the grant by name afterwards. Both described the 18.x upgrade path, which 18.0 does not carry.

‎packages/plugins/plugin-security/src/bootstrap-platform-admin.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -880,8 +880,8 @@ export async function bootstrapPlatformAdmin(
880880
'[security] platform-admin promotion WITHHELD: an unscoped grant exists whose permission-set name '
881881
+ '(sys_user_permission_set.permission_set) was never written — a grant from a release before that '
882882
+ 'column existed. Which set it holds can no longer be read, and it may be the platform administrator\'s, '
883-
+ 'so this pass will not mint a second one. The organization-ownership migration\'s id-to-name rewrite '
884-
+ '(ADR-0131 D10) names it; the next pass after that reads it by name.';
883+
+ 'so this pass will not mint a second one. 18.0 supports fresh installs only and does not upgrade a v17 '
884+
+ 'database: keep a v17 database on a 17.x release until the 18.x release that carries the upgrade path.';
885885
if (logger?.warn) logger.warn(withheld);
886886
else logger?.info?.(withheld);
887887
return {

‎packages/plugins/plugin-security/src/grant-readers-unnamed-grant.test.ts‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,43 @@ describe('[ADR-0131 D4] a grant that names nothing — the platform-admin bootst
221221
await unname(engine, { id: 'g_admin' });
222222
expect(await findExistingPlatformAdmin(engine, defaultPermissionSets)).toBeUndefined();
223223
});
224+
225+
// The one wording pin on the withheld-promotion warning. 18.0 supports fresh
226+
// installs only and its `os migrate organization-ownership` refuses plan and
227+
// apply on every database, so the warning an operator reads at boot must not
228+
// send them to it; it ends with the sentence the unnamed-grant line of the
229+
// catalog reference report and the boot refusal carry, in the same terms.
230+
// Everything before the sentence is the warning's own: what is withheld, why,
231+
// and that this pass mints no second grant.
232+
const V17_SENTENCE = '18.0 supports fresh installs only and does not upgrade a v17 database: keep a v17 database on a '
233+
+ '17.x release until the 18.x release that carries the upgrade path.';
234+
235+
it('unnamed, the withheld warning keeps what is withheld and why, ends with the 18.0 sentence, and prescribes no migration', async () => {
236+
const engine = await standingAdminUnnamed();
237+
await unname(engine, { id: 'g_admin' });
238+
// An existing database: the platform-bucket rows an earlier release seeded.
239+
await seedLegacyPlatformBucket(engine, defaultPermissionSets);
240+
const logger = { info: vi.fn(), warn: vi.fn(), error: vi.fn() };
241+
const report = await bootstrapPlatformAdmin(engine, defaultPermissionSets, { logger });
242+
243+
// Anti-vacuity: the pass took the unnamed-holder branch and logged its warning exactly once.
244+
expect(report).toMatchObject({ adminPromoted: false, reason: 'admin_grant_unnamed' });
245+
const withheld = logger.warn.mock.calls
246+
.map(([message]) => String(message))
247+
.filter((message) => message.startsWith('[security] platform-admin promotion WITHHELD'));
248+
expect(withheld).toHaveLength(1);
249+
const [message] = withheld;
250+
251+
expect(message).toBe(
252+
'[security] platform-admin promotion WITHHELD: an unscoped grant exists whose permission-set name '
253+
+ '(sys_user_permission_set.permission_set) was never written — a grant from a release before that '
254+
+ 'column existed. Which set it holds can no longer be read, and it may be the platform administrator\'s, '
255+
+ 'so this pass will not mint a second one. ' + V17_SENTENCE,
256+
);
257+
expect(message.endsWith(V17_SENTENCE), 'the sentence closes the warning').toBe(true);
258+
// The refused command and the rewrite it performed are gone, not merely qualified.
259+
expect(message).not.toMatch(/organization-ownership|os migrate|--out|--apply|--plan-file|ceremony|id-to-name/);
260+
});
224261
});
225262

226263
describe('[ADR-0131 D4] a grant that names nothing — the organization-administrator reconcile', () => {

0 commit comments

Comments
 (0)