diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModal.unit.spec.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModal.unit.spec.tsx index 388553ff92fa7fd15db9eb615c3fbfb6020a7e42..e3e4c718766bb4fc940937535b688e561e586c91 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModal.unit.spec.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModal.unit.spec.tsx @@ -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"] }); diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.styled.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.styled.tsx index b09cf5eb83f53b4a07fcb2a5fad1adf62b5601e8..68674d7e2a4a5443bdd3460b1562ab72cab4b52b 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.styled.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.styled.tsx @@ -9,4 +9,5 @@ export const ImpersonationModalViewRoot = styled.div` export const ImpersonationDescription = styled.p` line-height: 1.4rem; + margin: 0.5rem 0; `; diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx index 3091ecec44aa3ce567a3274aacbe9b640d0a528b..b4ab3900eb8efb364232d86b31c1f63b0c97290b 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationModal/ImpersonationModalView.tsx @@ -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"> diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx index 3c10555c593645fdeaf7e2e678afaa98911c7c73..93c4ffc375f3ee359987f28b89b91a2aa406b165 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.tsx @@ -1,5 +1,5 @@ -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" : "")} diff --git a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.unit.spec.tsx b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.unit.spec.tsx index b758f15f1a16c232f41981a53f4afa7310d732b7..009cd65c8f004f3304894aa0cd250afcfc9abff8 100644 --- a/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.unit.spec.tsx +++ b/enterprise/frontend/src/metabase-enterprise/advanced_permissions/components/ImpersonationWarning/ImpersonationWarning.unit.spec.tsx @@ -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(); diff --git a/frontend/src/metabase/components/Code/Code.jsx b/frontend/src/metabase/components/Code/Code.jsx index 67ecd95bfad1f7d584dd3a4d87b3876204809777..d89a617efafcaeb173e7059791b3107c46df48d8 100644 --- a/frontend/src/metabase/components/Code/Code.jsx +++ b/frontend/src/metabase/components/Code/Code.jsx @@ -1,6 +1,13 @@ /* 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, diff --git a/frontend/src/metabase/components/Code/index.jsx b/frontend/src/metabase/components/Code/index.jsx index 27aa31e493c3f7095519aa08948631f2fb469572..ad68829b87459052b4e6563dd6258c36f6cf8edb 100644 --- a/frontend/src/metabase/components/Code/index.jsx +++ b/frontend/src/metabase/components/Code/index.jsx @@ -1 +1,2 @@ export { default } from "./Code"; +export * from "./Code";