@things-factory/auth-ui 10.1.107 → 10.1.111

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.
@@ -42,3 +42,11 @@ export declare function showsRoleOrigin(roles: RoleWithOrigin[] | null | undefin
42
42
  * labelled, so an unlabelled row reads as "not said" instead of as a domain called something.
43
43
  */
44
44
  export declare function roleOrigin(role: RoleWithOrigin): string;
45
+ /**
46
+ * The parent domain's roles a person here may not change (things-factory ADR-0103 decision 8).
47
+ *
48
+ * Holding a role is not tied to a domain: a parent's role given from a child counts in the parent and in its other
49
+ * children. So only someone who decides roles in the parent may give or take it - the server refuses everyone else
50
+ * (`role-belongs-to-parent`), and the screen locks those boxes rather than let a save fail.
51
+ */
52
+ export declare function lockedParentRoles(parentRoleIds: Iterable<string>, mayChangeParentRoles: boolean): Set<string>;
@@ -44,4 +44,14 @@ export function showsRoleOrigin(roles) {
44
44
  export function roleOrigin(role) {
45
45
  return role?.domain?.name || '';
46
46
  }
47
+ /**
48
+ * The parent domain's roles a person here may not change (things-factory ADR-0103 decision 8).
49
+ *
50
+ * Holding a role is not tied to a domain: a parent's role given from a child counts in the parent and in its other
51
+ * children. So only someone who decides roles in the parent may give or take it - the server refuses everyone else
52
+ * (`role-belongs-to-parent`), and the screen locks those boxes rather than let a save fail.
53
+ */
54
+ export function lockedParentRoles(parentRoleIds, mayChangeParentRoles) {
55
+ return mayChangeParentRoles ? new Set() : new Set(parentRoleIds);
56
+ }
47
57
  //# sourceMappingURL=role-origin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"role-origin.js","sourceRoot":"","sources":["../../client/utils/role-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAA0C;IACxE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAA;IAEpE,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,CAAA;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAoB;IAC7C,OAAO,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAA;AACjC,CAAC","sourcesContent":["/**\n * When a list of roles has to say where each one came from. (Pure rule.)\n *\n * ## The situation it is for\n *\n * Roles are visible one level up: a domain sees its own and its parent's. Nothing stops a\n * domain having a role with the same name as one it inherits — the duplicate checks on the\n * server ask only about the domain itself — so a picker can offer two rows reading `approver`\n * with no way to tell them apart. Whichever gets ticked decides who a gate lets through, and\n * an approval line can end up pointing at the one nobody looks at (ADR-0062 decision 3).\n *\n * ## Why the rule is about the list and not about one role\n *\n * A role does not know whether it is inherited — that depends on which domain is being looked\n * at, and a list of roles from one domain is the ordinary case. So the question is asked of the\n * list: **does this set span more than one domain?** If it does, every row says which, and the\n * rows that shared a name stop being identical. If it does not, nothing is added and the\n * ordinary case stays quiet.\n *\n * That also answers correctly for a role whose name happens to be unique but which still comes\n * from somewhere else — granting one of those is a different act from granting your own, and\n * the operator should be able to see which they are doing.\n */\n\n/** The part of a role this rule reads. `domain` is absent on lists that never asked for it. */\nexport type RoleWithOrigin = {\n domain?: { name?: string | null } | null\n} | null | undefined\n\n/**\n * Should this list show where each role comes from?\n *\n * False when every role names the same domain, when none of them names one at all, and for an\n * empty list. A list that is only partly labelled counts as spanning — one row that cannot say\n * where it came from, beside rows that can, is the ambiguity this exists to remove.\n */\nexport function showsRoleOrigin(roles: RoleWithOrigin[] | null | undefined): boolean {\n if (!roles || roles.length < 2) {\n return false\n }\n\n const origins = new Set(roles.map(role => role?.domain?.name || ''))\n\n return origins.size > 1\n}\n\n/**\n * What to print beside a role, or an empty string.\n *\n * A role with no domain on it prints nothing rather than a placeholder: the list is already\n * labelled, so an unlabelled row reads as \"not said\" instead of as a domain called something.\n */\nexport function roleOrigin(role: RoleWithOrigin): string {\n return role?.domain?.name || ''\n}\n"]}
1
+ {"version":3,"file":"role-origin.js","sourceRoot":"","sources":["../../client/utils/role-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAA0C;IACxE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAA;IAEpE,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,CAAA;AACzB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAoB;IAC7C,OAAO,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAA;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,aAA+B,EAAE,oBAA6B;IAC9F,OAAO,oBAAoB,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,aAAa,CAAC,CAAA;AAClE,CAAC","sourcesContent":["/**\n * When a list of roles has to say where each one came from. (Pure rule.)\n *\n * ## The situation it is for\n *\n * Roles are visible one level up: a domain sees its own and its parent's. Nothing stops a\n * domain having a role with the same name as one it inherits — the duplicate checks on the\n * server ask only about the domain itself — so a picker can offer two rows reading `approver`\n * with no way to tell them apart. Whichever gets ticked decides who a gate lets through, and\n * an approval line can end up pointing at the one nobody looks at (ADR-0062 decision 3).\n *\n * ## Why the rule is about the list and not about one role\n *\n * A role does not know whether it is inherited — that depends on which domain is being looked\n * at, and a list of roles from one domain is the ordinary case. So the question is asked of the\n * list: **does this set span more than one domain?** If it does, every row says which, and the\n * rows that shared a name stop being identical. If it does not, nothing is added and the\n * ordinary case stays quiet.\n *\n * That also answers correctly for a role whose name happens to be unique but which still comes\n * from somewhere else — granting one of those is a different act from granting your own, and\n * the operator should be able to see which they are doing.\n */\n\n/** The part of a role this rule reads. `domain` is absent on lists that never asked for it. */\nexport type RoleWithOrigin = {\n domain?: { name?: string | null } | null\n} | null | undefined\n\n/**\n * Should this list show where each role comes from?\n *\n * False when every role names the same domain, when none of them names one at all, and for an\n * empty list. A list that is only partly labelled counts as spanning — one row that cannot say\n * where it came from, beside rows that can, is the ambiguity this exists to remove.\n */\nexport function showsRoleOrigin(roles: RoleWithOrigin[] | null | undefined): boolean {\n if (!roles || roles.length < 2) {\n return false\n }\n\n const origins = new Set(roles.map(role => role?.domain?.name || ''))\n\n return origins.size > 1\n}\n\n/**\n * What to print beside a role, or an empty string.\n *\n * A role with no domain on it prints nothing rather than a placeholder: the list is already\n * labelled, so an unlabelled row reads as \"not said\" instead of as a domain called something.\n */\nexport function roleOrigin(role: RoleWithOrigin): string {\n return role?.domain?.name || ''\n}\n\n/**\n * The parent domain's roles a person here may not change (things-factory ADR-0103 decision 8).\n *\n * Holding a role is not tied to a domain: a parent's role given from a child counts in the parent and in its other\n * children. So only someone who decides roles in the parent may give or take it - the server refuses everyone else\n * (`role-belongs-to-parent`), and the screen locks those boxes rather than let a save fail.\n */\nexport function lockedParentRoles(parentRoleIds: Iterable<string>, mayChangeParentRoles: boolean): Set<string> {\n return mayChangeParentRoles ? new Set() : new Set(parentRoleIds)\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { roleOrigin, showsRoleOrigin } from './role-origin';
1
+ import { lockedParentRoles, roleOrigin, showsRoleOrigin } from './role-origin';
2
2
  /**
3
3
  * Whether a role picker has to say where each role came from.
4
4
  *
@@ -57,4 +57,10 @@ describe('what is printed beside a role', () => {
57
57
  expect(roleOrigin({ domain: { name: null } })).toEqual('');
58
58
  });
59
59
  });
60
+ describe('the parent domain\'s roles (ADR-0103 decision 8)', () => {
61
+ it('★ locked for someone who does not decide in the parent, open for someone who does', () => {
62
+ expect([...lockedParentRoles(['p1', 'p2'], false)].sort()).toEqual(['p1', 'p2']);
63
+ expect([...lockedParentRoles(['p1', 'p2'], true)]).toEqual([]);
64
+ });
65
+ });
60
66
  //# sourceMappingURL=role-origin.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"role-origin.test.js","sourceRoot":"","sources":["../../client/utils/role-origin.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { roleOrigin, showsRoleOrigin } from './role-origin'\n\n/**\n * Whether a role picker has to say where each role came from.\n *\n * The cost of being wrong is not symmetric. Saying nothing when the list spans two domains\n * leaves two identical checkboxes, and ticking the wrong one points a gate — or an approval\n * line — at a role nobody looks at. Saying it when there is nothing to disambiguate only adds\n * a word.\n */\n\nconst OURS = { domain: { name: 'acme' } }\nconst THEIRS = { domain: { name: 'system' } }\n\ndescribe('a list that needs to say', () => {\n it('spans two domains', () => {\n /* ⚠ The case. Two rows can read the same name and only this separates them. */\n expect(showsRoleOrigin([OURS, THEIRS])).toBe(true)\n })\n\n it('says so even when the names do not collide', () => {\n /*\n * Granting an inherited role is a different act from granting your own, whether or not\n * some other row happens to share its name.\n */\n expect(showsRoleOrigin([{ domain: { name: 'acme' } }, { domain: { name: 'system' } }])).toBe(true)\n })\n\n it('counts a row that cannot say where it came from as a difference', () => {\n /*\n * ⚠ A half-labelled list is the ambiguity, not the cure. One blank row beside labelled ones\n * is exactly what the reader cannot resolve.\n */\n expect(showsRoleOrigin([OURS, {}])).toBe(true)\n expect(showsRoleOrigin([OURS, { domain: null }])).toBe(true)\n })\n})\n\ndescribe('a list that does not', () => {\n it('is all one domain', () => {\n expect(showsRoleOrigin([OURS, OURS, OURS])).toBe(false)\n })\n\n it('never asked for the domain at all', () => {\n /* Nothing to say. Labelling every row with nothing would be noise, not information. */\n expect(showsRoleOrigin([{}, {}])).toBe(false)\n })\n\n it('has one role, or none', () => {\n expect(showsRoleOrigin([OURS])).toBe(false)\n expect(showsRoleOrigin([])).toBe(false)\n expect(showsRoleOrigin(null)).toBe(false)\n expect(showsRoleOrigin(undefined)).toBe(false)\n })\n})\n\ndescribe('what is printed beside a role', () => {\n it('is the domain it came from', () => {\n expect(roleOrigin(OURS)).toEqual('acme')\n })\n\n it('is nothing when the role does not carry one', () => {\n /* Not a placeholder. An unlabelled row reads as \"not said\", not as a domain named that. */\n expect(roleOrigin({})).toEqual('')\n expect(roleOrigin({ domain: null })).toEqual('')\n expect(roleOrigin(null)).toEqual('')\n expect(roleOrigin({ domain: { name: null } })).toEqual('')\n })\n})\n"]}
1
+ {"version":3,"file":"role-origin.test.js","sourceRoot":"","sources":["../../client/utils/role-origin.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { lockedParentRoles, roleOrigin, showsRoleOrigin } from './role-origin'\n\n/**\n * Whether a role picker has to say where each role came from.\n *\n * The cost of being wrong is not symmetric. Saying nothing when the list spans two domains\n * leaves two identical checkboxes, and ticking the wrong one points a gate — or an approval\n * line — at a role nobody looks at. Saying it when there is nothing to disambiguate only adds\n * a word.\n */\n\nconst OURS = { domain: { name: 'acme' } }\nconst THEIRS = { domain: { name: 'system' } }\n\ndescribe('a list that needs to say', () => {\n it('spans two domains', () => {\n /* ⚠ The case. Two rows can read the same name and only this separates them. */\n expect(showsRoleOrigin([OURS, THEIRS])).toBe(true)\n })\n\n it('says so even when the names do not collide', () => {\n /*\n * Granting an inherited role is a different act from granting your own, whether or not\n * some other row happens to share its name.\n */\n expect(showsRoleOrigin([{ domain: { name: 'acme' } }, { domain: { name: 'system' } }])).toBe(true)\n })\n\n it('counts a row that cannot say where it came from as a difference', () => {\n /*\n * ⚠ A half-labelled list is the ambiguity, not the cure. One blank row beside labelled ones\n * is exactly what the reader cannot resolve.\n */\n expect(showsRoleOrigin([OURS, {}])).toBe(true)\n expect(showsRoleOrigin([OURS, { domain: null }])).toBe(true)\n })\n})\n\ndescribe('a list that does not', () => {\n it('is all one domain', () => {\n expect(showsRoleOrigin([OURS, OURS, OURS])).toBe(false)\n })\n\n it('never asked for the domain at all', () => {\n /* Nothing to say. Labelling every row with nothing would be noise, not information. */\n expect(showsRoleOrigin([{}, {}])).toBe(false)\n })\n\n it('has one role, or none', () => {\n expect(showsRoleOrigin([OURS])).toBe(false)\n expect(showsRoleOrigin([])).toBe(false)\n expect(showsRoleOrigin(null)).toBe(false)\n expect(showsRoleOrigin(undefined)).toBe(false)\n })\n})\n\ndescribe('what is printed beside a role', () => {\n it('is the domain it came from', () => {\n expect(roleOrigin(OURS)).toEqual('acme')\n })\n\n it('is nothing when the role does not carry one', () => {\n /* Not a placeholder. An unlabelled row reads as \"not said\", not as a domain named that. */\n expect(roleOrigin({})).toEqual('')\n expect(roleOrigin({ domain: null })).toEqual('')\n expect(roleOrigin(null)).toEqual('')\n expect(roleOrigin({ domain: { name: null } })).toEqual('')\n })\n})\n\ndescribe('the parent domain\\'s roles (ADR-0103 decision 8)', () => {\n it('★ locked for someone who does not decide in the parent, open for someone who does', () => {\n expect([...lockedParentRoles(['p1', 'p2'], false)].sort()).toEqual(['p1', 'p2'])\n expect([...lockedParentRoles(['p1', 'p2'], true)]).toEqual([])\n })\n})\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/auth-ui",
3
- "version": "10.1.107",
3
+ "version": "10.1.111",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -39,11 +39,11 @@
39
39
  "@operato/popup": "^10.15.0",
40
40
  "@operato/shell": "^10.29.2",
41
41
  "@operato/styles": "^10.0.0",
42
- "@things-factory/auth-base": "^10.1.107",
43
- "@things-factory/component-ui": "^10.1.107",
42
+ "@things-factory/auth-base": "^10.1.111",
43
+ "@things-factory/component-ui": "^10.1.111",
44
44
  "@things-factory/more-base": "^10.1.106",
45
45
  "base64url": "^3.0.1",
46
46
  "clipboard": "^2.0.6"
47
47
  },
48
- "gitHead": "65a3d5889e687d8122a65f029b926b1a4769d1c0"
48
+ "gitHead": "f33122c15e5b3e45393129e9418e2c367a273ade"
49
49
  }
@@ -409,5 +409,7 @@
409
409
  "access-request.text.recorded and told": "the request is recorded and {count} people who decide access are told.",
410
410
  "access-request.text.nobody to tell": "nobody in this domain decides access requests, so it cannot be sent. ask your administrator directly.",
411
411
  "access-request.text.denied lately": "denied on {at}: {reason}. to ask again, write what has changed.",
412
- "access-request.button.request": "request access…"
412
+ "access-request.button.request": "request access…",
413
+ "text.parent roles are changed by the parent domain": "roles of the parent domain can be changed only by someone who decides roles there.",
414
+ "access-request.issue.role-belongs-to-parent": "{role} belongs to the parent domain - only someone who decides roles there can give it."
413
415
  }
@@ -409,5 +409,7 @@
409
409
  "access-request.text.recorded and told": "このリクエストは記録され、権限を決める管理者 {count} 名に通知されます。",
410
410
  "access-request.text.nobody to tell": "この$t(term.domain)には権限を決める管理者がいないため送信できません。管理者に直接問い合わせてください。",
411
411
  "access-request.text.denied lately": "{at} に拒否されました: {reason}。再度リクエストするには、何が変わったかを書いてください。",
412
- "access-request.button.request": "権限をリクエスト…"
412
+ "access-request.button.request": "権限をリクエスト…",
413
+ "text.parent roles are changed by the parent domain": "上位$t(term.domain)のロールは、その$t(term.domain)で権限を決める人だけが変更できます。",
414
+ "access-request.issue.role-belongs-to-parent": "{role} は上位$t(term.domain)のロールです。その$t(term.domain)で権限を決める人だけが付与できます。"
413
415
  }
@@ -409,5 +409,7 @@
409
409
  "access-request.text.recorded and told": "이 요청은 기록되고, 권한을 정하는 관리자 {count}명에게 알림이 갑니다.",
410
410
  "access-request.text.nobody to tell": "이 $t(term.domain)에는 권한을 정할 관리자가 없어 보낼 수 없습니다. 관리자에게 직접 문의하세요.",
411
411
  "access-request.text.denied lately": "{at}에 거부되었습니다: {reason}. 다시 요청하려면 무엇이 달라졌는지 적어 주세요.",
412
- "access-request.button.request": "권한 요청…"
412
+ "access-request.button.request": "권한 요청…",
413
+ "text.parent roles are changed by the parent domain": "상위 $t(term.domain)의 역할은 그 $t(term.domain)에서 권한을 정하는 사람만 바꿀 수 있습니다.",
414
+ "access-request.issue.role-belongs-to-parent": "{role} 역할은 상위 $t(term.domain)의 것입니다. 그 $t(term.domain)에서 권한을 정하는 사람만 줄 수 있습니다."
413
415
  }
@@ -409,5 +409,7 @@
409
409
  "access-request.text.recorded and told": "permintaan ini direkodkan dan {count} orang yang memutuskan akses akan dimaklumkan.",
410
410
  "access-request.text.nobody to tell": "tiada sesiapa dalam $t(term.domain) ini yang memutuskan akses, jadi ia tidak boleh dihantar. hubungi pentadbir anda terus.",
411
411
  "access-request.text.denied lately": "ditolak pada {at}: {reason}. untuk memohon semula, tulis apa yang telah berubah.",
412
- "access-request.button.request": "mohon akses…"
412
+ "access-request.button.request": "mohon akses…",
413
+ "text.parent roles are changed by the parent domain": "peranan $t(term.domain) induk hanya boleh diubah oleh mereka yang memutuskan peranan di sana.",
414
+ "access-request.issue.role-belongs-to-parent": "{role} milik $t(term.domain) induk - hanya mereka yang memutuskan peranan di sana boleh memberikannya."
413
415
  }
@@ -409,5 +409,7 @@
409
409
  "access-request.text.recorded and told": "此请求会被记录,并通知 {count} 位负责权限的管理员。",
410
410
  "access-request.text.nobody to tell": "此$t(term.domain)没有负责权限的管理员,无法发送。请直接联系管理员。",
411
411
  "access-request.text.denied lately": "已于 {at} 被拒绝: {reason}。如需再次请求,请写明有何变化。",
412
- "access-request.button.request": "请求权限…"
412
+ "access-request.button.request": "请求权限…",
413
+ "text.parent roles are changed by the parent domain": "上级$t(term.domain)的角色只能由该$t(term.domain)负责权限的人更改。",
414
+ "access-request.issue.role-belongs-to-parent": "{role} 属于上级$t(term.domain),只有在该$t(term.domain)负责权限的人才能授予。"
413
415
  }