@lightdash/common 0.1369.3 → 0.1370.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.
@@ -1,6 +1,13 @@
1
1
  import { AbilityBuilder } from '@casl/ability';
2
2
  import { type ProjectMemberProfile } from '../types/projectMemberProfile';
3
3
  import { type LightdashUser } from '../types/user';
4
+ import { type OrganizationMemberAbilitiesArgs } from './organizationMemberAbility';
4
5
  import { type MemberAbility } from './types';
5
- export declare const getUserAbilityBuilder: (user: Pick<LightdashUser, 'role' | 'organizationUuid' | 'userUuid'>, projectProfiles: Pick<ProjectMemberProfile, 'projectUuid' | 'role' | 'userUuid'>[]) => AbilityBuilder<MemberAbility>;
6
+ type UserAbilityBuilderArgs = {
7
+ user: Pick<LightdashUser, 'role' | 'organizationUuid' | 'userUuid'>;
8
+ projectProfiles: Pick<ProjectMemberProfile, 'projectUuid' | 'role' | 'userUuid'>[];
9
+ permissionsConfig: OrganizationMemberAbilitiesArgs['permissionsConfig'];
10
+ };
11
+ export declare const getUserAbilityBuilder: ({ user, projectProfiles, permissionsConfig, }: UserAbilityBuilderArgs) => AbilityBuilder<MemberAbility>;
6
12
  export declare const defineUserAbility: (user: Pick<LightdashUser, 'role' | 'organizationUuid' | 'userUuid'>, projectProfiles: Pick<ProjectMemberProfile, 'projectUuid' | 'role' | 'userUuid'>[]) => MemberAbility;
13
+ export {};
@@ -1,16 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defineUserAbility = exports.getUserAbilityBuilder = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const ability_1 = require("@casl/ability");
5
- const organizationMemberAbility_1 = require("./organizationMemberAbility");
6
+ const organizationMemberAbility_1 = tslib_1.__importDefault(require("./organizationMemberAbility"));
6
7
  const projectMemberAbility_1 = require("./projectMemberAbility");
