Skip to content

Commit 249b14d

Browse files
committed
Convert Session.roles & Policy.roles to sets
1 parent e2e1ac7 commit 249b14d

File tree

9 files changed

+37
-31
lines changed

9 files changed

+37
-31
lines changed

src/components/authorization/handler/can-impersonate.handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ export class CanImpersonateHandler {
99

1010
handle(event: CanImpersonateEvent) {
1111
const p = this.privileges.for(AssignableRoles);
12-
const valid = event.session.roles.every((role) => p.can('edit', role));
12+
const valid = event.session.roles
13+
.values()
14+
.every((role) => p.can('edit', role));
1315
event.allow.vote(valid);
1416
}
1517
}

src/components/authorization/policies/conditions/role.condition.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class RoleCondition implements Condition {
1212
constructor(readonly allowed: ReadonlySet<Role>) {}
1313

1414
isAllowed({ session }: IsAllowedParams<any>) {
15-
const given = session.roles;
15+
const given = session.roles.values();
1616
return given.some((role) => this.allowed.has(role));
1717
}
1818

src/components/authorization/policy/executor/policy-executor.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { forwardRef, Inject, Injectable } from '@nestjs/common';
22
import { CachedByArg } from '@seedcompany/common';
3-
import { identity, intersection } from 'lodash';
3+
import { identity } from 'lodash';
44
import { type EnhancedResource } from '~/common';
55
import { Identity, type Session } from '~/core/authentication';
66
import { type QueryFragment } from '~/core/database/query';
@@ -147,8 +147,8 @@ export class PolicyExecutor {
147147
}
148148

149149
const roleCondition =
150-
policy.roles && policy.roles.length > 0
151-
? new RoleCondition(new Set(policy.roles))
150+
policy.roles && policy.roles.size > 0
151+
? new RoleCondition(policy.roles)
152152
: undefined;
153153

154154
if (!roleCondition && condition === true) {
@@ -279,11 +279,10 @@ export class PolicyExecutor {
279279
if (!policy.roles) {
280280
return true; // policy doesn't limit roles
281281
}
282-
const rolesSpecifiedByPolicyThatUserHas = intersection(
283-
policy.roles,
282+
const rolesSpecifiedByPolicyThatUserHas = policy.roles.intersection(
284283
session.roles,
285284
);
286-
return rolesSpecifiedByPolicyThatUserHas.length > 0;
285+
return rolesSpecifiedByPolicyThatUserHas.size > 0;
287286
});
288287
return policies;
289288
}

src/components/authorization/policy/policy.factory.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
DiscoveryService,
44
} from '@golevelup/nestjs-discovery';
55
import { Injectable, type OnModuleInit } from '@nestjs/common';
6-
import { entries, mapEntries, mapValues } from '@seedcompany/common';
6+
import { entries, mapEntries, mapValues, setOf } from '@seedcompany/common';
77
import { pick, startCase } from 'lodash';
88
import { type DeepWritable, type Writable } from 'ts-essentials';
99
import { type EnhancedResource, many, type Role } from '~/common';
@@ -35,7 +35,7 @@ export interface Policy {
3535
/* Policy Name */
3636
name: string;
3737
/* Only applies to these roles */
38-
roles?: readonly Role[];
38+
roles?: ReadonlySet<Role>;
3939
/* What the policy grants */
4040
grants: Grants;
4141
/* An optimization to determine Powers this policy contains */
@@ -103,7 +103,7 @@ export class PolicyFactory implements OnModuleInit {
103103
): PlainPolicy {
104104
const name = startCase(discoveredClass.name.replace(/Policy$/, ''));
105105

106-
const roles = meta.role === 'all' ? undefined : many(meta.role);
106+
const roles = meta.role === 'all' ? undefined : setOf(many(meta.role));
107107

108108
const grants: WritableGrants = new Map();
109109
const resultList = many(meta.def(resGranter)).flat();

src/components/engagement/engagement.rules.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable no-case-declarations */
22
import { Injectable } from '@nestjs/common';
3+
import { setOf } from '@seedcompany/common';
34
import { node, relation } from 'cypher-query-builder';
45
import { first, intersection } from 'lodash';
56
import {
@@ -28,7 +29,7 @@ interface StatusRule {
2829
transitions: Transition[];
2930
}
3031

31-
const rolesThatCanBypassWorkflow: Role[] = [Role.Administrator];
32+
const rolesThatCanBypassWorkflow = setOf<Role>([Role.Administrator]);
3233

3334
@Injectable()
3435
export class EngagementRules {
@@ -328,8 +329,7 @@ export class EngagementRules {
328329
);
329330

330331
// If current user is not an approver (based on roles) then don't allow any transitions
331-
const currentUserRoles = session.roles;
332-
if (intersection(approvers, currentUserRoles).length === 0) {
332+
if (session.roles.intersection(setOf(approvers)).size === 0) {
333333
return [];
334334
}
335335

@@ -356,7 +356,7 @@ export class EngagementRules {
356356

357357
canBypassWorkflow() {
358358
const { roles } = this.identity.current;
359-
return intersection(rolesThatCanBypassWorkflow, roles).length > 0;
359+
return roles.intersection(rolesThatCanBypassWorkflow).size > 0;
360360
}
361361

362362
async verifyStatusChange(

src/components/project/project.service.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,10 @@ export class ProjectService {
159159
await this.projectMembers.create(
160160
{
161161
userId: session.userId,
162-
roles: session.roles.filter((role) =>
163-
Role.applicableToProjectMembership.has(role),
164-
),
162+
roles: session.roles
163+
.values()
164+
.filter((role) => Role.applicableToProjectMembership.has(role))
165+
.toArray(),
165166
projectId: project,
166167
},
167168
false,

src/core/authentication/session/session.dto.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class RawSession extends DataObject {
66
readonly token: string;
77
readonly issuedAt: DateTime;
88
readonly userId: ID;
9-
readonly roles: readonly Role[];
9+
readonly roles: Iterable<Role>;
1010
readonly anonymous: boolean;
1111

1212
/**
@@ -18,13 +18,17 @@ class RawSession extends DataObject {
1818
*/
1919
readonly impersonatee?: {
2020
id?: ID;
21-
roles: readonly Role[];
21+
roles: ReadonlySet<Role>;
2222
};
2323
}
2424

2525
export class Session extends RawSession {
26+
declare readonly roles: ReadonlySet<Role>;
27+
2628
static from(session: RawSession): Session {
27-
return Session.defaultValue(Session, session);
29+
return Object.assign(Session.defaultValue(Session), session, {
30+
roles: new Set(session.roles),
31+
});
2832
}
2933

3034
with(next: Partial<RawSession>): Session {
@@ -41,7 +45,7 @@ export class Session extends RawSession {
4145
}
4246

4347
get isAdmin() {
44-
return this.roles.includes('Administrator');
48+
return this.roles.has('Administrator');
4549
}
4650

4751
isSelf(id: ID<'User'>) {

src/core/authentication/session/session.initiator.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { forwardRef, Inject, Injectable } from '@nestjs/common';
2-
import { csv } from '@seedcompany/common';
2+
import { csv, setOf } from '@seedcompany/common';
33
import {
44
type ID,
55
InputException,
@@ -90,15 +90,15 @@ export class SessionInitiator {
9090
);
9191
}
9292

93-
const roles = csvHeader(request?.headers?.['x-cord-impersonate-role']);
93+
const rawRoles = csvHeader(request?.headers?.['x-cord-impersonate-role']);
9494

95-
if (!roles && !user) {
95+
if (!rawRoles && !user) {
9696
return undefined;
9797
}
9898

99-
const scoped = (roles ?? []).map(assertValidRole);
99+
const roles = setOf((rawRoles ?? []).map(assertValidRole));
100100

101-
return { id: user, roles: scoped };
101+
return { id: user, roles };
102102
}
103103
}
104104

src/core/authentication/session/session.manager.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { CachedByArg } from '@seedcompany/common';
2+
import { CachedByArg, setOf } from '@seedcompany/common';
33
import { DateTime } from 'luxon';
44
import type { Writable } from 'ts-essentials';
55
import {
@@ -78,10 +78,10 @@ export class SessionManager {
7878
impersonatee && result.userId
7979
? {
8080
id: impersonatee?.id ?? ghost?.id,
81-
roles: [
81+
roles: setOf([
8282
...(impersonatee.roles ?? []),
8383
...(result.impersonateeRoles ?? []),
84-
],
84+
]),
8585
}
8686
: undefined;
8787

@@ -125,7 +125,7 @@ export class SessionManager {
125125
}
126126

127127
@CachedByArg()
128-
lazySessionForRootUser(input?: Partial<Session>) {
128+
lazySessionForRootUser(input?: Parameters<Session['with']>[0]) {
129129
const promiseOfRootId = this.waitForRootUserIdOnce().then((id) => {
130130
(session as Writable<Session>).userId = id;
131131
return id;

0 commit comments

Comments
 (0)