fix(custom-request): permission issues (#3306)

* fix(custom-request-plugin): cannot see custom request action in non-root role when acl doesn't set

* fix: list all roles

* feat: display all roles

* feat: support

* fix: remove unused code

* fix: options is null

* fix: translation

* fix: migration error

---------

Co-authored-by: chenos <chenlinxh@gmail.com>
This commit is contained in:
Dunqing 2024-01-13 18:13:18 +08:00 committed by GitHub
parent 1adaa53c2b
commit 8ab69500c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 43 additions and 48 deletions

View File

@ -191,6 +191,7 @@
"Insert if not exists, or update": "Insert if not exists, or update",
"Determine whether a record exists by the following fields": "Determine whether a record exists by the following fields",
"Update": "Update",
"Update record": "Update record",
"View": "View",
"View record": "View record",
"Refresh": "Refresh",

View File

@ -200,6 +200,7 @@
"Action type": "操作类型",
"Actions": "操作",
"Update": "更新",
"Update record": "更新数据",
"View": "查看",
"View record": "查看数据",
"Refresh": "刷新",

View File

@ -13,6 +13,7 @@ export const BlockInitializer = (props) => {
const s = merge(schema || {}, item.schema || {});
item?.schemaInitialize?.(s);
insert(s);
props.onClick?.(s);
}}
/>
);

View File

@ -1,9 +1,9 @@
import { Action, useAPIClient, useRequest } from '@nocobase/client';
import React from 'react';
import { CustomRequestActionDesigner } from './CustomRequestActionDesigner';
import { useFieldSchema } from '@formily/react';
import { listByCurrentRoleUrl } from '../constants';
import { useCustomizeRequestActionProps } from '../hooks';
import { CustomRequestActionDesigner } from './CustomRequestActionDesigner';
export const CustomRequestActionACLDecorator = (props) => {
const apiClient = useAPIClient();

View File

@ -6,7 +6,6 @@ import {
SchemaSettingsActionModalItem,
actionSettingsItems,
useCollection,
useCurrentRoles,
useRequest,
} from '@nocobase/client';
import React from 'react';
@ -74,14 +73,11 @@ function CustomRequestACL() {
},
);
const currentRoles = useCurrentRoles();
return (
<>
<SchemaSettingsActionModalItem
title={t('Access Control')}
schema={CustomRequestACLSchema}
scope={{ currentRoles }}
initialValues={{
roles: data?.data?.roles,
}}

View File

@ -1,6 +1,6 @@
import { useCollection, useCollectionFilterOptions, useCompile } from '@nocobase/client';
import { useTranslation } from '../locale';
import { useMemo } from 'react';
import { useTranslation } from '../locale';
export const useCustomRequestVariableOptions = () => {
const collection = useCollection();
@ -16,17 +16,17 @@ export const useCustomRequestVariableOptions = () => {
return [
{
name: 'currentRecord',
title: t('Current record'),
title: t('Current record', { ns: 'client' }),
children: [...fields],
},
{
name: 'currentUser',
title: t('Current user'),
title: t('Current user', { ns: 'client' }),
children: userFields,
},
{
name: 'currentTime',
title: t('Current time'),
title: t('Current time', { ns: 'client' }),
children: null,
},
];

View File

@ -24,12 +24,10 @@ export const useCustomizeRequestActionProps = () => {
const actionSchema = useFieldSchema();
const compile = useCompile();
const form = useForm();
const { fields, getField, getPrimaryKey } = useCollection();
const { field, resource, __parent, service } = useBlockRequestContext();
const { getActiveFieldsName } = useFormActiveFields() || {};
const { getPrimaryKey } = useCollection();
const { resource, __parent, service } = useBlockRequestContext();
const record = useRecord();
const fieldSchema = useFieldSchema();
const { data, runAsync } = useGetCustomRequest();
const actionField = useField();
const { setVisible } = useActionContext();
const { modal, message } = App.useApp();
@ -38,29 +36,14 @@ export const useCustomizeRequestActionProps = () => {
return {
async onClick() {
const { skipValidator, onSuccess } = actionSchema?.['x-action-settings'] ?? {};
const options = data ? data?.data?.options : (await runAsync())?.data?.options;
if (!options?.['url']) {
return message.error(t('Please configure the request settings first'));
}
const xAction = actionSchema?.['x-action'];
if (skipValidator !== true && xAction === 'customize:form:request') {
await form.submit();
}
let formValues = {};
const methods = ['POST', 'PUT', 'PATCH'];
if (xAction === 'customize:form:request' && methods.includes(options['method'])) {
const fieldNames = fields.map((field) => field.name);
const values = getFormValues({
filterByTk,
field,
form,
fieldNames,
getField,
resource,
actionFields: getActiveFieldsName?.('form') || [],
});
formValues = values;
if (xAction === 'customize:form:request') {
formValues = form.values;
}
actionField.data ??= {};

View File

@ -27,14 +27,13 @@ export const CustomRequestInitializer: React.FC<any> = (props) => {
<BlockInitializer
{...itemConfig}
item={itemConfig}
insert={async (s) => {
await customRequestsResource.updateOrCreate({
onClick={async (s) => {
// create a custom request
await customRequestsResource.create({
values: {
key: s['x-uid'],
},
filterKeys: ['key'],
});
await props?.insert(s);
}}
schema={schema}
/>

View File

@ -10,15 +10,18 @@ export const CustomRequestACLSchema = {
'x-decorator-props': {
tooltip: generateNTemplate('If not set, all roles can see this action'),
},
'x-component': 'Select',
'x-component': 'RemoteSelect',
'x-component-props': {
multiple: true,
objectValue: true,
service: {
resource: 'roles',
},
manual: false,
fieldNames: {
label: 'title',
value: 'name',
},
objectValue: true,
options: '{{ currentRoles }}',
},
},
},

View File

@ -1,9 +1,9 @@
import { Context, Next } from '@nocobase/actions';
import { parse } from '@nocobase/utils';
import { appendArrayColumn } from '@nocobase/evaluators';
import axios from 'axios';
import CustomRequestPlugin from '../plugin';
import { appendArrayColumn } from '@nocobase/evaluators';
const getHeaders = (headers: Record<string, any>) => {
return Object.keys(headers).reduce((hds, key) => {
@ -84,7 +84,10 @@ export async function send(this: CustomRequestPlugin, ctx: Context, next: Next)
ctx.withoutDataWrapping = true;
const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options;
const { collectionName, url, headers = [], params = [], data = {}, ...options } = requestConfig.options || {};
if (!url) {
return ctx.throw(400, ctx.t('Please configure the request settings first', { ns: 'custom-request' }));
}
let currentRecordValues = {};
if (collectionName && typeof currentRecord.id !== 'undefined') {
const recordRepo = ctx.db.getRepository(collectionName);

View File

@ -36,7 +36,7 @@ export class CustomRequestPlugin extends Plugin {
this.app.acl.registerSnippet({
name: `ui.${this.name}`,
actions: ['customRequests:*'],
actions: ['customRequests:*', 'roles:list'],
});
this.app.acl.allow('customRequests', ['send', 'listByCurrentRole'], 'loggedIn');

View File

@ -23,10 +23,14 @@ export default class AddUsersPhoneMigration extends Migration {
type: DataTypes.STRING,
});
}
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
type: 'unique',
fields: [field.columnName()],
});
try {
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
type: 'unique',
fields: [field.columnName()],
});
} catch (error) {
//
}
this.db.removeCollection('users');
}

View File

@ -23,10 +23,14 @@ export default class AddUserNameMigration extends Migration {
type: DataTypes.STRING,
});
}
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
type: 'unique',
fields: [field.columnName()],
});
try {
await this.db.sequelize.getQueryInterface().addConstraint(tableNameWithSchema, {
type: 'unique',
fields: [field.columnName()],
});
} catch (error) {
//
}
this.db.removeCollection('users');
}
}