featEnable permission (#229)
* feat: acl skip * feat: skip-manager * feat: root user permission skip * fix: test * feat: set user role * fix: code review * feat: setDefaultRole for users
This commit is contained in:
		
							parent
							
								
									accb2a59b9
								
							
						
					
					
						commit
						d98714d9fd
					
				
							
								
								
									
										93
									
								
								packages/acl/src/__tests__/skip.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								packages/acl/src/__tests__/skip.test.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,93 @@
 | 
				
			|||||||
 | 
					import { ACL } from '..';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('skip', () => {
 | 
				
			||||||
 | 
					  let acl: ACL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    acl = new ACL();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should skip action', async () => {
 | 
				
			||||||
 | 
					    const middlewareFunc = acl.middleware();
 | 
				
			||||||
 | 
					    const ctx: any = {
 | 
				
			||||||
 | 
					      state: {},
 | 
				
			||||||
 | 
					      action: {
 | 
				
			||||||
 | 
					        resourceName: 'users',
 | 
				
			||||||
 | 
					        actionName: 'login',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      app: {
 | 
				
			||||||
 | 
					        acl,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      throw() {},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const nextFunc = jest.fn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await middlewareFunc(ctx, nextFunc);
 | 
				
			||||||
 | 
					    expect(nextFunc).toHaveBeenCalledTimes(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    acl.skip('users', 'login');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await middlewareFunc(ctx, nextFunc);
 | 
				
			||||||
 | 
					    expect(nextFunc).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should skip action with condition', async () => {
 | 
				
			||||||
 | 
					    const middlewareFunc = acl.middleware();
 | 
				
			||||||
 | 
					    const ctx: any = {
 | 
				
			||||||
 | 
					      state: {},
 | 
				
			||||||
 | 
					      action: {
 | 
				
			||||||
 | 
					        resourceName: 'users',
 | 
				
			||||||
 | 
					        actionName: 'login',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      app: {
 | 
				
			||||||
 | 
					        acl,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      throw() {},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const nextFunc = jest.fn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let skip = false;
 | 
				
			||||||
 | 
					    acl.skip('users', 'login', (ctx) => {
 | 
				
			||||||
 | 
					      return skip;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await middlewareFunc(ctx, nextFunc);
 | 
				
			||||||
 | 
					    expect(nextFunc).toHaveBeenCalledTimes(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    skip = true;
 | 
				
			||||||
 | 
					    await middlewareFunc(ctx, nextFunc);
 | 
				
			||||||
 | 
					    expect(nextFunc).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should skip action with registered condition', async () => {
 | 
				
			||||||
 | 
					    const middlewareFunc = acl.middleware();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const conditionFn = jest.fn();
 | 
				
			||||||
 | 
					    acl.skipManager.registerSkipCondition('superUser', () => {
 | 
				
			||||||
 | 
					      conditionFn();
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const ctx: any = {
 | 
				
			||||||
 | 
					      state: {},
 | 
				
			||||||
 | 
					      action: {
 | 
				
			||||||
 | 
					        resourceName: 'users',
 | 
				
			||||||
 | 
					        actionName: 'login',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      app: {
 | 
				
			||||||
 | 
					        acl,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      throw() {},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const nextFunc = jest.fn();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    acl.skip('users', 'login', 'superUser');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await middlewareFunc(ctx, nextFunc);
 | 
				
			||||||
 | 
					    expect(nextFunc).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					    expect(conditionFn).toHaveBeenCalledTimes(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -5,6 +5,7 @@ import lodash from 'lodash';
 | 
				
			|||||||
import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
 | 
					import { AclAvailableAction, AvailableActionOptions } from './acl-available-action';
 | 
				
			||||||
import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';
 | 
					import { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';
 | 
				
			||||||
import { ACLRole, RoleActionParams } from './acl-role';
 | 
					import { ACLRole, RoleActionParams } from './acl-role';
 | 
				
			||||||
 | 
					import { SkipManager } from './skip-manager';
 | 
				
			||||||
const parse = require('json-templates');
 | 
					const parse = require('json-templates');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface CanResult {
 | 
					interface CanResult {
 | 
				
			||||||
@ -46,6 +47,8 @@ export class ACL extends EventEmitter {
 | 
				
			|||||||
  protected availableStrategy = new Map<string, ACLAvailableStrategy>();
 | 
					  protected availableStrategy = new Map<string, ACLAvailableStrategy>();
 | 
				
			||||||
  protected middlewares = [];
 | 
					  protected middlewares = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public skipManager = new SkipManager(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  roles = new Map<string, ACLRole>();
 | 
					  roles = new Map<string, ACLRole>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  actionAlias = new Map<string, string>();
 | 
					  actionAlias = new Map<string, string>();
 | 
				
			||||||
@ -81,6 +84,8 @@ export class ACL extends EventEmitter {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.middlewares.push(this.skipManager.aclMiddleware());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  define(options: DefineOptions): ACLRole {
 | 
					  define(options: DefineOptions): ACLRole {
 | 
				
			||||||
@ -207,6 +212,10 @@ export class ACL extends EventEmitter {
 | 
				
			|||||||
    this.middlewares.push(fn);
 | 
					    this.middlewares.push(fn);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  skip(resourceName: string, actionName: string, condition?: any) {
 | 
				
			||||||
 | 
					    this.skipManager.skip(resourceName, actionName, condition);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  middleware() {
 | 
					  middleware() {
 | 
				
			||||||
    const acl = this;
 | 
					    const acl = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										76
									
								
								packages/acl/src/skip-manager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								packages/acl/src/skip-manager.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					import { ACL } from './acl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ConditionFunc = (ctx: any) => boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SkipManager {
 | 
				
			||||||
 | 
					  protected skipActions = new Map<string, Map<string, string | ConditionFunc | true>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected registeredCondition = new Map<string, ConditionFunc>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(public acl: ACL) {
 | 
				
			||||||
 | 
					    this.registerSkipCondition('logged-in', (ctx) => {
 | 
				
			||||||
 | 
					      return ctx.state.currentUser;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  skip(resourceName: string, actionName: string, condition?: string | ConditionFunc) {
 | 
				
			||||||
 | 
					    const actionMap = this.skipActions.get(resourceName) || new Map<string, string | ConditionFunc>();
 | 
				
			||||||
 | 
					    actionMap.set(actionName, condition || true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.skipActions.set(resourceName, actionMap);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getSkippedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true> {
 | 
				
			||||||
 | 
					    const fetchActionSteps: string[] = ['*', resourceName];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const results = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const fetchActionStep of fetchActionSteps) {
 | 
				
			||||||
 | 
					      const resource = this.skipActions.get(fetchActionStep);
 | 
				
			||||||
 | 
					      if (resource) {
 | 
				
			||||||
 | 
					        const condition = resource.get('*') || resource.get(actionName);
 | 
				
			||||||
 | 
					        if (condition) {
 | 
				
			||||||
 | 
					          results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return results;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  registerSkipCondition(name: string, condition: ConditionFunc) {
 | 
				
			||||||
 | 
					    this.registeredCondition.set(name, condition);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  aclMiddleware() {
 | 
				
			||||||
 | 
					    return async (ctx, next) => {
 | 
				
			||||||
 | 
					      const { resourceName, actionName } = ctx.action;
 | 
				
			||||||
 | 
					      const skippedConditions = ctx.app.acl.skipManager.getSkippedConditions(resourceName, actionName);
 | 
				
			||||||
 | 
					      let skip = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (const skippedCondition of skippedConditions) {
 | 
				
			||||||
 | 
					        if (skippedCondition) {
 | 
				
			||||||
 | 
					          let skipResult = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (typeof skippedCondition === 'function') {
 | 
				
			||||||
 | 
					            skipResult = skippedCondition(ctx);
 | 
				
			||||||
 | 
					          } else if (skippedCondition) {
 | 
				
			||||||
 | 
					            skipResult = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (skipResult) {
 | 
				
			||||||
 | 
					            skip = true;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (skip) {
 | 
				
			||||||
 | 
					        ctx.permission = {
 | 
				
			||||||
 | 
					          skip: true,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      await next();
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -53,13 +53,6 @@ for (const plugin of plugins) {
 | 
				
			|||||||
  api.plugin(require(plugin).default);
 | 
					  api.plugin(require(plugin).default);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
api.acl.use(async (ctx, next) => {
 | 
					 | 
				
			||||||
  ctx.permission = {
 | 
					 | 
				
			||||||
    skip: true,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  await next();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if (process.argv.length < 3) {
 | 
					if (process.argv.length < 3) {
 | 
				
			||||||
  // @ts-ignore
 | 
					  // @ts-ignore
 | 
				
			||||||
  process.argv.push('start', '--port', process.env.API_PORT || 12302);
 | 
					  process.argv.push('start', '--port', process.env.API_PORT || 12302);
 | 
				
			||||||
 | 
				
			|||||||
@ -77,6 +77,8 @@ export class CollectionManagerPlugin extends Plugin {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      await next();
 | 
					      await next();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.app.acl.skip('collections', 'list', 'logged-in');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async load() {
 | 
					  async load() {
 | 
				
			||||||
 | 
				
			|||||||
@ -60,6 +60,8 @@ export class UiSchemaStoragePlugin extends Plugin {
 | 
				
			|||||||
      name: 'uiSchemas',
 | 
					      name: 'uiSchemas',
 | 
				
			||||||
      actions: uiSchemaActions,
 | 
					      actions: uiSchemaActions,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.app.acl.skip('uiSchemas', '*', 'logged-in');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async load() {
 | 
					  async load() {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import Database from '@nocobase/database';
 | 
					import Database, { BelongsToManyRepository } from '@nocobase/database';
 | 
				
			||||||
import PluginACL from '@nocobase/plugin-acl';
 | 
					import PluginACL from '@nocobase/plugin-acl';
 | 
				
			||||||
import { MockServer, mockServer } from '@nocobase/test';
 | 
					import { MockServer, mockServer } from '@nocobase/test';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -69,4 +69,50 @@ describe('role', () => {
 | 
				
			|||||||
    expect(roles.length).toEqual(1);
 | 
					    expect(roles.length).toEqual(1);
 | 
				
			||||||
    expect(roles[0].get('name')).toEqual('test2');
 | 
					    expect(roles[0].get('name')).toEqual('test2');
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should set users default role', async () => {
 | 
				
			||||||
 | 
					    await db.getRepository('roles').create({
 | 
				
			||||||
 | 
					      values: {
 | 
				
			||||||
 | 
					        name: 'test1',
 | 
				
			||||||
 | 
					        title: 'Admin User',
 | 
				
			||||||
 | 
					        allowConfigure: true,
 | 
				
			||||||
 | 
					        default: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await db.getRepository('roles').create({
 | 
				
			||||||
 | 
					      values: {
 | 
				
			||||||
 | 
					        name: 'test2',
 | 
				
			||||||
 | 
					        title: 'test2 user',
 | 
				
			||||||
 | 
					        allowConfigure: true,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const user = await db.getRepository('users').create({
 | 
				
			||||||
 | 
					      values: {
 | 
				
			||||||
 | 
					        token: '123',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const userRolesRepo = db.getRepository<BelongsToManyRepository>('users.roles', user.get('id') as string);
 | 
				
			||||||
 | 
					    await userRolesRepo.add('test1');
 | 
				
			||||||
 | 
					    await userRolesRepo.add('test2');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const response = await api
 | 
				
			||||||
 | 
					      .agent()
 | 
				
			||||||
 | 
					      .post('/users:setDefaultRole')
 | 
				
			||||||
 | 
					      .send({
 | 
				
			||||||
 | 
					        defaultRole: 'test2',
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .set({
 | 
				
			||||||
 | 
					        Authorization: `Bearer ${user.get('token')}`,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(response.statusCode).toEqual(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const userRoles = await userRolesRepo.find();
 | 
				
			||||||
 | 
					    const defaultRole = userRoles.find((userRole) => userRole.get('rolesUsers').default);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(defaultRole['name']).toEqual('test2');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -148,3 +148,34 @@ export async function changePassword(ctx: Context, next: Next) {
 | 
				
			|||||||
  ctx.body = ctx.state.currentUser.toJSON();
 | 
					  ctx.body = ctx.state.currentUser.toJSON();
 | 
				
			||||||
  await next();
 | 
					  await next();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function setDefaultRole(ctx: Context, next: Next) {
 | 
				
			||||||
 | 
					  const {
 | 
				
			||||||
 | 
					    values: { defaultRole },
 | 
				
			||||||
 | 
					  } = ctx.action.params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const currentUserId = ctx.state.currentUser.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await ctx.db.getRepository('rolesUsers').update({
 | 
				
			||||||
 | 
					    filter: {
 | 
				
			||||||
 | 
					      userId: currentUserId,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    values: {
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await ctx.db.getRepository('rolesUsers').update({
 | 
				
			||||||
 | 
					    filter: {
 | 
				
			||||||
 | 
					      userId: currentUserId,
 | 
				
			||||||
 | 
					      roleName: defaultRole,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    values: {
 | 
				
			||||||
 | 
					      default: true,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ctx.body = 'ok';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await next();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								packages/plugin-users/src/collections/roles-users.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								packages/plugin-users/src/collections/roles-users.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					import { CollectionOptions } from '@nocobase/database';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'rolesUsers',
 | 
				
			||||||
 | 
					  fields: [{ type: 'boolean', name: 'default' }],
 | 
				
			||||||
 | 
					} as CollectionOptions;
 | 
				
			||||||
@ -6,19 +6,41 @@ import { Context, Next } from '@nocobase/actions';
 | 
				
			|||||||
// 因为是否提供匿名访问资源是应用决定的,不是使用插件就一定不能匿名访问。
 | 
					// 因为是否提供匿名访问资源是应用决定的,不是使用插件就一定不能匿名访问。
 | 
				
			||||||
export function parseToken(options?: any) {
 | 
					export function parseToken(options?: any) {
 | 
				
			||||||
  return async function parseToken(ctx: Context, next: Next) {
 | 
					  return async function parseToken(ctx: Context, next: Next) {
 | 
				
			||||||
    const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
 | 
					    const user = await findUserByToken(ctx);
 | 
				
			||||||
    const User = ctx.db.getCollection('users');
 | 
					 | 
				
			||||||
    const user = await User.repository.findOne({
 | 
					 | 
				
			||||||
      filter: {
 | 
					 | 
				
			||||||
        token,
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      appends: ['roles'],
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
      ctx.state.currentUser = user;
 | 
					      ctx.state.currentUser = user;
 | 
				
			||||||
 | 
					      setCurrentRole(ctx, user);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return next();
 | 
					    return next();
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function setCurrentRole(ctx, user) {
 | 
				
			||||||
 | 
					  const userRoles = user.get('roles');
 | 
				
			||||||
 | 
					  let userRole;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (userRoles.length == 1) {
 | 
				
			||||||
 | 
					    userRole = userRoles[0].get('name');
 | 
				
			||||||
 | 
					  } else if (userRoles.length > 1) {
 | 
				
			||||||
 | 
					    const defaultRole = userRoles.findIndex((role) => role.get('rolesUsers').default);
 | 
				
			||||||
 | 
					    userRole = defaultRole !== -1 ? userRoles[defaultRole] : userRoles[0];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (userRole) {
 | 
				
			||||||
 | 
					    ctx.state.currentRole = userRole;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function findUserByToken(ctx: Context) {
 | 
				
			||||||
 | 
					  const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
 | 
				
			||||||
 | 
					  const User = ctx.db.getCollection('users');
 | 
				
			||||||
 | 
					  const user = await User.repository.findOne({
 | 
				
			||||||
 | 
					    filter: {
 | 
				
			||||||
 | 
					      token,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    appends: ['roles'],
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return user;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -59,6 +59,16 @@ export default class UsersPlugin extends Plugin {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.app.resourcer.use(middlewares.parseToken());
 | 
					    this.app.resourcer.use(middlewares.parseToken());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const publicActions = ['check', 'signin', 'signup', 'lostpassword', 'resetpassword', 'getUserByResetToken'];
 | 
				
			||||||
 | 
					    const loggedInActions = ['signout', 'updateProfile', 'changePassword', 'setDefaultRole'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    publicActions.forEach((action) => this.app.acl.skip('users', action));
 | 
				
			||||||
 | 
					    loggedInActions.forEach((action) => this.app.acl.skip('users', action, 'logged-in'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.app.acl.skip('*', '*', (ctx) => {
 | 
				
			||||||
 | 
					      return ctx.state.currentUser?.id == 1;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async load() {
 | 
					  async load() {
 | 
				
			||||||
@ -67,13 +77,22 @@ export default class UsersPlugin extends Plugin {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async install() {
 | 
					  getRootUserInfo() {
 | 
				
			||||||
    const {
 | 
					    const {
 | 
				
			||||||
      adminNickname = 'Super Admin',
 | 
					      adminNickname = 'Super Admin',
 | 
				
			||||||
      adminEmail = 'admin@nocobase.com',
 | 
					      adminEmail = 'admin@nocobase.com',
 | 
				
			||||||
      adminPassword = 'admin123',
 | 
					      adminPassword = 'admin123',
 | 
				
			||||||
    } = this.options;
 | 
					    } = this.options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      adminNickname,
 | 
				
			||||||
 | 
					      adminEmail,
 | 
				
			||||||
 | 
					      adminPassword,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  async install() {
 | 
				
			||||||
 | 
					    const { adminNickname, adminPassword, adminEmail } = this.getRootUserInfo();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const User = this.db.getCollection('users');
 | 
					    const User = this.db.getCollection('users');
 | 
				
			||||||
    await User.repository.create({
 | 
					    await User.repository.create({
 | 
				
			||||||
      values: {
 | 
					      values: {
 | 
				
			||||||
 | 
				
			|||||||
@ -102,6 +102,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    registerMiddlewares(this, options);
 | 
					    registerMiddlewares(this, options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (options.registerActions !== false) {
 | 
					    if (options.registerActions !== false) {
 | 
				
			||||||
      registerActions(this);
 | 
					      registerActions(this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user