fix: meta acl with association query (#1695)
* fix: meta acl with association query * fix: test
This commit is contained in:
		
							parent
							
								
									2ed3565d43
								
							
						
					
					
						commit
						86de0733ad
					
				@ -1,8 +1,9 @@
 | 
			
		||||
import { Database } from '@nocobase/database';
 | 
			
		||||
import { prepareApp } from './prepare';
 | 
			
		||||
import { MockServer } from '@nocobase/test';
 | 
			
		||||
 | 
			
		||||
describe('list action with acl', () => {
 | 
			
		||||
  let app;
 | 
			
		||||
  let app: MockServer;
 | 
			
		||||
 | 
			
		||||
  let Post;
 | 
			
		||||
 | 
			
		||||
@ -17,6 +18,11 @@ describe('list action with acl', () => {
 | 
			
		||||
          type: 'bigInt',
 | 
			
		||||
          name: 'createdById',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'belongsTo',
 | 
			
		||||
          name: 'createdBy',
 | 
			
		||||
          target: 'users',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -85,6 +91,7 @@ describe('list action with acl', () => {
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    const response = await app.agent().set('X-With-ACL-Meta', true).resource('tests').list({});
 | 
			
		||||
 | 
			
		||||
    const data = response.body;
 | 
			
		||||
@ -93,6 +100,48 @@ describe('list action with acl', () => {
 | 
			
		||||
    expect(data.meta.allowedActions.destroy).toEqual(['t3']);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should list items meta permissions by association field', async () => {
 | 
			
		||||
    const userRole = app.acl.define({
 | 
			
		||||
      role: 'user',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    userRole.grantAction('posts:view', {});
 | 
			
		||||
 | 
			
		||||
    userRole.grantAction('posts:update', {
 | 
			
		||||
      filter: {
 | 
			
		||||
        'createdBy.id': '{{ ctx.state.currentUser.id }}',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await Post.repository.create({
 | 
			
		||||
      values: [
 | 
			
		||||
        { title: 'p1', createdById: 1 },
 | 
			
		||||
        { title: 'p2', createdById: 1 },
 | 
			
		||||
        { title: 'p3', createdById: 2 },
 | 
			
		||||
      ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    app.resourcer.use(
 | 
			
		||||
      (ctx, next) => {
 | 
			
		||||
        ctx.state.currentRole = 'user';
 | 
			
		||||
        ctx.state.currentUser = {
 | 
			
		||||
          id: 1,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return next();
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        before: 'acl',
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const response = await (app as any).agent().set('X-With-ACL-Meta', true).resource('posts').list();
 | 
			
		||||
    const data = response.body;
 | 
			
		||||
    expect(data.meta.allowedActions.view).toEqual([1, 2, 3]);
 | 
			
		||||
    expect(data.meta.allowedActions.update).toEqual([1, 2]);
 | 
			
		||||
    expect(data.meta.allowedActions.destroy).toEqual([]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should list items with meta permission', async () => {
 | 
			
		||||
    const userRole = app.acl.define({
 | 
			
		||||
      role: 'user',
 | 
			
		||||
@ -126,6 +175,7 @@ describe('list action with acl', () => {
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const response = await app.agent().set('X-With-ACL-Meta', true).resource('posts').list({});
 | 
			
		||||
 | 
			
		||||
    const data = response.body;
 | 
			
		||||
@ -167,6 +217,7 @@ describe('list action with acl', () => {
 | 
			
		||||
      },
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    const getResponse = await app.agent().set('X-With-ACL-Meta', true).resource('posts').get({
 | 
			
		||||
      filterByTk: 1,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
import { mockServer } from '@nocobase/test';
 | 
			
		||||
import { mockServer, MockServer } from '@nocobase/test';
 | 
			
		||||
import PluginACL from '../server';
 | 
			
		||||
 | 
			
		||||
export async function prepareApp() {
 | 
			
		||||
export async function prepareApp(): Promise<MockServer> {
 | 
			
		||||
  const app = mockServer({
 | 
			
		||||
    registerActions: true,
 | 
			
		||||
    acl: true,
 | 
			
		||||
 | 
			
		||||
@ -585,20 +585,12 @@ export class PluginACL extends Plugin {
 | 
			
		||||
    const withACLMeta = async (ctx: any, next) => {
 | 
			
		||||
      await next();
 | 
			
		||||
 | 
			
		||||
      if (!ctx.action) {
 | 
			
		||||
      if (!ctx.action || !ctx.get('X-With-ACL-Meta') || ctx.status !== 200) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const { resourceName, actionName } = ctx.action;
 | 
			
		||||
 | 
			
		||||
      if (!ctx.get('X-With-ACL-Meta')) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (ctx.status !== 200) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!['list', 'get'].includes(actionName)) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
@ -620,11 +612,11 @@ export class PluginACL extends Plugin {
 | 
			
		||||
        listData = lodash.castArray(listData);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const actions = ['view', 'update', 'destroy'];
 | 
			
		||||
      const inspectActions = ['view', 'update', 'destroy'];
 | 
			
		||||
 | 
			
		||||
      const actionsParams = [];
 | 
			
		||||
 | 
			
		||||
      for (const action of actions) {
 | 
			
		||||
      for (const action of inspectActions) {
 | 
			
		||||
        const actionCtx: any = {
 | 
			
		||||
          db: ctx.db,
 | 
			
		||||
          action: {
 | 
			
		||||
@ -707,10 +699,16 @@ export class PluginACL extends Plugin {
 | 
			
		||||
          {
 | 
			
		||||
            where: (() => {
 | 
			
		||||
              const filterObj = queryParams.where;
 | 
			
		||||
 | 
			
		||||
              if (!this.db.options.underscored) {
 | 
			
		||||
                return filterObj;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              const isAssociationKey = (key) => {
 | 
			
		||||
                return key.startsWith('$') && key.endsWith('$');
 | 
			
		||||
              };
 | 
			
		||||
 | 
			
		||||
              // change camelCase to snake_case
 | 
			
		||||
              const iterate = (rootObj, path = []) => {
 | 
			
		||||
                const obj = path.length == 0 ? rootObj : lodash.get(rootObj, path);
 | 
			
		||||
 | 
			
		||||
@ -738,8 +736,21 @@ export class PluginACL extends Plugin {
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
                  if (typeof key === 'string' && key !== snakeCase(key)) {
 | 
			
		||||
                    lodash.set(rootObj, [...path, snakeCase(key)], lodash.cloneDeep(obj[key]));
 | 
			
		||||
                    const setKey = isAssociationKey(key)
 | 
			
		||||
                      ? (() => {
 | 
			
		||||
                          const parts = key.split('.');
 | 
			
		||||
 | 
			
		||||
                          parts[parts.length - 1] = lodash.snakeCase(parts[parts.length - 1]);
 | 
			
		||||
 | 
			
		||||
                          const result = parts.join('.');
 | 
			
		||||
 | 
			
		||||
                          return result.endsWith('$') ? result : `${result}$`;
 | 
			
		||||
                        })()
 | 
			
		||||
                      : snakeCase(key);
 | 
			
		||||
                    const setValue = lodash.cloneDeep(obj[key]);
 | 
			
		||||
                    lodash.unset(rootObj, [...path, key]);
 | 
			
		||||
 | 
			
		||||
                    lodash.set(rootObj, [...path, setKey], setValue);
 | 
			
		||||
                  }
 | 
			
		||||
                });
 | 
			
		||||
              };
 | 
			
		||||
@ -776,7 +787,7 @@ export class PluginACL extends Plugin {
 | 
			
		||||
        include: conditions.map((condition) => condition.include).flat(),
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const allowedActions = actions
 | 
			
		||||
      const allowedActions = inspectActions
 | 
			
		||||
        .map((action) => {
 | 
			
		||||
          if (allAllowed.includes(action)) {
 | 
			
		||||
            return [action, ids];
 | 
			
		||||
@ -831,4 +842,4 @@ export class PluginACL extends Plugin {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default PluginACL;
 | 
			
		||||
export default PluginACL;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user