@stigmer/server 3.35.0 → 3.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -465,13 +465,7 @@
465
465
  }
466
466
  ]
467
467
  },
468
- "owner": {
469
- "directly_related_user_types": [
470
- {
471
- "type": "identity_account"
472
- }
473
- ]
474
- },
468
+ "owner": {},
475
469
  "viewer": {
476
470
  "directly_related_user_types": [
477
471
  {
@@ -514,7 +508,14 @@
514
508
  "this": {}
515
509
  },
516
510
  "owner": {
517
- "this": {}
511
+ "tupleToUserset": {
512
+ "computedUserset": {
513
+ "relation": "admin"
514
+ },
515
+ "tupleset": {
516
+ "relation": "organization"
517
+ }
518
+ }
518
519
  },
519
520
  "viewer": {
520
521
  "union": {
@@ -526,16 +527,6 @@
526
527
  "computedUserset": {
527
528
  "relation": "owner"
528
529
  }
529
- },
530
- {
531
- "tupleToUserset": {
532
- "computedUserset": {
533
- "relation": "admin"
534
- },
535
- "tupleset": {
536
- "relation": "organization"
537
- }
538
- }
539
530
  }
540
531
  ]
541
532
  }
@@ -559,13 +550,7 @@
559
550
  }
560
551
  ]
561
552
  },
562
- "owner": {
563
- "directly_related_user_types": [
564
- {
565
- "type": "identity_account"
566
- }
567
- ]
568
- },
553
+ "owner": {},
569
554
  "viewer": {
570
555
  "directly_related_user_types": [
571
556
  {
@@ -608,7 +593,14 @@
608
593
  "this": {}
609
594
  },
610
595
  "owner": {
611
- "this": {}
596
+ "tupleToUserset": {
597
+ "computedUserset": {
598
+ "relation": "admin"
599
+ },
600
+ "tupleset": {
601
+ "relation": "organization"
602
+ }
603
+ }
612
604
  },
613
605
  "viewer": {
614
606
  "union": {
@@ -620,16 +612,6 @@
620
612
  "computedUserset": {
621
613
  "relation": "owner"
622
614
  }
623
- },
624
- {
625
- "tupleToUserset": {
626
- "computedUserset": {
627
- "relation": "admin"
628
- },
629
- "tupleset": {
630
- "relation": "organization"
631
- }
632
- }
633
615
  }
634
616
  ]
635
617
  }
@@ -22,9 +22,9 @@
22
22
  * loads, then authorizes the loaded app exactly as `get` would
23
23
  * (AuthorizeResolvedTarget); listByOrg narrows through the list read
24
24
  * scope, so a caller who may not `get` an app does not see it listed
25
- * either: the model shows an app to its creator and the organization's
26
- * admins, never to members or viewers (fga/model/iam/oauth_app.fga,
27
- * stigmer/stigmer#1257). OAuthApp is deliberately not search-indexed (a
25
+ * either: the model shows an app to the organization's admins, who manage
26
+ * it whoever created it, and never to members or viewers
27
+ * (fga/model/iam/oauth_app.fga, stigmer/stigmer#1257, #1329). OAuthApp is deliberately not search-indexed (a
28
28
  * configuration resource; `not_search_indexed` in the kind registry).
29
29
  */
30
30
  import type { ConnectRouter } from "@connectrpc/connect";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/server",
3
- "version": "3.35.0",
3
+ "version": "3.36.0",
4
4
  "description": "The Stigmer OSS control plane as a composable library: composeServer, the extension registry and the driver seams a composition builds on. The self-contained deployable of the same server is @stigmer/server-slim.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -63,11 +63,11 @@
63
63
  "@connectrpc/connect-node": "^2.0.0",
64
64
  "@kubernetes/client-node": "2.0.0",
65
65
  "@opentelemetry/api": "^1.9.0",
66
- "@stigmer/outbound": "3.35.0",
67
- "@stigmer/plugin-package": "3.35.0",
68
- "@stigmer/protos": "3.35.0",
69
- "@stigmer/temporal-codecs": "3.35.0",
70
- "@stigmer/zip-structure": "3.35.0",
66
+ "@stigmer/outbound": "3.36.0",
67
+ "@stigmer/plugin-package": "3.36.0",
68
+ "@stigmer/protos": "3.36.0",
69
+ "@stigmer/temporal-codecs": "3.36.0",
70
+ "@stigmer/zip-structure": "3.36.0",
71
71
  "@temporalio/activity": "1.16.2",
72
72
  "@temporalio/client": "1.16.2",
73
73
  "@temporalio/common": "1.16.2",
@@ -365,10 +365,11 @@ describe.each(driverFixtures(SEEDED_KINDS))(
365
365
  });
366
366
  });
367
367
 
