mirror of https://github.com/portainer/portainer
fix(rbac): redirect on unauthorized namespace [r8s-564] (#1246)
Merging because this PR doesn't introduce any CI failures, compared to the release 2.33 CI run https://github.com/portainer/portainer-suite/actions/runs/17957775674release/2.33
parent
732337615e
commit
c21c91632f
|
@ -473,7 +473,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
|
|||
},
|
||||
};
|
||||
|
||||
const resourcePools = {
|
||||
const namespaces = {
|
||||
name: 'kubernetes.resourcePools',
|
||||
url: '/namespaces',
|
||||
views: {
|
||||
|
@ -499,7 +499,7 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
|
|||
},
|
||||
};
|
||||
|
||||
const resourcePool = {
|
||||
const namespace = {
|
||||
name: 'kubernetes.resourcePools.resourcePool',
|
||||
url: '/:id?tab',
|
||||
views: {
|
||||
|
@ -681,9 +681,9 @@ angular.module('portainer.kubernetes', ['portainer.app', registriesModule, custo
|
|||
$stateRegistryProvider.register(node);
|
||||
$stateRegistryProvider.register(nodeStats);
|
||||
$stateRegistryProvider.register(kubectlShell);
|
||||
$stateRegistryProvider.register(resourcePools);
|
||||
$stateRegistryProvider.register(namespaces);
|
||||
$stateRegistryProvider.register(namespaceCreation);
|
||||
$stateRegistryProvider.register(resourcePool);
|
||||
$stateRegistryProvider.register(namespace);
|
||||
$stateRegistryProvider.register(namespaceAccess);
|
||||
$stateRegistryProvider.register(volumes);
|
||||
$stateRegistryProvider.register(volume);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { AlertTriangle, Code, History, Minimize2 } from 'lucide-react';
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
import LaptopCode from '@/assets/ico/laptop-code.svg?c';
|
||||
import { useNamespaceAccessRedirect } from '@/react/kubernetes/namespaces/hooks/useNamespaceAccessRedirect';
|
||||
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { Tab, WidgetTabs, findSelectedTabIndex } from '@@/Widget/WidgetTabs';
|
||||
|
@ -30,6 +31,7 @@ export function ApplicationDetailsView() {
|
|||
const {
|
||||
params: { namespace, name },
|
||||
} = stateAndParams;
|
||||
useNamespaceAccessRedirect(namespace, { to: 'kubernetes.applications' });
|
||||
|
||||
// placements table data
|
||||
const { placementsData, isPlacementsTableLoading, hasPlacementWarning } =
|
||||
|
|
|
@ -156,6 +156,20 @@ function createCommonHandlers() {
|
|||
http.get('/api/endpoints/3/kubernetes/helm/test-release/history', () =>
|
||||
HttpResponse.json(helmReleaseHistory)
|
||||
),
|
||||
http.get('/api/kubernetes/3/namespaces', () =>
|
||||
HttpResponse.json([
|
||||
{
|
||||
Id: 'default',
|
||||
Name: 'default',
|
||||
Status: { phase: 'Active' },
|
||||
Annotations: null,
|
||||
CreationDate: '2021-01-01T00:00:00Z',
|
||||
NamespaceOwner: '',
|
||||
IsSystem: false,
|
||||
IsDefault: true,
|
||||
},
|
||||
])
|
||||
),
|
||||
http.get('/api/kubernetes/3/namespaces/default/events', () =>
|
||||
HttpResponse.json([])
|
||||
),
|
||||
|
|
|
@ -5,6 +5,7 @@ import helm from '@/assets/ico/vendor/helm.svg?c';
|
|||
import { PageHeader } from '@/react/components/PageHeader';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
import { useNamespaceAccessRedirect } from '@/react/kubernetes/namespaces/hooks/useNamespaceAccessRedirect';
|
||||
|
||||
import { WidgetTitle, WidgetBody, Widget, Loading } from '@@/Widget';
|
||||
import { Card } from '@@/Card';
|
||||
|
@ -26,6 +27,7 @@ export function HelmApplicationView() {
|
|||
const queryClient = useQueryClient();
|
||||
const { params } = useCurrentStateAndParams();
|
||||
const { name, namespace, revision } = params;
|
||||
useNamespaceAccessRedirect(namespace, { to: 'kubernetes.applications' });
|
||||
const helmHistoryQuery = useHelmHistory(environmentId, name, namespace);
|
||||
const latestRevision = helmHistoryQuery.data?.[0]?.version;
|
||||
const earlistRevision =
|
||||
|
|
|
@ -9,6 +9,7 @@ import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
|||
import { useAuthorizations } from '@/react/hooks/useUser';
|
||||
import { Annotation } from '@/react/kubernetes/annotations/types';
|
||||
import { prepareAnnotations } from '@/react/kubernetes/utils';
|
||||
import { useNamespaceAccessRedirect } from '@/react/kubernetes/namespaces/hooks/useNamespaceAccessRedirect';
|
||||
|
||||
import { Link } from '@@/Link';
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
|
@ -43,6 +44,7 @@ import {
|
|||
export function CreateIngressView() {
|
||||
const environmentId = useEnvironmentId();
|
||||
const { params } = useCurrentStateAndParams();
|
||||
useNamespaceAccessRedirect(params.namespace, { to: 'kubernetes.ingresses' });
|
||||
const { authorized: isAuthorizedToAddEdit } = useAuthorizations([
|
||||
'K8sIngressesW',
|
||||
]);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useCurrentStateAndParams } from '@uirouter/react';
|
|||
import { AlertTriangle, Code, Layers, History } from 'lucide-react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { useNamespaceAccessRedirect } from '@/react/kubernetes/namespaces/hooks/useNamespaceAccessRedirect';
|
||||
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { findSelectedTabIndex, Tab, WidgetTabs } from '@@/Widget/WidgetTabs';
|
||||
|
@ -20,6 +21,9 @@ export function NamespaceView() {
|
|||
const {
|
||||
params: { id: namespace },
|
||||
} = stateAndParams;
|
||||
useNamespaceAccessRedirect(namespace, {
|
||||
to: 'kubernetes.resourcePools',
|
||||
});
|
||||
|
||||
const environmentId = useEnvironmentId();
|
||||
const eventWarningCount = useEventWarningsCount(environmentId, namespace);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useCurrentStateAndParams, useRouter } from '@uirouter/react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { useNamespacesQuery } from '../queries/useNamespacesQuery';
|
||||
|
||||
type RedirectOptions = {
|
||||
to: string;
|
||||
params?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirects away when the provided namespace is not in the allowed namespaces list for the current environment.
|
||||
*/
|
||||
export function useNamespaceAccessRedirect(
|
||||
namespace?: string,
|
||||
{ to, params } = { to: 'kubernetes.dashboard', params: {} } as RedirectOptions
|
||||
) {
|
||||
const router = useRouter();
|
||||
const namespaceInParams = useCurrentStateAndParams().params.namespace;
|
||||
const currentNamespace = namespace || namespaceInParams;
|
||||
const environmentId = useEnvironmentId();
|
||||
|
||||
const namespacesQuery = useNamespacesQuery(environmentId);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentNamespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (namespacesQuery.isLoading || namespacesQuery.isFetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
const namespaces = namespacesQuery.data ?? [];
|
||||
const isAllowed = namespaces.some((ns) => ns.Name === currentNamespace);
|
||||
|
||||
if (!isAllowed) {
|
||||
router.stateService.go(to, params);
|
||||
}
|
||||
}, [
|
||||
currentNamespace,
|
||||
to,
|
||||
params,
|
||||
router.stateService,
|
||||
namespacesQuery.isLoading,
|
||||
namespacesQuery.isFetching,
|
||||
namespacesQuery.data,
|
||||
]);
|
||||
}
|
Loading…
Reference in New Issue