@tatara/cli 0.12.0 → 0.13.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.
package/dist/bin.mjs
CHANGED
|
@@ -84,6 +84,18 @@ const authz = {
|
|
|
84
84
|
label: "Permission system",
|
|
85
85
|
dependencies: ["@tatara/authz"],
|
|
86
86
|
slots: [
|
|
87
|
+
{
|
|
88
|
+
file: "src/core/authz/system-roles.ts",
|
|
89
|
+
slot: "admin",
|
|
90
|
+
unless: ["auth-keycloak"],
|
|
91
|
+
content: "{ key: \"admin\", name: \"Admin\", description: \"Full access, including roles and members.\" },"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
file: "src/core/authz/system-roles.ts",
|
|
95
|
+
slot: "admin",
|
|
96
|
+
when: ["auth-keycloak"],
|
|
97
|
+
content: "{ key: \"admin\", name: \"Admin\", description: \"Full access, including roles and members.\", heldBy: \"identity\" },"
|
|
98
|
+
},
|
|
87
99
|
{
|
|
88
100
|
file: "src/core/authz/index.ts",
|
|
89
101
|
slot: "options",
|
|
@@ -136,8 +148,11 @@ const authz = {
|
|
|
136
148
|
- Guard routes with \`middleware: [requirePermission("project:read")]\` from \`#core/authz/index\`
|
|
137
149
|
and declare \`security: [{ bearerAuth: ["project:read"] }]\`. For scoped actions, call
|
|
138
150
|
\`resolveScope(principal, "doc:read")\` in the service and filter rows by the returned scope.
|
|
139
|
-
- Effective permissions =
|
|
140
|
-
|
|
151
|
+
- Effective permissions = grants of the caller's roles + grant overlays − deny overlays. Deny
|
|
152
|
+
always wins. Roles are assigned in authz (the org's creator gets \`admin\`); a built-in role with
|
|
153
|
+
\`heldBy: "members"\` goes to every member, \`heldBy: "identity"\` to holders of that identity role.
|
|
154
|
+
- better-auth's member role is membership only (\`owner\` or \`member\`): \`authzPlugin\` in
|
|
155
|
+
\`src/modules/users/services/auth.ts\` refuses others and gives the creator \`admin\`.
|
|
141
156
|
- Custom roles and grant overlays can never grant \`role\`, \`member\` or \`audit_log\`.
|
|
142
157
|
- Built-in roles live in \`src/core/authz/system-roles.ts\`. \`authz:sync-permissions\` brings the database
|
|
143
158
|
in line with them; \`deploy\` runs it every deploy, \`db:seed\` locally.
|
|
@@ -476,7 +491,7 @@ const admin = {
|
|
|
476
491
|
{
|
|
477
492
|
file: "src/core/authz/system-roles.ts",
|
|
478
493
|
slot: "roles",
|
|
479
|
-
content: "{ key: \"staff\", name: \"Staff\", description: \"Internal staff: platform permissions on the admin app.\" },"
|
|
494
|
+
content: "{ key: \"staff\", name: \"Staff\", description: \"Internal staff: platform permissions on the admin app.\", heldBy: \"identity\" },"
|
|
480
495
|
},
|
|
481
496
|
{
|
|
482
497
|
file: "src/modules/staff/services/auth.ts",
|
|
@@ -1546,16 +1561,15 @@ const authOrgs = {
|
|
|
1546
1561
|
slot: "plugins",
|
|
1547
1562
|
when: ["authz"],
|
|
1548
1563
|
content: [
|
|
1549
|
-
"organization(
|
|
1550
|
-
"
|
|
1551
|
-
"
|
|
1552
|
-
"
|
|
1553
|
-
"
|
|
1554
|
-
"
|
|
1555
|
-
"
|
|
1556
|
-
"
|
|
1557
|
-
"
|
|
1558
|
-
" },",
|
|
1564
|
+
"organization(),",
|
|
1565
|
+
"// better-auth keeps who is in which org (owner or member); authz decides what they may do.",
|
|
1566
|
+
"// The creator gets admin (`creatorRole`). better-auth shows these lists, emails included,",
|
|
1567
|
+
"// to any member: `endpoints` puts them behind an authz permission",
|
|
1568
|
+
"authzPlugin(authz, {",
|
|
1569
|
+
" endpoints: {",
|
|
1570
|
+
" \"/organization/list-members\": \"member:read\",",
|
|
1571
|
+
" \"/organization/get-full-organization\": \"member:read\",",
|
|
1572
|
+
" \"/organization/list-invitations\": \"member:read\",",
|
|
1559
1573
|
" },",
|
|
1560
1574
|
"}),",
|
|
1561
1575
|
"admin(),"
|
|
@@ -1566,7 +1580,7 @@ const authOrgs = {
|
|
|
1566
1580
|
file: "src/modules/users/services/auth.ts",
|
|
1567
1581
|
slot: "imports",
|
|
1568
1582
|
when: ["authz"],
|
|
1569
|
-
content: "import { authz } from \"#core/authz/index\";"
|
|
1583
|
+
content: "import { authzPlugin } from \"@tatara/authz/better-auth\";\nimport { authz } from \"#core/authz/index\";"
|
|
1570
1584
|
},
|
|
1571
1585
|
{
|
|
1572
1586
|
file: "tests/helpers/auth.ts",
|
|
@@ -3484,6 +3498,41 @@ const authzPackage = {
|
|
|
3484
3498
|
}
|
|
3485
3499
|
};
|
|
3486
3500
|
//#endregion
|
|
3501
|
+
//#region src/upgrade/codemods/authz-roles.ts
|
|
3502
|
+
const PATH = "src/core/authz/system-roles.ts";
|
|
3503
|
+
/** Adds `heldBy` to the role literal `{ key: "<key>", … }`, unless it has one. */
|
|
3504
|
+
function holdRole(text, key, heldBy) {
|
|
3505
|
+
const start = text.search(new RegExp(`\\{\\s*key:\\s*"${key}"`));
|
|
3506
|
+
if (start === -1) return text;
|
|
3507
|
+
const end = text.indexOf("}", start);
|
|
3508
|
+
if (end === -1) return text;
|
|
3509
|
+
const literal = text.slice(start, end);
|
|
3510
|
+
if (literal.includes("heldBy")) return text;
|
|
3511
|
+
const body = literal.replace(/,?\s*$/, "");
|
|
3512
|
+
return `${text.slice(0, start)}${body}, heldBy: "${heldBy}" ${text.slice(end)}`;
|
|
3513
|
+
}
|
|
3514
|
+
/** Roles only follow better-auth's member or user role where the app says so (`heldBy`). */
|
|
3515
|
+
const authzRoles = {
|
|
3516
|
+
version: "0.13.0",
|
|
3517
|
+
id: "authz-roles",
|
|
3518
|
+
description: "Built-in roles say who holds them (heldBy); better-auth's member role is membership only",
|
|
3519
|
+
async run(ctx) {
|
|
3520
|
+
const { config } = ctx.project;
|
|
3521
|
+
if (!config.addons.includes("authz")) return;
|
|
3522
|
+
const text = await ctx.read(PATH).catch(() => void 0);
|
|
3523
|
+
if (text !== void 0) {
|
|
3524
|
+
let next = holdRole(text, "member", "members");
|
|
3525
|
+
next = holdRole(next, "staff", "identity");
|
|
3526
|
+
if (config.authMethods.includes("keycloak")) {
|
|
3527
|
+
for (const [, key] of text.matchAll(/\{\s*key:\s*"([^"]+)"/g)) if (key && key !== "member") next = holdRole(next, key, "identity");
|
|
3528
|
+
}
|
|
3529
|
+
if (next !== text) await ctx.write(PATH, next);
|
|
3530
|
+
}
|
|
3531
|
+
ctx.report.notes.push("Built-in roles no longer follow better-auth's member role (or the user role, unless the role has heldBy: \"identity\"). Give access with authz roles (grantSystemRole, assignRole); `member` is held by every member (heldBy: \"members\").");
|
|
3532
|
+
if (config.authMethods.includes("orgs")) ctx.report.notes.push("In src/modules/users/services/auth.ts, replace the organizationHooks that grant the creator's role and remove member access with authzPlugin(authz, { endpoints: { \"/organization/list-members\": \"member:read\", \"/organization/get-full-organization\": \"member:read\", \"/organization/list-invitations\": \"member:read\" } }) from @tatara/authz/better-auth, after organization(). It also keeps member roles to owner and member.");
|
|
3533
|
+
}
|
|
3534
|
+
};
|
|
3535
|
+
//#endregion
|
|
3487
3536
|
//#region src/upgrade/codemods/authz-stores.ts
|
|
3488
3537
|
const group = {
|
|
3489
3538
|
name: "authz store",
|
|
@@ -3985,7 +4034,8 @@ const CODEMODS = [
|
|
|
3985
4034
|
httpLimits,
|
|
3986
4035
|
jobsDrivers,
|
|
3987
4036
|
emailV2,
|
|
3988
|
-
deployLedger
|
|
4037
|
+
deployLedger,
|
|
4038
|
+
authzRoles
|
|
3989
4039
|
];
|
|
3990
4040
|
//#endregion
|
|
3991
4041
|
//#region src/upgrade/context.ts
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Built-in roles. Add a key here, then grant it permissions in module permission files. A role is
|
|
3
|
+
* held only by those it's assigned to, unless `heldBy` says every org member or an identity role.
|
|
4
|
+
*/
|
|
2
5
|
export const SYSTEM_ROLES = [
|
|
3
|
-
|
|
4
|
-
{ key: "member", name: "Member", description: "
|
|
6
|
+
// tatara:slot admin
|
|
7
|
+
{ key: "member", name: "Member", description: "Every member of the org.", heldBy: "members" },
|
|
5
8
|
// tatara:slot roles
|
|
6
9
|
] as const;
|
|
7
10
|
|
package/templates/versions.json
CHANGED
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"@tatara/http": "0.1.2",
|
|
36
36
|
"@tatara/db": "0.2.2",
|
|
37
37
|
"@tatara/testing": "0.1.1",
|
|
38
|
-
"@tatara/auth": "0.2.
|
|
38
|
+
"@tatara/auth": "0.2.1",
|
|
39
39
|
"@tatara/keycloak": "0.1.2",
|
|
40
|
-
"@tatara/authz": "0.
|
|
40
|
+
"@tatara/authz": "0.5.0",
|
|
41
41
|
"@tatara/storage": "0.2.0",
|
|
42
42
|
"@tatara/email": "0.3.0",
|
|
43
43
|
"@tatara/i18n": "0.1.0",
|