Repository navigation
Commit 915e940
feat(plugin-security): under a walled posture, raise the access upgrade report in-app in the organization OS_PLATFORM_OWNER_ORGANIZATION names (#25489)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuUSKmaWjm1sW7kkQuwKewbx)_
Fixes #25413
Clause-②: yes (widening)
Metadata-protocol: none
Claim 6367880115 (`domain:services` seat 1, #6021). The branch is the
harness-pinned `claude/project-thread-mfvys8`. It names no issue, so the
claim is the identity record for it. #24447 (claim 6367925751) follows
on this branch after this PR merges, because its report calls the helper
added here.
## What changed
Under `group` and `isolated`, the boot access upgrade report reached the
operator only as one `error` line in the server log (option A, ruling
6353787407). The maintainer chose to configure the owner's organization
the way the owner's email is configured.
- **`@objectstack/types`:** `PLATFORM_OWNER_ORGANIZATION_ENV`
(`OS_PLATFORM_OWNER_ORGANIZATION`) and
`resolvePlatformOwnerOrganization()`, beside the
`OS_PLATFORM_OWNER_EMAIL` pair. They use the same trim and blank rule.
- **`plugin-security`, new `upgrade-report-delivery.ts`:** the delivery
of a boot upgrade report, moved out of `access-upgrade-report.ts` so
#24447 can call it (Wanted 6).
- **`single`:** unchanged and byte-identical. The notification is
stamped with the Default Organization and goes to its
`admin_full_access` holders plus the operators. The four "reached no
administrator" causes are kept.
- **Walled, setting unset:** the operator line only, worded as before
(Wanted 5).
- **Walled, setting set:** the value is resolved in `sys_organization`
by id, then by slug. The same `security.access_upgrade` notification is
emitted, with the same `dedupKey`, stamped with that organization and
addressed to the configured operators. The operator line stays beside it
and says a copy went in-app (Wanted 2).
- **Set, naming no organization:** one `error` naming the value and the
fix, then the operator line. Nothing is raised in-app and there is no
fallback (Wanted 3). An unreadable `sys_organization`, a missing
messaging service, no configured operator, or a refused emit each get
the same shape with their own cause.
- **Visibility warnings (Wanted 3 and 4):**
- A configured operator who is not a verified member of that
organization gets a `warn`, with a state of `no-account`, `unverified`
or `not-member`.
- An administrator of it who is not a configured operator gets a `warn`.
An administrator here is an `owner`/`admin` membership grade, or an
`admin_full_access` / `organization_admin` /
`organization_admin_no_bypass` grant scoped to it.
- The check costs two `find`s (`sys_member` and grants, by organization)
plus the platform-admin standing read the bootstrap already uses.
- A read failure gets one `warn`, and the copy still goes.
- **`access-upgrade-report.ts`:** `reportAccessUpgrade` hands its
finding set to the helper. The comparison, the per-category log lines
and the payload are unchanged. `AccessUpgradeNotifier` and
`AccessUpgradeReportLogger` stay exported as aliases, so
`security-plugin.ts` is not touched (the #25223 split series edits it).
- **`auto-org-admin-grant.ts`:** `isAdminRole` is exported. It is the
existing owner/admin grade test, reused by the warning instead of
copied.
- **Docs:** a row in `content/docs/deployment/environment-variables.mdx`
beside `OS_PLATFORM_OWNER_EMAIL`. It says the organization must hold the
operators only, and why.
Not touched: `security-plugin.ts`, `packages/core`, `packages/spec`,
service-messaging, the ADR.
## Tests
- **`upgrade-report-delivery.test.ts` (new, 17 cases), each with a
CONTROL:**
- resolved by id, by slug, and with stray spaces
- unset (the exact unchanged line) and blank
- an unknown value, with its CONTROL once the organization exists
- an unreadable `sys_organization`, no messaging, a refused emit, no
operator
- a non-member operator; no-account and unverified operators
- an administrator by grade and by grant, with the CONTROL that a plain
member and the tenant's own administrator are not flagged
- an unreadable `sys_member`
- `single` with the setting set and unset
- **`access-upgrade-report.wiring.test.ts`:** a real ObjectQL +
SqlDriver + `SecurityPlugin`. It covers `isolated` and `group` with the
setting naming the operator's dedicated organization (one emit, stamped
with it, audience the operator, operator line beside it, and the same
`dedupKey` on the next boot), and `isolated` with an unknown value. The
existing `single` / walled / CONTROL cases pass unchanged.
- **`packages/types/src/env.test.ts`:** spelling, unset and blank, and a
trimmed id or slug.
## Verification
- **Package suites:**
- plugin-security `pnpm test`: exit 0, 215 files, 4485 passed and 45
skipped.
- types `env.test.ts`: 58 passed.
- `pnpm typecheck` for plugin-security (tsc, scripts and test-typecheck)
and for types: exit 0.
- eslint on the touched files: exit 0.
- **Dispatch gates:** all 100 commands `dispatch-gates --commands`
derived exit 0. Two were re-run after a fix:
- `check-plugin-teardown-shape --self-test` exited 3 in a shallow clone.
After `git fetch --unshallow` it exits 0.
- `check:where-matcher` flagged the new test double. The double now
refuses a combinator, and the gate exits 0.
- `check-governed-merges --branch`: not governed, 1,100 changed lines.
- **Ablations** (committed source, mutated with
`scripts/ablation-replace.mjs`, restored to HEAD, `git diff HEAD`
empty):
1. Walled branch always takes the unset path: 16 cases go red (13
helper, 3 wiring).
2. Membership warning off: 2 go red.
3. Admin grade not read: 1 goes red.
4. Slug lookup off: 4 go red (2 helper, and the isolated and group
wiring cases).
- **R1:** `OS_LOG_LEVEL=info pnpm dev -- --fresh` on the showcase logs
`[security] access upgrade report: every activation ledger row … matches
the registry at this boot {"absent":[]}`, with no ERROR line.
- **R4:** production `os serve` of the showcase on a persistent SQLite
file, under `isolated` and `group`. Rig details:
- environment: `OS_PLATFORM_OWNER_EMAIL=op@r4.example` and
`OS_AUTH_MEMBERSHIP_POLICY=invite-only`
- rows written directly: the operator as a verified `sys_user`, `owner`
of a dedicated organization `r4-platform-ops`, a tenant organization
beside it, and the customer's switch-off of `showcase_auditor`
(`com.example.showcase`, `active=0`)
- the vendor rename was a temporary source edit to
`showcase_audit_reader`, with the `auditor` position binding it,
reverted afterwards
| boot | isolated | group |
|:--|:--|:--|
| clean | nothing reported | nothing reported |
| CONTROL (name still shipped, setting set) | nothing reported, no
`sys_notification` | same |
| rename, setting `r4-platform-ops` | re-enablement `ERROR`, then the
operator line with "A copy is raised in-app …" and `organizationId:
org_r4_platform`. One `sys_notification` row (`organization_id`
`org_r4_platform`, dedup key `security.access_upgrade:a9b3…`), one inbox
delivery and one `sys_inbox_message` row for `usr_r4_operator` in that
organization. No row in any other organization, no refused write, no
"reached no administrator". | same |
| rename again | `[messaging] emit: dedupKey … already emitted;
skipping`, still one row | same |
| setting `r4-platform-opz` (unknown) | one `ERROR … "r4-platform-opz"
names no organization by id or by slug … Fix: …`, then the unset-form
operator line | not run |
| setting unset | the unset-form operator line only, no line naming the
setting | not run |
| an `admin` member added to the operator's organization | `WARN … 1
administrator(s) … are not configured operators …` | not run |
| setting pointing at the tenant organization | `WARN … 1 configured
operator(s) are not verified members … state not-member` | not run |
The only other ERROR line on every rig boot is `[auth]
no_sign_in_account_at_boot`. The rig caused it by inserting `sys_user`
directly, and it is present on the CONTROL boot too. The WARN lines
match the CONTROL boot's.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01XHebWJmL3ZgbVpjM6JERNA)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01XHebWJmL3ZgbVpjM6JERNA
Co-authored-by: Claude <noreply@anthropic.com>1 parent 4c8f40a commit 915e940
9 files changed
Lines changed: 936 additions & 164 deletions
File tree
- .changeset
- content/docs/deployment
- packages
- plugins/plugin-security/src
- types/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
102 | 102 | | |
103 | 103 | | |
104 | 104 | | |
| 105 | + | |
105 | 106 | | |
106 | 107 | | |
107 | 108 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
77 | 77 | | |
78 | 78 | | |
79 | 79 | | |
80 | | - | |
| 80 | + | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
97 | 95 | | |
98 | 96 | | |
99 | 97 | | |
| |||
104 | 102 | | |
105 | 103 | | |
106 | 104 | | |
107 | | - | |
108 | | - | |
109 | 105 | | |
110 | 106 | | |
111 | 107 | | |
112 | 108 | | |
113 | | - | |
114 | | - | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
115 | 115 | | |
116 | 116 | | |
117 | 117 | | |
| |||
549 | 549 | | |
550 | 550 | | |
551 | 551 | | |
552 | | - | |
553 | | - | |
554 | | - | |
555 | | - | |
556 | | - | |
| 552 | + | |
557 | 553 | | |
558 | 554 | | |
559 | | - | |
560 | | - | |
561 | | - | |
562 | | - | |
563 | | - | |
564 | | - | |
565 | | - | |
566 | | - | |
567 | | - | |
568 | | - | |
| 555 | + | |
569 | 556 | | |
570 | 557 | | |
571 | 558 | | |
572 | 559 | | |
573 | 560 | | |
574 | | - | |
| 561 | + | |
575 | 562 | | |
576 | 563 | | |
577 | 564 | | |
578 | | - | |
579 | | - | |
580 | | - | |
581 | | - | |
582 | | - | |
583 | | - | |
584 | | - | |
585 | | - | |
586 | | - | |
587 | | - | |
588 | | - | |
589 | | - | |
590 | | - | |
591 | | - | |
592 | | - | |
593 | | - | |
594 | | - | |
595 | | - | |
596 | | - | |
597 | | - | |
598 | | - | |
599 | | - | |
600 | | - | |
601 | | - | |
602 | | - | |
603 | | - | |
604 | | - | |
605 | | - | |
606 | | - | |
607 | | - | |
608 | | - | |
609 | 565 | | |
610 | 566 | | |
611 | 567 | | |
| |||
706 | 662 | | |
707 | 663 | | |
708 | 664 | | |
709 | | - | |
710 | | - | |
711 | | - | |
712 | | - | |
713 | | - | |
714 | | - | |
715 | | - | |
716 | | - | |
717 | | - | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
718 | 668 | | |
719 | 669 | | |
720 | | - | |
721 | 670 | | |
722 | 671 | | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
723 | 677 | | |
724 | 678 | | |
725 | | - | |
726 | | - | |
727 | | - | |
728 | | - | |
729 | | - | |
730 | | - | |
731 | | - | |
| 679 | + | |
| 680 | + | |
732 | 681 | | |
733 | 682 | | |
734 | 683 | | |
| |||
739 | 688 | | |
740 | 689 | | |
741 | 690 | | |
742 | | - | |
743 | | - | |
744 | | - | |
745 | | - | |
746 | | - | |
747 | | - | |
748 | | - | |
749 | | - | |
750 | | - | |
751 | | - | |
752 | | - | |
753 | | - | |
754 | | - | |
755 | | - | |
756 | | - | |
757 | | - | |
758 | | - | |
759 | | - | |
760 | | - | |
761 | | - | |
762 | | - | |
763 | | - | |
764 | | - | |
765 | | - | |
766 | | - | |
767 | | - | |
768 | | - | |
769 | | - | |
770 | | - | |
771 | | - | |
772 | | - | |
773 | | - | |
774 | | - | |
775 | | - | |
776 | | - | |
777 | | - | |
778 | | - | |
779 | | - | |
780 | | - | |
781 | | - | |
782 | | - | |
783 | | - | |
784 | | - | |
785 | | - | |
786 | | - | |
787 | | - | |
788 | | - | |
789 | | - | |
790 | | - | |
791 | 691 | | |
792 | 692 | | |
793 | 693 | | |
794 | 694 | | |
795 | 695 | | |
796 | 696 | | |
797 | 697 | | |
798 | | - | |
799 | | - | |
800 | | - | |
801 | | - | |
802 | | - | |
803 | | - | |
804 | | - | |
805 | | - | |
806 | | - | |
807 | | - | |
808 | | - | |
809 | | - | |
810 | | - | |
811 | | - | |
812 | | - | |
813 | | - | |
814 | | - | |
815 | | - | |
816 | | - | |
817 | | - | |
818 | | - | |
819 | | - | |
820 | | - | |
821 | | - | |
822 | | - | |
823 | | - | |
824 | | - | |
825 | | - | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
826 | 717 | | |
0 commit comments