mirror of https://github.com/portainer/portainer
fix(docker): hide write buttons for non authorized [EE-6775] (#11260)
parent
d8e374fb76
commit
19a6a5c608
|
@ -79,7 +79,7 @@ const ngModule = angular
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
'dockerConfigsDatatable',
|
'dockerConfigsDatatable',
|
||||||
r2a(withUIRouter(ConfigsDatatable), [
|
r2a(withUIRouter(withCurrentUser(ConfigsDatatable)), [
|
||||||
'dataset',
|
'dataset',
|
||||||
'onRemoveClick',
|
'onRemoveClick',
|
||||||
'onRefresh',
|
'onRefresh',
|
||||||
|
@ -121,7 +121,11 @@ const ngModule = angular
|
||||||
.component('dockerEventsDatatable', r2a(EventsDatatable, ['dataset']))
|
.component('dockerEventsDatatable', r2a(EventsDatatable, ['dataset']))
|
||||||
.component(
|
.component(
|
||||||
'dockerSecretsDatatable',
|
'dockerSecretsDatatable',
|
||||||
r2a(withUIRouter(SecretsDatatable), ['dataset', 'onRefresh', 'onRemove'])
|
r2a(withUIRouter(withCurrentUser(SecretsDatatable)), [
|
||||||
|
'dataset',
|
||||||
|
'onRefresh',
|
||||||
|
'onRemove',
|
||||||
|
])
|
||||||
)
|
)
|
||||||
.component(
|
.component(
|
||||||
'dockerStacksDatatable',
|
'dockerStacksDatatable',
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { Clipboard, Plus, Trash2 } from 'lucide-react';
|
import { Clipboard, Plus, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
|
|
||||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
||||||
import { useRepeater } from '@@/datatables/useRepeater';
|
import { useRepeater } from '@@/datatables/useRepeater';
|
||||||
|
@ -26,6 +28,11 @@ export function ConfigsDatatable({ dataset, onRefresh, onRemoveClick }: Props) {
|
||||||
|
|
||||||
useRepeater(tableState.autoRefreshRate, onRefresh);
|
useRepeater(tableState.autoRefreshRate, onRefresh);
|
||||||
|
|
||||||
|
const hasWriteAccessQuery = useAuthorizations([
|
||||||
|
'DockerConfigCreate',
|
||||||
|
'DockerConfigDelete',
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Datatable
|
<Datatable
|
||||||
dataset={dataset}
|
dataset={dataset}
|
||||||
|
@ -42,21 +49,33 @@ export function ConfigsDatatable({ dataset, onRefresh, onRemoveClick }: Props) {
|
||||||
/>
|
/>
|
||||||
</TableSettingsMenu>
|
</TableSettingsMenu>
|
||||||
)}
|
)}
|
||||||
renderTableActions={(selectedRows) => (
|
disableSelect={!hasWriteAccessQuery.authorized}
|
||||||
<div className="flex items-center gap-3">
|
renderTableActions={(selectedRows) =>
|
||||||
<Button
|
hasWriteAccessQuery.authorized && (
|
||||||
icon={Trash2}
|
<div className="flex items-center gap-3">
|
||||||
color="dangerlight"
|
<Authorized authorizations="DockerConfigDelete">
|
||||||
onClick={() => onRemoveClick(selectedRows)}
|
<Button
|
||||||
disabled={selectedRows.length === 0}
|
icon={Trash2}
|
||||||
>
|
color="dangerlight"
|
||||||
Remove
|
onClick={() => onRemoveClick(selectedRows)}
|
||||||
</Button>
|
disabled={selectedRows.length === 0}
|
||||||
<Button icon={Plus} as={Link} props={{ to: 'docker.configs.new' }}>
|
>
|
||||||
Add config
|
Remove
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</Authorized>
|
||||||
)}
|
|
||||||
|
<Authorized authorizations="DockerConfigCreate">
|
||||||
|
<Button
|
||||||
|
icon={Plus}
|
||||||
|
as={Link}
|
||||||
|
props={{ to: 'docker.configs.new' }}
|
||||||
|
>
|
||||||
|
Add config
|
||||||
|
</Button>
|
||||||
|
</Authorized>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Lock, Plus, Trash2 } from 'lucide-react';
|
||||||
|
|
||||||
import { SecretViewModel } from '@/docker/models/secret';
|
import { SecretViewModel } from '@/docker/models/secret';
|
||||||
import { isoDate } from '@/portainer/filters/filters';
|
import { isoDate } from '@/portainer/filters/filters';
|
||||||
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
|
|
||||||
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
import { buildNameColumn } from '@@/datatables/buildNameColumn';
|
||||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
|
@ -53,6 +54,11 @@ export function SecretsDatatable({
|
||||||
const tableState = useTableState(store, storageKey);
|
const tableState = useTableState(store, storageKey);
|
||||||
useRepeater(tableState.autoRefreshRate, onRefresh);
|
useRepeater(tableState.autoRefreshRate, onRefresh);
|
||||||
|
|
||||||
|
const hasWriteAccessQuery = useAuthorizations([
|
||||||
|
'DockerSecretCreate',
|
||||||
|
'DockerSecretDelete',
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Datatable
|
<Datatable
|
||||||
title="Secrets"
|
title="Secrets"
|
||||||
|
@ -60,11 +66,14 @@ export function SecretsDatatable({
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataset={dataset || []}
|
dataset={dataset || []}
|
||||||
isLoading={!dataset}
|
isLoading={!dataset}
|
||||||
|
disableSelect={!hasWriteAccessQuery.authorized}
|
||||||
settingsManager={tableState}
|
settingsManager={tableState}
|
||||||
emptyContentLabel="No secret available."
|
emptyContentLabel="No secret available."
|
||||||
renderTableActions={(selectedItems) => (
|
renderTableActions={(selectedItems) =>
|
||||||
<TableActions selectedItems={selectedItems} onRemove={onRemove} />
|
hasWriteAccessQuery.authorized && (
|
||||||
)}
|
<TableActions selectedItems={selectedItems} onRemove={onRemove} />
|
||||||
|
)
|
||||||
|
}
|
||||||
renderTableSettings={() => (
|
renderTableSettings={() => (
|
||||||
<TableSettingsMenu>
|
<TableSettingsMenu>
|
||||||
<TableSettingsMenuAutoRefresh
|
<TableSettingsMenuAutoRefresh
|
||||||
|
@ -86,26 +95,30 @@ function TableActions({
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Authorized authorizations="DockerSecretDelete">
|
||||||
color="dangerlight"
|
<Button
|
||||||
disabled={selectedItems.length === 0}
|
color="dangerlight"
|
||||||
onClick={() => onRemove(selectedItems)}
|
disabled={selectedItems.length === 0}
|
||||||
icon={Trash2}
|
onClick={() => onRemove(selectedItems)}
|
||||||
className="!m-0"
|
icon={Trash2}
|
||||||
data-cy="secret-removeSecretButton"
|
className="!m-0"
|
||||||
>
|
data-cy="secret-removeSecretButton"
|
||||||
Remove
|
>
|
||||||
</Button>
|
Remove
|
||||||
|
</Button>
|
||||||
|
</Authorized>
|
||||||
|
|
||||||
<Button
|
<Authorized authorizations="DockerSecretCreate">
|
||||||
as={Link}
|
<Button
|
||||||
props={{ to: '.new' }}
|
as={Link}
|
||||||
icon={Plus}
|
props={{ to: '.new' }}
|
||||||
className="!m-0"
|
icon={Plus}
|
||||||
data-cy="secret-addSecretButton"
|
className="!m-0"
|
||||||
>
|
data-cy="secret-addSecretButton"
|
||||||
Add secret
|
>
|
||||||
</Button>
|
Add secret
|
||||||
|
</Button>
|
||||||
|
</Authorized>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,6 +88,14 @@ export function useIsEdgeAdmin({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user has some of the authorizations
|
||||||
|
*
|
||||||
|
* @param authorizations a list of authorizations to check
|
||||||
|
* @param forceEnvironmentId to force the environment id, used where the environment id can't be loaded from the router, like sidebar
|
||||||
|
* @param adminOnlyCE if true, will return false if the user is not an admin in CE
|
||||||
|
* @returns query result with isLoading and authorized - authorized is true if the user has some of the authorizations
|
||||||
|
*/
|
||||||
export function useAuthorizations(
|
export function useAuthorizations(
|
||||||
authorizations: string | string[],
|
authorizations: string | string[],
|
||||||
forceEnvironmentId?: EnvironmentId,
|
forceEnvironmentId?: EnvironmentId,
|
||||||
|
@ -137,7 +145,7 @@ export function useIsEnvironmentAdmin({
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* will return true if the user has the authorizations. assumes the user is authenticated and not an admin
|
* will return true if the user has some of the authorizations. assumes the user is authenticated and not an admin
|
||||||
*
|
*
|
||||||
* @private Please use `useAuthorizations` instead. Exported only for angular's authentication service app/portainer/services/authentication.js:154
|
* @private Please use `useAuthorizations` instead. Exported only for angular's authentication service app/portainer/services/authentication.js:154
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue