@headless-lms/core 0.3.0 → 0.5.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/assets/events.d.ts +24 -18
- package/dist/assets/file-urls.d.ts +11 -7
- package/dist/assets/file-urls.js +14 -4
- package/dist/assets/file-urls.js.map +1 -1
- package/dist/assets/index.d.ts +5 -4
- package/dist/assets/index.js +4 -3
- package/dist/assets/jobs.d.ts +15 -0
- package/dist/assets/jobs.js +30 -0
- package/dist/assets/jobs.js.map +1 -0
- package/dist/assets/ports.d.ts +37 -17
- package/dist/assets/service.d.ts +30 -15
- package/dist/assets/service.js +229 -59
- package/dist/assets/service.js.map +1 -1
- package/dist/automations/actions.js +2 -63
- package/dist/automations/actions.js.map +1 -1
- package/dist/automations/catalog.d.ts +1 -2
- package/dist/automations/catalog.js +137 -65
- package/dist/automations/catalog.js.map +1 -1
- package/dist/automations/community-derivations.js +20 -20
- package/dist/automations/community-derivations.js.map +1 -1
- package/dist/automations/community-mail.d.ts +36 -20
- package/dist/automations/community-mail.js +32 -24
- package/dist/automations/community-mail.js.map +1 -1
- package/dist/automations/event-permissions.d.ts +12 -0
- package/dist/automations/event-permissions.js +38 -0
- package/dist/automations/event-permissions.js.map +1 -0
- package/dist/automations/events.d.ts +0 -10
- package/dist/automations/index.d.ts +12 -4
- package/dist/automations/index.js +9 -1
- package/dist/automations/notifications/applicable.d.ts +8 -0
- package/dist/automations/notifications/applicable.js +12 -0
- package/dist/automations/notifications/applicable.js.map +1 -0
- package/dist/automations/notifications/debounce.d.ts +25 -0
- package/dist/automations/notifications/debounce.js +93 -0
- package/dist/automations/notifications/debounce.js.map +1 -0
- package/dist/automations/notifications/defaults.d.ts +8 -0
- package/dist/automations/notifications/defaults.js +346 -0
- package/dist/automations/notifications/defaults.js.map +1 -0
- package/dist/automations/notifications/delivery.d.ts +28 -0
- package/dist/automations/notifications/delivery.js +88 -0
- package/dist/automations/notifications/delivery.js.map +1 -0
- package/dist/automations/notifications/processor.d.ts +64 -0
- package/dist/automations/notifications/processor.js +270 -0
- package/dist/automations/notifications/processor.js.map +1 -0
- package/dist/automations/notifications/rules/content.d.ts +6 -0
- package/dist/automations/notifications/rules/content.js +96 -0
- package/dist/automations/notifications/rules/content.js.map +1 -0
- package/dist/automations/notifications/rules/discussion.d.ts +7 -0
- package/dist/automations/notifications/rules/discussion.js +678 -0
- package/dist/automations/notifications/rules/discussion.js.map +1 -0
- package/dist/automations/notifications/rules/entitlements.d.ts +6 -0
- package/dist/automations/notifications/rules/entitlements.js +276 -0
- package/dist/automations/notifications/rules/entitlements.js.map +1 -0
- package/dist/automations/notifications/rules/events.d.ts +9 -0
- package/dist/automations/notifications/rules/events.js +27 -0
- package/dist/automations/notifications/rules/events.js.map +1 -0
- package/dist/automations/notifications/rules/index.d.ts +6 -0
- package/dist/automations/notifications/rules/index.js +15 -0
- package/dist/automations/notifications/rules/index.js.map +1 -0
- package/dist/automations/notifications/rules/organizations.d.ts +6 -0
- package/dist/automations/notifications/rules/organizations.js +35 -0
- package/dist/automations/notifications/rules/organizations.js.map +1 -0
- package/dist/automations/notifications/rules/recipients.d.ts +32 -0
- package/dist/automations/notifications/rules/recipients.js +98 -0
- package/dist/automations/notifications/rules/recipients.js.map +1 -0
- package/dist/automations/notifications/service.d.ts +29 -0
- package/dist/automations/notifications/service.js +45 -0
- package/dist/automations/notifications/service.js.map +1 -0
- package/dist/automations/notifications/types.d.ts +7 -0
- package/dist/automations/notifications/types.js +560 -0
- package/dist/automations/notifications/types.js.map +1 -0
- package/dist/automations/ports.d.ts +134 -21
- package/dist/automations/service.d.ts +30 -30
- package/dist/automations/service.js +111 -144
- package/dist/automations/service.js.map +1 -1
- package/dist/content/event-sight.d.ts +11 -0
- package/dist/content/event-sight.js +11 -0
- package/dist/content/event-sight.js.map +1 -0
- package/dist/content/events.d.ts +437 -46
- package/dist/content/events.js +45 -1
- package/dist/content/events.js.map +1 -1
- package/dist/content/index.d.ts +8 -4
- package/dist/content/index.js +5 -1
- package/dist/content/jobs.d.ts +16 -0
- package/dist/content/jobs.js +37 -0
- package/dist/content/jobs.js.map +1 -0
- package/dist/content/model.d.ts +2 -2
- package/dist/content/notification-categories.d.ts +37 -0
- package/dist/content/notification-categories.js +73 -0
- package/dist/content/notification-categories.js.map +1 -0
- package/dist/content/ports.d.ts +132 -68
- package/dist/content/service.d.ts +100 -68
- package/dist/content/service.js +703 -146
- package/dist/content/service.js.map +1 -1
- package/dist/content/titles.d.ts +25 -0
- package/dist/content/titles.js +82 -0
- package/dist/content/titles.js.map +1 -0
- package/dist/content/types.d.ts +2 -2
- package/dist/discussion/authors.d.ts +4 -2
- package/dist/discussion/authors.js +9 -5
- package/dist/discussion/authors.js.map +1 -1
- package/dist/discussion/chats.d.ts +63 -39
- package/dist/discussion/chats.js +455 -190
- package/dist/discussion/chats.js.map +1 -1
- package/dist/discussion/digests.d.ts +24 -0
- package/dist/discussion/digests.js +104 -0
- package/dist/discussion/digests.js.map +1 -0
- package/dist/discussion/events.d.ts +227 -23
- package/dist/discussion/events.js +114 -19
- package/dist/discussion/events.js.map +1 -1
- package/dist/discussion/index.d.ts +12 -4
- package/dist/discussion/index.js +9 -2
- package/dist/discussion/jobs.d.ts +16 -0
- package/dist/discussion/jobs.js +40 -0
- package/dist/discussion/jobs.js.map +1 -0
- package/dist/discussion/live.d.ts +106 -0
- package/dist/discussion/live.js +642 -0
- package/dist/discussion/live.js.map +1 -0
- package/dist/discussion/mentions.d.ts +11 -0
- package/dist/discussion/mentions.js +35 -0
- package/dist/discussion/mentions.js.map +1 -0
- package/dist/discussion/ports.d.ts +279 -114
- package/dist/discussion/ports.js.map +1 -1
- package/dist/discussion/posts.d.ts +60 -44
- package/dist/discussion/posts.js +453 -287
- package/dist/discussion/posts.js.map +1 -1
- package/dist/discussion/rules.d.ts +18 -0
- package/dist/discussion/rules.js +22 -0
- package/dist/discussion/rules.js.map +1 -0
- package/dist/discussion/service.d.ts +50 -35
- package/dist/discussion/service.js +333 -205
- package/dist/discussion/service.js.map +1 -1
- package/dist/discussion/subscriptions.d.ts +37 -0
- package/dist/discussion/subscriptions.js +115 -0
- package/dist/discussion/subscriptions.js.map +1 -0
- package/dist/discussion/titles.d.ts +28 -0
- package/dist/discussion/titles.js +98 -0
- package/dist/discussion/titles.js.map +1 -0
- package/dist/entitlements/events.d.ts +20 -20
- package/dist/entitlements/index.d.ts +5 -4
- package/dist/entitlements/index.js +3 -2
- package/dist/entitlements/jobs.d.ts +12 -0
- package/dist/entitlements/jobs.js +18 -0
- package/dist/entitlements/jobs.js.map +1 -0
- package/dist/entitlements/model.d.ts +2 -2
- package/dist/entitlements/ports.d.ts +68 -38
- package/dist/entitlements/rules.d.ts +3 -6
- package/dist/entitlements/rules.js +5 -10
- package/dist/entitlements/rules.js.map +1 -1
- package/dist/entitlements/service.d.ts +43 -28
- package/dist/entitlements/service.js +387 -160
- package/dist/entitlements/service.js.map +1 -1
- package/dist/identity/devices.d.ts +30 -0
- package/dist/identity/devices.js +109 -0
- package/dist/identity/devices.js.map +1 -0
- package/dist/identity/index.d.ts +6 -5
- package/dist/identity/index.js +4 -3
- package/dist/identity/model.d.ts +2 -2
- package/dist/identity/notification-settings.d.ts +28 -0
- package/dist/identity/notification-settings.js +114 -0
- package/dist/identity/notification-settings.js.map +1 -0
- package/dist/identity/ports.d.ts +80 -56
- package/dist/identity/service.d.ts +8 -7
- package/dist/identity/service.js +17 -13
- package/dist/identity/service.js.map +1 -1
- package/dist/integrations/ports.d.ts +9 -8
- package/dist/integrations/service.d.ts +9 -8
- package/dist/integrations/service.js +33 -16
- package/dist/integrations/service.js.map +1 -1
- package/dist/organizations/default-roles.d.ts +22 -0
- package/dist/organizations/default-roles.js +89 -0
- package/dist/organizations/default-roles.js.map +1 -0
- package/dist/organizations/effective-permissions.d.ts +45 -0
- package/dist/organizations/effective-permissions.js +113 -0
- package/dist/organizations/effective-permissions.js.map +1 -0
- package/dist/organizations/events.d.ts +320 -181
- package/dist/organizations/events.js +90 -18
- package/dist/organizations/events.js.map +1 -1
- package/dist/organizations/index.d.ts +15 -10
- package/dist/organizations/index.js +8 -3
- package/dist/organizations/members.d.ts +66 -8
- package/dist/organizations/members.js.map +1 -1
- package/dist/organizations/notification-catalog.d.ts +31 -0
- package/dist/organizations/notification-catalog.js +32 -0
- package/dist/organizations/notification-catalog.js.map +1 -0
- package/dist/organizations/notification-categories.d.ts +34 -0
- package/dist/organizations/notification-categories.js +66 -0
- package/dist/organizations/notification-categories.js.map +1 -0
- package/dist/organizations/org-roles.d.ts +33 -0
- package/dist/organizations/org-roles.js +49 -0
- package/dist/organizations/org-roles.js.map +1 -0
- package/dist/organizations/permissions.d.ts +311 -0
- package/dist/organizations/permissions.js +327 -0
- package/dist/organizations/permissions.js.map +1 -0
- package/dist/organizations/ports.d.ts +187 -65
- package/dist/organizations/service.d.ts +104 -35
- package/dist/organizations/service.js +1247 -323
- package/dist/organizations/service.js.map +1 -1
- package/dist/organizations/testing.d.ts +18 -0
- package/dist/organizations/testing.js +37 -0
- package/dist/organizations/testing.js.map +1 -0
- package/dist/organizations/types.d.ts +5 -16
- package/dist/progress/index.d.ts +2 -2
- package/dist/progress/ports.d.ts +11 -3
- package/dist/progress/service.d.ts +6 -5
- package/dist/progress/service.js +23 -8
- package/dist/progress/service.js.map +1 -1
- package/dist/reporting/admin/index.d.ts +6 -4
- package/dist/reporting/admin/model.d.ts +36 -3
- package/dist/reporting/admin/ports.d.ts +39 -14
- package/dist/reporting/admin/service.d.ts +32 -11
- package/dist/reporting/admin/service.js +102 -18
- package/dist/reporting/admin/service.js.map +1 -1
- package/dist/reporting/events/index.d.ts +27 -0
- package/dist/reporting/events/index.js +20 -0
- package/dist/reporting/events/index.js.map +1 -0
- package/dist/reporting/member/calendar.d.ts +11 -0
- package/dist/reporting/member/calendar.js +75 -0
- package/dist/reporting/member/calendar.js.map +1 -0
- package/dist/reporting/member/index.d.ts +3 -2
- package/dist/reporting/member/model.d.ts +80 -26
- package/dist/reporting/member/ports.d.ts +50 -28
- package/dist/reporting/member/service.d.ts +81 -35
- package/dist/reporting/member/service.js +438 -145
- package/dist/reporting/member/service.js.map +1 -1
- package/dist/shared/actor.d.ts +22 -3
- package/dist/shared/errors.d.ts +15 -2
- package/dist/shared/errors.js +21 -2
- package/dist/shared/errors.js.map +1 -1
- package/dist/shared/id.d.ts +7 -0
- package/dist/shared/id.js +8 -1
- package/dist/shared/id.js.map +1 -1
- package/dist/shared/logger.d.ts +2 -1
- package/dist/shared/logger.js +10 -1
- package/dist/shared/logger.js.map +1 -1
- package/dist/shared/mailer.d.ts +11 -1
- package/dist/shared/mailer.js +32 -5
- package/dist/shared/mailer.js.map +1 -1
- package/dist/shared/notification-categories.d.ts +16 -0
- package/dist/shared/notification-categories.js +94 -0
- package/dist/shared/notification-categories.js.map +1 -0
- package/dist/shared/notification-settings.d.ts +14 -0
- package/dist/shared/notification-settings.js +61 -0
- package/dist/shared/notification-settings.js.map +1 -0
- package/dist/shared/ports.d.ts +68 -2
- package/dist/shared/settings.d.ts +2 -1
- package/dist/shared/settings.js +1 -0
- package/dist/shared/settings.js.map +1 -1
- package/dist/types/automations.d.ts +8 -2
- package/dist/types/content.d.ts +2 -2
- package/dist/types/discussion.d.ts +4 -5
- package/dist/types/email-templates.d.ts +119 -6
- package/dist/types/entitlements.d.ts +2 -2
- package/dist/types/identity.d.ts +14 -17
- package/dist/types/index.d.ts +13 -12
- package/dist/types/index.js +2 -1
- package/dist/types/notifications.d.ts +157 -0
- package/dist/types/notifications.js +1 -0
- package/dist/types/organizations.d.ts +3 -2
- package/dist/types/ports.d.ts +8 -1
- package/dist/types/ports.js +7 -1
- package/dist/types/ports.js.map +1 -0
- package/dist/types/schemas/assets.d.ts +5 -5
- package/dist/types/schemas/assets.js +4 -3
- package/dist/types/schemas/assets.js.map +1 -1
- package/dist/types/schemas/automations.d.ts +1 -10
- package/dist/types/schemas/automations.js +1 -3
- package/dist/types/schemas/automations.js.map +1 -1
- package/dist/types/schemas/content.d.ts +596 -11
- package/dist/types/schemas/content.js +225 -9
- package/dist/types/schemas/content.js.map +1 -1
- package/dist/types/schemas/discussion.d.ts +362 -66
- package/dist/types/schemas/discussion.js +123 -15
- package/dist/types/schemas/discussion.js.map +1 -1
- package/dist/types/schemas/entitlements.d.ts +14 -1
- package/dist/types/schemas/entitlements.js +11 -4
- package/dist/types/schemas/entitlements.js.map +1 -1
- package/dist/types/schemas/identity.d.ts +1 -85
- package/dist/types/schemas/identity.js +1 -42
- package/dist/types/schemas/identity.js.map +1 -1
- package/dist/types/schemas/index.d.ts +8 -7
- package/dist/types/schemas/index.js +8 -7
- package/dist/types/schemas/notifications.d.ts +96 -0
- package/dist/types/schemas/notifications.js +57 -0
- package/dist/types/schemas/notifications.js.map +1 -0
- package/dist/types/schemas/organizations.d.ts +96 -60
- package/dist/types/schemas/organizations.js +78 -27
- package/dist/types/schemas/organizations.js.map +1 -1
- package/dist/types/schemas/progress.d.ts +0 -1
- package/dist/types/schemas/progress.js +0 -1
- package/dist/types/schemas/progress.js.map +1 -1
- package/package.json +1 -1
- package/dist/automations/community-notifications.d.ts +0 -16
- package/dist/automations/community-notifications.js +0 -84
- package/dist/automations/community-notifications.js.map +0 -1
- package/dist/automations/seeds.d.ts +0 -17
- package/dist/automations/seeds.js +0 -235
- package/dist/automations/seeds.js.map +0 -1
- package/dist/identity/notifications.d.ts +0 -27
- package/dist/identity/notifications.js +0 -101
- package/dist/identity/notifications.js.map +0 -1
- package/dist/organizations/access-scopes.d.ts +0 -19
- package/dist/organizations/access-scopes.js +0 -89
- package/dist/organizations/access-scopes.js.map +0 -1
- package/dist/organizations/roles.d.ts +0 -19
- package/dist/organizations/roles.js +0 -82
- package/dist/organizations/roles.js.map +0 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { levelSchema } from "../types/schemas/organizations.js";
|
|
2
|
+
import { ALL_PERMISSIONS, fitsLevels, isPermission, withImplied } from "./permissions.js";
|
|
3
|
+
//#region src/organizations/default-roles.ts
|
|
4
|
+
const OWNER_ROLE_KEY = "owner";
|
|
5
|
+
const MEMBER_ROLE_KEY = "member";
|
|
6
|
+
const ADMIN_PERMISSIONS = [...ALL_PERMISSIONS];
|
|
7
|
+
const DEFAULT_ROLES = [
|
|
8
|
+
{
|
|
9
|
+
key: OWNER_ROLE_KEY,
|
|
10
|
+
name: "Owner",
|
|
11
|
+
description: "Owns the organization and holds every permission.",
|
|
12
|
+
levels: ["org"],
|
|
13
|
+
team: true,
|
|
14
|
+
permissions: [...ALL_PERMISSIONS]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: "admin",
|
|
18
|
+
name: "Admin",
|
|
19
|
+
description: "Runs the organization: content, people, access, automations and settings.",
|
|
20
|
+
levels: ["org"],
|
|
21
|
+
team: true,
|
|
22
|
+
permissions: ADMIN_PERMISSIONS
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "instructor",
|
|
26
|
+
name: "Instructor",
|
|
27
|
+
description: "Teaches courses: edits and publishes them, moderates lesson comments and sees learners.",
|
|
28
|
+
levels: ["org", "course"],
|
|
29
|
+
team: true,
|
|
30
|
+
permissions: [
|
|
31
|
+
"courses:read",
|
|
32
|
+
"courses:write",
|
|
33
|
+
"courses:moderate",
|
|
34
|
+
"progress:read",
|
|
35
|
+
"api_keys:read",
|
|
36
|
+
"api_keys:write",
|
|
37
|
+
"api_keys:delete"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
key: "moderator",
|
|
42
|
+
name: "Moderator",
|
|
43
|
+
description: "Moderates communities: posts, comments, chats, reports and space members.",
|
|
44
|
+
levels: ["org", "community"],
|
|
45
|
+
team: true,
|
|
46
|
+
permissions: ["communities:moderate"]
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
key: MEMBER_ROLE_KEY,
|
|
50
|
+
name: "Member",
|
|
51
|
+
description: "Everyone who was not given another role.",
|
|
52
|
+
levels: ["org"],
|
|
53
|
+
team: false,
|
|
54
|
+
permissions: []
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
var RoleSetError = class extends Error {
|
|
58
|
+
constructor(message) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "RoleSetError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
function validateRoleSet(roles) {
|
|
64
|
+
const keys = /* @__PURE__ */ new Set();
|
|
65
|
+
for (const role of roles) {
|
|
66
|
+
if (keys.has(role.key)) throw new RoleSetError(`role key ${role.key} is repeated`);
|
|
67
|
+
keys.add(role.key);
|
|
68
|
+
if (role.levels.length === 0) throw new RoleSetError(`role ${role.key} has no level`);
|
|
69
|
+
for (const level of role.levels) if (!levelSchema.safeParse(level).success) throw new RoleSetError(`role ${role.key} names an unknown level ${String(level)}`);
|
|
70
|
+
for (const permission of role.permissions) {
|
|
71
|
+
if (!isPermission(permission)) throw new RoleSetError(`role ${role.key} names an unknown permission ${String(permission)}`);
|
|
72
|
+
if (!fitsLevels(permission, role.levels)) throw new RoleSetError(`role ${role.key} holds ${permission}, which doesn't fit its levels ${role.levels.join(", ")}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const owner = roles.find((role) => role.key === OWNER_ROLE_KEY);
|
|
76
|
+
const member = roles.find((role) => role.key === MEMBER_ROLE_KEY);
|
|
77
|
+
if (!owner) throw new RoleSetError(`the role set lacks ${OWNER_ROLE_KEY}`);
|
|
78
|
+
if (!member) throw new RoleSetError(`the role set lacks ${MEMBER_ROLE_KEY}`);
|
|
79
|
+
if (owner.levels.length !== 1 || owner.levels[0] !== "org") throw new RoleSetError(`${OWNER_ROLE_KEY} must list the org only`);
|
|
80
|
+
if (owner.permissions.length !== ALL_PERMISSIONS.length) throw new RoleSetError(`${OWNER_ROLE_KEY} must hold every permission`);
|
|
81
|
+
if (member.levels.length !== 1 || member.levels[0] !== "org" || member.permissions.length > 0) throw new RoleSetError(`${MEMBER_ROLE_KEY} must list the org only and hold nothing`);
|
|
82
|
+
}
|
|
83
|
+
function defaultHolders(permission) {
|
|
84
|
+
return DEFAULT_ROLES.filter((role) => withImplied(role.permissions).has(permission)).map((role) => role.name);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { DEFAULT_ROLES, MEMBER_ROLE_KEY, OWNER_ROLE_KEY, RoleSetError, defaultHolders, validateRoleSet };
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=default-roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-roles.js","names":[],"sources":["../../src/organizations/default-roles.ts"],"sourcesContent":["import { levelSchema } from '../types/schemas/organizations.js';\nimport type { Level, Permission } from '../types/index.js';\nimport { ALL_PERMISSIONS, fitsLevels, isPermission, withImplied } from './permissions.js';\n\nexport interface RoleDefinition {\n key: string;\n name: string;\n description: string;\n levels: Level[];\n team: boolean;\n permissions: Permission[];\n}\n\nexport const OWNER_ROLE_KEY = 'owner';\nexport const MEMBER_ROLE_KEY = 'member';\n\nconst ADMIN_PERMISSIONS: Permission[] = [...ALL_PERMISSIONS];\n\nexport const DEFAULT_ROLES: readonly RoleDefinition[] = [\n {\n key: OWNER_ROLE_KEY,\n name: 'Owner',\n description: 'Owns the organization and holds every permission.',\n levels: ['org'],\n team: true,\n permissions: [...ALL_PERMISSIONS],\n },\n {\n key: 'admin',\n name: 'Admin',\n description: 'Runs the organization: content, people, access, automations and settings.',\n levels: ['org'],\n team: true,\n permissions: ADMIN_PERMISSIONS,\n },\n {\n key: 'instructor',\n name: 'Instructor',\n description:\n 'Teaches courses: edits and publishes them, moderates lesson comments and sees learners.',\n levels: ['org', 'course'],\n team: true,\n permissions: [\n 'courses:read',\n 'courses:write',\n 'courses:moderate',\n 'progress:read',\n 'api_keys:read',\n 'api_keys:write',\n 'api_keys:delete',\n ],\n },\n {\n key: 'moderator',\n name: 'Moderator',\n description: 'Moderates communities: posts, comments, chats, reports and space members.',\n levels: ['org', 'community'],\n team: true,\n permissions: ['communities:moderate'],\n },\n {\n key: MEMBER_ROLE_KEY,\n name: 'Member',\n description: 'Everyone who was not given another role.',\n levels: ['org'],\n team: false,\n permissions: [],\n },\n];\n\nexport class RoleSetError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RoleSetError';\n }\n}\n\nexport function validateRoleSet(roles: readonly RoleDefinition[]): void {\n const keys = new Set<string>();\n for (const role of roles) {\n if (keys.has(role.key)) {\n throw new RoleSetError(`role key ${role.key} is repeated`);\n }\n keys.add(role.key);\n if (role.levels.length === 0) {\n throw new RoleSetError(`role ${role.key} has no level`);\n }\n for (const level of role.levels) {\n if (!levelSchema.safeParse(level).success) {\n throw new RoleSetError(`role ${role.key} names an unknown level ${String(level)}`);\n }\n }\n for (const permission of role.permissions) {\n if (!isPermission(permission)) {\n throw new RoleSetError(\n `role ${role.key} names an unknown permission ${String(permission)}`,\n );\n }\n if (!fitsLevels(permission, role.levels)) {\n throw new RoleSetError(\n `role ${role.key} holds ${permission}, which doesn't fit its levels ${role.levels.join(', ')}`,\n );\n }\n }\n }\n const owner = roles.find((role) => role.key === OWNER_ROLE_KEY);\n const member = roles.find((role) => role.key === MEMBER_ROLE_KEY);\n if (!owner) {\n throw new RoleSetError(`the role set lacks ${OWNER_ROLE_KEY}`);\n }\n if (!member) {\n throw new RoleSetError(`the role set lacks ${MEMBER_ROLE_KEY}`);\n }\n if (owner.levels.length !== 1 || owner.levels[0] !== 'org') {\n throw new RoleSetError(`${OWNER_ROLE_KEY} must list the org only`);\n }\n if (owner.permissions.length !== ALL_PERMISSIONS.length) {\n throw new RoleSetError(`${OWNER_ROLE_KEY} must hold every permission`);\n }\n if (member.levels.length !== 1 || member.levels[0] !== 'org' || member.permissions.length > 0) {\n throw new RoleSetError(`${MEMBER_ROLE_KEY} must list the org only and hold nothing`);\n }\n}\n\nexport function defaultHolders(permission: Permission): string[] {\n return DEFAULT_ROLES.filter((role) => withImplied(role.permissions).has(permission)).map(\n (role) => role.name,\n );\n}\n"],"mappings":";;;AAaA,MAAa,iBAAiB;AAC9B,MAAa,kBAAkB;AAE/B,MAAM,oBAAkC,CAAC,GAAG,eAAe;AAE3D,MAAa,gBAA2C;CACtD;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,QAAQ,CAAC,KAAK;EACd,MAAM;EACN,aAAa,CAAC,GAAG,eAAe;CAClC;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,QAAQ,CAAC,KAAK;EACd,MAAM;EACN,aAAa;CACf;CACA;EACE,KAAK;EACL,MAAM;EACN,aACE;EACF,QAAQ,CAAC,OAAO,QAAQ;EACxB,MAAM;EACN,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,QAAQ,CAAC,OAAO,WAAW;EAC3B,MAAM;EACN,aAAa,CAAC,sBAAsB;CACtC;CACA;EACE,KAAK;EACL,MAAM;EACN,aAAa;EACb,QAAQ,CAAC,KAAK;EACd,MAAM;EACN,aAAa,CAAC;CAChB;AACF;AAEA,IAAa,eAAb,cAAkC,MAAM;CACtC,YAAY,SAAiB;EAC3B,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;AAEA,SAAgB,gBAAgB,OAAwC;CACtE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,IAAI,KAAK,GAAG,GACnB,MAAM,IAAI,aAAa,YAAY,KAAK,IAAI,aAAa;EAE3D,KAAK,IAAI,KAAK,GAAG;EACjB,IAAI,KAAK,OAAO,WAAW,GACzB,MAAM,IAAI,aAAa,QAAQ,KAAK,IAAI,cAAc;EAExD,KAAK,MAAM,SAAS,KAAK,QACvB,IAAI,CAAC,YAAY,UAAU,KAAK,CAAC,CAAC,SAChC,MAAM,IAAI,aAAa,QAAQ,KAAK,IAAI,0BAA0B,OAAO,KAAK,GAAG;EAGrF,KAAK,MAAM,cAAc,KAAK,aAAa;GACzC,IAAI,CAAC,aAAa,UAAU,GAC1B,MAAM,IAAI,aACR,QAAQ,KAAK,IAAI,+BAA+B,OAAO,UAAU,GACnE;GAEF,IAAI,CAAC,WAAW,YAAY,KAAK,MAAM,GACrC,MAAM,IAAI,aACR,QAAQ,KAAK,IAAI,SAAS,WAAW,iCAAiC,KAAK,OAAO,KAAK,IAAI,GAC7F;EAEJ;CACF;CACA,MAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,QAAQ,cAAc;CAC9D,MAAM,SAAS,MAAM,MAAM,SAAS,KAAK,QAAQ,eAAe;CAChE,IAAI,CAAC,OACH,MAAM,IAAI,aAAa,sBAAsB,gBAAgB;CAE/D,IAAI,CAAC,QACH,MAAM,IAAI,aAAa,sBAAsB,iBAAiB;CAEhE,IAAI,MAAM,OAAO,WAAW,KAAK,MAAM,OAAO,OAAO,OACnD,MAAM,IAAI,aAAa,GAAG,eAAe,wBAAwB;CAEnE,IAAI,MAAM,YAAY,WAAW,gBAAgB,QAC/C,MAAM,IAAI,aAAa,GAAG,eAAe,4BAA4B;CAEvE,IAAI,OAAO,OAAO,WAAW,KAAK,OAAO,OAAO,OAAO,SAAS,OAAO,YAAY,SAAS,GAC1F,MAAM,IAAI,aAAa,GAAG,gBAAgB,yCAAyC;AAEvF;AAEA,SAAgB,eAAe,YAAkC;CAC/D,OAAO,cAAc,QAAQ,SAAS,YAAY,KAAK,WAAW,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,KAClF,SAAS,KAAK,IACjB;AACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ContentType } from "../types/schemas/content.js";
|
|
2
|
+
import { Permission } from "../types/schemas/organizations.js";
|
|
3
|
+
import { ContentId } from "../types/organizations.js";
|
|
4
|
+
import "../types/index.js";
|
|
5
|
+
import { EffectivePermissions, RoleRef } from "../shared/actor.js";
|
|
6
|
+
import { OrgRole } from "./org-roles.js";
|
|
7
|
+
//#region src/organizations/effective-permissions.d.ts
|
|
8
|
+
interface AssignmentRow {
|
|
9
|
+
roleKey: string;
|
|
10
|
+
contentId: ContentId | null;
|
|
11
|
+
contentType: ContentType | null;
|
|
12
|
+
}
|
|
13
|
+
interface ResolveInput {
|
|
14
|
+
assignments: readonly AssignmentRow[];
|
|
15
|
+
roles: ReadonlyMap<string, OrgRole>;
|
|
16
|
+
tokenScopes?: readonly Permission[];
|
|
17
|
+
}
|
|
18
|
+
interface ContentEntry {
|
|
19
|
+
permissions: Set<Permission>;
|
|
20
|
+
role: RoleRef;
|
|
21
|
+
}
|
|
22
|
+
declare class ResolvedPermissions implements EffectivePermissions {
|
|
23
|
+
private readonly org;
|
|
24
|
+
private readonly orgRole;
|
|
25
|
+
private readonly content;
|
|
26
|
+
private readonly owner;
|
|
27
|
+
constructor(org: Set<Permission>, orgRole: RoleRef, content: Map<ContentId, ContentEntry>, owner: boolean);
|
|
28
|
+
has(permission: Permission, on?: ContentId): boolean;
|
|
29
|
+
require(permission: Permission, on?: ContentId): void;
|
|
30
|
+
anywhere(permission: Permission): boolean;
|
|
31
|
+
where(permission: Permission): {
|
|
32
|
+
org: boolean;
|
|
33
|
+
content: ContentId[];
|
|
34
|
+
};
|
|
35
|
+
roleOn(on?: ContentId): RoleRef;
|
|
36
|
+
isOwner(): boolean;
|
|
37
|
+
orgPermissions(): Permission[];
|
|
38
|
+
contentPermissions(): Record<ContentId, Permission[]>;
|
|
39
|
+
teamOn(on?: ContentId): boolean;
|
|
40
|
+
}
|
|
41
|
+
declare function resolvePermissions({ assignments, roles, tokenScopes }: ResolveInput): ResolvedPermissions;
|
|
42
|
+
declare function systemPermissions(): ResolvedPermissions;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { AssignmentRow, ResolveInput, ResolvedPermissions, resolvePermissions, systemPermissions };
|
|
45
|
+
//# sourceMappingURL=effective-permissions.d.ts.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ForbiddenError } from "../shared/errors.js";
|
|
2
|
+
import { ALL_PERMISSIONS, permissionAbout, withImplied } from "./permissions.js";
|
|
3
|
+
import { MEMBER_ROLE_KEY } from "./default-roles.js";
|
|
4
|
+
//#region src/organizations/effective-permissions.ts
|
|
5
|
+
const MEMBER_REF = {
|
|
6
|
+
key: MEMBER_ROLE_KEY,
|
|
7
|
+
name: "Member",
|
|
8
|
+
team: false
|
|
9
|
+
};
|
|
10
|
+
function refOf(role) {
|
|
11
|
+
return {
|
|
12
|
+
key: role.key,
|
|
13
|
+
name: role.name,
|
|
14
|
+
team: role.team
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function intersect(set, scopes) {
|
|
18
|
+
if (!scopes) return set;
|
|
19
|
+
return new Set([...set].filter((permission) => scopes.has(permission)));
|
|
20
|
+
}
|
|
21
|
+
var ResolvedPermissions = class {
|
|
22
|
+
org;
|
|
23
|
+
orgRole;
|
|
24
|
+
content;
|
|
25
|
+
owner;
|
|
26
|
+
constructor(org, orgRole, content, owner) {
|
|
27
|
+
this.org = org;
|
|
28
|
+
this.orgRole = orgRole;
|
|
29
|
+
this.content = content;
|
|
30
|
+
this.owner = owner;
|
|
31
|
+
}
|
|
32
|
+
has(permission, on) {
|
|
33
|
+
if (this.org.has(permission)) return true;
|
|
34
|
+
return on !== void 0 && (this.content.get(on)?.permissions.has(permission) ?? false);
|
|
35
|
+
}
|
|
36
|
+
require(permission, on) {
|
|
37
|
+
if (!this.has(permission, on)) throw Object.assign(new ForbiddenError(`You need ${permission}`), {
|
|
38
|
+
permission,
|
|
39
|
+
...on !== void 0 && { contentId: on }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
anywhere(permission) {
|
|
43
|
+
if (this.org.has(permission)) return true;
|
|
44
|
+
for (const entry of this.content.values()) if (entry.permissions.has(permission)) return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
where(permission) {
|
|
48
|
+
if (this.org.has(permission)) return {
|
|
49
|
+
org: true,
|
|
50
|
+
content: []
|
|
51
|
+
};
|
|
52
|
+
const content = [];
|
|
53
|
+
for (const [id, entry] of this.content) if (entry.permissions.has(permission)) content.push(id);
|
|
54
|
+
return {
|
|
55
|
+
org: false,
|
|
56
|
+
content
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
roleOn(on) {
|
|
60
|
+
if (on !== void 0) {
|
|
61
|
+
const entry = this.content.get(on);
|
|
62
|
+
if (entry) return entry.role;
|
|
63
|
+
}
|
|
64
|
+
return this.orgRole;
|
|
65
|
+
}
|
|
66
|
+
isOwner() {
|
|
67
|
+
return this.owner;
|
|
68
|
+
}
|
|
69
|
+
orgPermissions() {
|
|
70
|
+
return ALL_PERMISSIONS.filter((permission) => this.org.has(permission));
|
|
71
|
+
}
|
|
72
|
+
contentPermissions() {
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const [id, entry] of this.content) out[id] = ALL_PERMISSIONS.filter((permission) => entry.permissions.has(permission));
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
teamOn(on) {
|
|
78
|
+
if (on !== void 0 && this.content.get(on)?.role.team) return true;
|
|
79
|
+
return this.orgRole.team;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function resolvePermissions({ assignments, roles, tokenScopes }) {
|
|
83
|
+
const scopes = tokenScopes ? withImplied(tokenScopes) : null;
|
|
84
|
+
const orgRow = assignments.find((row) => row.contentId === null);
|
|
85
|
+
const listed = orgRow ? roles.get(orgRow.roleKey) : void 0;
|
|
86
|
+
const orgRole = listed?.levels.includes("org") ? listed : void 0;
|
|
87
|
+
const orgSet = orgRole ? withImplied(orgRole.permissions) : /* @__PURE__ */ new Set();
|
|
88
|
+
const content = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const row of assignments) {
|
|
90
|
+
if (row.contentId === null || row.contentType === null) continue;
|
|
91
|
+
const role = roles.get(row.roleKey);
|
|
92
|
+
if (!role || !role.levels.includes(row.contentType)) continue;
|
|
93
|
+
const type = row.contentType;
|
|
94
|
+
const expanded = [...withImplied(role.permissions.filter((permission) => permissionAbout(permission) === type))].filter((permission) => permissionAbout(permission) === type);
|
|
95
|
+
content.set(row.contentId, {
|
|
96
|
+
permissions: intersect(new Set(expanded), scopes),
|
|
97
|
+
role: refOf(role)
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const owner = orgRow?.roleKey === "owner" && orgRole !== void 0;
|
|
101
|
+
return new ResolvedPermissions(intersect(orgSet, scopes), orgRole ? refOf(orgRole) : roles.get("member") ? refOf(roles.get(MEMBER_ROLE_KEY)) : MEMBER_REF, content, owner && scopes === null);
|
|
102
|
+
}
|
|
103
|
+
function systemPermissions() {
|
|
104
|
+
return new ResolvedPermissions(new Set(ALL_PERMISSIONS), {
|
|
105
|
+
key: "system",
|
|
106
|
+
name: "System",
|
|
107
|
+
team: false
|
|
108
|
+
}, /* @__PURE__ */ new Map(), false);
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { ResolvedPermissions, resolvePermissions, systemPermissions };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=effective-permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effective-permissions.js","names":[],"sources":["../../src/organizations/effective-permissions.ts"],"sourcesContent":["import type { ContentId, ContentType, Permission } from '../types/index.js';\nimport type { EffectivePermissions, RoleRef } from '../shared/actor.js';\nimport { ForbiddenError } from '../shared/errors.js';\nimport { ALL_PERMISSIONS, permissionAbout, withImplied } from './permissions.js';\nimport { MEMBER_ROLE_KEY, OWNER_ROLE_KEY } from './default-roles.js';\nimport type { OrgRole } from './org-roles.js';\n\nexport interface AssignmentRow {\n roleKey: string;\n contentId: ContentId | null;\n contentType: ContentType | null;\n}\n\nexport interface ResolveInput {\n assignments: readonly AssignmentRow[];\n roles: ReadonlyMap<string, OrgRole>;\n tokenScopes?: readonly Permission[];\n}\n\ninterface ContentEntry {\n permissions: Set<Permission>;\n role: RoleRef;\n}\n\nconst MEMBER_REF: RoleRef = { key: MEMBER_ROLE_KEY, name: 'Member', team: false };\n\nfunction refOf(role: OrgRole): RoleRef {\n return { key: role.key, name: role.name, team: role.team };\n}\n\nfunction intersect(set: Set<Permission>, scopes: Set<Permission> | null): Set<Permission> {\n if (!scopes) {\n return set;\n }\n return new Set([...set].filter((permission) => scopes.has(permission)));\n}\n\nexport class ResolvedPermissions implements EffectivePermissions {\n constructor(\n private readonly org: Set<Permission>,\n private readonly orgRole: RoleRef,\n private readonly content: Map<ContentId, ContentEntry>,\n private readonly owner: boolean,\n ) {}\n\n has(permission: Permission, on?: ContentId): boolean {\n if (this.org.has(permission)) {\n return true;\n }\n return on !== undefined && (this.content.get(on)?.permissions.has(permission) ?? false);\n }\n\n require(permission: Permission, on?: ContentId): void {\n if (!this.has(permission, on)) {\n throw Object.assign(new ForbiddenError(`You need ${permission}`), {\n permission,\n ...(on !== undefined && { contentId: on }),\n });\n }\n }\n\n anywhere(permission: Permission): boolean {\n if (this.org.has(permission)) {\n return true;\n }\n for (const entry of this.content.values()) {\n if (entry.permissions.has(permission)) {\n return true;\n }\n }\n return false;\n }\n\n where(permission: Permission): { org: boolean; content: ContentId[] } {\n if (this.org.has(permission)) {\n return { org: true, content: [] };\n }\n const content: ContentId[] = [];\n for (const [id, entry] of this.content) {\n if (entry.permissions.has(permission)) {\n content.push(id);\n }\n }\n return { org: false, content };\n }\n\n roleOn(on?: ContentId): RoleRef {\n if (on !== undefined) {\n const entry = this.content.get(on);\n if (entry) {\n return entry.role;\n }\n }\n return this.orgRole;\n }\n\n isOwner(): boolean {\n return this.owner;\n }\n\n orgPermissions(): Permission[] {\n return ALL_PERMISSIONS.filter((permission) => this.org.has(permission));\n }\n\n contentPermissions(): Record<ContentId, Permission[]> {\n const out: Record<ContentId, Permission[]> = {};\n for (const [id, entry] of this.content) {\n out[id] = ALL_PERMISSIONS.filter((permission) => entry.permissions.has(permission));\n }\n return out;\n }\n\n teamOn(on?: ContentId): boolean {\n if (on !== undefined && this.content.get(on)?.role.team) {\n return true;\n }\n return this.orgRole.team;\n }\n}\n\nexport function resolvePermissions({\n assignments,\n roles,\n tokenScopes,\n}: ResolveInput): ResolvedPermissions {\n const scopes = tokenScopes ? withImplied(tokenScopes) : null;\n const orgRow = assignments.find((row) => row.contentId === null);\n const listed = orgRow ? roles.get(orgRow.roleKey) : undefined;\n const orgRole = listed?.levels.includes('org') ? listed : undefined;\n const orgSet = orgRole ? withImplied(orgRole.permissions) : new Set<Permission>();\n const content = new Map<ContentId, ContentEntry>();\n for (const row of assignments) {\n if (row.contentId === null || row.contentType === null) {\n continue;\n }\n const role = roles.get(row.roleKey);\n if (!role || !role.levels.includes(row.contentType)) {\n continue;\n }\n const type = row.contentType;\n const own = role.permissions.filter((permission) => permissionAbout(permission) === type);\n const expanded = [...withImplied(own)].filter(\n (permission) => permissionAbout(permission) === type,\n );\n content.set(row.contentId, {\n permissions: intersect(new Set(expanded), scopes),\n role: refOf(role),\n });\n }\n const owner = orgRow?.roleKey === OWNER_ROLE_KEY && orgRole !== undefined;\n return new ResolvedPermissions(\n intersect(orgSet, scopes),\n orgRole\n ? refOf(orgRole)\n : roles.get(MEMBER_ROLE_KEY)\n ? refOf(roles.get(MEMBER_ROLE_KEY)!)\n : MEMBER_REF,\n content,\n owner && scopes === null,\n );\n}\n\nexport function systemPermissions(): ResolvedPermissions {\n return new ResolvedPermissions(\n new Set(ALL_PERMISSIONS),\n { key: 'system', name: 'System', team: false },\n new Map(),\n false,\n );\n}\n"],"mappings":"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"}
|