mirror of
https://github.com/langgenius/dify.git
synced 2026-06-25 22:31:10 +08:00
feat: enhance app permissions and access controls (#37933)
This commit is contained in:
parent
b33e8f0ddb
commit
affdc89f84
@ -39,7 +39,7 @@ vi.mock('@/context/app-context', () => ({
|
||||
},
|
||||
workspacePermissionKeys: [
|
||||
'plugin.install',
|
||||
'plugin.manage',
|
||||
'plugin.delete',
|
||||
'plugin.plugin_preferences',
|
||||
],
|
||||
}),
|
||||
|
||||
@ -23,12 +23,12 @@ vi.mock('@/app/components/plugins/hooks', () => ({
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
userProfile: { id: 'user-1', timezone: 'UTC' },
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.manage', 'plugin.plugin_preferences'],
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.delete', 'plugin.plugin_preferences'],
|
||||
langGeniusVersionInfo: { current_version: '1.0.0' },
|
||||
}),
|
||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) =>
|
||||
selector({
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.manage', 'plugin.plugin_preferences'],
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.delete', 'plugin.plugin_preferences'],
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -46,12 +46,12 @@ vi.mock('@/app/components/plugins/hooks', () => ({
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
userProfile: { id: 'user-1', timezone: 'UTC' },
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.manage', 'plugin.plugin_preferences'],
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.delete', 'plugin.plugin_preferences'],
|
||||
langGeniusVersionInfo: { current_version: '1.0.0' },
|
||||
}),
|
||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) =>
|
||||
selector({
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.manage', 'plugin.plugin_preferences'],
|
||||
workspacePermissionKeys: ['tool.manage', 'mcp.manage', 'plugin.install', 'plugin.delete', 'plugin.plugin_preferences'],
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -128,7 +128,7 @@ describe('AppDetailLayout', () => {
|
||||
expect(useStore.getState().appDetail).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should allow users with monitor access to open logs directly', async () => {
|
||||
it('should redirect logs pages when log and annotation access is missing', async () => {
|
||||
mockPathname = '/app/app-1/logs'
|
||||
mockFetchAppDetailDirect.mockResolvedValue(createAppDetail({ permission_keys: [AppACLPermission.Monitor] }))
|
||||
|
||||
@ -138,9 +138,26 @@ describe('AppDetailLayout', () => {
|
||||
</AppDetailLayout>,
|
||||
)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockReplace).toHaveBeenCalledWith('/app/app-1/overview')
|
||||
})
|
||||
expect(screen.queryByText('App page content')).not.toBeInTheDocument()
|
||||
expect(useStore.getState().appDetail).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should allow users with log and annotation access to open logs directly', async () => {
|
||||
mockPathname = '/app/app-1/logs'
|
||||
mockFetchAppDetailDirect.mockResolvedValue(createAppDetail({ permission_keys: [AppACLPermission.LogAndAnnotation] }))
|
||||
|
||||
render(
|
||||
<AppDetailLayout appId="app-1">
|
||||
<div>App page content</div>
|
||||
</AppDetailLayout>,
|
||||
)
|
||||
|
||||
await waitForAppContent()
|
||||
|
||||
expect(mockReplace).not.toHaveBeenCalledWith('/app/app-1/overview')
|
||||
expect(mockReplace).not.toHaveBeenCalled()
|
||||
expect(useStore.getState().appDetail?.id).toBe('app-1')
|
||||
})
|
||||
|
||||
@ -289,7 +306,7 @@ describe('AppDetailLayout', () => {
|
||||
expect(useStore.getState().appDetail).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should redirect annotation pages when edit access is missing', async () => {
|
||||
it('should redirect annotation pages when log and annotation access is missing', async () => {
|
||||
mockPathname = '/app/app-1/annotations'
|
||||
mockFetchAppDetailDirect.mockResolvedValue(createAppDetail({
|
||||
mode: AppModeEnum.CHAT,
|
||||
@ -309,11 +326,11 @@ describe('AppDetailLayout', () => {
|
||||
expect(useStore.getState().appDetail).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should allow users with edit access to open annotations directly', async () => {
|
||||
it('should allow users with log and annotation access to open annotations directly', async () => {
|
||||
mockPathname = '/app/app-1/annotations'
|
||||
mockFetchAppDetailDirect.mockResolvedValue(createAppDetail({
|
||||
mode: AppModeEnum.CHAT,
|
||||
permission_keys: [AppACLPermission.Edit],
|
||||
permission_keys: [AppACLPermission.LogAndAnnotation],
|
||||
}))
|
||||
|
||||
render(
|
||||
|
||||
@ -108,8 +108,8 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
||||
const isAccessConfigPath = pathname.endsWith('access-config')
|
||||
if (
|
||||
(isLayoutPath && !appACLCapabilities.canAccessLayout)
|
||||
|| (isLogsPath && !appACLCapabilities.canMonitor)
|
||||
|| (isAnnotationsPath && !appACLCapabilities.canEdit)
|
||||
|| (isLogsPath && !appACLCapabilities.canAccessLogAndAnnotation)
|
||||
|| (isAnnotationsPath && !appACLCapabilities.canAccessLogAndAnnotation)
|
||||
|| (isOverviewPath && !appACLCapabilities.canMonitor)
|
||||
|| (isAccessConfigPath && !appACLCapabilities.canAccessConfig)
|
||||
) {
|
||||
|
||||
@ -69,14 +69,34 @@ describe('OverviewView monitor permission', () => {
|
||||
expect(screen.queryByRole('button', { name: 'tracing' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render overview page content when app monitor permission is granted', () => {
|
||||
it('should render overview page content without tracing entry when only app monitor permission is granted', () => {
|
||||
testState.appDetail.permission_keys = [AppACLPermission.Monitor]
|
||||
|
||||
render(<OverviewView appId="app-1" />)
|
||||
|
||||
expect(screen.getByText('api key info panel')).toBeInTheDocument()
|
||||
expect(screen.getByText(/chart view app-1/)).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'tracing' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render tracing entry when app tracing config permission is granted with monitor access', () => {
|
||||
testState.appDetail.permission_keys = [AppACLPermission.Monitor, AppACLPermission.TracingConfig]
|
||||
|
||||
render(<OverviewView appId="app-1" />)
|
||||
|
||||
expect(screen.getByText('api key info panel')).toBeInTheDocument()
|
||||
expect(screen.getByText(/chart view app-1/)).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'tracing' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render overview page content when only app tracing config permission is granted', () => {
|
||||
testState.appDetail.permission_keys = [AppACLPermission.TracingConfig]
|
||||
|
||||
render(<OverviewView appId="app-1" />)
|
||||
|
||||
expect(screen.queryByText('api key info panel')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText(/chart view app-1/)).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'tracing' })).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -122,11 +122,24 @@ describe('Tracing overview panel permissions', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('allows tracing config when app ACL includes monitor permission', async () => {
|
||||
it('marks tracing config as read-only with app monitor permission only', async () => {
|
||||
testState.appPermissionKeys = [AppACLPermission.Monitor]
|
||||
|
||||
await renderPanel()
|
||||
|
||||
await waitFor(() => {
|
||||
expect(testState.configButtonProps[0]).toMatchObject({
|
||||
readOnly: true,
|
||||
hasConfigured: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('allows tracing config when app ACL includes tracing config permission', async () => {
|
||||
testState.appPermissionKeys = [AppACLPermission.TracingConfig]
|
||||
|
||||
await renderPanel()
|
||||
|
||||
await waitFor(() => {
|
||||
expect(testState.configButtonProps[0]).toMatchObject({
|
||||
readOnly: false,
|
||||
|
||||
@ -47,7 +47,7 @@ const Panel: FC = () => {
|
||||
resourceMaintainer: appDetail?.maintainer,
|
||||
workspacePermissionKeys,
|
||||
}), [appDetail?.maintainer, appDetail?.permission_keys, currentUserId, workspacePermissionKeys])
|
||||
const canConfigTracing = appACLCapabilities.canMonitor
|
||||
const canConfigTracing = appACLCapabilities.canConfigureTracing
|
||||
const readOnly = !canConfigTracing
|
||||
|
||||
const [isLoaded, {
|
||||
|
||||
@ -16,13 +16,13 @@ const OverviewView = ({ appId }: OverviewViewProps) => {
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const currentUserId = useAppContextWithSelector(state => state.userProfile?.id)
|
||||
const workspacePermissionKeys = useAppContextWithSelector(state => state.workspacePermissionKeys)
|
||||
const canMonitor = React.useMemo(() => getAppACLCapabilities(appDetail?.permission_keys, {
|
||||
const appACLCapabilities = React.useMemo(() => getAppACLCapabilities(appDetail?.permission_keys, {
|
||||
currentUserId,
|
||||
resourceMaintainer: appDetail?.maintainer,
|
||||
workspacePermissionKeys,
|
||||
}).canMonitor, [appDetail?.maintainer, appDetail?.permission_keys, currentUserId, workspacePermissionKeys])
|
||||
}), [appDetail?.maintainer, appDetail?.permission_keys, currentUserId, workspacePermissionKeys])
|
||||
|
||||
if (!appDetail || !canMonitor)
|
||||
if (!appDetail || !appACLCapabilities.canMonitor)
|
||||
return null
|
||||
|
||||
return (
|
||||
@ -31,7 +31,7 @@ const OverviewView = ({ appId }: OverviewViewProps) => {
|
||||
<div className="min-h-0 flex-1">
|
||||
<ChartView
|
||||
appId={appId}
|
||||
headerRight={<TracingPanel />}
|
||||
headerRight={appACLCapabilities.canConfigureTracing ? <TracingPanel /> : null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -69,28 +69,30 @@ describe('AppDetailSection', () => {
|
||||
|
||||
// Rendering behavior for app detail navigation entries.
|
||||
describe('Rendering', () => {
|
||||
it('should render logs and overview for chat apps with app monitor permission', () => {
|
||||
it('should render only overview for chat apps with app monitor permission', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'chat'
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.overview' })).toHaveAttribute('href', '/app/app-1/overview')
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.logs' })).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.annotations' })).not.toBeInTheDocument()
|
||||
expect(screen.queryAllByRole('separator')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('should render logs and annotations for chat apps with app log and annotation permission', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'chat'
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.logs' })).toHaveAttribute('href', '/app/app-1/logs')
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.overview' })).toHaveAttribute('href', '/app/app-1/overview')
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.annotations' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render annotations for chat apps with app edit permission', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'chat'
|
||||
mockAppPermissionKeys = [AppACLPermission.Edit]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.annotations' })).toHaveAttribute('href', '/app/app-1/annotations')
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.annotations' })).toHaveAttribute('data-icon', 'Annotations')
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.overview' })).not.toBeInTheDocument()
|
||||
@ -99,7 +101,7 @@ describe('AppDetailSection', () => {
|
||||
it('should render dividers before logs and after annotations for chat apps', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'chat'
|
||||
mockAppPermissionKeys = [AppACLPermission.Monitor, AppACLPermission.Edit]
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@ -111,6 +113,7 @@ describe('AppDetailSection', () => {
|
||||
it('should only render logs navigation for workflow apps', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'workflow'
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@ -123,6 +126,7 @@ describe('AppDetailSection', () => {
|
||||
it('should render dividers before and after logs for workflow apps', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'workflow'
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@ -134,6 +138,7 @@ describe('AppDetailSection', () => {
|
||||
it('should only render logs navigation for completion apps', () => {
|
||||
// Arrange
|
||||
mockAppMode = 'completion'
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@ -143,9 +148,9 @@ describe('AppDetailSection', () => {
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.annotations' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render monitor group dividers without monitor or edit permission', () => {
|
||||
it('should not render log and annotation group dividers without log and annotation permission', () => {
|
||||
// Arrange
|
||||
mockAppPermissionKeys = []
|
||||
mockAppPermissionKeys = [AppACLPermission.Monitor]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
@ -154,19 +159,20 @@ describe('AppDetailSection', () => {
|
||||
expect(screen.queryAllByRole('separator')).toHaveLength(0)
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.logs' })).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.annotations' })).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.overview' })).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.overview' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render logs for users with app monitor permission', () => {
|
||||
it('should render logs for users with app log and annotation permission', () => {
|
||||
// Arrange
|
||||
mockAppPermissionKeys = [AppACLPermission.Monitor]
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection />)
|
||||
|
||||
// Assert
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.logs' })).toHaveAttribute('href', '/app/app-1/logs')
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.annotations' })).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('link', { name: 'common.appMenus.annotations' })).toHaveAttribute('href', '/app/app-1/annotations')
|
||||
expect(screen.queryByRole('link', { name: 'common.appMenus.overview' })).not.toBeInTheDocument()
|
||||
expect(screen.getAllByRole('separator')).toHaveLength(2)
|
||||
})
|
||||
|
||||
@ -225,6 +231,9 @@ describe('AppDetailSection', () => {
|
||||
})
|
||||
|
||||
it('should pass collapsed mode to app info and navigation links when collapsed', () => {
|
||||
// Arrange
|
||||
mockAppPermissionKeys = [AppACLPermission.LogAndAnnotation]
|
||||
|
||||
// Act
|
||||
render(<AppDetailSection expand={false} />)
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ const AppDetailSection = ({
|
||||
icon: RiTerminalBoxLine,
|
||||
selectedIcon: RiTerminalBoxFill,
|
||||
},
|
||||
...(appACLCapabilities.canMonitor
|
||||
...(appACLCapabilities.canAccessLogAndAnnotation
|
||||
? [{
|
||||
name: t('appMenus.logs', { ns: 'common' }),
|
||||
href: `/app/${appId}/logs`,
|
||||
@ -120,7 +120,7 @@ const AppDetailSection = ({
|
||||
}]
|
||||
: []
|
||||
),
|
||||
...(appACLCapabilities.canEdit && supportsAnnotations
|
||||
...(appACLCapabilities.canAccessLogAndAnnotation && supportsAnnotations
|
||||
? [{
|
||||
name: t('appMenus.annotations', { ns: 'common' }),
|
||||
href: `/app/${appId}/annotations`,
|
||||
|
||||
@ -11,6 +11,8 @@ const expectedAppACLPermissionKeys = [
|
||||
'app.acl.delete',
|
||||
'app.acl.release_and_version',
|
||||
'app.acl.monitor',
|
||||
'app.acl.tracing_config',
|
||||
'app.acl.log_and_annotation',
|
||||
'app.acl.access_config',
|
||||
]
|
||||
|
||||
|
||||
@ -69,7 +69,6 @@ vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canSetPermissions: true,
|
||||
canSetPluginPreferences: true,
|
||||
canViewInstalledPlugins: true,
|
||||
}),
|
||||
default: () => ({
|
||||
canSetPermissions: true,
|
||||
|
||||
@ -96,7 +96,7 @@ vi.mock('@/utils/var', () => ({
|
||||
|
||||
vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canManagePlugin: true,
|
||||
canDeletePlugin: true,
|
||||
canUpdatePlugin: true,
|
||||
}),
|
||||
}))
|
||||
|
||||
@ -60,14 +60,13 @@ const DataSourcePage = ({
|
||||
const [searchText, setSearchText] = useState('')
|
||||
const {
|
||||
canSetPluginPreferences,
|
||||
canViewInstalledPlugins,
|
||||
} = usePluginSettingsAccess()
|
||||
const { data: enable_marketplace } = useSuspenseQuery({
|
||||
...systemFeaturesQueryOptions(),
|
||||
select: s => s.enable_marketplace,
|
||||
})
|
||||
const { data, isLoading: isDataSourceListLoading } = useGetDataSourceListAuth()
|
||||
const { data: installedPluginList } = useInstalledPluginList(!canViewInstalledPlugins)
|
||||
const { data: installedPluginList } = useInstalledPluginList()
|
||||
const pluginListWithLatestVersion = usePluginsWithLatestVersion(installedPluginList?.plugins)
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const invalidateDataSourceListAuth = useInvalidDataSourceListAuth()
|
||||
|
||||
@ -50,7 +50,7 @@ const DataSourcePluginActions = ({
|
||||
const locale = useLocale()
|
||||
const readmeTriggerId = useId()
|
||||
const openReadmePanel = useReadmePanelStore(s => s.openReadmePanel)
|
||||
const { canManagePlugin, canUpdatePlugin } = usePluginSettingsAccess()
|
||||
const { canDeletePlugin, canUpdatePlugin } = usePluginSettingsAccess()
|
||||
const detailHeaderState = usePluginDetailHeader(detail)
|
||||
const {
|
||||
modalStates,
|
||||
@ -69,7 +69,7 @@ const DataSourcePluginActions = ({
|
||||
modalStates,
|
||||
versionPicker,
|
||||
isFromMarketplace,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
onUpdate,
|
||||
})
|
||||
@ -150,7 +150,7 @@ const DataSourcePluginActions = ({
|
||||
detailUrl={getDetailUrl(detail, locale, theme || 'light')}
|
||||
triggerSize="xs"
|
||||
showCheckVersion={canUpdatePlugin}
|
||||
showRemove={canManagePlugin}
|
||||
showRemove={canDeletePlugin}
|
||||
/>
|
||||
<HeaderModals
|
||||
detail={detail}
|
||||
|
||||
@ -67,7 +67,6 @@ vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canSetPermissions: true,
|
||||
canSetPluginPreferences: true,
|
||||
canViewInstalledPlugins: true,
|
||||
}),
|
||||
default: () => ({
|
||||
referenceSetting: {
|
||||
|
||||
@ -151,7 +151,6 @@ vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canSetPermissions: true,
|
||||
canSetPluginPreferences: true,
|
||||
canViewInstalledPlugins: true,
|
||||
}),
|
||||
default: () => ({
|
||||
referenceSetting: mockReferenceSetting,
|
||||
|
||||
@ -50,7 +50,6 @@ const ModelProviderPage = ({
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
canSetPluginPreferences,
|
||||
canViewInstalledPlugins,
|
||||
} = usePluginSettingsAccess()
|
||||
const { data: textGenerationDefaultModel, isLoading: isTextGenerationDefaultModelLoading } = useDefaultModel(ModelTypeEnum.textGeneration)
|
||||
const { data: embeddingsDefaultModel, isLoading: isEmbeddingsDefaultModelLoading } = useDefaultModel(ModelTypeEnum.textEmbedding)
|
||||
@ -65,7 +64,7 @@ const ModelProviderPage = ({
|
||||
}, [providers])
|
||||
const { data: installedPlugins } = useQuery(consoleQuery.plugins.checkInstalled.queryOptions({
|
||||
input: { body: { plugin_ids: allPluginIds } },
|
||||
enabled: canViewInstalledPlugins && allPluginIds.length > 0,
|
||||
enabled: allPluginIds.length > 0,
|
||||
staleTime: 0,
|
||||
}))
|
||||
const enrichedPlugins = usePluginsWithLatestVersion(installedPlugins?.plugins)
|
||||
|
||||
@ -8,7 +8,7 @@ import { ConfigurationMethodEnum } from '../../declarations'
|
||||
import ProviderAddedCard from '../index'
|
||||
|
||||
let mockIsCurrentWorkspaceManager = true
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.manage', 'credential.use', 'credential.create', 'credential.manage']
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.model_config', 'credential.use', 'credential.create', 'credential.manage']
|
||||
const mockFetchModelProviderModels = vi.fn()
|
||||
const mockQueryOptions = vi.fn(({ input, ...options }: { input: { params: { provider: string } }, enabled?: boolean }) => ({
|
||||
queryKey: ['console', 'modelProviders', 'models', input.params.provider],
|
||||
@ -105,7 +105,7 @@ describe('ProviderAddedCard', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockIsCurrentWorkspaceManager = true
|
||||
mockWorkspacePermissionKeys = ['plugin.manage', 'credential.use', 'credential.create', 'credential.manage']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config', 'credential.use', 'credential.create', 'credential.manage']
|
||||
})
|
||||
|
||||
it('should render provider added card component', () => {
|
||||
@ -201,7 +201,7 @@ describe('ProviderAddedCard', () => {
|
||||
expect(screen.getByText('common.modelProvider.configureTip')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render custom model actions when user can manage plugins', () => {
|
||||
it('should render custom model actions when user can configure models', () => {
|
||||
const customConfigProvider = {
|
||||
...mockProvider,
|
||||
configurate_methods: [ConfigurationMethodEnum.customizableModel],
|
||||
@ -218,12 +218,12 @@ describe('ProviderAddedCard', () => {
|
||||
expect(screen.queryByTestId('manage-custom-model')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render custom model actions when user can manage plugins without credential permissions', () => {
|
||||
it('should render custom model actions when user can configure models without credential permissions', () => {
|
||||
const customConfigProvider = {
|
||||
...mockProvider,
|
||||
configurate_methods: [ConfigurationMethodEnum.customizableModel],
|
||||
} as unknown as ModelProvider
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
|
||||
renderWithQueryClient(<ProviderAddedCard provider={customConfigProvider} />)
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ function createWrapper() {
|
||||
|
||||
let mockModelLoadBalancingEnabled = false
|
||||
let mockPlanType: string = 'pro'
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.manage']
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.model_config']
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
@ -71,7 +71,7 @@ describe('ModelListItem', () => {
|
||||
vi.clearAllMocks()
|
||||
mockModelLoadBalancingEnabled = false
|
||||
mockPlanType = 'pro'
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
})
|
||||
|
||||
it('should render model item with icon and name', () => {
|
||||
@ -144,8 +144,8 @@ describe('ModelListItem', () => {
|
||||
expect(onModifyLoadBalancing).toHaveBeenCalledWith(mockModel)
|
||||
})
|
||||
|
||||
it('should allow model status and load balancing controls with plugin.manage', () => {
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
it('should allow model status and load balancing controls with plugin.model_config', () => {
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
mockModelLoadBalancingEnabled = true
|
||||
|
||||
render(
|
||||
@ -162,7 +162,7 @@ describe('ModelListItem', () => {
|
||||
expect(screen.getByRole('button', { name: 'modify load balancing' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide model status and load balancing controls without plugin.manage', () => {
|
||||
it('should hide model status and load balancing controls without plugin.model_config', () => {
|
||||
mockWorkspacePermissionKeys = []
|
||||
mockModelLoadBalancingEnabled = true
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import { ConfigurationMethodEnum } from '../../declarations'
|
||||
import ModelList from '../model-list'
|
||||
|
||||
const mockSetShowModelLoadBalancingModal = vi.fn()
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.manage', 'credential.manage', 'credential.use']
|
||||
let mockWorkspacePermissionKeys: string[] = ['plugin.model_config', 'credential.manage', 'credential.use']
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => unknown) =>
|
||||
@ -45,7 +45,7 @@ describe('ModelList', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockWorkspacePermissionKeys = ['plugin.manage', 'credential.manage', 'credential.use']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config', 'credential.manage', 'credential.use']
|
||||
})
|
||||
|
||||
it('should render model count and model items', () => {
|
||||
@ -91,7 +91,7 @@ describe('ModelList', () => {
|
||||
expect(mockSetShowModelLoadBalancingModal).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should hide custom model actions without plugin.manage', () => {
|
||||
it('should hide custom model actions without plugin.model_config', () => {
|
||||
mockWorkspacePermissionKeys = []
|
||||
render(
|
||||
<ModelList
|
||||
@ -230,13 +230,13 @@ describe('ModelList', () => {
|
||||
expect(screen.queryByTestId('add-custom-model')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should show custom model actions when provider is configurable and user can manage plugins', () => {
|
||||
it('should show custom model actions when provider is configurable and user can configure models', () => {
|
||||
const configurableProvider = {
|
||||
provider: 'test-provider',
|
||||
configurate_methods: [ConfigurationMethodEnum.customizableModel],
|
||||
} as unknown as ModelProvider
|
||||
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
|
||||
render(
|
||||
<ModelList
|
||||
@ -251,7 +251,7 @@ describe('ModelList', () => {
|
||||
expect(screen.getByTestId('add-custom-model'))!.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide custom model actions when provider is configurable but user cannot manage plugins', () => {
|
||||
it('should hide custom model actions when provider is configurable but user cannot configure models', () => {
|
||||
const configurableProvider = {
|
||||
provider: 'test-provider',
|
||||
configurate_methods: [ConfigurationMethodEnum.customizableModel],
|
||||
|
||||
@ -73,7 +73,7 @@ vi.mock('@/app/components/plugins/plugin-detail-panel/operation-dropdown', () =>
|
||||
|
||||
vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canManagePlugin: true,
|
||||
canDeletePlugin: true,
|
||||
canUpdatePlugin: true,
|
||||
}),
|
||||
default: () => ({
|
||||
|
||||
@ -69,11 +69,11 @@ const ProviderAddedCard: FC<ProviderAddedCardProps> = ({
|
||||
const showCollapsedSection = !expanded || !hasFetchedModelList
|
||||
const workspacePermissionKeys = useAppContextWithSelector(state => state.workspacePermissionKeys)
|
||||
const showModelProvider = systemConfig.enabled && MODEL_PROVIDER_QUOTA_GET_PAID.includes(currentProviderName as ModelProviderQuotaGetPaid) && !IS_CE_EDITION
|
||||
const canManagePlugins = hasPermission(workspacePermissionKeys, 'plugin.manage')
|
||||
const canConfigureModels = hasPermission(workspacePermissionKeys, 'plugin.model_config')
|
||||
const { canUseCredential, canCreateCredential, canManageCredential } = useCredentialPermissions()
|
||||
const canAccessCredentials = canUseCredential || canCreateCredential || canManageCredential
|
||||
const showCredential = supportsPredefinedModel && canAccessCredentials
|
||||
const showCustomModelActions = supportsCustomizableModel && canManagePlugins
|
||||
const showCustomModelActions = supportsCustomizableModel && canConfigureModels
|
||||
|
||||
const refreshModelList = useCallback((targetProviderName: string) => {
|
||||
if (targetProviderName !== currentProviderName)
|
||||
|
||||
@ -33,7 +33,7 @@ const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoad
|
||||
const { plan } = useProviderContext()
|
||||
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
||||
const { workspacePermissionKeys } = useAppContext()
|
||||
const canManagePlugin = hasPermission(workspacePermissionKeys, 'plugin.manage')
|
||||
const canConfigureModels = hasPermission(workspacePermissionKeys, 'plugin.model_config')
|
||||
const queryClient = useQueryClient()
|
||||
const updateModelList = useUpdateModelList()
|
||||
const modelProviderModelListQueryKey = consoleQuery.modelProviders.models.queryKey({
|
||||
@ -93,7 +93,7 @@ const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoad
|
||||
</Badge>
|
||||
)}
|
||||
{
|
||||
(canManagePlugin && (modelLoadBalancingEnabled || plan.type === Plan.sandbox) && !model.deprecated && [ModelStatusEnum.active, ModelStatusEnum.disabled].includes(model.status)) && (
|
||||
(canConfigureModels && (modelLoadBalancingEnabled || plan.type === Plan.sandbox) && !model.deprecated && [ModelStatusEnum.active, ModelStatusEnum.disabled].includes(model.status)) && (
|
||||
<ConfigModel
|
||||
onClick={() => onModifyLoadBalancing?.(model)}
|
||||
loadBalancingEnabled={model.load_balancing_enabled}
|
||||
@ -112,7 +112,7 @@ const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoad
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
: (canManagePlugin && (
|
||||
: (canConfigureModels && (
|
||||
<Switch
|
||||
className="ml-2"
|
||||
checked={model?.status === ModelStatusEnum.active}
|
||||
|
||||
@ -34,7 +34,7 @@ const ModelList: FC<ModelListProps> = ({
|
||||
const { t } = useTranslation()
|
||||
const configurativeMethods = provider.configurate_methods.filter(method => method !== ConfigurationMethodEnum.fetchFromRemote)
|
||||
const workspacePermissionKeys = useAppContextWithSelector(state => state.workspacePermissionKeys)
|
||||
const canManagePlugins = hasPermission(workspacePermissionKeys, 'plugin.manage')
|
||||
const canConfigureModels = hasPermission(workspacePermissionKeys, 'plugin.model_config')
|
||||
const isConfigurable = configurativeMethods.includes(ConfigurationMethodEnum.customizableModel)
|
||||
const setShowModelLoadBalancingModal = useModalContextSelector(state => state.setShowModelLoadBalancingModal)
|
||||
const onModifyLoadBalancing = useCallback((model: ModelItem, credential?: Credential) => {
|
||||
@ -67,7 +67,7 @@ const ModelList: FC<ModelListProps> = ({
|
||||
</span>
|
||||
</span>
|
||||
{
|
||||
isConfigurable && canManagePlugins && (
|
||||
isConfigurable && canConfigureModels && (
|
||||
<div className="flex grow justify-end">
|
||||
<ManageCustomModelCredentials
|
||||
provider={provider}
|
||||
|
||||
@ -25,7 +25,7 @@ const ProviderCardActions: FC<Props> = ({ detail, onUpdate }) => {
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const locale = useLocale()
|
||||
const { canManagePlugin, canUpdatePlugin } = usePluginSettingsAccess()
|
||||
const { canDeletePlugin, canUpdatePlugin } = usePluginSettingsAccess()
|
||||
|
||||
const { source, version, latest_version, latest_unique_identifier, meta } = detail
|
||||
const author = detail.declaration?.author ?? ''
|
||||
@ -49,7 +49,7 @@ const ProviderCardActions: FC<Props> = ({ detail, onUpdate }) => {
|
||||
modalStates,
|
||||
versionPicker,
|
||||
isFromMarketplace,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
onUpdate,
|
||||
})
|
||||
@ -137,7 +137,7 @@ const ProviderCardActions: FC<Props> = ({ detail, onUpdate }) => {
|
||||
placement="bottom-start"
|
||||
destructiveRemove
|
||||
showCheckVersion={canUpdatePlugin}
|
||||
showRemove={canManagePlugin}
|
||||
showRemove={canDeletePlugin}
|
||||
/>
|
||||
|
||||
<HeaderModals
|
||||
|
||||
@ -32,7 +32,7 @@ const mockInvalidateDefaultModel = vi.hoisted(() => vi.fn())
|
||||
const mockUpdateDefaultModel = vi.hoisted(() => vi.fn(() => Promise.resolve({ result: 'success' })))
|
||||
const mockModelSelectorProps = vi.hoisted(() => [] as Array<{ hideProviderSettingsFooter?: boolean, onConfigureEmptyState?: () => void, showModelMeta?: boolean }>)
|
||||
|
||||
let mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
let mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
|
||||
vi.mock('@/context/app-context', () => ({
|
||||
useAppContext: () => ({
|
||||
@ -108,7 +108,7 @@ describe('SystemModel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockModelSelectorProps.length = 0
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
mockWorkspacePermissionKeys = ['plugin.model_config']
|
||||
})
|
||||
|
||||
it('should render settings button', () => {
|
||||
@ -184,7 +184,7 @@ describe('SystemModel', () => {
|
||||
expect(mockUpdateModelList).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should disable save without plugin manage permission', async () => {
|
||||
it('should disable save without model config permission', async () => {
|
||||
mockWorkspacePermissionKeys = []
|
||||
render(<SystemModel {...defaultProps} />)
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
|
||||
const { t } = useTranslation()
|
||||
const { workspacePermissionKeys } = useAppContext()
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const canManageSystemDefaultModel = hasPermission(workspacePermissionKeys, 'plugin.manage')
|
||||
const canManageSystemDefaultModel = hasPermission(workspacePermissionKeys, 'plugin.model_config')
|
||||
const updateModelList = useUpdateModelList()
|
||||
const invalidateDefaultModel = useInvalidateDefaultModel()
|
||||
const { data: embeddingModelList } = useModelList(ModelTypeEnum.textEmbedding)
|
||||
|
||||
@ -53,25 +53,23 @@ vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
permission: mockReferenceSetting().permission,
|
||||
canInstallPlugin: mockCanManagement(),
|
||||
canManagePlugin: true,
|
||||
canDeletePlugin: true,
|
||||
canManagement: mockCanManagement(),
|
||||
canDebugger: mockCanDebugger(),
|
||||
canSetPermissions: mockCanSetPermissions(),
|
||||
canSetPluginPreferences: mockCanSetPermissions(),
|
||||
canUpdatePlugin: true,
|
||||
canViewInstalledPlugins: true,
|
||||
setPluginPermissionSettings: mockSetReferenceSettings,
|
||||
}),
|
||||
default: () => ({
|
||||
referenceSetting: mockReferenceSetting(),
|
||||
canInstallPlugin: mockCanManagement(),
|
||||
canManagePlugin: true,
|
||||
canDeletePlugin: true,
|
||||
canManagement: mockCanManagement(),
|
||||
canDebugger: mockCanDebugger(),
|
||||
canSetPermissions: mockCanSetPermissions(),
|
||||
canSetPluginPreferences: mockCanSetPermissions(),
|
||||
canUpdatePlugin: true,
|
||||
canViewInstalledPlugins: true,
|
||||
setReferenceSettings: mockSetReferenceSettings,
|
||||
}),
|
||||
}))
|
||||
|
||||
@ -142,9 +142,8 @@ vi.mock('@/app/components/plugins/plugin-page/use-reference-setting', () => ({
|
||||
usePluginSettingsAccess: () => ({
|
||||
canSetPermissions: mockCanSetPermissions(),
|
||||
canSetPluginPreferences: mockCanSetPermissions(),
|
||||
canManagePlugin: true,
|
||||
canDeletePlugin: true,
|
||||
canUpdatePlugin: true,
|
||||
canViewInstalledPlugins: true,
|
||||
}),
|
||||
default: () => ({
|
||||
referenceSetting: mockReferenceSetting(),
|
||||
|
||||
@ -12,11 +12,10 @@ export function useIntegrationPermissions(section: IntegrationSection) {
|
||||
permission,
|
||||
canDebugger,
|
||||
canInstallPlugin,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canSetPluginPreferences,
|
||||
canSetPermissions,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
isPermissionLoading,
|
||||
permissionError,
|
||||
setPluginPermissionSettings,
|
||||
@ -38,10 +37,9 @@ export function useIntegrationPermissions(section: IntegrationSection) {
|
||||
return {
|
||||
canDebugger,
|
||||
canInstallPlugin,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canSetPluginPreferences,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
canManagement: canInstallPlugin,
|
||||
handlePermissionChange,
|
||||
isPluginCategory,
|
||||
|
||||
@ -107,9 +107,8 @@ export default function IntegrationsPage({
|
||||
const {
|
||||
canDebugger,
|
||||
canInstallPlugin,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
handlePermissionChange,
|
||||
isPluginCategory,
|
||||
permission,
|
||||
@ -285,9 +284,8 @@ export default function IntegrationsPage({
|
||||
onProviderSearchTextChange={setProviderSearchText}
|
||||
onSwitchToMarketplace={handleSwitchToMarketplace}
|
||||
canInstallPlugin={canInstallPlugin}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
canViewInstalledPlugins={canViewInstalledPlugins}
|
||||
pluginCategoryToolbarAction={pluginSettingAction}
|
||||
/>
|
||||
</div>
|
||||
@ -311,9 +309,8 @@ export default function IntegrationsPage({
|
||||
onProviderSearchTextChange={setProviderSearchText}
|
||||
onSwitchToMarketplace={handleSwitchToMarketplace}
|
||||
canInstallPlugin={canInstallPlugin}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
canViewInstalledPlugins={canViewInstalledPlugins}
|
||||
pluginCategoryToolbarAction={pluginSettingAction}
|
||||
/>
|
||||
</ScrollArea>
|
||||
|
||||
@ -15,9 +15,8 @@ import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
|
||||
type PluginCategoryPageProps = {
|
||||
canInstall?: boolean
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
canViewInstalledPlugins?: boolean
|
||||
category: PluginCategoryEnum
|
||||
layout?: (parts: { body: ReactNode, toolbar: ReactNode }) => ReactNode
|
||||
onSwitchToMarketplace?: () => void
|
||||
@ -28,9 +27,8 @@ const supportedLocalPackageExtensions = SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS.sp
|
||||
|
||||
const PluginCategoryPageContent = ({
|
||||
canInstall = true,
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
canViewInstalledPlugins = true,
|
||||
category,
|
||||
layout,
|
||||
onSwitchToMarketplace,
|
||||
@ -73,9 +71,8 @@ const PluginCategoryPageContent = ({
|
||||
<div ref={containerRef} className="relative flex h-0 grow flex-col overflow-hidden bg-components-panel-bg">
|
||||
<PluginsPanel
|
||||
canInstall={canInstall}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
canViewInstalledPlugins={canViewInstalledPlugins}
|
||||
contentInset="compact"
|
||||
fixedCategory={category}
|
||||
layout={layout}
|
||||
@ -110,9 +107,8 @@ const PluginCategoryPageContent = ({
|
||||
|
||||
const PluginCategoryPage = ({
|
||||
canInstall = true,
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
canViewInstalledPlugins = true,
|
||||
category,
|
||||
layout,
|
||||
onSwitchToMarketplace,
|
||||
@ -128,9 +124,8 @@ const PluginCategoryPage = ({
|
||||
<PluginPageContextProvider key={category} initialFilters={initialFilters}>
|
||||
<PluginCategoryPageContent
|
||||
canInstall={canInstall}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
canViewInstalledPlugins={canViewInstalledPlugins}
|
||||
category={category}
|
||||
layout={layout}
|
||||
onSwitchToMarketplace={onSwitchToMarketplace}
|
||||
|
||||
@ -14,9 +14,8 @@ import ToolProviderList from './tool-provider-list'
|
||||
|
||||
type IntegrationSectionRendererProps = {
|
||||
canInstallPlugin?: boolean
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
canViewInstalledPlugins?: boolean
|
||||
description?: ReactNode
|
||||
onProviderSearchTextChange: (value: string) => void
|
||||
onSwitchToMarketplace?: () => void
|
||||
@ -29,9 +28,8 @@ type IntegrationSectionRendererProps = {
|
||||
|
||||
const IntegrationSectionRenderer = ({
|
||||
canInstallPlugin = true,
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
canViewInstalledPlugins = true,
|
||||
description,
|
||||
onProviderSearchTextChange,
|
||||
onSwitchToMarketplace,
|
||||
@ -75,9 +73,8 @@ const IntegrationSectionRenderer = ({
|
||||
const renderPluginCategoryPage = (category: PluginCategoryEnum) => (
|
||||
<PluginCategoryPage
|
||||
canInstall={canInstallPlugin}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
canViewInstalledPlugins={canViewInstalledPlugins}
|
||||
category={category}
|
||||
layout={renderDirectLayout}
|
||||
onSwitchToMarketplace={onSwitchToMarketplace}
|
||||
|
||||
@ -88,10 +88,9 @@ const ProviderList = ({
|
||||
const { t } = useTranslation()
|
||||
const { getTagLabel } = useTags()
|
||||
const {
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canSetPluginPreferences,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
} = usePluginSettingsAccess()
|
||||
const canManageTools = useCanManageTools()
|
||||
const canManageMCP = useCanManageMCP()
|
||||
@ -158,7 +157,7 @@ const ProviderList = ({
|
||||
}, [currentProviderId, filteredCollectionList])
|
||||
const { data: checkedInstalledData } = useCheckInstalled({
|
||||
pluginIds: currentProvider?.plugin_id ? [currentProvider.plugin_id] : [],
|
||||
enabled: canViewInstalledPlugins && !!currentProvider?.plugin_id,
|
||||
enabled: !!currentProvider?.plugin_id,
|
||||
})
|
||||
const invalidateInstalledPluginList = useInvalidateInstalledPluginList()
|
||||
const currentPluginDetail = useMemo(() => {
|
||||
@ -262,7 +261,7 @@ const ProviderList = ({
|
||||
detail={currentPluginDetail}
|
||||
onUpdate={() => invalidateInstalledPluginList()}
|
||||
onHide={() => setCurrentProviderId(undefined)}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
/>
|
||||
</>
|
||||
|
||||
@ -23,21 +23,25 @@ describe('useWorkspacePluginInstallPermission', () => {
|
||||
|
||||
expect(result.current.canInstallPlugin).toBe(true)
|
||||
expect(result.current.canUpdatePlugin).toBe(true)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(true)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canSetPluginPreferences).toBe(false)
|
||||
})
|
||||
|
||||
it('should grant update, view, and manage capabilities but not install with plugin.manage', () => {
|
||||
mockWorkspacePermissionKeys = ['plugin.manage']
|
||||
it('should not expose installed plugin list viewing as a permission capability', () => {
|
||||
const { result } = renderHook(() => useWorkspacePluginInstallPermission())
|
||||
|
||||
expect('canViewInstalledPlugins' in result.current).toBe(false)
|
||||
})
|
||||
|
||||
it('should grant delete capability but not install or update with plugin.delete', () => {
|
||||
mockWorkspacePermissionKeys = ['plugin.delete']
|
||||
|
||||
const { result } = renderHook(() => useWorkspacePluginInstallPermission())
|
||||
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(true)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(true)
|
||||
expect(result.current.canManagePlugin).toBe(true)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(true)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canSetPluginPreferences).toBe(false)
|
||||
})
|
||||
@ -49,8 +53,7 @@ describe('useWorkspacePluginInstallPermission', () => {
|
||||
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(false)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(true)
|
||||
expect(result.current.canSetPluginPreferences).toBe(false)
|
||||
})
|
||||
@ -62,8 +65,7 @@ describe('useWorkspacePluginInstallPermission', () => {
|
||||
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(false)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canSetPluginPreferences).toBe(true)
|
||||
})
|
||||
@ -73,8 +75,7 @@ describe('useWorkspacePluginInstallPermission', () => {
|
||||
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(false)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canSetPluginPreferences).toBe(false)
|
||||
})
|
||||
|
||||
@ -2,8 +2,6 @@ import { useMemo } from 'react'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { hasPermission } from '@/utils/permission'
|
||||
|
||||
const pluginReadAndUpdatePermissionKeys = ['plugin.install', 'plugin.manage']
|
||||
|
||||
const useWorkspacePluginInstallPermission = () => {
|
||||
const {
|
||||
langGeniusVersionInfo,
|
||||
@ -15,15 +13,11 @@ const useWorkspacePluginInstallPermission = () => {
|
||||
}, [workspacePermissionKeys])
|
||||
|
||||
const canUpdatePlugin = useMemo(() => {
|
||||
return hasPermission(workspacePermissionKeys, pluginReadAndUpdatePermissionKeys)
|
||||
return hasPermission(workspacePermissionKeys, 'plugin.install')
|
||||
}, [workspacePermissionKeys])
|
||||
|
||||
const canViewInstalledPlugins = useMemo(() => {
|
||||
return hasPermission(workspacePermissionKeys, pluginReadAndUpdatePermissionKeys)
|
||||
}, [workspacePermissionKeys])
|
||||
|
||||
const canManagePlugin = useMemo(() => {
|
||||
return hasPermission(workspacePermissionKeys, 'plugin.manage')
|
||||
const canDeletePlugin = useMemo(() => {
|
||||
return hasPermission(workspacePermissionKeys, 'plugin.delete')
|
||||
}, [workspacePermissionKeys])
|
||||
|
||||
const canDebugPlugin = useMemo(() => {
|
||||
@ -37,8 +31,7 @@ const useWorkspacePluginInstallPermission = () => {
|
||||
return {
|
||||
canInstallPlugin,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canDebugPlugin,
|
||||
canSetPluginPreferences,
|
||||
currentDifyVersion: langGeniusVersionInfo?.current_version,
|
||||
|
||||
@ -14,7 +14,7 @@ import { checkForUpdates, fetchReleases } from '../../../install-plugin/hooks'
|
||||
import { PluginSource } from '../../../types'
|
||||
|
||||
type UsePluginOperationsParams = {
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
detail: PluginDetail
|
||||
modalStates: ModalStates
|
||||
@ -33,7 +33,7 @@ type UsePluginOperationsReturn = {
|
||||
}
|
||||
|
||||
export const usePluginOperations = ({
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
detail,
|
||||
modalStates,
|
||||
@ -118,7 +118,7 @@ export const usePluginOperations = ({
|
||||
}, [handlePluginUpdated, modalStates])
|
||||
|
||||
const handleDelete = useCallback(async () => {
|
||||
if (!canManagePlugin)
|
||||
if (!canDeletePlugin)
|
||||
return
|
||||
|
||||
modalStates.showDeleting()
|
||||
@ -134,7 +134,7 @@ export const usePluginOperations = ({
|
||||
trackEvent('plugin_uninstalled', { plugin_id, plugin_name: name })
|
||||
}
|
||||
}, [
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
id,
|
||||
category,
|
||||
plugin_id,
|
||||
|
||||
@ -34,7 +34,7 @@ import { HeaderModals, PluginSourceBadge } from './components'
|
||||
import { useDetailHeaderState, usePluginOperations } from './hooks'
|
||||
|
||||
type Props = Readonly<{
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
detail: PluginDetail
|
||||
isReadmeView?: boolean
|
||||
@ -71,7 +71,7 @@ const getDetailUrl = (
|
||||
}
|
||||
|
||||
const DetailHeader = ({
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
detail,
|
||||
isReadmeView = false,
|
||||
@ -123,7 +123,7 @@ const DetailHeader = ({
|
||||
modalStates,
|
||||
versionPicker,
|
||||
isFromMarketplace,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
onUpdate,
|
||||
})
|
||||
@ -271,7 +271,7 @@ const DetailHeader = ({
|
||||
onViewReadme={canViewReadme ? handleViewReadme : undefined}
|
||||
detailUrl={detailUrl}
|
||||
showCheckVersion={canUpdatePlugin}
|
||||
showRemove={canManagePlugin}
|
||||
showRemove={canDeletePlugin}
|
||||
/>
|
||||
<ActionButton onClick={onHide}>
|
||||
<span aria-hidden className="i-ri-close-line size-4" />
|
||||
|
||||
@ -24,7 +24,7 @@ import { SubscriptionList } from './subscription-list'
|
||||
import { TriggerEventsList } from './trigger/event-list'
|
||||
|
||||
type Props = Readonly<{
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
detail?: PluginDetail
|
||||
onUpdate: () => void
|
||||
@ -32,7 +32,7 @@ type Props = Readonly<{
|
||||
}>
|
||||
|
||||
const PluginDetailPanel: FC<Props> = ({
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
detail,
|
||||
onUpdate,
|
||||
@ -83,7 +83,7 @@ const PluginDetailPanel: FC<Props> = ({
|
||||
detail={detail}
|
||||
onUpdate={handleUpdate}
|
||||
onHide={onHide}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
/>
|
||||
<div className="grow overflow-y-auto">
|
||||
|
||||
@ -33,14 +33,14 @@ import { PluginSource } from '../types'
|
||||
import Action from './action'
|
||||
|
||||
type Props = Readonly<{
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
className?: string
|
||||
plugin: PluginDetail
|
||||
}>
|
||||
|
||||
const PluginItem: FC<Props> = ({
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
className,
|
||||
plugin,
|
||||
@ -158,7 +158,7 @@ const PluginItem: FC<Props> = ({
|
||||
usedInApps={5}
|
||||
isShowFetchNewVersion={canUpdatePlugin && source === PluginSource.github}
|
||||
isShowInfo={source === PluginSource.github}
|
||||
isShowDelete={canManagePlugin}
|
||||
isShowDelete={canDeletePlugin}
|
||||
meta={meta}
|
||||
onDelete={handleDelete}
|
||||
category={category}
|
||||
|
||||
@ -43,7 +43,7 @@ vi.mock('@/context/app-context', () => ({
|
||||
isCurrentWorkspaceManager: true,
|
||||
isCurrentWorkspaceOwner: false,
|
||||
langGeniusVersionInfo: { current_version: '1.0.0' },
|
||||
workspacePermissionKeys: ['plugin.install', 'plugin.manage', 'plugin.debug', 'plugin.plugin_preferences'],
|
||||
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug', 'plugin.plugin_preferences'],
|
||||
}),
|
||||
}))
|
||||
|
||||
|
||||
@ -74,6 +74,12 @@ describe('useReferenceSetting Hook', () => {
|
||||
})
|
||||
|
||||
describe('permission key access', () => {
|
||||
it('should not expose installed plugin list viewing as a permission capability', () => {
|
||||
const { result } = renderHook(() => useReferenceSetting(PluginCategoryEnum.tool))
|
||||
|
||||
expect('canViewInstalledPlugins' in result.current).toBe(false)
|
||||
})
|
||||
|
||||
it('should return false without plugin permission keys', () => {
|
||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||
data: {
|
||||
@ -192,7 +198,7 @@ describe('useReferenceSetting Hook', () => {
|
||||
isCurrentWorkspaceManager: true,
|
||||
isCurrentWorkspaceOwner: false,
|
||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||
workspacePermissionKeys: ['plugin.install', 'plugin.manage', 'plugin.debug'],
|
||||
workspacePermissionKeys: ['plugin.install', 'plugin.delete', 'plugin.debug'],
|
||||
} as ReturnType<typeof useAppContext>)
|
||||
vi.mocked(usePluginPermissionSettings).mockReturnValue({
|
||||
data: {
|
||||
@ -208,8 +214,7 @@ describe('useReferenceSetting Hook', () => {
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canManagement).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(true)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canDebugger).toBe(false)
|
||||
})
|
||||
@ -351,7 +356,7 @@ describe('useReferenceSetting Hook', () => {
|
||||
langGeniusVersionInfo: { current_version: '1.0.0', latest_version: '', version: '' },
|
||||
workspacePermissionKeys: [
|
||||
'plugin.install',
|
||||
'plugin.manage',
|
||||
'plugin.delete',
|
||||
'plugin.debug',
|
||||
'plugin.plugin_preferences',
|
||||
],
|
||||
@ -370,8 +375,7 @@ describe('useReferenceSetting Hook', () => {
|
||||
expect(result.current.canInstallPlugin).toBe(true)
|
||||
expect(result.current.canManagement).toBe(true)
|
||||
expect(result.current.canUpdatePlugin).toBe(true)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(true)
|
||||
expect(result.current.canManagePlugin).toBe(true)
|
||||
expect(result.current.canDeletePlugin).toBe(true)
|
||||
expect(result.current.canDebugPlugin).toBe(true)
|
||||
expect(result.current.canDebugger).toBe(true)
|
||||
expect(result.current.canSetPermissions).toBe(false)
|
||||
@ -393,8 +397,7 @@ describe('useReferenceSetting Hook', () => {
|
||||
expect(result.current.canInstallPlugin).toBe(false)
|
||||
expect(result.current.canManagement).toBe(false)
|
||||
expect(result.current.canUpdatePlugin).toBe(false)
|
||||
expect(result.current.canViewInstalledPlugins).toBe(false)
|
||||
expect(result.current.canManagePlugin).toBe(false)
|
||||
expect(result.current.canDeletePlugin).toBe(false)
|
||||
expect(result.current.canDebugPlugin).toBe(false)
|
||||
expect(result.current.canDebugger).toBe(false)
|
||||
expect(result.current.canSetPermissions).toBe(false)
|
||||
|
||||
@ -56,9 +56,8 @@ export type PluginPageProps = {
|
||||
}
|
||||
type PluginPanelPermissionProps = {
|
||||
canInstall?: boolean
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
canViewInstalledPlugins?: boolean
|
||||
}
|
||||
const PluginPage = ({
|
||||
plugins,
|
||||
@ -89,8 +88,7 @@ const PluginPage = ({
|
||||
referenceSetting,
|
||||
canInstallPlugin,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canDebugger,
|
||||
canSetPermissions,
|
||||
canSetPluginPreferences,
|
||||
@ -184,11 +182,10 @@ const PluginPage = ({
|
||||
|
||||
return cloneElement(plugins as React.ReactElement<PluginPanelPermissionProps>, {
|
||||
canInstall: canInstallPlugin,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
})
|
||||
}, [canInstallPlugin, canManagePlugin, canUpdatePlugin, canViewInstalledPlugins, plugins])
|
||||
}, [canInstallPlugin, canDeletePlugin, canUpdatePlugin, plugins])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@ -3,14 +3,14 @@ import type { PluginDetail } from '../../types'
|
||||
import PluginItem from '../../plugin-item'
|
||||
|
||||
type IPluginListProps = {
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
children?: ReactNode
|
||||
pluginList: PluginDetail[]
|
||||
}
|
||||
|
||||
const PluginList: FC<IPluginListProps> = ({
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
children,
|
||||
pluginList,
|
||||
@ -22,7 +22,7 @@ const PluginList: FC<IPluginListProps> = ({
|
||||
<PluginItem
|
||||
key={plugin.plugin_id}
|
||||
plugin={plugin}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -58,7 +58,7 @@ const BuiltinMarketplacePanel = ({
|
||||
}
|
||||
|
||||
type PluginsPanelResultsProps = {
|
||||
canManagePlugin: boolean
|
||||
canDeletePlugin: boolean
|
||||
canUpdatePlugin: boolean
|
||||
containerRef: RefObject<HTMLDivElement | null>
|
||||
contentFrameClassName: string
|
||||
@ -80,7 +80,7 @@ type PluginsPanelResultsProps = {
|
||||
}
|
||||
|
||||
const PluginsPanelResults = ({
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canUpdatePlugin,
|
||||
containerRef,
|
||||
contentFrameClassName,
|
||||
@ -123,7 +123,7 @@ const PluginsPanelResults = ({
|
||||
{(hasVisiblePlugins || hasVisibleBuiltinTools) && (
|
||||
<List
|
||||
pluginList={filteredList}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
>
|
||||
{filteredBuiltinTools.map(collection => (
|
||||
|
||||
@ -52,9 +52,8 @@ const matchesSearchQuery = (plugin: PluginDetail & { latest_version: string }, q
|
||||
|
||||
type PluginsPanelProps = {
|
||||
canInstall?: boolean
|
||||
canManagePlugin?: boolean
|
||||
canDeletePlugin?: boolean
|
||||
canUpdatePlugin?: boolean
|
||||
canViewInstalledPlugins?: boolean
|
||||
contentInset?: PluginPageContentInset
|
||||
fixedCategory?: PluginCategoryEnum
|
||||
layout?: (parts: { body: ReactNode, toolbar: ReactNode }) => ReactNode
|
||||
@ -64,9 +63,8 @@ type PluginsPanelProps = {
|
||||
|
||||
const PluginsPanel = ({
|
||||
canInstall = true,
|
||||
canManagePlugin = true,
|
||||
canDeletePlugin = true,
|
||||
canUpdatePlugin = true,
|
||||
canViewInstalledPlugins = true,
|
||||
contentInset = 'default',
|
||||
fixedCategory,
|
||||
layout,
|
||||
@ -88,7 +86,7 @@ const PluginsPanel = ({
|
||||
select: s => s.enable_marketplace,
|
||||
})
|
||||
const { data: pluginList, isLoading: isPluginListLoading, isFetching, isLastPage, loadNextPage } = useInstalledPluginList(
|
||||
!canViewInstalledPlugins,
|
||||
false,
|
||||
100,
|
||||
fixedCategory
|
||||
? {
|
||||
@ -223,7 +221,7 @@ const PluginsPanel = ({
|
||||
scrollAreaLabel={scrollAreaLabel}
|
||||
setCurrentBuiltinToolID={setCurrentBuiltinToolID}
|
||||
tagFilterValue={filters.tags}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
/>
|
||||
)
|
||||
@ -259,7 +257,7 @@ const PluginsPanel = ({
|
||||
detail={currentPluginDetail}
|
||||
onUpdate={() => invalidateInstalledPluginList()}
|
||||
onHide={handleHide}
|
||||
canManagePlugin={canManagePlugin}
|
||||
canDeletePlugin={canDeletePlugin}
|
||||
canUpdatePlugin={canUpdatePlugin}
|
||||
/>
|
||||
{currentBuiltinTool && !currentBuiltinTool.plugin_id && (
|
||||
|
||||
@ -9,8 +9,6 @@ import { useInvalidateReferenceSettings, useMutationPluginPermissionSettings, us
|
||||
import { hasPermission } from '@/utils/permission'
|
||||
import { hasLegacyPluginPermissionAccess } from '../plugin-permissions'
|
||||
|
||||
const pluginReadAndUpdatePermissionKeys = ['plugin.install', 'plugin.manage']
|
||||
|
||||
const useCanSetPluginSettings = () => {
|
||||
const { workspacePermissionKeys } = useAppContext()
|
||||
const { data: rbacEnabled } = useSuspenseQuery({
|
||||
@ -52,9 +50,8 @@ export const usePluginSettingsAccess = () => {
|
||||
rbacEnabled,
|
||||
})
|
||||
const canInstallPlugin = hasPermission(workspacePermissionKeys, 'plugin.install') && legacyCanInstallPlugin
|
||||
const canUpdatePlugin = hasPermission(workspacePermissionKeys, pluginReadAndUpdatePermissionKeys) && legacyCanInstallPlugin
|
||||
const canViewInstalledPlugins = hasPermission(workspacePermissionKeys, pluginReadAndUpdatePermissionKeys)
|
||||
const canManagePlugin = hasPermission(workspacePermissionKeys, 'plugin.manage') && legacyCanInstallPlugin
|
||||
const canUpdatePlugin = hasPermission(workspacePermissionKeys, 'plugin.install') && legacyCanInstallPlugin
|
||||
const canDeletePlugin = hasPermission(workspacePermissionKeys, 'plugin.delete') && legacyCanInstallPlugin
|
||||
const canDebugPlugin = hasPermission(workspacePermissionKeys, 'plugin.debug') && legacyCanDebugPlugin
|
||||
|
||||
return {
|
||||
@ -62,8 +59,7 @@ export const usePluginSettingsAccess = () => {
|
||||
setPluginPermissionSettings,
|
||||
canInstallPlugin,
|
||||
canUpdatePlugin,
|
||||
canViewInstalledPlugins,
|
||||
canManagePlugin,
|
||||
canDeletePlugin,
|
||||
canDebugPlugin,
|
||||
canSetPluginPreferences,
|
||||
canManagement: canInstallPlugin,
|
||||
@ -103,8 +99,7 @@ const useReferenceSetting = (category: PluginCategoryEnum) => {
|
||||
canDebugger: permissionAccess.canDebugger,
|
||||
canInstallPlugin: permissionAccess.canInstallPlugin,
|
||||
canUpdatePlugin: permissionAccess.canUpdatePlugin,
|
||||
canViewInstalledPlugins: permissionAccess.canViewInstalledPlugins,
|
||||
canManagePlugin: permissionAccess.canManagePlugin,
|
||||
canDeletePlugin: permissionAccess.canDeletePlugin,
|
||||
canDebugPlugin: permissionAccess.canDebugPlugin,
|
||||
canSetPermissions: permissionAccess.canSetPermissions,
|
||||
canSetPluginPreferences: permissionAccess.canSetPluginPreferences,
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "حذف التطبيق",
|
||||
"app.acl.edit": "تعديل التطبيق وتنسيقه",
|
||||
"app.acl.import_export_dsl": "استيراد / تصدير DSL",
|
||||
"app.acl.monitor": "المراقبة والعمليات",
|
||||
"app.acl.log_and_annotation": "الوصول إلى السجلات والتعليقات التوضيحية",
|
||||
"app.acl.monitor": "الوصول إلى صفحة المراقبة",
|
||||
"app.acl.preview": "معاينة التطبيق",
|
||||
"app.acl.release_and_version": "نشر التطبيق وإدارة الإصدارات",
|
||||
"app.acl.test_and_run": "اختبار التطبيق واستخدامه",
|
||||
"app.acl.tracing_config": "تكوين التتبع الخارجي",
|
||||
"app.acl.view_layout": "صفحة التنسيق للقراءة فقط",
|
||||
"app.create_and_management": "إنشاء التطبيقات",
|
||||
"app.tag.manage": "إدارة وسوم التطبيق",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "إدارة وسوم قاعدة المعرفة",
|
||||
"mcp.manage": "إدارة MCP",
|
||||
"plugin.debug": "تصحيح الإضافات",
|
||||
"plugin.delete": "حذف الإضافات",
|
||||
"plugin.install": "تثبيت الإضافات وتحديثها",
|
||||
"plugin.manage": "إدارة الإضافات",
|
||||
"plugin.plugin_preferences": "إدارة تفضيلات الإضافات",
|
||||
"plugin.model_config": "تكوين النماذج",
|
||||
"plugin.plugin_preferences": "تكوين التحديثات التلقائية",
|
||||
"snippets.create_and_modify": "إنشاء المقتطفات وتعديلها",
|
||||
"snippets.management": "إدارة المقتطفات",
|
||||
"tool.manage": "إدارة الأدوات",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "App löschen",
|
||||
"app.acl.edit": "App bearbeiten und orchestrieren",
|
||||
"app.acl.import_export_dsl": "DSL importieren / exportieren",
|
||||
"app.acl.monitor": "Überwachung und Betrieb",
|
||||
"app.acl.log_and_annotation": "Auf Logs und Annotationen zugreifen",
|
||||
"app.acl.monitor": "Auf Monitoring-Seite zugreifen",
|
||||
"app.acl.preview": "App-Vorschau",
|
||||
"app.acl.release_and_version": "App-Veröffentlichung und Versionsverwaltung",
|
||||
"app.acl.test_and_run": "App testen und verwenden",
|
||||
"app.acl.tracing_config": "Externes Tracing konfigurieren",
|
||||
"app.acl.view_layout": "Schreibgeschützte Orchestrierungsseite",
|
||||
"app.create_and_management": "Apps erstellen",
|
||||
"app.tag.manage": "App-Tags verwalten",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Wissensdatenbank-Tags verwalten",
|
||||
"mcp.manage": "MCP verwalten",
|
||||
"plugin.debug": "Plugins debuggen",
|
||||
"plugin.delete": "Plugins löschen",
|
||||
"plugin.install": "Plugins installieren und aktualisieren",
|
||||
"plugin.manage": "Plugins verwalten",
|
||||
"plugin.plugin_preferences": "Plugin-Einstellungen verwalten",
|
||||
"plugin.model_config": "Modelle konfigurieren",
|
||||
"plugin.plugin_preferences": "Automatische Updates konfigurieren",
|
||||
"snippets.create_and_modify": "Snippets erstellen und ändern",
|
||||
"snippets.management": "Snippets verwalten",
|
||||
"tool.manage": "Tools verwalten",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Delete app",
|
||||
"app.acl.edit": "Edit and orchestrate app",
|
||||
"app.acl.import_export_dsl": "Import / export DSL",
|
||||
"app.acl.monitor": "Monitoring and operations",
|
||||
"app.acl.log_and_annotation": "Access logs and annotations",
|
||||
"app.acl.monitor": "Access monitoring page",
|
||||
"app.acl.preview": "Preview app",
|
||||
"app.acl.release_and_version": "App publishing and version management",
|
||||
"app.acl.test_and_run": "Test and use app",
|
||||
"app.acl.tracing_config": "Configure external tracing",
|
||||
"app.acl.view_layout": "Read-only orchestration page",
|
||||
"app.create_and_management": "Create apps",
|
||||
"app.tag.manage": "Manage app tags",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Manage knowledge base tags",
|
||||
"mcp.manage": "Manage MCP",
|
||||
"plugin.debug": "Debug plugins",
|
||||
"plugin.delete": "Delete plugins",
|
||||
"plugin.install": "Install and update plugins",
|
||||
"plugin.manage": "Manage plugins",
|
||||
"plugin.plugin_preferences": "Manage plugin preferences",
|
||||
"plugin.model_config": "Configure models",
|
||||
"plugin.plugin_preferences": "Configure auto updates",
|
||||
"snippets.create_and_modify": "Create and modify snippets",
|
||||
"snippets.management": "Manage snippets",
|
||||
"tool.manage": "Manage tools",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Eliminar app",
|
||||
"app.acl.edit": "Editar y orquestar la app",
|
||||
"app.acl.import_export_dsl": "Importar / exportar DSL",
|
||||
"app.acl.monitor": "Supervisión y operaciones",
|
||||
"app.acl.log_and_annotation": "Acceder a logs y anotaciones",
|
||||
"app.acl.monitor": "Acceder a la página de supervisión",
|
||||
"app.acl.preview": "Previsualizar app",
|
||||
"app.acl.release_and_version": "Publicación de la app y gestión de versiones",
|
||||
"app.acl.test_and_run": "Probar y usar la app",
|
||||
"app.acl.tracing_config": "Configurar tracing externo",
|
||||
"app.acl.view_layout": "Página de orquestación de solo lectura",
|
||||
"app.create_and_management": "Crear apps",
|
||||
"app.tag.manage": "Gestionar etiquetas de apps",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Gestionar etiquetas de la base de conocimiento",
|
||||
"mcp.manage": "Gestionar MCP",
|
||||
"plugin.debug": "Depurar plugins",
|
||||
"plugin.delete": "Eliminar plugins",
|
||||
"plugin.install": "Instalar y actualizar plugins",
|
||||
"plugin.manage": "Gestionar plugins",
|
||||
"plugin.plugin_preferences": "Gestionar las preferencias de plugins",
|
||||
"plugin.model_config": "Configurar modelos",
|
||||
"plugin.plugin_preferences": "Configurar actualizaciones automáticas",
|
||||
"snippets.create_and_modify": "Crear y modificar fragmentos",
|
||||
"snippets.management": "Gestionar fragmentos",
|
||||
"tool.manage": "Gestionar herramientas",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "حذف برنامه",
|
||||
"app.acl.edit": "ویرایش و هماهنگسازی برنامه",
|
||||
"app.acl.import_export_dsl": "وارد کردن / صادر کردن DSL",
|
||||
"app.acl.monitor": "نظارت و عملیات",
|
||||
"app.acl.log_and_annotation": "دسترسی به گزارشها و حاشیهنویسیها",
|
||||
"app.acl.monitor": "دسترسی به صفحه نظارت",
|
||||
"app.acl.preview": "پیشنمایش برنامه",
|
||||
"app.acl.release_and_version": "انتشار برنامه و مدیریت نسخه",
|
||||
"app.acl.test_and_run": "آزمایش و استفاده از برنامه",
|
||||
"app.acl.tracing_config": "پیکربندی رهگیری خارجی",
|
||||
"app.acl.view_layout": "صفحه هماهنگسازی فقطخواندنی",
|
||||
"app.create_and_management": "ایجاد برنامهها",
|
||||
"app.tag.manage": "مدیریت برچسبهای برنامه",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "مدیریت برچسبهای پایگاه دانش",
|
||||
"mcp.manage": "مدیریت MCP",
|
||||
"plugin.debug": "اشکالزدایی افزونهها",
|
||||
"plugin.delete": "حذف افزونهها",
|
||||
"plugin.install": "نصب و بهروزرسانی افزونهها",
|
||||
"plugin.manage": "مدیریت افزونهها",
|
||||
"plugin.plugin_preferences": "مدیریت ترجیحات افزونه",
|
||||
"plugin.model_config": "پیکربندی مدلها",
|
||||
"plugin.plugin_preferences": "پیکربندی بهروزرسانیهای خودکار",
|
||||
"snippets.create_and_modify": "ایجاد و اصلاح قطعهکدها",
|
||||
"snippets.management": "مدیریت قطعهکدها",
|
||||
"tool.manage": "مدیریت ابزارها",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Supprimer l'application",
|
||||
"app.acl.edit": "Modifier et orchestrer l'application",
|
||||
"app.acl.import_export_dsl": "Importer / exporter le DSL",
|
||||
"app.acl.monitor": "Surveillance et exploitation",
|
||||
"app.acl.log_and_annotation": "Accéder aux journaux et annotations",
|
||||
"app.acl.monitor": "Accéder à la page de surveillance",
|
||||
"app.acl.preview": "Prévisualiser l'application",
|
||||
"app.acl.release_and_version": "Publication de l'application et gestion des versions",
|
||||
"app.acl.test_and_run": "Tester et utiliser l'application",
|
||||
"app.acl.tracing_config": "Configurer le traçage externe",
|
||||
"app.acl.view_layout": "Page d'orchestration en lecture seule",
|
||||
"app.create_and_management": "Créer des applications",
|
||||
"app.tag.manage": "Gérer les étiquettes d'applications",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Gérer les étiquettes de bases de connaissances",
|
||||
"mcp.manage": "Gérer MCP",
|
||||
"plugin.debug": "Déboguer les plugins",
|
||||
"plugin.delete": "Supprimer les plugins",
|
||||
"plugin.install": "Installer et mettre à jour les plugins",
|
||||
"plugin.manage": "Gérer les plugins",
|
||||
"plugin.plugin_preferences": "Gérer les préférences des plugins",
|
||||
"plugin.model_config": "Configurer les modèles",
|
||||
"plugin.plugin_preferences": "Configurer les mises à jour automatiques",
|
||||
"snippets.create_and_modify": "Créer et modifier des extraits",
|
||||
"snippets.management": "Gérer les extraits",
|
||||
"tool.manage": "Gérer les outils",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "ऐप हटाएं",
|
||||
"app.acl.edit": "ऐप संपादित और ऑर्केस्ट्रेट करें",
|
||||
"app.acl.import_export_dsl": "DSL आयात / निर्यात करें",
|
||||
"app.acl.monitor": "निगरानी और संचालन",
|
||||
"app.acl.log_and_annotation": "लॉग और एनोटेशन तक पहुंच",
|
||||
"app.acl.monitor": "मॉनिटरिंग पेज तक पहुंच",
|
||||
"app.acl.preview": "ऐप पूर्वावलोकन करें",
|
||||
"app.acl.release_and_version": "ऐप प्रकाशन और संस्करण प्रबंधन",
|
||||
"app.acl.test_and_run": "ऐप परीक्षण और उपयोग करें",
|
||||
"app.acl.tracing_config": "बाहरी ट्रेसिंग कॉन्फ़िगर करें",
|
||||
"app.acl.view_layout": "केवल-पढ़ने योग्य ऑर्केस्ट्रेशन पृष्ठ",
|
||||
"app.create_and_management": "ऐप्स बनाएं",
|
||||
"app.tag.manage": "ऐप टैग प्रबंधित करें",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "ज्ञान आधार टैग प्रबंधित करें",
|
||||
"mcp.manage": "MCP प्रबंधित करें",
|
||||
"plugin.debug": "प्लगइन्स डिबग करें",
|
||||
"plugin.delete": "प्लगइन्स हटाएं",
|
||||
"plugin.install": "प्लगइन्स इंस्टॉल और अपडेट करें",
|
||||
"plugin.manage": "प्लगइन्स प्रबंधित करें",
|
||||
"plugin.plugin_preferences": "प्लगइन प्राथमिकताएं प्रबंधित करें",
|
||||
"plugin.model_config": "मॉडल कॉन्फ़िगर करें",
|
||||
"plugin.plugin_preferences": "ऑटो अपडेट कॉन्फ़िगर करें",
|
||||
"snippets.create_and_modify": "स्निपेट बनाएं और संशोधित करें",
|
||||
"snippets.management": "स्निपेट प्रबंधित करें",
|
||||
"tool.manage": "टूल प्रबंधित करें",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Hapus aplikasi",
|
||||
"app.acl.edit": "Edit dan orkestrasikan aplikasi",
|
||||
"app.acl.import_export_dsl": "Impor / ekspor DSL",
|
||||
"app.acl.monitor": "Pemantauan dan operasi",
|
||||
"app.acl.log_and_annotation": "Akses log dan anotasi",
|
||||
"app.acl.monitor": "Akses halaman pemantauan",
|
||||
"app.acl.preview": "Pratinjau aplikasi",
|
||||
"app.acl.release_and_version": "Penerbitan aplikasi dan manajemen versi",
|
||||
"app.acl.test_and_run": "Uji dan gunakan aplikasi",
|
||||
"app.acl.tracing_config": "Konfigurasikan tracing eksternal",
|
||||
"app.acl.view_layout": "Halaman orkestrasi hanya-baca",
|
||||
"app.create_and_management": "Buat aplikasi",
|
||||
"app.tag.manage": "Kelola tag aplikasi",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Kelola tag basis pengetahuan",
|
||||
"mcp.manage": "Kelola MCP",
|
||||
"plugin.debug": "Debug plugin",
|
||||
"plugin.delete": "Hapus plugin",
|
||||
"plugin.install": "Instal dan perbarui plugin",
|
||||
"plugin.manage": "Kelola plugin",
|
||||
"plugin.plugin_preferences": "Kelola preferensi plugin",
|
||||
"plugin.model_config": "Konfigurasi model",
|
||||
"plugin.plugin_preferences": "Konfigurasi pembaruan otomatis",
|
||||
"snippets.create_and_modify": "Buat dan ubah snippet",
|
||||
"snippets.management": "Kelola snippet",
|
||||
"tool.manage": "Kelola alat",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Elimina app",
|
||||
"app.acl.edit": "Modifica e orchestra l'app",
|
||||
"app.acl.import_export_dsl": "Importa / esporta DSL",
|
||||
"app.acl.monitor": "Monitoraggio e operazioni",
|
||||
"app.acl.log_and_annotation": "Accedi a log e annotazioni",
|
||||
"app.acl.monitor": "Accedi alla pagina di monitoraggio",
|
||||
"app.acl.preview": "Anteprima app",
|
||||
"app.acl.release_and_version": "Pubblicazione dell'app e gestione delle versioni",
|
||||
"app.acl.test_and_run": "Testa e usa l'app",
|
||||
"app.acl.tracing_config": "Configura il tracing esterno",
|
||||
"app.acl.view_layout": "Pagina di orchestrazione in sola lettura",
|
||||
"app.create_and_management": "Crea app",
|
||||
"app.tag.manage": "Gestisci i tag delle app",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Gestisci i tag della knowledge base",
|
||||
"mcp.manage": "Gestisci MCP",
|
||||
"plugin.debug": "Esegui il debug dei plugin",
|
||||
"plugin.delete": "Elimina plugin",
|
||||
"plugin.install": "Installa e aggiorna plugin",
|
||||
"plugin.manage": "Gestisci plugin",
|
||||
"plugin.plugin_preferences": "Gestisci le preferenze dei plugin",
|
||||
"plugin.model_config": "Configura modelli",
|
||||
"plugin.plugin_preferences": "Configura aggiornamenti automatici",
|
||||
"snippets.create_and_modify": "Crea e modifica snippet",
|
||||
"snippets.management": "Gestisci snippet",
|
||||
"tool.manage": "Gestisci strumenti",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "アプリを削除",
|
||||
"app.acl.edit": "アプリの編集とオーケストレーション",
|
||||
"app.acl.import_export_dsl": "DSLのインポート / エクスポート",
|
||||
"app.acl.monitor": "監視と運用",
|
||||
"app.acl.log_and_annotation": "ログと注釈へのアクセス",
|
||||
"app.acl.monitor": "監視ページへのアクセス",
|
||||
"app.acl.preview": "アプリをプレビュー",
|
||||
"app.acl.release_and_version": "アプリ公開とバージョン管理",
|
||||
"app.acl.test_and_run": "アプリのテストと使用",
|
||||
"app.acl.tracing_config": "外部トレーシングの設定",
|
||||
"app.acl.view_layout": "オーケストレーションページの読み取り専用",
|
||||
"app.create_and_management": "アプリを作成",
|
||||
"app.tag.manage": "アプリタグを管理",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "ナレッジベースタグを管理",
|
||||
"mcp.manage": "MCPを管理",
|
||||
"plugin.debug": "プラグインをデバッグ",
|
||||
"plugin.delete": "プラグインを削除",
|
||||
"plugin.install": "プラグインのインストールと更新",
|
||||
"plugin.manage": "プラグインを管理",
|
||||
"plugin.plugin_preferences": "プラグイン設定を管理",
|
||||
"plugin.model_config": "モデルを設定",
|
||||
"plugin.plugin_preferences": "自動更新を設定",
|
||||
"snippets.create_and_modify": "スニペットの作成と変更",
|
||||
"snippets.management": "スニペットを管理",
|
||||
"tool.manage": "ツールを管理",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "앱 삭제",
|
||||
"app.acl.edit": "앱 편집 및 오케스트레이션",
|
||||
"app.acl.import_export_dsl": "DSL 가져오기 / 내보내기",
|
||||
"app.acl.monitor": "모니터링 및 운영",
|
||||
"app.acl.log_and_annotation": "로그 및 주석 접근",
|
||||
"app.acl.monitor": "모니터링 페이지 접근",
|
||||
"app.acl.preview": "앱 미리보기",
|
||||
"app.acl.release_and_version": "앱 게시 및 버전 관리",
|
||||
"app.acl.test_and_run": "앱 테스트 및 사용",
|
||||
"app.acl.tracing_config": "외부 추적 구성",
|
||||
"app.acl.view_layout": "읽기 전용 오케스트레이션 페이지",
|
||||
"app.create_and_management": "앱 생성",
|
||||
"app.tag.manage": "앱 태그 관리",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "지식 베이스 태그 관리",
|
||||
"mcp.manage": "MCP 관리",
|
||||
"plugin.debug": "플러그인 디버그",
|
||||
"plugin.delete": "플러그인 삭제",
|
||||
"plugin.install": "플러그인 설치 및 업데이트",
|
||||
"plugin.manage": "플러그인 관리",
|
||||
"plugin.plugin_preferences": "플러그인 환경설정 관리",
|
||||
"plugin.model_config": "모델 구성",
|
||||
"plugin.plugin_preferences": "자동 업데이트 구성",
|
||||
"snippets.create_and_modify": "스니펫 생성 및 수정",
|
||||
"snippets.management": "스니펫 관리",
|
||||
"tool.manage": "도구 관리",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "App verwijderen",
|
||||
"app.acl.edit": "App bewerken en orkestreren",
|
||||
"app.acl.import_export_dsl": "DSL importeren / exporteren",
|
||||
"app.acl.monitor": "Monitoring en bewerkingen",
|
||||
"app.acl.log_and_annotation": "Logs en annotaties openen",
|
||||
"app.acl.monitor": "Monitoringpagina openen",
|
||||
"app.acl.preview": "App voorbeeld bekijken",
|
||||
"app.acl.release_and_version": "App publiceren en versiebeheer",
|
||||
"app.acl.test_and_run": "App testen en gebruiken",
|
||||
"app.acl.tracing_config": "Externe tracing configureren",
|
||||
"app.acl.view_layout": "Alleen-lezen orkestratiepagina",
|
||||
"app.create_and_management": "Apps maken",
|
||||
"app.tag.manage": "App-tags beheren",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Kennisbank-tags beheren",
|
||||
"mcp.manage": "MCP beheren",
|
||||
"plugin.debug": "Plug-ins debuggen",
|
||||
"plugin.delete": "Plug-ins verwijderen",
|
||||
"plugin.install": "Plug-ins installeren en bijwerken",
|
||||
"plugin.manage": "Plug-ins beheren",
|
||||
"plugin.plugin_preferences": "Plug-invoorkeuren beheren",
|
||||
"plugin.model_config": "Modellen configureren",
|
||||
"plugin.plugin_preferences": "Automatische updates configureren",
|
||||
"snippets.create_and_modify": "Snippets maken en wijzigen",
|
||||
"snippets.management": "Snippets beheren",
|
||||
"tool.manage": "Tools beheren",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Usuń aplikację",
|
||||
"app.acl.edit": "Edytuj i orkiestruj aplikację",
|
||||
"app.acl.import_export_dsl": "Importuj / eksportuj DSL",
|
||||
"app.acl.monitor": "Monitorowanie i operacje",
|
||||
"app.acl.log_and_annotation": "Dostęp do logów i adnotacji",
|
||||
"app.acl.monitor": "Dostęp do strony monitorowania",
|
||||
"app.acl.preview": "Podgląd aplikacji",
|
||||
"app.acl.release_and_version": "Publikowanie aplikacji i zarządzanie wersjami",
|
||||
"app.acl.test_and_run": "Testuj i używaj aplikacji",
|
||||
"app.acl.tracing_config": "Konfiguruj zewnętrzne śledzenie",
|
||||
"app.acl.view_layout": "Strona orkiestracji tylko do odczytu",
|
||||
"app.create_and_management": "Twórz aplikacje",
|
||||
"app.tag.manage": "Zarządzaj tagami aplikacji",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Zarządzaj tagami bazy wiedzy",
|
||||
"mcp.manage": "Zarządzaj MCP",
|
||||
"plugin.debug": "Debuguj wtyczki",
|
||||
"plugin.delete": "Usuń wtyczki",
|
||||
"plugin.install": "Instaluj i aktualizuj wtyczki",
|
||||
"plugin.manage": "Zarządzaj wtyczkami",
|
||||
"plugin.plugin_preferences": "Zarządzaj preferencjami wtyczek",
|
||||
"plugin.model_config": "Konfiguruj modele",
|
||||
"plugin.plugin_preferences": "Konfiguruj automatyczne aktualizacje",
|
||||
"snippets.create_and_modify": "Twórz i modyfikuj fragmenty kodu",
|
||||
"snippets.management": "Zarządzaj fragmentami kodu",
|
||||
"tool.manage": "Zarządzaj narzędziami",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Excluir aplicativo",
|
||||
"app.acl.edit": "Editar e orquestrar aplicativo",
|
||||
"app.acl.import_export_dsl": "Importar / exportar DSL",
|
||||
"app.acl.monitor": "Monitoramento e operações",
|
||||
"app.acl.log_and_annotation": "Acessar logs e anotações",
|
||||
"app.acl.monitor": "Acessar página de monitoramento",
|
||||
"app.acl.preview": "Visualizar aplicativo",
|
||||
"app.acl.release_and_version": "Publicação de aplicativo e gerenciamento de versões",
|
||||
"app.acl.test_and_run": "Testar e usar aplicativo",
|
||||
"app.acl.tracing_config": "Configurar rastreamento externo",
|
||||
"app.acl.view_layout": "Página de orquestração somente leitura",
|
||||
"app.create_and_management": "Criar aplicativos",
|
||||
"app.tag.manage": "Gerenciar tags de aplicativo",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Gerenciar tags do Conhecimento",
|
||||
"mcp.manage": "Gerenciar MCP",
|
||||
"plugin.debug": "Depurar plug-ins",
|
||||
"plugin.delete": "Excluir plug-ins",
|
||||
"plugin.install": "Instalar e atualizar plug-ins",
|
||||
"plugin.manage": "Gerenciar plug-ins",
|
||||
"plugin.plugin_preferences": "Gerenciar preferências de plug-ins",
|
||||
"plugin.model_config": "Configurar modelos",
|
||||
"plugin.plugin_preferences": "Configurar atualizações automáticas",
|
||||
"snippets.create_and_modify": "Criar e modificar snippets",
|
||||
"snippets.management": "Gerenciar snippets",
|
||||
"tool.manage": "Gerenciar ferramentas",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Șterge aplicația",
|
||||
"app.acl.edit": "Editează și orchestrează aplicația",
|
||||
"app.acl.import_export_dsl": "Importă / exportă DSL",
|
||||
"app.acl.monitor": "Monitorizare și operațiuni",
|
||||
"app.acl.log_and_annotation": "Accesează jurnale și adnotări",
|
||||
"app.acl.monitor": "Accesează pagina de monitorizare",
|
||||
"app.acl.preview": "Previzualizează aplicația",
|
||||
"app.acl.release_and_version": "Publicarea aplicației și gestionarea versiunilor",
|
||||
"app.acl.test_and_run": "Testează și utilizează aplicația",
|
||||
"app.acl.tracing_config": "Configurează tracing extern",
|
||||
"app.acl.view_layout": "Pagină de orchestrare doar pentru citire",
|
||||
"app.create_and_management": "Creează aplicații",
|
||||
"app.tag.manage": "Gestionează etichetele aplicațiilor",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Gestionează etichetele bazei de cunoștințe",
|
||||
"mcp.manage": "Gestionează MCP",
|
||||
"plugin.debug": "Depanează plugin-uri",
|
||||
"plugin.delete": "Șterge plugin-uri",
|
||||
"plugin.install": "Instalează și actualizează plugin-uri",
|
||||
"plugin.manage": "Gestionează plugin-uri",
|
||||
"plugin.plugin_preferences": "Gestionează preferințele plugin-urilor",
|
||||
"plugin.model_config": "Configurează modele",
|
||||
"plugin.plugin_preferences": "Configurează actualizările automate",
|
||||
"snippets.create_and_modify": "Creează și modifică fragmente",
|
||||
"snippets.management": "Gestionează fragmente",
|
||||
"tool.manage": "Gestionează instrumente",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Удаление приложения",
|
||||
"app.acl.edit": "Редактирование и оркестрация приложения",
|
||||
"app.acl.import_export_dsl": "Импорт / экспорт DSL",
|
||||
"app.acl.monitor": "Мониторинг и эксплуатация",
|
||||
"app.acl.log_and_annotation": "Доступ к журналам и аннотациям",
|
||||
"app.acl.monitor": "Доступ к странице мониторинга",
|
||||
"app.acl.preview": "Предпросмотр приложения",
|
||||
"app.acl.release_and_version": "Публикация приложения и управление версиями",
|
||||
"app.acl.test_and_run": "Тестирование и использование приложения",
|
||||
"app.acl.tracing_config": "Настройка внешнего трассинга",
|
||||
"app.acl.view_layout": "Страница оркестрации только для чтения",
|
||||
"app.create_and_management": "Создание приложений",
|
||||
"app.tag.manage": "Управление тегами приложений",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Управление тегами баз знаний",
|
||||
"mcp.manage": "Управление MCP",
|
||||
"plugin.debug": "Отладка плагинов",
|
||||
"plugin.delete": "Удаление плагинов",
|
||||
"plugin.install": "Установка и обновление плагинов",
|
||||
"plugin.manage": "Управление плагинами",
|
||||
"plugin.plugin_preferences": "Управление настройками плагинов",
|
||||
"plugin.model_config": "Настройка моделей",
|
||||
"plugin.plugin_preferences": "Настройка автообновлений",
|
||||
"snippets.create_and_modify": "Создание и изменение сниппетов",
|
||||
"snippets.management": "Управление сниппетами",
|
||||
"tool.manage": "Управление инструментами",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Izbriši aplikacijo",
|
||||
"app.acl.edit": "Uredi in orkestriraj aplikacijo",
|
||||
"app.acl.import_export_dsl": "Uvozi / izvozi DSL",
|
||||
"app.acl.monitor": "Spremljanje in operacije",
|
||||
"app.acl.log_and_annotation": "Dostop do dnevnikov in opomb",
|
||||
"app.acl.monitor": "Dostop do strani za spremljanje",
|
||||
"app.acl.preview": "Predogled aplikacije",
|
||||
"app.acl.release_and_version": "Objavljanje aplikacije in upravljanje različic",
|
||||
"app.acl.test_and_run": "Preizkusi in uporabi aplikacijo",
|
||||
"app.acl.tracing_config": "Konfiguracija zunanjega sledenja",
|
||||
"app.acl.view_layout": "Stran za orkestracijo samo za branje",
|
||||
"app.create_and_management": "Ustvarjanje aplikacij",
|
||||
"app.tag.manage": "Upravljanje oznak aplikacij",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Upravljanje oznak baze znanja",
|
||||
"mcp.manage": "Upravljanje MCP",
|
||||
"plugin.debug": "Razhroščevanje vtičnikov",
|
||||
"plugin.delete": "Brisanje vtičnikov",
|
||||
"plugin.install": "Namesti in posodobi vtičnike",
|
||||
"plugin.manage": "Upravljanje vtičnikov",
|
||||
"plugin.plugin_preferences": "Upravljanje nastavitev vtičnikov",
|
||||
"plugin.model_config": "Konfiguriranje modelov",
|
||||
"plugin.plugin_preferences": "Konfiguriranje samodejnih posodobitev",
|
||||
"snippets.create_and_modify": "Ustvarjanje in spreminjanje izsekov",
|
||||
"snippets.management": "Upravljanje izsekov",
|
||||
"tool.manage": "Upravljanje orodij",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "ลบแอป",
|
||||
"app.acl.edit": "แก้ไขและจัดวางแอป",
|
||||
"app.acl.import_export_dsl": "นําเข้า / ส่งออก DSL",
|
||||
"app.acl.monitor": "การตรวจสอบและการดําเนินการ",
|
||||
"app.acl.log_and_annotation": "เข้าถึงบันทึกและคำอธิบายประกอบ",
|
||||
"app.acl.monitor": "เข้าถึงหน้าการตรวจสอบ",
|
||||
"app.acl.preview": "ดูตัวอย่างแอป",
|
||||
"app.acl.release_and_version": "การเผยแพร่แอปและการจัดการเวอร์ชัน",
|
||||
"app.acl.test_and_run": "ทดสอบและใช้งานแอป",
|
||||
"app.acl.tracing_config": "กำหนดค่าการติดตามภายนอก",
|
||||
"app.acl.view_layout": "หน้าจัดวางแบบอ่านอย่างเดียว",
|
||||
"app.create_and_management": "สร้างแอป",
|
||||
"app.tag.manage": "จัดการแท็กแอป",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "จัดการแท็กฐานความรู้",
|
||||
"mcp.manage": "จัดการ MCP",
|
||||
"plugin.debug": "ดีบักปลั๊กอิน",
|
||||
"plugin.delete": "ลบปลั๊กอิน",
|
||||
"plugin.install": "ติดตั้งและอัปเดตปลั๊กอิน",
|
||||
"plugin.manage": "จัดการปลั๊กอิน",
|
||||
"plugin.plugin_preferences": "จัดการการตั้งค่าปลั๊กอิน",
|
||||
"plugin.model_config": "กำหนดค่าโมเดล",
|
||||
"plugin.plugin_preferences": "กำหนดค่าการอัปเดตอัตโนมัติ",
|
||||
"snippets.create_and_modify": "สร้างและแก้ไขสนิปเปต",
|
||||
"snippets.management": "จัดการสนิปเปต",
|
||||
"tool.manage": "จัดการเครื่องมือ",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Uygulamayı sil",
|
||||
"app.acl.edit": "Uygulamayı düzenle ve düzenle",
|
||||
"app.acl.import_export_dsl": "DSL içe / dışa aktar",
|
||||
"app.acl.monitor": "İzleme ve operasyonlar",
|
||||
"app.acl.log_and_annotation": "Günlüklere ve açıklamalara eriş",
|
||||
"app.acl.monitor": "İzleme sayfasına eriş",
|
||||
"app.acl.preview": "Uygulamayı önizle",
|
||||
"app.acl.release_and_version": "Uygulama yayınlama ve sürüm yönetimi",
|
||||
"app.acl.test_and_run": "Uygulamayı test et ve kullan",
|
||||
"app.acl.tracing_config": "Harici izlemeyi yapılandır",
|
||||
"app.acl.view_layout": "Salt okunur düzenleme sayfası",
|
||||
"app.create_and_management": "Uygulama oluştur",
|
||||
"app.tag.manage": "Uygulama etiketlerini yönet",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Bilgi tabanı etiketlerini yönet",
|
||||
"mcp.manage": "MCP yönet",
|
||||
"plugin.debug": "Eklentileri hata ayıkla",
|
||||
"plugin.delete": "Eklentileri sil",
|
||||
"plugin.install": "Eklentileri yükle ve güncelle",
|
||||
"plugin.manage": "Eklentileri yönet",
|
||||
"plugin.plugin_preferences": "Eklenti tercihlerini yönet",
|
||||
"plugin.model_config": "Modelleri yapılandır",
|
||||
"plugin.plugin_preferences": "Otomatik güncellemeleri yapılandır",
|
||||
"snippets.create_and_modify": "Snippet oluştur ve değiştir",
|
||||
"snippets.management": "Snippet'leri yönet",
|
||||
"tool.manage": "Araçları yönet",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Видалити застосунок",
|
||||
"app.acl.edit": "Редагувати та оркеструвати застосунок",
|
||||
"app.acl.import_export_dsl": "Імпорт / експорт DSL",
|
||||
"app.acl.monitor": "Моніторинг та операції",
|
||||
"app.acl.log_and_annotation": "Доступ до журналів і анотацій",
|
||||
"app.acl.monitor": "Доступ до сторінки моніторингу",
|
||||
"app.acl.preview": "Попередній перегляд застосунку",
|
||||
"app.acl.release_and_version": "Публікація застосунку та керування версіями",
|
||||
"app.acl.test_and_run": "Тестувати та використовувати застосунок",
|
||||
"app.acl.tracing_config": "Налаштування зовнішнього трасування",
|
||||
"app.acl.view_layout": "Сторінка оркестрації лише для читання",
|
||||
"app.create_and_management": "Створювати застосунки",
|
||||
"app.tag.manage": "Керування тегами застосунків",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Керування тегами бази знань",
|
||||
"mcp.manage": "Керування MCP",
|
||||
"plugin.debug": "Налагодження плагінів",
|
||||
"plugin.delete": "Видалення плагінів",
|
||||
"plugin.install": "Встановлення та оновлення плагінів",
|
||||
"plugin.manage": "Керування плагінами",
|
||||
"plugin.plugin_preferences": "Керування налаштуваннями плагінів",
|
||||
"plugin.model_config": "Налаштування моделей",
|
||||
"plugin.plugin_preferences": "Налаштування автоматичних оновлень",
|
||||
"snippets.create_and_modify": "Створювати та змінювати фрагменти",
|
||||
"snippets.management": "Керування фрагментами",
|
||||
"tool.manage": "Керування інструментами",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "Xóa ứng dụng",
|
||||
"app.acl.edit": "Chỉnh sửa và điều phối ứng dụng",
|
||||
"app.acl.import_export_dsl": "Nhập / xuất DSL",
|
||||
"app.acl.monitor": "Giám sát và vận hành",
|
||||
"app.acl.log_and_annotation": "Truy cập nhật ký và chú thích",
|
||||
"app.acl.monitor": "Truy cập trang giám sát",
|
||||
"app.acl.preview": "Xem trước ứng dụng",
|
||||
"app.acl.release_and_version": "Phát hành ứng dụng và quản lý phiên bản",
|
||||
"app.acl.test_and_run": "Kiểm thử và sử dụng ứng dụng",
|
||||
"app.acl.tracing_config": "Cấu hình tracing bên ngoài",
|
||||
"app.acl.view_layout": "Trang điều phối chỉ đọc",
|
||||
"app.create_and_management": "Tạo ứng dụng",
|
||||
"app.tag.manage": "Quản lý thẻ ứng dụng",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "Quản lý thẻ cơ sở tri thức",
|
||||
"mcp.manage": "Quản lý MCP",
|
||||
"plugin.debug": "Gỡ lỗi plugin",
|
||||
"plugin.delete": "Xóa plugin",
|
||||
"plugin.install": "Cài đặt và cập nhật plugin",
|
||||
"plugin.manage": "Quản lý plugin",
|
||||
"plugin.plugin_preferences": "Quản lý tùy chọn plugin",
|
||||
"plugin.model_config": "Cấu hình mô hình",
|
||||
"plugin.plugin_preferences": "Cấu hình cập nhật tự động",
|
||||
"snippets.create_and_modify": "Tạo và chỉnh sửa đoạn mã",
|
||||
"snippets.management": "Quản lý đoạn mã",
|
||||
"tool.manage": "Quản lý công cụ",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "删除应用",
|
||||
"app.acl.edit": "编辑与编排应用",
|
||||
"app.acl.import_export_dsl": "导入 / 导出 DSL",
|
||||
"app.acl.monitor": "监测与运维",
|
||||
"app.acl.log_and_annotation": "访问日志与标注",
|
||||
"app.acl.monitor": "访问监测页面",
|
||||
"app.acl.preview": "预览应用",
|
||||
"app.acl.release_and_version": "应用发布与版本管理",
|
||||
"app.acl.test_and_run": "测试与使用应用",
|
||||
"app.acl.tracing_config": "配置外部追踪",
|
||||
"app.acl.view_layout": "编排页只读",
|
||||
"app.create_and_management": "创建应用",
|
||||
"app.tag.manage": "管理应用标签",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "管理知识库标签",
|
||||
"mcp.manage": "管理MCP",
|
||||
"plugin.debug": "调试插件",
|
||||
"plugin.delete": "删除插件",
|
||||
"plugin.install": "安装与更新插件",
|
||||
"plugin.manage": "管理插件",
|
||||
"plugin.plugin_preferences": "管理插件偏好",
|
||||
"plugin.model_config": "配置模型",
|
||||
"plugin.plugin_preferences": "配置自动更新",
|
||||
"snippets.create_and_modify": "创建和修改 Snippets",
|
||||
"snippets.management": "管理 Snippets",
|
||||
"tool.manage": "管理工具",
|
||||
|
||||
@ -5,10 +5,12 @@
|
||||
"app.acl.delete": "刪除應用",
|
||||
"app.acl.edit": "編輯與編排應用",
|
||||
"app.acl.import_export_dsl": "匯入 / 匯出 DSL",
|
||||
"app.acl.monitor": "監測與維運",
|
||||
"app.acl.log_and_annotation": "訪問日誌與標註",
|
||||
"app.acl.monitor": "訪問監測頁面",
|
||||
"app.acl.preview": "預覽應用",
|
||||
"app.acl.release_and_version": "應用發布與版本管理",
|
||||
"app.acl.test_and_run": "測試與使用應用",
|
||||
"app.acl.tracing_config": "配置外部追蹤",
|
||||
"app.acl.view_layout": "編排頁唯讀",
|
||||
"app.create_and_management": "建立應用",
|
||||
"app.tag.manage": "管理應用標籤",
|
||||
@ -40,9 +42,10 @@
|
||||
"dataset.tag.manage": "管理知識庫標籤",
|
||||
"mcp.manage": "管理MCP",
|
||||
"plugin.debug": "調試插件",
|
||||
"plugin.delete": "刪除插件",
|
||||
"plugin.install": "安裝與更新插件",
|
||||
"plugin.manage": "管理插件",
|
||||
"plugin.plugin_preferences": "管理插件偏好設定",
|
||||
"plugin.model_config": "設定模型",
|
||||
"plugin.plugin_preferences": "設定自動更新",
|
||||
"snippets.create_and_modify": "建立和修改 Snippets",
|
||||
"snippets.management": "管理 Snippets",
|
||||
"tool.manage": "管理工具",
|
||||
|
||||
@ -85,6 +85,12 @@ describe('app-redirection', () => {
|
||||
|
||||
expect(getRedirectionPath(app)).toBe('/app/app-123/overview')
|
||||
})
|
||||
|
||||
it('returns logs path when app ACL can only access logs and annotations', () => {
|
||||
const app = { id: 'app-123', mode: AppModeEnum.CHAT, permission_keys: [AppACLPermission.LogAndAnnotation] }
|
||||
|
||||
expect(getRedirectionPath(app)).toBe('/app/app-123/logs')
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@ -24,6 +24,9 @@ export const getRedirectionPath = (
|
||||
if (appACLCapabilities.canMonitor)
|
||||
return `/app/${app.id}/overview`
|
||||
|
||||
if (appACLCapabilities.canAccessLogAndAnnotation)
|
||||
return `/app/${app.id}/logs`
|
||||
|
||||
if (appACLCapabilities.canAccessConfig)
|
||||
return `/app/${app.id}/access-config`
|
||||
|
||||
|
||||
@ -42,6 +42,24 @@ describe('permission', () => {
|
||||
expect(capabilities.canComment).toBe(true)
|
||||
expect(capabilities.canTestAndRun).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps monitor, tracing config, and log/annotation permissions independent', () => {
|
||||
const monitorCapabilities = getAppACLCapabilities([AppACLPermission.Monitor])
|
||||
const tracingCapabilities = getAppACLCapabilities([AppACLPermission.TracingConfig])
|
||||
const logAndAnnotationCapabilities = getAppACLCapabilities([AppACLPermission.LogAndAnnotation])
|
||||
|
||||
expect(monitorCapabilities.canMonitor).toBe(true)
|
||||
expect(monitorCapabilities.canConfigureTracing).toBe(false)
|
||||
expect(monitorCapabilities.canAccessLogAndAnnotation).toBe(false)
|
||||
|
||||
expect(tracingCapabilities.canMonitor).toBe(false)
|
||||
expect(tracingCapabilities.canConfigureTracing).toBe(true)
|
||||
expect(tracingCapabilities.canAccessLogAndAnnotation).toBe(false)
|
||||
|
||||
expect(logAndAnnotationCapabilities.canMonitor).toBe(false)
|
||||
expect(logAndAnnotationCapabilities.canConfigureTracing).toBe(false)
|
||||
expect(logAndAnnotationCapabilities.canAccessLogAndAnnotation).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasOnlyAppPreviewPermission', () => {
|
||||
@ -87,6 +105,8 @@ describe('permission', () => {
|
||||
expect(capabilities.canDelete).toBe(true)
|
||||
expect(capabilities.canReleaseAndVersion).toBe(true)
|
||||
expect(capabilities.canMonitor).toBe(true)
|
||||
expect(capabilities.canConfigureTracing).toBe(true)
|
||||
expect(capabilities.canAccessLogAndAnnotation).toBe(true)
|
||||
expect(capabilities.canAccessConfig).toBe(true)
|
||||
expect(permissionKeys).toEqual([])
|
||||
})
|
||||
|
||||
@ -9,6 +9,8 @@ export const AppACLPermission = {
|
||||
Delete: 'app.acl.delete',
|
||||
ReleaseAndVersion: 'app.acl.release_and_version',
|
||||
Monitor: 'app.acl.monitor',
|
||||
TracingConfig: 'app.acl.tracing_config',
|
||||
LogAndAnnotation: 'app.acl.log_and_annotation',
|
||||
AccessConfig: 'app.acl.access_config',
|
||||
} as const
|
||||
|
||||
@ -51,6 +53,8 @@ type AppACLCapabilities = {
|
||||
canDelete: boolean
|
||||
canReleaseAndVersion: boolean
|
||||
canMonitor: boolean
|
||||
canConfigureTracing: boolean
|
||||
canAccessLogAndAnnotation: boolean
|
||||
canAccessConfig: boolean
|
||||
}
|
||||
|
||||
@ -124,6 +128,8 @@ export const getAppACLCapabilities = (
|
||||
canDelete: hasResourcePermission(permissionKeys, AppACLPermission.Delete, hasMaintainerPermissions),
|
||||
canReleaseAndVersion: hasResourcePermission(permissionKeys, AppACLPermission.ReleaseAndVersion, hasMaintainerPermissions),
|
||||
canMonitor: hasResourcePermission(permissionKeys, AppACLPermission.Monitor, hasMaintainerPermissions),
|
||||
canConfigureTracing: hasResourcePermission(permissionKeys, AppACLPermission.TracingConfig, hasMaintainerPermissions),
|
||||
canAccessLogAndAnnotation: hasResourcePermission(permissionKeys, AppACLPermission.LogAndAnnotation, hasMaintainerPermissions),
|
||||
canAccessConfig: Boolean(options?.isRbacEnabled) && hasResourcePermission(permissionKeys, AppACLPermission.AccessConfig, hasMaintainerPermissions),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user