Skip to content
Snippets Groups Projects
Unverified Commit 0492a88b authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Connection Impersonation UI for Redshift (#38530)


* Connection Impersonation for Redshift

* redshift test based on the existing postgres one

* fix test

* fix test

* change impersonation UI for redshift

* copy/style updates

* update tests for new copy

* update copy

* lets tweak it even more

---------

Co-authored-by: default avatarNoah Moss <noahbmoss@gmail.com>
parent cc28ee60
No related branches found
No related tags found
No related merge requests found
......@@ -115,6 +115,21 @@ describe("impersonation modal", () => {
).toHaveAttribute("href", "/admin/databases/1");
});
it("should refer to 'users' instead of 'roles' for redshift impersonation", async () => {
await setup({ databaseDetails: { engine: "redshift" } });
expect(
await screen.findByText(
"Map a Metabase user attribute to database users",
),
).toBeInTheDocument();
expect(
await screen.findByText(
"When the person runs a query (including native queries), Metabase will impersonate the privileges of the database user you associate with the user attribute.",
),
).toBeInTheDocument();
});
it("should not update impersonation if it has not changed", async () => {
const store = await setup({ userAttributes: ["foo"] });
......
......@@ -9,4 +9,5 @@ export const ImpersonationModalViewRoot = styled.div`
export const ImpersonationDescription = styled.p`
line-height: 1.4rem;
margin: 0.5rem 0;
`;
......@@ -70,18 +70,28 @@ export const ImpersonationModalView = ({
database.features.includes("connection-impersonation-requires-role") &&
database.details["role"] == null;
// for redshift, we impersonate using users, not roles
const impersonationUsesUsers = database.engine === "redshift";
const modalTitle = impersonationUsesUsers
? t`Map a Metabase user attribute to database users`
: t`Map a user attribute to database roles`;
const modalMessage = impersonationUsesUsers
? t`When the person runs a query (including native queries), Metabase will impersonate the privileges of the database user you associate with the user attribute.`
: 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.`;
return (
<ImpersonationModalViewRoot>
<h2>{t`Map a user attribute to database roles`}</h2>
<h2>{modalTitle}</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.`}{" "}
{modalMessage}{" "}
<ExternalLink
className="link"
// eslint-disable-next-line no-unconditional-metabase-links-render -- Admin settings
href={MetabaseSettings.docsUrl("permissions/data")}
>{t`Learn More`}</ExternalLink>
</ImpersonationDescription>
{roleRequired ? (
<>
<Alert icon="warning" variant="warning">
......
import { t } from "ttag";
import { t, jt } from "ttag";
import { BoldCode } from "metabase/components/Code";
import * as Urls from "metabase/lib/urls";
import Link from "metabase/core/components/Link";
import { isEmpty } from "metabase/lib/validate";
......@@ -14,14 +14,47 @@ export const ImpersonationWarning = ({
database,
}: ImpersonationWarningProps) => {
const databaseUser = database.details.user;
const isRedshift = database.engine === "redshift";
const emptyText = t`Make sure the main database credential has access to everything different user groups may need access to. It's what Metabase uses to sync table information.`;
const redshiftWarning = jt`You’re connecting Metabase to the ${(
<BoldCode key="2" size={13}>
{database.name}
</BoldCode>
)} database using the credentials for the Redshift user ${(
<BoldCode key="3" size={13}>
{databaseUser}
</BoldCode>
)}. For impersonation to work, ${(
<BoldCode key="3" size={13}>
{databaseUser}
</BoldCode>
)} must be a superuser in Redshift.`;
const regularWarning = jt`${(
<BoldCode key="1" size={13}>
{databaseUser}
</BoldCode>
)} is the database user Metabase is using to connect to your ${(
<BoldCode key="2" size={13}>
{database.name}
</BoldCode>
)} database. Make sure that ${(
<BoldCode key="3" size={13}>
{databaseUser}
</BoldCode>
)} has access to everything in ${(
<BoldCode key="4" size={13}>
{database.name}
</BoldCode>
)} that all Metabase groups may need access to, as that database user account is what Metabase uses to sync table information.`;
const text = isEmpty(databaseUser)
? t`Make sure the main database credential has access to everything different user groups may need access to. It's what Metabase uses to sync table information.`
: t`${databaseUser} is the database user Metabase is using to connect to ${database.name}. Make sure that ${database.details.user} has access to everything in ${database.name} that all Metabase groups may need access to, as that database user account is what Metabase uses to sync table information.`;
const warningText = isRedshift ? redshiftWarning : regularWarning;
return (
<ImpersonationAlert icon="warning" variant="warning">
{text}{" "}
{isEmpty(databaseUser) ? emptyText : warningText}{" "}
<Link
className="link"
to={Urls.editDatabase(database.id) + (databaseUser ? "#user" : "")}
......
......@@ -49,7 +49,7 @@ describe("ImpersonationWarning", () => {
expect(
screen.getByText(
"metabase_user is the database user Metabase is using to connect to My Database. Make sure that metabase_user has access to everything in My Database that all Metabase groups may need access to, as that database user account is what Metabase uses to sync table information.",
/that all Metabase groups may need access to, as that database user account is what Metabase uses to sync table information./i,
),
).toBeInTheDocument();
......
/* eslint "react/prop-types": "warn" */
import { Fragment } from "react";
import PropTypes from "prop-types";
import { Text } from "metabase/ui";
export const BoldCode = ({ children, ...props }) => (
<Text fw="bold" color="brand" component="span" {...props}>
<code>{children}</code>
</Text>
);
const Code = ({ children, block }) => {
if (block) {
......@@ -23,6 +30,10 @@ const Code = ({ children, block }) => {
}
};
BoldCode.propTypes = {
children: PropTypes.any.isRequired,
};
Code.propTypes = {
children: PropTypes.any.isRequired,
block: PropTypes.bool,
......
export { default } from "./Code";
export * from "./Code";
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment