From 0fd41c9036c8967ef302eb803cbde89089e3aa1f Mon Sep 17 00:00:00 2001 From: chenos Date: Sun, 10 Apr 2022 22:05:18 +0800 Subject: [PATCH] fix: set acl role --- packages/client/src/acl/ACLProvider.tsx | 5 + .../src/__tests__/setCurrentRole.test.ts | 104 ++++++++++++++++++ packages/plugin-users/src/actions/users.ts | 26 +---- .../plugin-users/src/collections/users.ts | 1 + .../src/middlewares/parseToken.ts | 26 +++-- packages/plugin-users/src/models/UserModel.ts | 41 +++++++ packages/plugin-users/src/server.ts | 6 +- 7 files changed, 171 insertions(+), 38 deletions(-) create mode 100644 packages/plugin-users/src/__tests__/setCurrentRole.test.ts create mode 100644 packages/plugin-users/src/models/UserModel.ts diff --git a/packages/client/src/acl/ACLProvider.tsx b/packages/client/src/acl/ACLProvider.tsx index 5a59407b7..8e54f29d7 100644 --- a/packages/client/src/acl/ACLProvider.tsx +++ b/packages/client/src/acl/ACLProvider.tsx @@ -1,4 +1,5 @@ import { useFieldSchema } from '@formily/react'; +import { useCookieState } from 'ahooks'; import { Spin } from 'antd'; import React, { createContext, useContext } from 'react'; import { Redirect } from 'react-router-dom'; @@ -20,6 +21,7 @@ export const ACLProvider = (props) => { export const ACLRolesCheckProvider = (props) => { const { setDesignable } = useDesignable(); + const [roleName, setRoleName] = useCookieState('currentRoleName'); const result = useRequest( { url: 'roles:check', @@ -29,6 +31,9 @@ export const ACLRolesCheckProvider = (props) => { if (!data?.data?.allowConfigure && !data?.data?.allowAll) { setDesignable(false); } + if (data?.data?.role !== roleName) { + setRoleName(data?.data?.role); + } }, }, ); diff --git a/packages/plugin-users/src/__tests__/setCurrentRole.test.ts b/packages/plugin-users/src/__tests__/setCurrentRole.test.ts new file mode 100644 index 000000000..e93410bb6 --- /dev/null +++ b/packages/plugin-users/src/__tests__/setCurrentRole.test.ts @@ -0,0 +1,104 @@ +import Database from '@nocobase/database'; +import PluginACL from '@nocobase/plugin-acl'; +import UsersPlugin from '@nocobase/plugin-users'; +import { MockServer, mockServer } from '@nocobase/test'; +import { setCurrentRole } from '../middlewares/parseToken'; +import { userPluginConfig } from './utils'; + +describe('role', () => { + let api: MockServer; + let db: Database; + + let usersPlugin: UsersPlugin; + + beforeEach(async () => { + api = mockServer(); + await api.cleanDb(); + api.plugin(UsersPlugin, userPluginConfig); + api.plugin(PluginACL); + await api.loadAndInstall(); + + db = api.db; + usersPlugin = api.getPlugin('@nocobase/plugin-users'); + }); + + afterEach(async () => { + await api.destroy(); + }); + + it('should set role with X-Role when exists', async () => { + const currentUser = await db.getRepository('users').findOne({ + appends: ['roles'], + }); + const ctx = { + get(name) { + if (name === 'X-Role') { + return 'admin'; + } + }, + state: { + currentUser, + currentRole: '', + } + } + setCurrentRole(ctx); + expect(ctx.state.currentRole).toBe('admin'); + }); + + it('should set role with default', async () => { + const currentUser = await db.getRepository('users').findOne({ + appends: ['roles'], + }); + const ctx = { + get(name) { + if (name === 'X-Role') { + return ''; + } + }, + state: { + currentUser, + currentRole: '', + } + } + setCurrentRole(ctx); + expect(ctx.state.currentRole).toBe('root'); + }); + + it('should set role with default when x-role does not exist', async () => { + const currentUser = await db.getRepository('users').findOne({ + appends: ['roles'], + }); + const ctx = { + get(name) { + if (name === 'X-Role') { + return 'abc'; + } + }, + state: { + currentUser, + currentRole: '', + } + } + setCurrentRole(ctx); + expect(ctx.state.currentRole).toBe('root'); + }); + + it('should set role with anonymous', async () => { + const currentUser = await db.getRepository('users').findOne({ + appends: ['roles'], + }); + const ctx = { + get(name) { + if (name === 'X-Role') { + return 'anonymous'; + } + }, + state: { + currentUser, + currentRole: '', + } + } + setCurrentRole(ctx); + expect(ctx.state.currentRole).toBe('anonymous'); + }); +}); diff --git a/packages/plugin-users/src/actions/users.ts b/packages/plugin-users/src/actions/users.ts index d08668213..22e16e058 100644 --- a/packages/plugin-users/src/actions/users.ts +++ b/packages/plugin-users/src/actions/users.ts @@ -155,31 +155,7 @@ export async function setDefaultRole(ctx: Context, next: Next) { values: { roleName }, } = ctx.action.params; - if (roleName == 'anonymous') { - ctx.body = 'ok'; - return next(); - } - - 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, - }, - values: { - default: true, - }, - }); + await ctx.state.currentUser.setDefaultRole(roleName); ctx.body = 'ok'; diff --git a/packages/plugin-users/src/collections/users.ts b/packages/plugin-users/src/collections/users.ts index bad62f649..707cca7df 100644 --- a/packages/plugin-users/src/collections/users.ts +++ b/packages/plugin-users/src/collections/users.ts @@ -4,6 +4,7 @@ export default { name: 'users', title: '{{t("Users")}}', sortable: 'sort', + model: 'UserModel', fields: [ { interface: 'input', diff --git a/packages/plugin-users/src/middlewares/parseToken.ts b/packages/plugin-users/src/middlewares/parseToken.ts index 091ffd61f..0f8bb723b 100644 --- a/packages/plugin-users/src/middlewares/parseToken.ts +++ b/packages/plugin-users/src/middlewares/parseToken.ts @@ -6,32 +6,34 @@ export function parseToken(options?: { plugin: UsersPlugin }) { const user = await findUserByToken(ctx, options.plugin); if (user) { ctx.state.currentUser = user; - setCurrentRole(ctx, user); + setCurrentRole(ctx); } return next(); }; } -function setCurrentRole(ctx, user) { - const roleName = ctx.get('X-Role'); +export function setCurrentRole(ctx) { + let currentRole = ctx.get('X-Role'); - if (roleName === 'anonymous') { - ctx.state.currentRole = roleName; + if (currentRole === 'anonymous') { + ctx.state.currentRole = currentRole; return; } - const userRoles = user.get('roles'); - let userRole; + const userRoles = ctx.state.currentUser.roles; if (userRoles.length == 1) { - userRole = userRoles[0].get('name'); + currentRole = userRoles[0].name; } else if (userRoles.length > 1) { - const defaultRole = userRoles.findIndex((role) => role.get('rolesUsers').default); - userRole = (defaultRole !== -1 ? userRoles[defaultRole] : userRoles[0]).get('name'); + const role = userRoles.find((role) => role.name === currentRole); + if (!role) { + const defaultRole = userRoles.find((role) => role.rolesUsers?.default); + currentRole = (defaultRole || userRoles[0])?.name; + } } - if (userRole) { - ctx.state.currentRole = userRole; + if (currentRole) { + ctx.state.currentRole = currentRole; } } diff --git a/packages/plugin-users/src/models/UserModel.ts b/packages/plugin-users/src/models/UserModel.ts new file mode 100644 index 000000000..191c66a08 --- /dev/null +++ b/packages/plugin-users/src/models/UserModel.ts @@ -0,0 +1,41 @@ +import Database, { Model, TransactionAble } from '@nocobase/database'; + +export class UserModel extends Model { + async setDefaultRole(roleName: string, options: TransactionAble = {}) { + if (roleName == 'anonymous') { + return false; + } + + const db = (this.constructor as any).database as Database; + const transaction = options.transaction || (await db.sequelize.transaction()); + + try { + await db.getRepository('rolesUsers').update({ + filter: { + userId: this.get('id'), + }, + values: { + default: false, + }, + transaction, + }); + + await db.getRepository('rolesUsers').update({ + filter: { + userId: this.get('id'), + roleName, + }, + values: { + default: true, + }, + transaction, + }); + await transaction.commit(); + } catch (error) { + await transaction.rollback(); + throw error; + } + + return true; + } +} diff --git a/packages/plugin-users/src/server.ts b/packages/plugin-users/src/server.ts index 8c0312644..11efbb010 100644 --- a/packages/plugin-users/src/server.ts +++ b/packages/plugin-users/src/server.ts @@ -4,6 +4,7 @@ import { resolve } from 'path'; import * as actions from './actions/users'; import { JwtOptions, JwtService } from './jwt-service'; import * as middlewares from './middlewares'; +import { UserModel } from './models/UserModel'; export interface UserPluginConfig { jwt: JwtOptions; @@ -24,6 +25,7 @@ export default class UsersPlugin extends Plugin { } async beforeLoad() { + this.db.registerModels({ UserModel }); this.db.on('users.afterCreateWithAssociations', async (model, options) => { const { transaction } = options; @@ -113,7 +115,7 @@ export default class UsersPlugin extends Plugin { const { adminNickname, adminPassword, adminEmail } = this.getRootUserInfo(); const User = this.db.getCollection('users'); - await User.repository.create({ + const user = await User.repository.create({ values: { nickname: adminNickname, email: adminEmail, @@ -122,6 +124,8 @@ export default class UsersPlugin extends Plugin { }, }); + await user.setDefaultRole('root'); + const repo = this.db.getRepository('collections'); if (repo) { await repo.db2cm('users');