-
Aleksandr Lesnenko authoredAleksandr Lesnenko authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ImpersonationModalView.tsx 4.05 KiB
import { useMemo } from "react";
import { t } from "ttag";
import * as Yup from "yup";
import FormSelect from "metabase/core/components/FormSelect";
import FormProvider from "metabase/core/components/FormProvider";
import FormSubmitButton from "metabase/core/components/FormSubmitButton";
import FormErrorMessage from "metabase/core/components/FormErrorMessage";
import Form from "metabase/core/components/Form";
import MetabaseSettings from "metabase/lib/settings";
import * as Errors from "metabase/core/utils/errors";
import { UserAttribute } from "metabase-types/api";
import Alert from "metabase/core/components/Alert";
import FormFooter from "metabase/core/components/FormFooter";
import Button from "metabase/core/components/Button";
import ExternalLink from "metabase/core/components/ExternalLink/ExternalLink";
import Link from "metabase/core/components/Link/Link";
import Database from "metabase-lib/metadata/Database";
import { ImpersonationWarning } from "../ImpersonationWarning";
import {
ImpersonationDescription,
ImpersonationModalViewRoot,
} from "./ImpersonationModalView.styled";
const ROLE_ATTRIBUTION_MAPPING_SCHEMA = Yup.object({
attribute: Yup.string().required(Errors.required).default(""),
});
type ImpersonationModalViewProps = {
attributes: UserAttribute[];
selectedAttribute?: UserAttribute;
database: Database;
onSave: (attribute: UserAttribute) => void;
onCancel: () => void;
};
export const ImpersonationModalView = ({
attributes,
database,
selectedAttribute,
onSave,
onCancel,
}: ImpersonationModalViewProps) => {
const initialValues = {
attribute:
selectedAttribute ??
(attributes.length === 1 ? attributes[0] : undefined),
};
const attributeOptions = useMemo(() => {
const selectableAttributes =
selectedAttribute && !attributes.includes(selectedAttribute)
? [selectedAttribute, ...attributes]
: attributes;
return selectableAttributes.map(attribute => ({
name: attribute,
value: attribute,
}));
}, [attributes, selectedAttribute]);
const hasAttributes = attributeOptions.length > 0;
const handleSubmit = ({ attribute }: { attribute?: UserAttribute }) => {
if (attribute != null) {
onSave(attribute);
}
};
return (
<ImpersonationModalViewRoot>
<h2>{t`Map a user attribute to database roles`}</h2>
<ImpersonationDescription>
{t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database role you associate with the user attribute.`}{" "}
<ExternalLink
className="link"
href={MetabaseSettings.docsUrl("permissions/data")}
>{t`Learn More`}</ExternalLink>
</ImpersonationDescription>
{hasAttributes ? (
<FormProvider
initialValues={initialValues}
validationSchema={ROLE_ATTRIBUTION_MAPPING_SCHEMA}
enableReinitialize
onSubmit={handleSubmit}
>
{({ isValid }) => (
<Form>
<FormSelect
name="attribute"
placeholder={t`Pick a user attribute`}
title={t`User attribute`}
options={attributeOptions}
/>
<ImpersonationWarning database={database} />
<FormFooter>
<FormErrorMessage inline />
<Button type="button" onClick={onCancel}>{t`Cancel`}</Button>
<FormSubmitButton title={t`Save`} disabled={!isValid} primary />
</FormFooter>
</Form>
)}
</FormProvider>
) : (
<>
<Alert icon="warning" variant="warning">
{t`To associate a user with a database role, you'll need to give that user at least one user attribute.`}{" "}
<Link
className="link"
to="/admin/people"
>{t`Edit user settings`}</Link>
</Alert>
<FormFooter>
<Button type="button" onClick={onCancel}>{t`Close`}</Button>
</FormFooter>
</>
)}
</ImpersonationModalViewRoot>
);
};