@lightdash/common 0.1369.4 → 0.1370.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.4",
3
+ "version": "0.1370.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [