mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
fix(attachment-url): only allow file collections with public URL access (#6664)
* refactor: isPublicAccessStorage * fix: bug * fix: bug * fix: only allow file collections with public URL access * fix: build * fix: test
This commit is contained in:
parent
3e2abe3d3c
commit
f99cdb5f02
@ -54,7 +54,7 @@ describe('CollectionSelect', () => {
|
|||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="css-9mlexe ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
class="css-a7w9kk ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-formily-item-label"
|
class="ant-formily-item-label"
|
||||||
@ -195,7 +195,7 @@ describe('CollectionSelect', () => {
|
|||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="css-9mlexe ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
class="css-a7w9kk ant-formily-item ant-formily-item-layout-horizontal ant-formily-item-feedback-layout-loose ant-formily-item-label-align-right ant-formily-item-control-align-left css-dev-only-do-not-override-1rquknz"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="ant-formily-item-label"
|
class="ant-formily-item-label"
|
||||||
|
@ -48,6 +48,7 @@ export type RemoteSelectProps<P = any> = SelectProps<P, any> & {
|
|||||||
CustomDropdownRender?: (v: any) => any;
|
CustomDropdownRender?: (v: any) => any;
|
||||||
optionFilter?: (option: any) => boolean;
|
optionFilter?: (option: any) => boolean;
|
||||||
toOptionsItem?: (data) => any;
|
toOptionsItem?: (data) => any;
|
||||||
|
onSuccess?: (data) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const InternalRemoteSelect = withDynamicSchemaProps(
|
const InternalRemoteSelect = withDynamicSchemaProps(
|
||||||
@ -68,6 +69,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
|||||||
dataSource: propsDataSource,
|
dataSource: propsDataSource,
|
||||||
toOptionsItem = (value) => value,
|
toOptionsItem = (value) => value,
|
||||||
popupMatchSelectWidth = false,
|
popupMatchSelectWidth = false,
|
||||||
|
onSuccess,
|
||||||
...others
|
...others
|
||||||
} = props;
|
} = props;
|
||||||
const dataSource = useDataSourceKey();
|
const dataSource = useDataSourceKey();
|
||||||
@ -178,6 +180,7 @@ const InternalRemoteSelect = withDynamicSchemaProps(
|
|||||||
{
|
{
|
||||||
manual,
|
manual,
|
||||||
debounceWait: wait,
|
debounceWait: wait,
|
||||||
|
onSuccess,
|
||||||
...(service.defaultParams ? { defaultParams: [service.defaultParams] } : {}),
|
...(service.defaultParams ? { defaultParams: [service.defaultParams] } : {}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -113,7 +113,6 @@ const InnerAttachmentUrl = (props) => {
|
|||||||
if (underFilter) {
|
if (underFilter) {
|
||||||
return <Input {...props} />;
|
return <Input {...props} />;
|
||||||
}
|
}
|
||||||
console.log(collectionField);
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', overflow: 'auto' }}>
|
<div style={{ width: '100%', overflow: 'auto' }}>
|
||||||
<AssociationField.FileSelector
|
<AssociationField.FileSelector
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useFieldSchema } from '@formily/react';
|
import { useFieldSchema, useField } from '@formily/react';
|
||||||
import { useCollectionField, useDesignable, useRequest } from '@nocobase/client';
|
import { useCollectionField, useDesignable, useRequest } from '@nocobase/client';
|
||||||
import { cloneDeep, uniqBy } from 'lodash';
|
import { cloneDeep, uniqBy } from 'lodash';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
@ -63,15 +63,11 @@ export const useInsertSchema = (component) => {
|
|||||||
|
|
||||||
export const useAttachmentTargetProps = () => {
|
export const useAttachmentTargetProps = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// TODO(refactor): whitelist should be changed to storage property,url is signed by plugin-s3-pro, this enmus is from plugin-file-manager
|
const field = useField();
|
||||||
const buildInStorage = ['local', 'ali-oss', 's3', 'tx-cos'];
|
|
||||||
return {
|
return {
|
||||||
service: {
|
service: {
|
||||||
resource: 'collections',
|
resource: 'collections:listFileCollectionsWithPublicStorage',
|
||||||
params: {
|
params: {
|
||||||
filter: {
|
|
||||||
'options.template': 'file',
|
|
||||||
},
|
|
||||||
paginate: false,
|
paginate: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -80,27 +76,9 @@ export const useAttachmentTargetProps = () => {
|
|||||||
label: 'title',
|
label: 'title',
|
||||||
value: 'name',
|
value: 'name',
|
||||||
},
|
},
|
||||||
mapOptions: (value) => {
|
onSuccess: (data) => {
|
||||||
if (value.name === 'attachments') {
|
field.data = field.data || {};
|
||||||
return {
|
field.data.options = data?.data;
|
||||||
...value,
|
|
||||||
title: t('Attachments'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
toOptionsItem: (data) => {
|
|
||||||
data.unshift({
|
|
||||||
name: 'attachments',
|
|
||||||
title: t('Attachments'),
|
|
||||||
});
|
|
||||||
return uniqBy(
|
|
||||||
data.filter((v) => v.name),
|
|
||||||
'name',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
optionFilter: (option) => {
|
|
||||||
return !option.storage || buildInStorage.includes(option.storage);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -51,12 +51,18 @@ export class AttachmentURLFieldInterface extends CollectionFieldInterface {
|
|||||||
...defaultProps,
|
...defaultProps,
|
||||||
target: {
|
target: {
|
||||||
required: true,
|
required: true,
|
||||||
default: 'attachments',
|
|
||||||
type: 'string',
|
type: 'string',
|
||||||
title: tStr('Which file collection should it be uploaded to'),
|
title: tStr('Which file collection should it be uploaded to'),
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'RemoteSelect',
|
'x-component': 'RemoteSelect',
|
||||||
'x-use-component-props': useAttachmentTargetProps,
|
'x-use-component-props': useAttachmentTargetProps,
|
||||||
|
'x-reactions': (field) => {
|
||||||
|
const options = field.data?.options || [];
|
||||||
|
const hasAttachments = options.some((opt) => opt?.name === 'attachments');
|
||||||
|
if (hasAttachments) {
|
||||||
|
!field.initialValue && field.setInitialValue('attachments');
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
targetKey: {
|
targetKey: {
|
||||||
'x-hidden': true,
|
'x-hidden': true,
|
||||||
|
@ -8,13 +8,50 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Plugin } from '@nocobase/server';
|
import { Plugin } from '@nocobase/server';
|
||||||
|
import PluginFileManagerServer from '@nocobase/plugin-file-manager';
|
||||||
|
|
||||||
export class PluginFieldAttachmentUrlServer extends Plugin {
|
export class PluginFieldAttachmentUrlServer extends Plugin {
|
||||||
async afterAdd() {}
|
async afterAdd() {}
|
||||||
|
|
||||||
async beforeLoad() {}
|
async beforeLoad() {}
|
||||||
|
|
||||||
async load() {}
|
async load() {
|
||||||
|
this.app.resourceManager.registerActionHandlers({
|
||||||
|
'collections:listFileCollectionsWithPublicStorage': async (ctx, next) => {
|
||||||
|
const fileCollections = await this.db.getRepository('collections').find({
|
||||||
|
filter: {
|
||||||
|
'options.template': 'file',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const filePlugin = this.pm.get('file-manager') as PluginFileManagerServer | any;
|
||||||
|
|
||||||
|
const options = [];
|
||||||
|
|
||||||
|
const fileCollection = this.db.getCollection('attachments');
|
||||||
|
|
||||||
|
if (await filePlugin.isPublicAccessStorage(fileCollection?.options?.storage)) {
|
||||||
|
options.push({
|
||||||
|
title: '{{t("Attachments")}}',
|
||||||
|
name: 'attachments',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fileCollection of fileCollections) {
|
||||||
|
if (await filePlugin.isPublicAccessStorage(fileCollection?.options?.storage)) {
|
||||||
|
options.push({
|
||||||
|
name: fileCollection.name,
|
||||||
|
title: fileCollection.title,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = options;
|
||||||
|
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async install() {}
|
async install() {}
|
||||||
|
|
||||||
|
@ -313,6 +313,27 @@ export class PluginFileManagerServer extends Plugin {
|
|||||||
const storageType = this.storageTypes.get(storage.type);
|
const storageType = this.storageTypes.get(storage.type);
|
||||||
return new storageType(storage).getFileURL(file, preview ? storage.options.thumbnailRule : '');
|
return new storageType(storage).getFileURL(file, preview ? storage.options.thumbnailRule : '');
|
||||||
}
|
}
|
||||||
|
async isPublicAccessStorage(storageName) {
|
||||||
|
const storageRepository = this.db.getRepository('storages');
|
||||||
|
const storages = await storageRepository.findOne({
|
||||||
|
filter: { default: true },
|
||||||
|
});
|
||||||
|
let storage;
|
||||||
|
if (!storageName) {
|
||||||
|
storage = storages;
|
||||||
|
} else {
|
||||||
|
storage = await storageRepository.findOne({
|
||||||
|
filter: {
|
||||||
|
name: storageName,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
storage = this.parseStorage(storage);
|
||||||
|
if (['local', 'ali-oss', 's3', 'tx-cos'].includes(storage.type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !!storage.options?.public;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PluginFileManagerServer;
|
export default PluginFileManagerServer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user