mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	fix(permissions): circular dependency (#18986)
* fix(permissions): circular dependency * Apply suggestions from code review Co-authored-by: Ben Irvin <ben@innerdvations.com> * Apply suggestions from code review Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu> --------- Co-authored-by: Ben Irvin <ben@innerdvations.com> Co-authored-by: Jean-Sébastien Herbaux <jean-sebastien.herbaux@epitech.eu>
This commit is contained in:
		
							parent
							
								
									295178369f
								
							
						
					
					
						commit
						e85a23d937
					
				@ -2,12 +2,11 @@ import * as sift from 'sift';
 | 
			
		||||
import qs from 'qs';
 | 
			
		||||
import { AbilityBuilder, Ability } from '@casl/ability';
 | 
			
		||||
import { pick, isNil, isObject } from 'lodash/fp';
 | 
			
		||||
// eslint-disable-next-line import/no-extraneous-dependencies, node/no-extraneous-import
 | 
			
		||||
import { Permissions as PermissionsTypes } from '@strapi/types';
 | 
			
		||||
import type { ParametrizedAction, PermissionRule } from '../../types';
 | 
			
		||||
 | 
			
		||||
export interface CustomAbilityBuilder {
 | 
			
		||||
  can(permission: PermissionsTypes.PermissionRule): ReturnType<AbilityBuilder<Ability>['can']>;
 | 
			
		||||
  buildParametrizedAction: (parametrizedAction: PermissionsTypes.ParametrizedAction) => string;
 | 
			
		||||
  can(permission: PermissionRule): ReturnType<AbilityBuilder<Ability>['can']>;
 | 
			
		||||
  buildParametrizedAction: (parametrizedAction: ParametrizedAction) => string;
 | 
			
		||||
  build(): Ability;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -32,7 +31,7 @@ const conditionsMatcher = (conditions: unknown) => {
 | 
			
		||||
  return sift.createQueryTester(conditions, { operations });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const buildParametrizedAction = ({ name, params }: PermissionsTypes.ParametrizedAction) => {
 | 
			
		||||
const buildParametrizedAction = ({ name, params }: ParametrizedAction) => {
 | 
			
		||||
  return `${name}?${qs.stringify(params)}`;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -43,7 +42,7 @@ export const caslAbilityBuilder = (): CustomAbilityBuilder => {
 | 
			
		||||
  const { can, build, ...rest } = new AbilityBuilder(Ability);
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    can(permission: PermissionsTypes.PermissionRule) {
 | 
			
		||||
    can(permission: PermissionRule) {
 | 
			
		||||
      const { action, subject, properties = {}, condition } = permission;
 | 
			
		||||
      const { fields } = properties;
 | 
			
		||||
 | 
			
		||||
@ -57,7 +56,7 @@ export const caslAbilityBuilder = (): CustomAbilityBuilder => {
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    buildParametrizedAction({ name, params }: PermissionsTypes.ParametrizedAction) {
 | 
			
		||||
    buildParametrizedAction({ name, params }: ParametrizedAction) {
 | 
			
		||||
      return `${name}?${qs.stringify(params)}`;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
import { cloneDeep, has, isArray } from 'lodash/fp';
 | 
			
		||||
import { hooks } from '@strapi/utils';
 | 
			
		||||
// eslint-disable-next-line node/no-extraneous-import
 | 
			
		||||
import type { Permissions as PermissionsTypes } from '@strapi/types';
 | 
			
		||||
 | 
			
		||||
import * as domain from '../domain';
 | 
			
		||||
import type { Permission } from '../domain/permission';
 | 
			
		||||
import type { PermissionRule } from '../types';
 | 
			
		||||
 | 
			
		||||
export interface PermissionEngineHooks {
 | 
			
		||||
  'before-format::validate.permission': ReturnType<typeof hooks.createAsyncBailHook>;
 | 
			
		||||
@ -52,7 +51,7 @@ const createBeforeEvaluateContext = (permission: Permission) => ({
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface WillRegisterContextParams {
 | 
			
		||||
  permission: PermissionsTypes.PermissionRule;
 | 
			
		||||
  permission: PermissionRule;
 | 
			
		||||
  options: Record<string, unknown>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,6 @@ import _ from 'lodash/fp';
 | 
			
		||||
import qs from 'qs';
 | 
			
		||||
import { Ability } from '@casl/ability';
 | 
			
		||||
import { providerFactory } from '@strapi/utils';
 | 
			
		||||
// eslint-disable-next-line import/no-extraneous-dependencies, node/no-extraneous-import
 | 
			
		||||
import { Permissions as PermissionsTypes } from '@strapi/types';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  createEngineHooks,
 | 
			
		||||
@ -15,6 +13,7 @@ import type { PermissionEngineHooks, HookName } from './hooks';
 | 
			
		||||
 | 
			
		||||
import * as abilities from './abilities';
 | 
			
		||||
import { Permission } from '../domain/permission';
 | 
			
		||||
import type { PermissionRule } from '../types';
 | 
			
		||||
 | 
			
		||||
export { abilities };
 | 
			
		||||
 | 
			
		||||
@ -30,9 +29,9 @@ export interface Engine {
 | 
			
		||||
  on(hook: HookName, handler: (...args: any[]) => any): Engine;
 | 
			
		||||
  generateAbility(permissions: Permission[], options?: object): Promise<Ability>;
 | 
			
		||||
  createRegisterFunction(
 | 
			
		||||
    can: (permission: PermissionsTypes.PermissionRule) => unknown,
 | 
			
		||||
    can: (permission: PermissionRule) => unknown,
 | 
			
		||||
    options: Record<string, unknown>
 | 
			
		||||
  ): (permission: PermissionsTypes.PermissionRule) => Promise<unknown>;
 | 
			
		||||
  ): (permission: PermissionRule) => Promise<unknown>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EngineParams {
 | 
			
		||||
@ -42,7 +41,7 @@ export interface EngineParams {
 | 
			
		||||
 | 
			
		||||
interface EvaluateParams {
 | 
			
		||||
  options: Record<string, unknown>;
 | 
			
		||||
  register: (permission: PermissionsTypes.PermissionRule) => Promise<unknown>;
 | 
			
		||||
  register: (permission: PermissionRule) => Promise<unknown>;
 | 
			
		||||
  permission: Permission;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -179,7 +178,7 @@ const newEngine = (params: EngineParams): Engine => {
 | 
			
		||||
     * used to register a permission in the ability builder
 | 
			
		||||
     */
 | 
			
		||||
    createRegisterFunction(can, options: Record<string, unknown>) {
 | 
			
		||||
      return async (permission: PermissionsTypes.PermissionRule) => {
 | 
			
		||||
      return async (permission: PermissionRule) => {
 | 
			
		||||
        const hookContext = createWillRegisterContext({ options, permission });
 | 
			
		||||
 | 
			
		||||
        await state.hooks['before-register.permission'].call(hookContext);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								packages/core/permissions/src/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								packages/core/permissions/src/types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
/**
 | 
			
		||||
 * These were imported from `@strapi/types` but if we do that
 | 
			
		||||
 * it becomes a circular dependency. This is the source of truth,
 | 
			
		||||
 * they're re-exported from `@strapi/types` for convenience.
 | 
			
		||||
 */
 | 
			
		||||
import type { Subject } from '@casl/ability';
 | 
			
		||||
 | 
			
		||||
export interface ParametrizedAction {
 | 
			
		||||
  name: string;
 | 
			
		||||
  params: Record<string, unknown>;
 | 
			
		||||
}
 | 
			
		||||
export interface PermissionRule {
 | 
			
		||||
  action: string | ParametrizedAction;
 | 
			
		||||
  subject?: Subject | null;
 | 
			
		||||
  properties?: {
 | 
			
		||||
    fields?: string[];
 | 
			
		||||
  };
 | 
			
		||||
  condition?: Record<string, unknown>;
 | 
			
		||||
}
 | 
			
		||||
@ -50,11 +50,9 @@ export {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  // @ts-expect-error - global strapi variable is also defined in the index.d.ts file
 | 
			
		||||
  var strapi: LoadedStrapi;
 | 
			
		||||
  namespace NodeJS {
 | 
			
		||||
    interface Global {
 | 
			
		||||
      // @ts-expect-error - global strapi variable is also defined in the index.d.ts file
 | 
			
		||||
      strapi: LoadedStrapi;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,8 @@
 | 
			
		||||
import { Subject } from '@casl/ability';
 | 
			
		||||
import type { engine } from '@strapi/permissions';
 | 
			
		||||
 | 
			
		||||
export interface ParametrizedAction {
 | 
			
		||||
  name: string;
 | 
			
		||||
  params: Record<string, unknown>;
 | 
			
		||||
}
 | 
			
		||||
export interface PermissionRule {
 | 
			
		||||
  action: string | ParametrizedAction;
 | 
			
		||||
  subject?: Subject | null;
 | 
			
		||||
  properties?: {
 | 
			
		||||
    fields?: string[];
 | 
			
		||||
  };
 | 
			
		||||
  condition?: Record<string, unknown>;
 | 
			
		||||
}
 | 
			
		||||
type PermissionRule = Parameters<engine.abilities.CustomAbilityBuilder['can']>[0];
 | 
			
		||||
type ParametrizedAction = Parameters<
 | 
			
		||||
  engine.abilities.CustomAbilityBuilder['buildParametrizedAction']
 | 
			
		||||
>[0];
 | 
			
		||||
 | 
			
		||||
export type { PermissionRule, ParametrizedAction };
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user