@openshift-console/dynamic-plugin-sdk
Version:
Provides core APIs, types and utilities used by dynamic plugins at runtime.
103 lines (102 loc) • 4.96 kB
JavaScript
import * as React from 'react';
import * as _ from 'lodash';
import { ProjectModel, SelfSubjectAccessReviewModel } from '../../../models';
import { k8sCreate } from '../../../utils/k8s/k8s-resource';
import { getImpersonate } from '../../core/reducers/coreSelectors';
import storeHandler from '../../storeHandler';
import { useSafetyFirst } from '../safety-first';
/**
* It provides impersonation key based on data from the redux store.
*/
const getImpersonateKey = (impersonate) => {
const newImpersonate = impersonate || getImpersonate(storeHandler.getStore().getState());
return newImpersonate ? `${newImpersonate.kind}~${newImpersonate.name}` : '';
};
/**
* Memoizes the result so it is possible to only make the request once for each access review.
* This does mean that the user will have to refresh the page to see updates.
* Function takes in the destructured resource attributes so that the cache keys are stable.
* `JSON.stringify` is not guaranteed to give the same result for equivalent objects.
* Impersonate headers are added automatically by `k8sCreate`.
* @param group resource group.
* @param resource resource string.
* @param subresource subresource string.
* @param verb K8s verb.
* @param namespace namespace.
* @param impersonateKey parameter to include in the cache key even though it's not used in the function body.
* @returns Memoized result of the access review.
*/
const checkAccessInternal = _.memoize((group, resource, subresource, verb, name, namespace,
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
impersonateKey) => {
// Projects are a special case. `namespace` must be set to the project name
// even though it's a cluster-scoped resource.
const reviewNamespace = group === ProjectModel.apiGroup && resource === ProjectModel.plural ? name : namespace;
const ssar = {
apiVersion: 'authorization.k8s.io/v1',
kind: 'SelfSubjectAccessReview',
spec: {
resourceAttributes: {
group,
resource,
subresource,
verb,
name,
namespace: reviewNamespace,
},
},
};
return k8sCreate(SelfSubjectAccessReviewModel, ssar);
}, (...args) => args.join('~'));
/**
* Provides information about user access to a given resource.
* @param resourceAttributes resource attributes for access review
* @param impersonate impersonation details
* @returns Object with resource access information.
*/
export const checkAccess = (resourceAttributes, impersonate) => {
// Destructure the attributes with defaults so we can create a stable cache key.
const { group = '', resource = '', subresource = '', verb = '', name = '', namespace = '', } = resourceAttributes || {};
return checkAccessInternal(group, resource, subresource, verb, name, namespace, getImpersonateKey(impersonate));
};
/**
* Hook that provides information about user access to a given resource.
* @param resourceAttributes resource attributes for access review
* @param impersonate impersonation details
* @returns Array with `isAllowed` and `loading` values.
*/
export const useAccessReview = (resourceAttributes, impersonate) => {
const [loading, setLoading] = useSafetyFirst(true);
const [isAllowed, setAllowed] = useSafetyFirst(false);
// Destructure the attributes to pass them as dependencies to `useEffect`,
// which doesn't do deep comparison of object dependencies.
const { group = '', resource = '', subresource = '', verb = '', name = '', namespace = '', } = resourceAttributes;
const impersonateKey = getImpersonateKey(impersonate);
React.useEffect(() => {
checkAccessInternal(group, resource, subresource, verb, name, namespace, impersonateKey)
.then((result) => {
setAllowed(result.status.allowed);
setLoading(false);
})
.catch((e) => {
// eslint-disable-next-line no-console
console.warn('SelfSubjectAccessReview failed', e);
// Default to enabling the action if the access review fails so that we
// don't incorrectly block users from actions they can perform. The server
// still enforces access control.
setAllowed(true);
setLoading(false);
});
}, [setLoading, setAllowed, group, resource, subresource, verb, name, namespace, impersonateKey]);
return [isAllowed, loading];
};
/**
* @deprecated - Use useAccessReview from \@console/dynamic-plugin-sdk instead.
* Hook that provides allowed status about user access to a given resource.
* @param resourceAttributes resource attributes for access review
* @param impersonate impersonation details
* @returns The isAllowed boolean value.
*/
export const useAccessReviewAllowed = (resourceAttributes, impersonate) => {
return useAccessReview(resourceAttributes, impersonate)[0];
};