7
- const getUserAbilityBuilder = (user, projectProfiles) => {
8
+ const getUserAbilityBuilder = ({ user, projectProfiles, permissionsConfig, }) => {
8
9
  const builder = new ability_1.AbilityBuilder(ability_1.Ability);
9
10
  if (user.role && user.organizationUuid) {
10
- organizationMemberAbility_1.organizationMemberAbilities[user.role]({
11
- organizationUuid: user.organizationUuid,
12
- userUuid: user.userUuid,
13
- }, builder);
11
+ (0, organizationMemberAbility_1.default)({
12
+ role: user.role,
13
+ member: {
14
+ organizationUuid: user.organizationUuid,
15
+ userUuid: user.userUuid,
16
+ },
17
+ builder,
18
+ permissionsConfig,
19
+ });
14
20
  projectProfiles.forEach((projectProfile) => {
15
21
  projectMemberAbility_1.projectMemberAbilities[projectProfile.role](projectProfile, builder);
16
22
  });
@@ -18,8 +24,18 @@ const getUserAbilityBuilder = (user, projectProfiles) => {
18
24
  return builder;
19
25
  };
20
26
  exports.getUserAbilityBuilder = getUserAbilityBuilder;
27
+ // Defines user ability for test purposes
21
28
  const defineUserAbility = (user, projectProfiles) => {
22
- const builder = (0, exports.getUserAbilityBuilder)(user, projectProfiles);
29
+ const builder = (0, exports.getUserAbilityBuilder)({
30
+ user,
31
+ projectProfiles,
32
+ permissionsConfig: {
33
+ pat: {
34
+ enabled: false,
35
+ allowedOrgRoles: [],
36
+ },
37
+ },
38
+ });
23
39
  return builder.build();
24
40
  };
25
41
  exports.defineUserAbility = defineUserAbility;
@@ -1,4 +1,15 @@
1
1
  import { type AbilityBuilder } from '@casl/ability';
2
2
  import { type OrganizationMemberProfile, type OrganizationMemberRole } from '../types/organizationMemberProfile';
3
3
  import { type MemberAbility } from './types';
4
- export declare const organizationMemberAbilities: Record<OrganizationMemberRole, (member: Pick<OrganizationMemberProfile, 'organizationUuid' | 'userUuid'>, builder: Pick<AbilityBuilder<MemberAbility>, 'can'>) => void>;
4
+ export type OrganizationMemberAbilitiesArgs = {
5
+ role: OrganizationMemberRole;
6
+ member: Pick<OrganizationMemberProfile, 'organizationUuid' | 'userUuid'>;
7
+ builder: Pick<AbilityBuilder<MemberAbility>, 'can'>;
8
+ permissionsConfig: {
9
+ pat: {
10
+ enabled: boolean;
11
+ allowedOrgRoles: OrganizationMemberRole[];
12
+ };
13
+ };
14
+ };
15
+ export default function applyOrganizationMemberAbilities({ role, member, builder, permissionsConfig, }: OrganizationMemberAbilitiesArgs): void;
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.organizationMemberAbilities = void 0;
4
3
  const projects_1 = require("../types/projects");
5
4
  const space_1 = require("../types/space");
6
- // eslint-disable-next-line import/prefer-default-export
7
- exports.organizationMemberAbilities = {
5
+ const applyOrganizationMemberDynamicAbilities = ({ role, builder: { can }, permissionsConfig, }) => {
6
+ if (permissionsConfig.pat.enabled &&
7
+ permissionsConfig.pat.allowedOrgRoles.includes(role)) {
8
+ can('manage', 'PersonalAccessToken', {});
9
+ }
10
+ };
11
+ const applyOrganizationMemberStaticAbilities = {
8
12
  member(member, { can }) {
9
13
  can('view', 'OrganizationMemberProfile', {
10
14
  organizationUuid: member.organizationUuid,
@@ -17,7 +21,7 @@ exports.organizationMemberAbilities = {
17
21
  });
18
22
  },
19
23
  viewer(member, { can }) {
20
- exports.organizationMemberAbilities.member(member, { can });
24
+ applyOrganizationMemberStaticAbilities.member(member, { can });
21
25
  can('view', 'Dashboard', {
22
26
  organizationUuid: member.organizationUuid,
23
27
  isPrivate: false,
@@ -65,7 +69,7 @@ exports.organizationMemberAbilities = {
65
69
  });
66
70
  },
67
71
  interactive_viewer(member, { can }) {
68
- exports.organizationMemberAbilities.viewer(member, { can });
72
+ applyOrganizationMemberStaticAbilities.viewer(member, { can });
69
73
  can('create', 'Job');
70
74
  can('view', 'Job', { userUuid: member.userUuid });
71
75
  can('view', 'UnderlyingData', {
@@ -142,7 +146,9 @@ exports.organizationMemberAbilities = {
142
146
  });
143
147
  },
144
148
  editor(member, { can }) {
145
- exports.organizationMemberAbilities.interactive_viewer(member, { can });
149
+ applyOrganizationMemberStaticAbilities.interactive_viewer(member, {
150
+ can,
151
+ });
146
152
  can('create', 'Space', {
147
153
  organizationUuid: member.organizationUuid,
148
154
  });
@@ -167,7 +173,7 @@ exports.organizationMemberAbilities = {
167
173
  });
168
174
  },
169
175
  developer(member, { can }) {
170
- exports.organizationMemberAbilities.editor(member, { can });
176
+ applyOrganizationMemberStaticAbilities.editor(member, { can });
171
177
  can('manage', 'VirtualView', {
172
178
  organizationUuid: member.organizationUuid,
173
179
  });
@@ -211,7 +217,7 @@ exports.organizationMemberAbilities = {
211
217
  });
212
218
  },
213
219
  admin(member, { can }) {
214
- exports.organizationMemberAbilities.developer(member, { can });
220
+ applyOrganizationMemberStaticAbilities.developer(member, { can });
215
221
  can('manage', 'Dashboard', {
216
222
  organizationUuid: member.organizationUuid,
217
223
  });
@@ -251,3 +257,13 @@ exports.organizationMemberAbilities = {
251
257
  });
252
258
  },
253
259
  };
260
+ function applyOrganizationMemberAbilities({ role, member, builder, permissionsConfig, }) {
261
+ applyOrganizationMemberStaticAbilities[role](member, builder);
262
+ applyOrganizationMemberDynamicAbilities({
263
+ role,
264
+ member,
265
+ builder,
266
+ permissionsConfig,
267
+ });
268
+ }
269
+ exports.default = applyOrganizationMemberAbilities;
@@ -1,14 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const ability_1 = require("@casl/ability");
5
+ const organizationMemberProfile_1 = require("../types/organizationMemberProfile");
4
6
  const projects_1 = require("../types/projects");
5
7
  const space_1 = require("../types/space");
6
- const organizationMemberAbility_1 = require("./organizationMemberAbility");
8
+ const organizationMemberAbility_1 = tslib_1.__importDefault(require("./organizationMemberAbility"));
7
9
  const organizationMemberAbility_mock_1 = require("./organizationMemberAbility.mock");
8
- const defineAbilityForOrganizationMember = (member) => {
10
+ const defineAbilityForOrganizationMember = (member, permissionsConfig) => {
9
11
  const builder = new ability_1.AbilityBuilder(ability_1.Ability);
10
12
  if (member) {
11
- organizationMemberAbility_1.organizationMemberAbilities[member.role](member, builder);
13
+ (0, organizationMemberAbility_1.default)({
14
+ role: member.role,
15
+ member,
16
+ builder,
17
+ permissionsConfig: permissionsConfig ?? {
18
+ pat: {
19
+ enabled: true,
20
+ allowedOrgRoles: Object.values(organizationMemberProfile_1.OrganizationMemberRole),
21
+ },
22
+ },
23
+ });
12
24
  }
13
25
  return builder.build();
14
26
  };
@@ -1001,4 +1013,43 @@ describe('Organization member permissions', () => {
1001
1013
  });
1002
1014
  });
1003
1015
  });
1016
+ // test permissionsConfig
1017
+ describe('Personal Access Tokens permissions', () => {
1018
+ it('cannot create a personal access token as PAT is disabled', () => {
1019
+ const ability = defineAbilityForOrganizationMember(organizationMemberAbility_mock_1.ORGANIZATION_ADMIN, {
1020
+ pat: {
1021
+ enabled: false,
1022
+ allowedOrgRoles: Object.values(organizationMemberProfile_1.OrganizationMemberRole),
1023
+ },
1024
+ });
1025
+ expect(ability.can('create', (0, ability_1.subject)('PersonalAccessToken', {
1026
+ organizationUuid: organizationMemberAbility_mock_1.ORGANIZATION_ADMIN.organizationUuid,
1027
+ userUuid: organizationMemberAbility_mock_1.ORGANIZATION_ADMIN.userUuid,
1028
+ }))).toEqual(false);
1029
+ });
1030
+ it('cannot create a personal access token as PAT allowed roles dont match', () => {
1031
+ const ability = defineAbilityForOrganizationMember(organizationMemberAbility_mock_1.ORGANIZATION_DEVELOPER, {
1032
+ pat: {
1033
+ enabled: true,
1034
+ allowedOrgRoles: [organizationMemberProfile_1.OrganizationMemberRole.ADMIN],
1035
+ },
1036
+ });
1037
+ expect(ability.can('create', (0, ability_1.subject)('PersonalAccessToken', {
1038
+ organizationUuid: organizationMemberAbility_mock_1.ORGANIZATION_DEVELOPER.organizationUuid,
1039
+ userUuid: organizationMemberAbility_mock_1.ORGANIZATION_DEVELOPER.userUuid,
1040
+ }))).toEqual(false);
1041
+ });
1042
+ it('can create a personal access token as PAT is enabled', () => {
1043
+ const ability = defineAbilityForOrganizationMember(organizationMemberAbility_mock_1.ORGANIZATION_ADMIN, {
1044
+ pat: {
1045
+ enabled: true,
1046
+ allowedOrgRoles: [organizationMemberProfile_1.OrganizationMemberRole.ADMIN],
1047
+ },
1048
+ });
1049
+ expect(ability.can('create', (0, ability_1.subject)('PersonalAccessToken', {
1050
+ organizationUuid: organizationMemberAbility_mock_1.ORGANIZATION_ADMIN.organizationUuid,
1051
+ userUuid: organizationMemberAbility_mock_1.ORGANIZATION_ADMIN.userUuid,
1052
+ }))).toEqual(true);
1053
+ });
1054
+ });
1004
1055
  });
