Skip to content

Commit

Permalink
Allow returning multiple content scopes in ScopedEntity-decorator (#…
Browse files Browse the repository at this point in the history
…1708)

Co-authored-by: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com>
  • Loading branch information
fraxachun and johnnyomair authored Feb 19, 2024
1 parent 1f6c58e commit 737ab3b
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/mighty-forks-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@comet/cms-api": minor
---

Allow returning multiple content scopes in `ScopedEntity`-decorator
21 changes: 12 additions & 9 deletions packages/api/cms-api/src/builds/changes-checker.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,20 @@ export class ChangesCheckerInterceptor implements NestInterceptor {
this.reflector.get<string[]>(SKIP_BUILD_METADATA_KEY, context.getClass());

if (!skipBuild) {
const scope = await this.contentScopeService.inferScopeFromExecutionContext(context);

if (process.env.NODE_ENV === "development" && this.changeAffectsAllScopes(scope)) {
if (operation.name) {
console.warn(`Mutation "${operation.name.value}" affects all scopes. Are you sure this is correct?`);
} else {
console.warn(`Unknown mutation affects all scopes. Are you sure this is correct?`);
const scopes = await this.contentScopeService.inferScopesFromExecutionContext(context);
if (scopes) {
for (const scope of scopes) {
if (process.env.NODE_ENV === "development" && this.changeAffectsAllScopes(scope)) {
if (operation.name) {
console.warn(`Mutation "${operation.name.value}" affects all scopes. Are you sure this is correct?`);
} else {
console.warn(`Unknown mutation affects all scopes. Are you sure this is correct?`);
}
}

await this.buildsService.setChangesSinceLastBuild(scope);
}
}

await this.buildsService.setChangesSinceLastBuild(scope);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ export class UserPermissionsGuard implements CanActivate {
throw new Error(`RequiredPermission decorator is missing in ${context.getClass().name}::${context.getHandler().name}()`);
}

let contentScope: ContentScope | undefined;
let requiredContentScopes: ContentScope[] | undefined;
if (!this.isResolvingGraphQLField(context) && !requiredPermission.options?.skipScopeCheck) {
contentScope = await this.contentScopeService.inferScopeFromExecutionContext(context);
if (!contentScope) {
requiredContentScopes = await this.contentScopeService.inferScopesFromExecutionContext(context);
if (!requiredContentScopes) {
throw new Error(
`Could not get ContentScope. Either pass a scope-argument or add @AffectedEntity()-decorator or enable skipScopeCheck in @RequiredPermission() (${
context.getClass().name
Expand All @@ -57,7 +57,11 @@ export class UserPermissionsGuard implements CanActivate {
if (requiredPermissions.length === 0) {
throw new Error(`RequiredPermission decorator has empty permissions in ${context.getClass().name}::${context.getHandler().name}()`);
}
return requiredPermissions.some((permission) => this.accessControlService.isAllowed(user, permission, contentScope));
return requiredPermissions.some((permission) =>
requiredContentScopes
? requiredContentScopes.some((contentScope) => this.accessControlService.isAllowed(user, permission, contentScope))
: this.accessControlService.isAllowed(user, permission),
);
}

// See https://docs.nestjs.com/graphql/other-features#execute-enhancers-at-the-field-resolver-level
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export class ContentScopeService {
return isEqual({ ...scope1 }, { ...scope2 });
}

async inferScopeFromExecutionContext(context: ExecutionContext): Promise<ContentScope | undefined> {
async inferScopeFromExecutionContext(context: ExecutionContext): Promise<ContentScope | ContentScope[] | undefined> {
const args = await this.getArgs(context);

const affectedEntity = this.reflector.getAllAndOverride<AffectedEntityMeta>("affectedEntity", [context.getHandler(), context.getClass()]);
if (affectedEntity) {
let contentScope: ContentScope | undefined;
let contentScope: ContentScope | ContentScope[] | undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const repo = this.orm.em.getRepository<any>(affectedEntity.entity);
if (affectedEntity.options.idArg) {
Expand Down Expand Up @@ -74,6 +74,12 @@ export class ContentScopeService {
}
}

async inferScopesFromExecutionContext(context: ExecutionContext): Promise<ContentScope[] | undefined> {
const scope = await this.inferScopeFromExecutionContext(context);
if (scope === undefined) return scope;
return Array.isArray(scope) ? scope : [scope];
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private async getArgs(context: ExecutionContext): Promise<Record<string, any>> {
if (context.getType().toString() === "graphql") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { ContentScope } from "../../user-permissions/interfaces/content-scope.in

export interface ScopedEntityMeta {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
fn: (entity: any) => Promise<ContentScope>;
fn: (entity: any) => Promise<ContentScope | ContentScope[]>;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const ScopedEntity = (fn: (entity: any) => Promise<ContentScope>): CustomDecorator<string> => {
export const ScopedEntity = (fn: (entity: any) => Promise<ContentScope | ContentScope[]>): CustomDecorator<string> => {
return SetMetadata("scopedEntity", { fn });
};

0 comments on commit 737ab3b

Please sign in to comment.