368
- it("an OAuth app is read by its creator and the organization's admins, and changed by its creator alone — the one org-scoped kind whose owner has no admin arm", async () => {
369
- // No pinned store document covers oauth_app (the security read's
370
- // fact 11), so its distinctive line is pinned here: `viewer` has
371
- // `admin from organization`, `can_edit` and `can_delete` do not.
368
+ it("an OAuth app is managed by its organization's current admins alone — a creator stamped on the row, now a plain member, holds nothing", async () => {
369
+ // The creator stamp derives no owner (kind_meta `owner_type` NONE),
370
+ // so the row a member created answers them as it answers any
371
+ // member; `owner` is `admin from organization` and nothing else
372
+ // (fga/tests/oauth-app-administration.fga.yaml pins the model).
372
373
  await seed("oauth_app", "oap_vendor", {
373
374
  org: "acme",
374
375
  visibility:
@@ -393,28 +394,17 @@ describe.each(driverFixtures(SEEDED_KINDS))(
393
394
  const auth = authorizer();
394
395
  for (const c of [view, edit, remove]) {
395
396
  expect(await auth.authorize(resolved(MEMBER), c)).toEqual({
397
+ kind: "deny",
398
+ reason: "",
399
+ });
400
+ expect(await auth.authorize(resolved(ADMIN), c)).toEqual({
401
+ kind: "allow",
402
+ });
403
+ // The organization's OWNER is an admin by the ladder.
404
+ expect(await auth.authorize(resolved(FOUNDER), c)).toEqual({
396
405
  kind: "allow",
397
406
  });
398
407
  }
399
- expect(await auth.authorize(resolved(ADMIN), view)).toEqual({
400
- kind: "allow",
401
- });
402
- expect(await auth.authorize(resolved(ADMIN), edit)).toEqual({
403
- kind: "deny",
404
- reason: "",
405
- });
406
- expect(await auth.authorize(resolved(ADMIN), remove)).toEqual({
407
- kind: "deny",
408
- reason: "",
409
- });
410
- // The organization's OWNER is an admin by the ladder: reads, does not change.
411
- expect(await auth.authorize(resolved(FOUNDER), view)).toEqual({
412
- kind: "allow",
413
- });
414
- expect(await auth.authorize(resolved(FOUNDER), edit)).toEqual({
415
- kind: "deny",
416
- reason: "",
417
- });
418
408
  expect(await auth.authorize(resolved(OUTSIDER), view)).toEqual({
419
409
  kind: "deny",
420
410
  reason: "",
@@ -62,10 +62,12 @@ const DOCUMENTS = [
62
62
  "license-issuer.fga.yaml",
63
63
  "mcp-server-authoring.fga.yaml",
64
64
  "memory-subject-only.fga.yaml",
65
+ "oauth-app-administration.fga.yaml",
65
66
  "org-admin-owner-inheritance.fga.yaml",
66
67
  "org-shared-environment.fga.yaml",
67
68
  "organization-role-assignment.fga.yaml",
68
69
  "plan-catalog-manager.fga.yaml",
70
+ "platform-client-administration.fga.yaml",
69
71
  "platform-visibility.fga.yaml",
70
72
  "plugin-owner.fga.yaml",
71
73
  "provider-standing-viewer.fga.yaml",
@@ -465,13 +465,7 @@
465
465
  }
466
466
  ]
467
467
  },
468
- "owner": {
469
- "directly_related_user_types": [
470
- {
471
- "type": "identity_account"
472
- }
473
- ]
474
- },
468
+ "owner": {},
475
469
  "viewer": {
476
470
  "directly_related_user_types": [
477
471
  {
@@ -514,7 +508,14 @@
514
508
  "this": {}
515
509
  },
516
510
  "owner": {
517
- "this": {}
511
+ "tupleToUserset": {
512
+ "computedUserset": {
513
+ "relation": "admin"
514
+ },
515
+ "tupleset": {
516
+ "relation": "organization"
517
+ }
518
+ }
518
519
  },
519
520
  "viewer": {
520
521
  "union": {
@@ -526,16 +527,6 @@
526
527
  "computedUserset": {
527
528
  "relation": "owner"
528
529
  }
529
- },
530
- {
531
- "tupleToUserset": {
532
- "computedUserset": {
533
- "relation": "admin"
534
- },
535
- "tupleset": {
536
- "relation": "organization"
537
- }
538
- }
539
530
  }
540
531
  ]
541
532
  }
@@ -559,13 +550,7 @@
559
550
  }
560
551
  ]
561
552
  },
562
- "owner": {
563
- "directly_related_user_types": [
564
- {
565
- "type": "identity_account"
566
- }
567
- ]
568
- },
553
+ "owner": {},
569
554
  "viewer": {
570
555
  "directly_related_user_types": [
571
556
  {
@@ -608,7 +593,14 @@
608
593
  "this": {}
609
594
  },
610
595
  "owner": {
611
- "this": {}
596
+ "tupleToUserset": {
597
+ "computedUserset": {
598
+ "relation": "admin"
599
+ },
600
+ "tupleset": {
601
+ "relation": "organization"
602
+ }
603
+ }
612
604
  },
613
605
  "viewer": {
614
606
  "union": {
@@ -620,16 +612,6 @@
620
612
  "computedUserset": {
621
613
  "relation": "owner"
622
614
  }
623
- },
624
- {
625
- "tupleToUserset": {
626
- "computedUserset": {
627
- "relation": "admin"
628
- },
629
- "tupleset": {
630
- "relation": "organization"
631
- }
632
- }
633
615
  }
634
616
  ]
635
617
  }
@@ -22,9 +22,9 @@
22
22
  * loads, then authorizes the loaded app exactly as `get` would
23
23
  * (AuthorizeResolvedTarget); listByOrg narrows through the list read
24
24
  * scope, so a caller who may not `get` an app does not see it listed
25
- * either: the model shows an app to its creator and the organization's
26
- * admins, never to members or viewers (fga/model/iam/oauth_app.fga,
27
- * stigmer/stigmer#1257). OAuthApp is deliberately not search-indexed (a
25
+ * either: the model shows an app to the organization's admins, who manage
26
+ * it whoever created it, and never to members or viewers
27
+ * (fga/model/iam/oauth_app.fga, stigmer/stigmer#1257, #1329). OAuthApp is deliberately not search-indexed (a
28
28
  * configuration resource; `not_search_indexed` in the kind registry).
29
29
  */
30
30
  import type { ConnectRouter, HandlerContext } from "@connectrpc/connect";