@@ -8,7 +8,7 @@ interface Project {
8
8
  interface Organization {
9
9
  organizationUuid: string;
10
10
  }
11
- type Subject = Project | Organization | OrganizationMemberProfile | 'Project' | 'Organization' | 'OrganizationMemberProfile' | 'Dashboard' | 'Space' | 'SavedChart' | 'InviteLink' | 'Job' | 'SqlRunner' | 'Analytics' | 'Explore' | 'UnderlyingData' | 'ExportCsv' | 'CsvJobResult' | 'PinnedItems' | 'Validation' | 'Group' | 'ChangeCsvResults' | 'ScheduledDeliveries' | 'DashboardComments' | 'CustomSql' | 'CompileProject' | 'SemanticViewer' | 'VirtualView' | 'Tags' | 'all';
11
+ type Subject = Project | Organization | OrganizationMemberProfile | 'Project' | 'Organization' | 'OrganizationMemberProfile' | 'Dashboard' | 'Space' | 'SavedChart' | 'InviteLink' | 'Job' | 'SqlRunner' | 'Analytics' | 'Explore' | 'UnderlyingData' | 'ExportCsv' | 'CsvJobResult' | 'PinnedItems' | 'Validation' | 'Group' | 'ChangeCsvResults' | 'ScheduledDeliveries' | 'DashboardComments' | 'CustomSql' | 'CompileProject' | 'SemanticViewer' | 'VirtualView' | 'Tags' | 'PersonalAccessToken' | 'all';
12
12
  type PossibleAbilities = [
13
13
  AbilityAction,
14
14
  Subject | ForcedSubject<Exclude<Subject, 'all'>>
package/dist/index.d.ts CHANGED
@@ -468,6 +468,9 @@ export type HealthState = {
468
468
  enabled: boolean;
469
469
  loginPath: string;
470
470
  };
471
+ pat: {
472
+ maxExpirationTimeInDays: number | undefined;
473
+ };
471
474
  };
472
475
  posthog: {
473
476
  projectApiKey: string;
@@ -8,6 +8,7 @@ export declare enum OrganizationMemberRole {
8
8
  DEVELOPER = "developer",
9
9
  ADMIN = "admin"
10
10
  }
11
+ export declare const isOrganizationMemberRole: (x: string) => x is OrganizationMemberRole;
11
12
  /**
12
13
  * Profile for a user's membership in an organization
13
14
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRoleDescription = exports.isOrganizationMemberProfileWithGroups = exports.OrganizationMemberRole = void 0;
3
+ exports.getRoleDescription = exports.isOrganizationMemberProfileWithGroups = exports.isOrganizationMemberRole = exports.OrganizationMemberRole = void 0;
4
4
  var OrganizationMemberRole;
5
5
  (function (OrganizationMemberRole) {
6
6
  OrganizationMemberRole["MEMBER"] = "member";
@@ -10,6 +10,8 @@ var OrganizationMemberRole;
10
10
  OrganizationMemberRole["DEVELOPER"] = "developer";
11
11
  OrganizationMemberRole["ADMIN"] = "admin";
12
12
  })(OrganizationMemberRole = exports.OrganizationMemberRole || (exports.OrganizationMemberRole = {}));
13
+ const isOrganizationMemberRole = (x) => Object.values(OrganizationMemberRole).includes(x);
14
+ exports.isOrganizationMemberRole = isOrganizationMemberRole;
13
15
  const isOrganizationMemberProfileWithGroups = (obj) => 'groups' in obj;
14
16
  exports.isOrganizationMemberProfileWithGroups = isOrganizationMemberProfileWithGroups;
15
17
  const getRoleDescription = (role) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/common",
3
- "version": "0.1369.3",
3
+ "version": "0.1370.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [