diff --git a/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.jsx b/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.jsx new file mode 100644 index 0000000000000000000000000000000000000000..14e248bede340d609021fa5d734d70e74115d07d --- /dev/null +++ b/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.jsx @@ -0,0 +1,66 @@ +import React from "react"; +import { t, jt } from "ttag"; + +import { color } from "metabase/lib/colors"; +import MetabaseSettings from "metabase/lib/settings"; + +import { + PermissionIcon, + DataPermissionsHelpRoot, + DataPermissionsHelpFooter, + DataPermissionsHelpContent, + DataPermissionsHelpLink, + DataPermissionsHelpLinkIcon, +} from "./DataPermissionsHelp.styled"; + +export const DataPermissionsHelp = () => ( + <DataPermissionsHelpRoot> + <DataPermissionsHelpContent> + <h2>{t`About data permissions`}</h2> + <p>{t`Each of your user groups can have a level of access for each of your databases on the tables they contain.`}</p> + <p>{jt`Users can be members of multiple groups, and are given the ${( + <strong>{t`most permissive`}</strong> + )} level of access for a database or table across all the groups they’re a member of.`}</p> + <p>{t`Unless a user group has “no access†for a given database or table, they’ll be able to view any saved questions based on that data if they have access to the collection it’s saved in.`}</p> + <h2>{t`Access levels`}</h2> + + <h3> + <PermissionIcon name="check" style={{ color: color("success") }} /> + {t`Unrestricted access`} + </h3> + <p>{t`Users can use the visual query builder to questions based on all tables in this database. A user group must have Unrestricted access for a database if you want to give them access to the SQL/native query editor.`}</p> + + <h3> + <PermissionIcon name="permissions_limited" color="warning" /> + {t`Granular access`} + </h3> + <p>{t`Restrict user access to specific tables in a database. When you select this option, you’ll be taken to the table-level view of that database to make more granular options.`}</p> + + <h3> + <PermissionIcon name="eye" color="accent5" /> + {t`No self-service access`} + </h3> + <p>{t`Prevent users create new ad hoc queries or questions based on that data, or see that data in the Browse Data screen. But users can still see existing saved questions and charts based on that data in Collections that they have access to.`}</p> + + <h3> + <PermissionIcon name="close" color="danger" /> + {t`No access`} + </h3> + <p>{t`Ensure users can’t ever see the data from a certain database regardless of their permissions at the collection level. Keep in mind that if the user is part of another group with data access, it will take precedence and their access will not be blocked.`}</p> + + <p>{t`Only available in certain Metabase plans.`}</p> + </DataPermissionsHelpContent> + + <DataPermissionsHelpFooter> + <DataPermissionsHelpLink + href={MetabaseSettings.docsUrl( + "administration-guide/05-setting-permissions", + )} + target="_blank" + > + <DataPermissionsHelpLinkIcon size={28} name="reference" /> + {t`Learn more about permissions`} + </DataPermissionsHelpLink> + </DataPermissionsHelpFooter> + </DataPermissionsHelpRoot> +); diff --git a/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.styled.jsx b/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.styled.jsx new file mode 100644 index 0000000000000000000000000000000000000000..c6fcb81d37a63e81506b59aa1187b72b375d42ef --- /dev/null +++ b/frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.styled.jsx @@ -0,0 +1,71 @@ +import styled from "styled-components"; +import Icon from "metabase/components/Icon"; +import { color, lighten } from "metabase/lib/colors"; +import ExternalLink from "metabase/components/ExternalLink"; + +export const DataPermissionsHelpRoot = styled.div` + h2 { + margin-top: 2rem; + margin-bottom: 1rem; + font-size: 18px; + line-height: 20px; + + &:first-of-type { + margin-top: 8px; + } + } + + h3 { + margin-top: 1.5rem; + font-size: 14px; + line-height: 20px; + } + + h2 + h3 { + margin-top: 1rem; + } + + p { + font-size: 13px; + line-height: 18px; + margin: 0.5rem 0; + } +`; + +export const PermissionIcon = styled(Icon).attrs({ size: 16 })` + padding-right: 0.375rem; + vertical-align: text-bottom; + color: ${props => color(props.color)}; +`; + +export const DataPermissionsHelpContent = styled.div` + padding: 1rem 2rem; +`; + +export const DataPermissionsHelpFooter = styled.footer` + padding: 2rem; + border-top: 1px solid ${color("border")}; +`; + +export const DataPermissionsHelpLink = styled(ExternalLink)` + display: flex; + align-items: center; + padding: 16px 24px; + font-size: 14px; + font-weight: 700; + line-height: 20px; + color: ${color("text-dark")}; + border: 1px solid ${color("border")}; + border-radius: 8px; + transition: all 200ms; + + &:hover { + border-color: ${color("brand")}; + background-color: ${lighten("brand", 0.6)}; + } +`; + +export const DataPermissionsHelpLinkIcon = styled(Icon)` + color: ${color("text-light")}; + margin-right: 1rem; +`; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.jsx index 7ac2a3bded1177d31b94a69eb83253435ee04ead..f75e04bb8339025445ade79444e75c3f6e8c10d7 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.jsx @@ -57,7 +57,7 @@ export function PermissionsEditor({ return ( <PermissionsEditorRoot> - <Box px="3rem" pt={2}> + <Box px="3rem"> <Subhead> {title}{" "} {breadcrumbs && ( diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.styled.js b/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.styled.js index 0ed8b50291467d9f5d2a955bf7e92b2304edaf4c..ec317cd3269f57b8d8dac6b98f51011a054d83fd 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.styled.js +++ b/frontend/src/metabase/admin/permissions/components/PermissionsEditor/PermissionsEditor.styled.js @@ -3,4 +3,5 @@ import styled from "styled-components"; export const PermissionsEditorRoot = styled.div` flex-grow: 1; overflow: auto; + padding: 1rem 0 2rem 0; `; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.jsx index c5fa092c2a8ce354f15f5d3a30ac987ccd625a76..ee2963731a42f3e325abcc3e59850ed92e7ab9f8 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import PropTypes from "prop-types"; import _ from "underscore"; import { t } from "ttag"; @@ -11,11 +11,20 @@ import Modal from "metabase/components/Modal"; import ModalContent from "metabase/components/ModalContent"; import { PermissionsTabs } from "./PermissionsTabs"; -import { FullHeightContainer } from "./PermissionsPageLayout.styled"; +import { + FullHeightContainer, + TabsContainer, + PermissionPageRoot, + HelpButton, + PermissionPageContent, + PermissionPageSidebar, + CloseSidebarButton, +} from "./PermissionsPageLayout.styled"; import { PermissionsEditBar } from "./PermissionsEditBar"; import { useLeaveConfirmation } from "../../hooks/use-leave-confirmation"; import { withRouter } from "react-router"; import { clearSaveError } from "../../permissions"; +import Icon from "metabase/components/Icon"; const mapDispatchToProps = { navigateToTab: tab => push(`/admin/permissions/${tab}`), @@ -43,6 +52,7 @@ const propTypes = { router: PropTypes.object, route: PropTypes.object, navigateToTab: PropTypes.func.isRequired, + helpContent: PropTypes.node, }; function PermissionsPageLayout({ @@ -58,7 +68,9 @@ function PermissionsPageLayout({ route, navigateToLocation, navigateToTab, + helpContent, }) { + const [shouldShowHelp, setShouldShowHelp] = useState(false); const beforeLeaveConfirmation = useLeaveConfirmation({ router, route, @@ -67,37 +79,55 @@ function PermissionsPageLayout({ }); return ( - <FullHeightContainer flexDirection="column"> - {isDirty && ( - <PermissionsEditBar - diff={diff} - isDirty={isDirty} - onSave={onSave} - onCancel={() => onLoad()} - /> - )} + <PermissionPageRoot> + <PermissionPageContent> + {isDirty && ( + <PermissionsEditBar + diff={diff} + isDirty={isDirty} + onSave={onSave} + onCancel={() => onLoad()} + /> + )} + + <Modal isOpen={saveError != null}> + <ModalContent + title={t`There was an error saving`} + formModal + onClose={clearSaveError} + > + <p className="mb4">{saveError}</p> + <div className="ml-auto"> + <Button onClick={clearSaveError}>{t`OK`}</Button> + </div> + </ModalContent> + </Modal> - <Modal isOpen={saveError != null}> - <ModalContent - title={t`There was an error saving`} - formModal - onClose={clearSaveError} - > - <p className="mb4">{saveError}</p> - <div className="ml-auto"> - <Button onClick={clearSaveError}>{t`OK`}</Button> - </div> - </ModalContent> - </Modal> + {beforeLeaveConfirmation} - {beforeLeaveConfirmation} + <TabsContainer className="border-bottom"> + <PermissionsTabs tab={tab} onChangeTab={navigateToTab} /> + {helpContent && !shouldShowHelp && ( + <HelpButton onClick={() => setShouldShowHelp(prev => !prev)}> + <Icon name="info" size={20} mr={1} /> + {t`Permission help`} + </HelpButton> + )} + </TabsContainer> - <div className="border-bottom"> - <PermissionsTabs tab={tab} onChangeTab={navigateToTab} /> - </div> + <FullHeightContainer>{children}</FullHeightContainer> + </PermissionPageContent> - <FullHeightContainer>{children}</FullHeightContainer> - </FullHeightContainer> + {shouldShowHelp && ( + <PermissionPageSidebar> + <CloseSidebarButton + size={20} + onClick={() => setShouldShowHelp(prev => !prev)} + /> + {helpContent} + </PermissionPageSidebar> + )} + </PermissionPageRoot> ); } diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.styled.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.styled.jsx index bc35ec14332619177ed2475a3a5fde78658ebc00..9f8c8ced862f799bec2b8c30d2f6c06e83ecb66b 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.styled.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsPageLayout/PermissionsPageLayout.styled.jsx @@ -1,7 +1,63 @@ +import Icon from "metabase/components/Icon"; +import { color } from "metabase/lib/colors"; import styled from "styled-components"; -import { Flex } from "grid-styled"; -export const FullHeightContainer = styled(Flex)` +export const PermissionPageRoot = styled.div` + display: flex; height: 100%; overflow: hidden; `; + +export const PermissionPageContent = styled.div` + display: flex; + flex: 1; + flex-direction: column; + height: 100%; + overflow: hidden; +`; + +export const PermissionPageSidebar = styled.aside` + position: relative; + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + border-left: 1px solid ${color("border")}; + max-width: 300px; +`; + +export const TabsContainer = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const FullHeightContainer = styled.div` + display: flex; + height: 100%; + overflow: hidden; +`; + +export const HelpButton = styled.button` + font-family: var(--default-font-family); + display: flex; + align-items: center; + cursor: pointer; + color: ${color("text-dark")}; + padding: 0.25rem 1.5rem; + font-size: 14px; + font-weight: 700; +`; + +export const CloseSidebarButton = styled(Icon).attrs({ name: "close" })` + top: 24px; + right: 24px; + color: ${color("text-light")}; + position: absolute; + cursor: pointer; + transition: color 200ms; + + &:hover { + color: ${color("text-medium")}; + } +`; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.jsx index 6f138ba76ef73872c405abf0cbdbef44946c17f0..b19d34c21bf9d5077b41aa05eeaffadc18c1aebb 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.jsx @@ -20,6 +20,7 @@ import { ToggleContainer, ToggleLabel, WarningIcon, + DisabledPermissionOption, } from "./PermissionsSelect.styled"; const propTypes = { @@ -30,6 +31,7 @@ const propTypes = { onChange: PropTypes.func.isRequired, onAction: PropTypes.func, isDisabled: PropTypes.bool, + isHighlighted: PropTypes.bool, disabledTooltip: PropTypes.string, warning: PropTypes.string, }; @@ -44,26 +46,38 @@ export const PermissionsSelect = memo(function PermissionsSelect({ isDisabled, disabledTooltip, warning, + isHighlighted, }) { const [toggleState, setToggleState] = useState(false); - const selected = options.find(option => option.value === value); - const selectableOptions = options.filter(option => option !== selected); + const selectedOption = options.find(option => option.value === value); + const selectableOptions = options.filter(option => option !== selectedOption); const shouldShowDisabledTooltip = isDisabled; - const selectedValue = ( + const selectedOptionValue = ( <Tooltip tooltip={disabledTooltip} isEnabled={shouldShowDisabledTooltip}> <PermissionsSelectRoot isDisabled={isDisabled} aria-haspopup="listbox" data-testid="permissions-select" > - <PermissionsSelectOption {...selected} /> + {isDisabled ? ( + <DisabledPermissionOption + {...selectedOption} + isHighlighted={isHighlighted} + iconColor="text-light" + /> + ) : ( + <PermissionsSelectOption {...selectedOption} /> + )} + {warning && ( <Tooltip tooltip={warning}> <WarningIcon /> </Tooltip> )} + <Icon + style={{ visibility: isDisabled ? "hidden" : "visible" }} name="chevrondown" size={16} color={lighten("text-light", 0.15)} @@ -72,13 +86,13 @@ export const PermissionsSelect = memo(function PermissionsSelect({ </Tooltip> ); - const actionsForCurrentValue = actions?.[selected.value] || []; + const actionsForCurrentValue = actions?.[selectedOption?.value] || []; const hasActions = actionsForCurrentValue.length > 0; return ( <PopoverWithTrigger disabled={isDisabled} - triggerElement={selectedValue} + triggerElement={selectedOptionValue} targetOffsetX={16} targetOffsetY={8} > diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.styled.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.styled.jsx index 3cdcd906e2c72f26092a7dd4a2b1159bf3174070..c196e6de434b2df2c558d2da3e09d2d8b9c9155d 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.styled.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsSelect/PermissionsSelect.styled.jsx @@ -2,13 +2,13 @@ import styled from "styled-components"; import Label from "metabase/components/type/Label"; import { color, lighten } from "metabase/lib/colors"; import Icon from "metabase/components/Icon"; +import { PermissionsSelectOption } from "./PermissionsSelectOption"; export const PermissionsSelectRoot = styled.div` display: flex; align-items: center; width: 180px; cursor: ${props => (props.isDisabled ? "default" : "pointer")}; - opacity: ${props => (props.isDisabled ? "0.6" : "1")}; `; export const PermissionsSelectText = styled(Label)` @@ -54,3 +54,8 @@ export const WarningIcon = styled(Icon).attrs({ margin-right: 0.25rem; color: ${color("text-light")}; `; + +export const DisabledPermissionOption = styled(PermissionsSelectOption)` + color: ${props => + props.isHighlighted ? color("text-medium") : color("text-light")}; +`; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsSidebar/PermissionsSidebar.styled.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsSidebar/PermissionsSidebar.styled.jsx index fcc22f8a039fa7d9c7c8b8d37a838bdfef2720eb..405988476a9b684ccbc73847492545d1b720c8d0 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsSidebar/PermissionsSidebar.styled.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsSidebar/PermissionsSidebar.styled.jsx @@ -5,6 +5,7 @@ import Icon from "metabase/components/Icon"; export const SidebarRoot = styled.aside` display: flex; flex-direction: column; + flex-shrink: 0; overflow: hidden; width: 300px; border-right: 1px solid ${color("border")}; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.jsx index 56789518b85505103a43af66c1bde71793f838d1..190ef535620cafc16adcdec885d6093253aca394 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.jsx @@ -2,7 +2,6 @@ import React, { useState, useRef } from "react"; import PropTypes from "prop-types"; import Label from "metabase/components/type/Label"; -import Icon from "metabase/components/Icon"; import Tooltip from "metabase/components/Tooltip"; import Modal from "metabase/components/Modal"; import ConfirmContent from "metabase/components/ConfirmContent"; @@ -15,6 +14,7 @@ import { EntityNameCell, EntityNameLink, EntityName, + HintIcon, } from "./PermissionsTable.styled"; const propTypes = { @@ -77,21 +77,18 @@ export function PermissionsTable({ <thead> <tr> {columns.map((column, index) => { - const isFirst = index === 0; - const isLast = index === columns.length - 1; - - const width = isFirst ? "340px" : isLast ? null : "200px"; - return ( <PermissionsTableCell key={column} - style={{ width }} horizontalPadding={horizontalPadding} > <Label>{column}</Label> </PermissionsTableCell> ); })} + <PermissionsTableCell + style={{ width: "100%", minWidth: "unset" }} + /> </tr> </thead> <tbody> @@ -108,11 +105,8 @@ export function PermissionsTable({ )} {entity.hint && ( - <Tooltip tooltip="text"> - <Icon - style={{ marginLeft: "0.25rem", cursor: "pointer" }} - name="question" - /> + <Tooltip tooltip={entity.hint}> + <HintIcon /> </Tooltip> )} </EntityNameCell> diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.styled.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.styled.jsx index 5cd1b3ad4539cdb6fc14a4cc886575526ad123f2..739e25cd9eaef42c145e82ec621b414d941b380b 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.styled.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsTable/PermissionsTable.styled.jsx @@ -1,6 +1,7 @@ import styled from "styled-components"; -import { color, alpha } from "metabase/lib/colors"; +import { color, alpha, lighten } from "metabase/lib/colors"; import Link from "metabase/components/Link"; +import Icon from "metabase/components/Icon"; const HORIZONTAL_PADDING_VARIANTS = { sm: "0.5rem", @@ -17,9 +18,12 @@ export const PermissionsTableRow = styled.tr` `; export const PermissionsTableCell = styled.td` - padding: 0.5rem 0.5rem; + padding: 0.5rem 1rem; + width: auto; + min-width: 220px; &:first-of-type { + max-width: 340px; padding: 0.5rem ${props => HORIZONTAL_PADDING_VARIANTS[props.horizontalPadding]}; } @@ -42,3 +46,9 @@ export const EntityNameLink = styled(Link)` `; export const PermissionTableHeaderRow = styled.tr``; + +export const HintIcon = styled(Icon).attrs({ name: "info", size: 12 })` + color: ${lighten("text-dark", 0.3)}; + margin-left: 0.375rem; + cursor: pointer; +`; diff --git a/frontend/src/metabase/admin/permissions/constants/data-permissions.js b/frontend/src/metabase/admin/permissions/constants/data-permissions.js index 40efeadf883834c55dc3b030817e319a2ecfef54..965876b68f6433c6e86084ef6fe228e927ccb5a7 100644 --- a/frontend/src/metabase/admin/permissions/constants/data-permissions.js +++ b/frontend/src/metabase/admin/permissions/constants/data-permissions.js @@ -2,25 +2,37 @@ import { t } from "ttag"; export const DATA_PERMISSION_OPTIONS = { all: { - label: t`Allowed`, + label: t`Unrestricted`, value: "all", icon: "check", iconColor: "success", }, controlled: { - label: t`Limited`, + label: t`Granular`, value: "controlled", icon: "permissions_limited", iconColor: "warning", }, + noSelfService: { + label: t`No self-service`, + value: "none", + icon: "eye", + iconColor: "accent5", + }, + block: { + label: t`Block`, + value: "block", + icon: "close", + iconColor: "danger", + }, none: { - label: t`No access`, + label: t`No`, value: "none", icon: "close", iconColor: "danger", }, write: { - label: t`Allowed`, + label: t`Yes`, value: "write", icon: "check", iconColor: "success", diff --git a/frontend/src/metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage.jsx b/frontend/src/metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage.jsx index d08b76958e449c88a95a76eae9de547f6eb1f779..a2bafb7e10da1a91ecd2df5b78791fbe5b40e093 100644 --- a/frontend/src/metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage.jsx +++ b/frontend/src/metabase/admin/permissions/pages/DataPermissionsPage/DataPermissionsPage.jsx @@ -13,6 +13,7 @@ import { initializeDataPermissions, } from "../../permissions"; import PermissionsPageLayout from "../../components/PermissionsPageLayout/PermissionsPageLayout"; +import { DataPermissionsHelp } from "../../components/DataPermissionsHelp/DataPermissionsHelp"; const mapDispatchToProps = { loadPermissions: loadDataPermissions, @@ -56,6 +57,7 @@ function DataPermissionsPage({ diff={diff} isDirty={isDirty} route={route} + helpContent={<DataPermissionsHelp />} > {children} </PermissionsPageLayout> diff --git a/frontend/src/metabase/admin/permissions/selectors/confirmations.js b/frontend/src/metabase/admin/permissions/selectors/confirmations.js index 2d147b8034b22c123c0d4fbf38554518f2e7cbb5..7814f24f26b9651c6a17b203029f72e7c2e2382c 100644 --- a/frontend/src/metabase/admin/permissions/selectors/confirmations.js +++ b/frontend/src/metabase/admin/permissions/selectors/confirmations.js @@ -8,7 +8,7 @@ import { } from "metabase/lib/permissions"; // these are all the permission levels ordered by level of access -const PERM_LEVELS = ["write", "read", "all", "controlled", "none"]; +const PERM_LEVELS = ["write", "read", "all", "controlled", "none", "block"]; function hasGreaterPermissions(a, b) { return PERM_LEVELS.indexOf(a) - PERM_LEVELS.indexOf(b) < 0; } diff --git a/frontend/src/metabase/admin/permissions/selectors/data-permissions.js b/frontend/src/metabase/admin/permissions/selectors/data-permissions.js index 67f72e5a7173e5dec4b448cf408210e521331532..3bfca57aeea95845edfd1e8d554d229bab81391e 100644 --- a/frontend/src/metabase/admin/permissions/selectors/data-permissions.js +++ b/frontend/src/metabase/admin/permissions/selectors/data-permissions.js @@ -45,6 +45,11 @@ import { getGroupFocusPermissionsUrl, } from "../utils/urls"; +const getGroupsWithoutMetabot = createSelector( + Group.selectors.getList, + groups => groups.filter(group => !isMetaBotGroup(group)), +); + export const getIsDirty = createSelector( state => state.admin.permissions.dataPermissions, state => state.admin.permissions.originalDataPermissions, @@ -54,7 +59,7 @@ export const getIsDirty = createSelector( export const getDiff = createSelector( getMetadata, - Group.selectors.getList, + getGroupsWithoutMetabot, state => state.admin.permissions.dataPermissions, state => state.admin.permissions.originalDataPermissions, (metadata, groups, permissions, originalPermissions) => @@ -191,13 +196,22 @@ const getGroupsDataEditorBreadcrumbs = (params, metadata) => { ); }; -const getGroupsWithoutMetabot = createSelector( - Group.selectors.getList, - groups => groups.filter(group => !isMetaBotGroup(group)), -); - const getDataPermissions = state => state.admin.permissions.dataPermissions; +const NATIVE_QUERIES_OPTIONS = [ + DATA_PERMISSION_OPTIONS.write, + DATA_PERMISSION_OPTIONS.none, +]; + +const getTableAndFieldAccessOptions = value => + value === DATA_PERMISSION_OPTIONS.block.value + ? [DATA_PERMISSION_OPTIONS.block] + : [ + DATA_PERMISSION_OPTIONS.all, + ...PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_OPTIONS, + DATA_PERMISSION_OPTIONS.noSelfService, + ]; + const buildFieldsPermissions = ( entityId, groupId, @@ -242,19 +256,26 @@ const buildFieldsPermissions = ( return [ { name: "access", - isDisabled: isAdmin, + isDisabled: isAdmin || value === DATA_PERMISSION_OPTIONS.block.value, disabledTooltip: isAdmin ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS : null, + isHighlighted: isAdmin, value, warning, - options: [ - DATA_PERMISSION_OPTIONS.all, - ...PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_OPTIONS, - DATA_PERMISSION_OPTIONS.none, - ], + options: getTableAndFieldAccessOptions(value), actions: PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_ACTIONS, postActions: PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_POST_ACTION, confirmations, }, + { + name: "native", + isDisabled: true, + disabledTooltip: isAdmin + ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS + : DATA_ACCESS_IS_REQUIRED, + isHighlighted: isAdmin, + value: getNativePermission(permissions, groupId, entityId), + options: NATIVE_QUERIES_OPTIONS, + }, ]; }; @@ -295,6 +316,7 @@ const buildTablesPermissions = ( { name: "access", isDisabled: isAdmin, + isHighlighted: isAdmin, disabledTooltip: isAdmin ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS : null, value, warning, @@ -305,11 +327,17 @@ const buildTablesPermissions = ( `/admin/permissions/data/group/${groupId}/database/${entityId.databaseId}/schema/${entityId.schemaName}`, ), }, - options: [ - DATA_PERMISSION_OPTIONS.all, - DATA_PERMISSION_OPTIONS.controlled, - DATA_PERMISSION_OPTIONS.none, - ], + options: getTableAndFieldAccessOptions(value), + }, + { + name: "native", + isDisabled: true, + disabledTooltip: isAdmin + ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS + : DATA_ACCESS_IS_REQUIRED, + isHighlighted: isAdmin, + value: getNativePermission(permissions, groupId, entityId), + options: NATIVE_QUERIES_OPTIONS, }, ]; }; @@ -379,18 +407,25 @@ const buildDatabasePermissions = ( getRawQueryWarningModal(permissions, groupId, entityId, newValue), ]; + const isNativePermissionDisabled = + isAdmin || + accessPermissionValue === DATA_PERMISSION_OPTIONS.none.value || + accessPermissionValue === DATA_PERMISSION_OPTIONS.none.block; + return [ { name: "access", isDisabled: isAdmin, disabledTooltip: isAdmin ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS : null, + isHighlighted: isAdmin, value: accessPermissionValue, warning: accessPermissionWarning, confirmations: accessPermissionConfirmations, options: [ DATA_PERMISSION_OPTIONS.all, DATA_PERMISSION_OPTIONS.controlled, - DATA_PERMISSION_OPTIONS.none, + DATA_PERMISSION_OPTIONS.noSelfService, + DATA_PERMISSION_OPTIONS.block, ], postActions: { controlled: () => @@ -401,14 +436,15 @@ const buildDatabasePermissions = ( }, { name: "native", - isDisabled: isAdmin || accessPermissionValue === "none", + isDisabled: isNativePermissionDisabled, disabledTooltip: isAdmin ? UNABLE_TO_CHANGE_ADMIN_PERMISSIONS : DATA_ACCESS_IS_REQUIRED, + isHighlighted: isAdmin, value: nativePermissionValue, warning: nativePermissionWarning, confirmations: nativePermissionConfirmations, - options: [DATA_PERMISSION_OPTIONS.write, DATA_PERMISSION_OPTIONS.none], + options: NATIVE_QUERIES_OPTIONS, }, ]; }; @@ -427,12 +463,7 @@ export const getGroupsDataPermissionEditor = createSelector( const defaultGroup = _.find(groups, isDefaultGroup); - const isDatabaseLevelPermission = tableId == null && schemaName == null; - const columns = [ - t`Group name`, - t`Data access`, - isDatabaseLevelPermission ? t`Native query editing` : null, - ].filter(Boolean); + const columns = [t`Group name`, t`Data access`, t`Native query editing`]; const entities = groups.map(group => { const isAdmin = isAdminGroup(group); @@ -478,6 +509,9 @@ export const getGroupsDataPermissionEditor = createSelector( return { id: group.id, name: group.name, + hint: isAdmin + ? t`The Administrators group is special, and always has Unrestricted access.` + : null, entityId: params, permissions: groupPermissions, }; @@ -506,7 +540,7 @@ const isPinnedGroup = group => isAdminGroup(group) || isDefaultGroup(group) || isMetaBotGroup(group); export const getGroupsSidebar = createSelector( - Group.selectors.getList, + getGroupsWithoutMetabot, getGroupRouteParams, (groups, params) => { const { groupId } = params; @@ -576,12 +610,11 @@ export const getDatabasesPermissionEditor = createSelector( databaseId != null && metadata.database(databaseId).getSchemas().length === 1; - const isDatabaseLevelPermission = schemaName == null && databaseId == null; const columns = [ getEditorEntityName(params, hasSingleSchema), t`Data access`, - isDatabaseLevelPermission ? t`Native query editing` : null, - ].filter(Boolean); + t`Native query editing`, + ]; let entities = []; diff --git a/frontend/src/metabase/admin/permissions/selectors/data-permissions.unit.spec.js b/frontend/src/metabase/admin/permissions/selectors/data-permissions.unit.spec.js index ae17f3cd6ab726838820e05468728c2187e4ea04..beeb698cf7bb21f3cb78212b4068694f82cd2fca 100644 --- a/frontend/src/metabase/admin/permissions/selectors/data-permissions.unit.spec.js +++ b/frontend/src/metabase/admin/permissions/selectors/data-permissions.unit.spec.js @@ -291,20 +291,26 @@ describe("getGroupsDataPermissionEditor", () => { { icon: "check", iconColor: "success", - label: "Allowed", + label: "Unrestricted", value: "all", }, { icon: "permissions_limited", iconColor: "warning", - label: "Limited", + label: "Granular", value: "controlled", }, + { + icon: "eye", + iconColor: "accent5", + label: "No self-service", + value: "none", + }, { icon: "close", iconColor: "danger", - label: "No access", - value: "none", + label: "Block", + value: "block", }, ]); @@ -313,13 +319,13 @@ describe("getGroupsDataPermissionEditor", () => { { icon: "check", iconColor: "success", - label: "Allowed", + label: "Yes", value: "write", }, { icon: "close", iconColor: "danger", - label: "No access", + label: "No", value: "none", }, ]); diff --git a/frontend/src/metabase/lib/permissions.js b/frontend/src/metabase/lib/permissions.js index c1fa25ede14f4f84a0f940af45ad636fb7d5a11e..f6d47b5f208736f8caf46d9a6ad297f6564e7017 100644 --- a/frontend/src/metabase/lib/permissions.js +++ b/frontend/src/metabase/lib/permissions.js @@ -150,7 +150,7 @@ export function downgradeNativePermissionsIfNeeded( databaseId, }); - if (value === "none") { + if (value === "none" || value === "block") { // if changing schemas to none, downgrade native to none return updateNativePermission( permissions, @@ -452,7 +452,7 @@ function diffDatabasePermissions( tableId: table.id, }); if (oldFieldsPerm !== newFieldsPerm) { - if (newFieldsPerm === "none") { + if (newFieldsPerm === "none" || newFieldsPerm === "block") { databaseDiff.revokedTables[table.id] = { name: table.display_name }; } else { databaseDiff.grantedTables[table.id] = { name: table.display_name }; diff --git a/frontend/src/metabase/plugins/index.js b/frontend/src/metabase/plugins/index.js index dd74b7ab0326a1cc8c93c2cf0e03408c15e44e17..8566956402c45dbd0e020d6271983450569c2140 100644 --- a/frontend/src/metabase/plugins/index.js +++ b/frontend/src/metabase/plugins/index.js @@ -21,7 +21,7 @@ export const PLUGIN_ADMIN_ROUTES = []; // functions that update the sections export const PLUGIN_ADMIN_SETTINGS_UPDATES = []; -// admin permissions grid +// admin permissions export const PLUGIN_ADMIN_PERMISSIONS_TABLE_ROUTES = []; export const PLUGIN_ADMIN_PERMISSIONS_TABLE_GROUP_ROUTES = []; export const PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_OPTIONS = []; diff --git a/frontend/test/metabase/scenarios/admin/permissions/permissions.cy.spec.js b/frontend/test/metabase/scenarios/admin/permissions/permissions.cy.spec.js index 5d958fd681519def5b25865ad355f18fce10e019..493155e68d2f7737448045ce92d921f3a5e288c7 100644 --- a/frontend/test/metabase/scenarios/admin/permissions/permissions.cy.spec.js +++ b/frontend/test/metabase/scenarios/admin/permissions/permissions.cy.spec.js @@ -2,7 +2,6 @@ import { restore, popover, modal, - describeWithoutToken, describeWithToken, } from "__support__/e2e/cypress"; @@ -11,7 +10,7 @@ const COLLECTION_ACCESS_PERMISSION_INDEX = 0; const DATA_ACCESS_PERMISSION_INDEX = 0; const NATIVE_QUERIES_PERMISSION_INDEX = 1; -describeWithoutToken("scenarios > admin > permissions", () => { +describe("scenarios > admin > permissions", () => { beforeEach(() => { restore(); cy.signInAsAdmin(); @@ -20,11 +19,11 @@ describeWithoutToken("scenarios > admin > permissions", () => { it("should display error on failed save", () => { // revoke some permissions cy.visit("/admin/permissions/data/group/1"); - cy.icon("close") + cy.icon("eye") .first() .click(); cy.findAllByRole("option") - .contains("Allowed") + .contains("Unrestricted") .click(); // stub out the PUT and save @@ -189,7 +188,7 @@ describeWithoutToken("scenarios > admin > permissions", () => { modifyPermission( "Sample Dataset", DATA_ACCESS_PERMISSION_INDEX, - "Allowed", + "Unrestricted", ); cy.findByText("You've made changes to permissions."); @@ -274,12 +273,17 @@ describeWithoutToken("scenarios > admin > permissions", () => { cy.findByText("Permissions for the Administrators group"); cy.findByText("1 person"); - checkAdministratorsHaveAccessToEverything(); + assertPermissionTable([["Sample Dataset", "Unrestricted", "Yes"]]); // Drill down to tables permissions cy.findByText("Sample Dataset").click(); - checkAdministratorsHaveAccessToEverything(); + assertPermissionTable([ + ["Orders", "Unrestricted", "Yes"], + ["People", "Unrestricted", "Yes"], + ["Products", "Unrestricted", "Yes"], + ["Reviews", "Unrestricted", "Yes"], + ]); }); it("allows view and edit permissions", () => { @@ -287,19 +291,23 @@ describeWithoutToken("scenarios > admin > permissions", () => { selectSidebarItem("collection"); - assertPermissionTable([["Sample Dataset", "No access", "No access"]]); + assertPermissionTable([["Sample Dataset", "No self-service", "No"]]); // Drill down to tables permissions cy.findByText("Sample Dataset").click(); assertPermissionTable([ - ["Orders", "No access"], - ["People", "No access"], - ["Products", "No access"], - ["Reviews", "No access"], + ["Orders", "No self-service", "No"], + ["People", "No self-service", "No"], + ["Products", "No self-service", "No"], + ["Reviews", "No self-service", "No"], ]); - modifyPermission("Orders", DATA_ACCESS_PERMISSION_INDEX, "Allowed"); + modifyPermission( + "Orders", + DATA_ACCESS_PERMISSION_INDEX, + "Unrestricted", + ); modal().within(() => { cy.findByText("Change access to this database to limited?"); @@ -307,21 +315,21 @@ describeWithoutToken("scenarios > admin > permissions", () => { }); assertPermissionTable([ - ["Orders", "Allowed"], - ["People", "No access"], - ["Products", "No access"], - ["Reviews", "No access"], + ["Orders", "Unrestricted", "No"], + ["People", "No self-service", "No"], + ["Products", "No self-service", "No"], + ["Reviews", "No self-service", "No"], ]); // Navigate back selectSidebarItem("collection"); - assertPermissionTable([["Sample Dataset", "Limited", "No access"]]); + assertPermissionTable([["Sample Dataset", "Granular", "No"]]); modifyPermission( "Sample Dataset", NATIVE_QUERIES_PERMISSION_INDEX, - "Allowed", + "Yes", ); modal().within(() => { @@ -329,16 +337,16 @@ describeWithoutToken("scenarios > admin > permissions", () => { cy.button("Allow").click(); }); - assertPermissionTable([["Sample Dataset", "Allowed", "Allowed"]]); + assertPermissionTable([["Sample Dataset", "Unrestricted", "Yes"]]); // Drill down to tables permissions cy.findByText("Sample Dataset").click(); assertPermissionTable([ - ["Orders", "Allowed"], - ["People", "Allowed"], - ["Products", "Allowed"], - ["Reviews", "Allowed"], + ["Orders", "Unrestricted", "Yes"], + ["People", "Unrestricted", "Yes"], + ["Products", "Unrestricted", "Yes"], + ["Reviews", "Unrestricted", "Yes"], ]); cy.button("Save changes").click(); @@ -357,10 +365,10 @@ describeWithoutToken("scenarios > admin > permissions", () => { cy.findByText("Save changes").should("not.exist"); assertPermissionTable([ - ["Orders", "Allowed"], - ["People", "Allowed"], - ["Products", "Allowed"], - ["Reviews", "Allowed"], + ["Orders", "Unrestricted", "Yes"], + ["People", "Unrestricted", "Yes"], + ["Products", "Unrestricted", "Yes"], + ["Reviews", "Unrestricted", "Yes"], ]); }); }); @@ -378,26 +386,30 @@ describeWithoutToken("scenarios > admin > permissions", () => { selectSidebarItem("Sample Dataset"); assertPermissionTable([ - ["Administrators", "Allowed", "Allowed"], - ["All Users", "No access", "No access"], - ["collection", "No access", "No access"], - ["data", "Allowed", "Allowed"], - ["nosql", "Allowed", "No access"], - ["readonly", "No access", "No access"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "No self-service", "No"], ]); selectSidebarItem("Orders"); assertPermissionTable([ - ["Administrators", "Allowed"], - ["All Users", "No access"], - ["collection", "No access"], - ["data", "Allowed"], - ["nosql", "Allowed"], - ["readonly", "No access"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "No self-service", "No"], ]); - modifyPermission("readonly", DATA_ACCESS_PERMISSION_INDEX, "Allowed"); + modifyPermission( + "readonly", + DATA_ACCESS_PERMISSION_INDEX, + "Unrestricted", + ); modal().within(() => { cy.findByText("Change access to this database to limited?"); @@ -405,12 +417,12 @@ describeWithoutToken("scenarios > admin > permissions", () => { }); assertPermissionTable([ - ["Administrators", "Allowed"], - ["All Users", "No access"], - ["collection", "No access"], - ["data", "Allowed"], - ["nosql", "Allowed"], - ["readonly", "Allowed"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "Unrestricted", "No"], ]); // Navigate back @@ -419,19 +431,15 @@ describeWithoutToken("scenarios > admin > permissions", () => { .click(); assertPermissionTable([ - ["Administrators", "Allowed", "Allowed"], - ["All Users", "No access", "No access"], - ["collection", "No access", "No access"], - ["data", "Allowed", "Allowed"], - ["nosql", "Allowed", "No access"], - ["readonly", "Limited", "No access"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "Granular", "No"], ]); - modifyPermission( - "readonly", - NATIVE_QUERIES_PERMISSION_INDEX, - "Allowed", - ); + modifyPermission("readonly", NATIVE_QUERIES_PERMISSION_INDEX, "Yes"); modal().within(() => { cy.findByText("Allow native query editing?"); @@ -439,12 +447,12 @@ describeWithoutToken("scenarios > admin > permissions", () => { }); assertPermissionTable([ - ["Administrators", "Allowed", "Allowed"], - ["All Users", "No access", "No access"], - ["collection", "No access", "No access"], - ["data", "Allowed", "Allowed"], - ["nosql", "Allowed", "No access"], - ["readonly", "Allowed", "Allowed"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "Unrestricted", "Yes"], ]); cy.button("Save changes").click(); @@ -463,16 +471,44 @@ describeWithoutToken("scenarios > admin > permissions", () => { cy.findByText("Save changes").should("not.exist"); assertPermissionTable([ - ["Administrators", "Allowed", "Allowed"], - ["All Users", "No access", "No access"], - ["collection", "No access", "No access"], - ["data", "Allowed", "Allowed"], - ["nosql", "Allowed", "No access"], - ["readonly", "Allowed", "Allowed"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "No self-service", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "Unrestricted", "Yes"], ]); }); }); }); + + // TODO: uncomment when starts returning an error + it.skip("block permission block access to questions that use blocked sources", () => { + cy.signInAsNormalUser(); + + cy.visit("/question/1"); + cy.findAllByText("Orders"); + + cy.signInAsAdmin(); + + cy.visit("/admin/permissions/data/database/1"); + + ["All Users", "collection", "data", "nosql", "readonly"].forEach(group => + modifyPermission(group, DATA_ACCESS_PERMISSION_INDEX, "Block"), + ); + + cy.findByText("Save changes").click(); + + modal().within(() => { + cy.button("Yes").click(); + }); + + cy.signInAsNormalUser(); + + cy.visit("/question/1"); + + cy.findAllByText("Orders").should("not.exist"); + }); }); describeWithToken("scenarios > admin > permissions", () => { @@ -506,12 +542,12 @@ describeWithToken("scenarios > admin > permissions", () => { cy.button("Save").click(); assertPermissionTable([ - ["Administrators", "Allowed"], - ["All Users", "Sandboxed"], - ["collection", "No access"], - ["data", "Allowed"], - ["nosql", "Allowed"], - ["readonly", "No access"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "Sandboxed", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "No self-service", "No"], ]); modifyPermission( @@ -532,12 +568,12 @@ describeWithToken("scenarios > admin > permissions", () => { cy.button("Save changes").click(); assertPermissionTable([ - ["Administrators", "Allowed"], - ["All Users", "Sandboxed"], - ["collection", "No access"], - ["data", "Allowed"], - ["nosql", "Allowed"], - ["readonly", "No access"], + ["Administrators", "Unrestricted", "Yes"], + ["All Users", "Sandboxed", "No"], + ["collection", "No self-service", "No"], + ["data", "Unrestricted", "Yes"], + ["nosql", "Unrestricted", "No"], + ["readonly", "No self-service", "No"], ]); }); }); @@ -597,19 +633,3 @@ function assertPermissionTable(rows) { }); }); } - -function checkAdministratorsHaveAccessToEverything() { - cy.findAllByTestId("permissions-select").each($permissionSelect => { - cy.wrap($permissionSelect) - .should("have.text", "Allowed") - .trigger("mouseenter"); - - popover().within(() => { - cy.findByText( - "Administrators always have the highest level of access to everything in Metabase.", - ); - }); - - cy.wrap($permissionSelect).trigger("mouseleave"); - }); -} diff --git a/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js b/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js index 5c9b8622a1ccfd64f40620e3bbeac96121492fca..1b0ea4a936a317e8434183a9bcd1433c4a98ce03 100644 --- a/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js +++ b/frontend/test/metabase/scenarios/admin/permissions/sandboxes.cy.spec.js @@ -742,7 +742,7 @@ describeWithToken("formatting > sandboxes", () => { cy.visit("/admin/permissions/data/database/1/schema/PUBLIC/table/2"); cy.wait("@tablePermissions"); - cy.icon("close") + cy.icon("eye") .eq(1) // No better way of doing this, undfortunately (see table above) .click(); cy.findByText("Sandboxed").click(); diff --git a/frontend/test/metabase/scenarios/smoketest/admin_setup.cy.spec.js b/frontend/test/metabase/scenarios/smoketest/admin_setup.cy.spec.js index 773ce3330691b0fdd8722045ffe1cd36b2f81fee..74a9221ddc31b45728f8862cd8c92794aa5e2e7d 100644 --- a/frontend/test/metabase/scenarios/smoketest/admin_setup.cy.spec.js +++ b/frontend/test/metabase/scenarios/smoketest/admin_setup.cy.spec.js @@ -630,15 +630,14 @@ describe("smoketest > admin_setup", () => { cy.findByText("Sample Dataset").click(); cy.findByText("Products"); - cy.findByText("Native query editing").should("not.exist"); // Turn on data access for all users to Test Table - cy.icon("close") + cy.icon("eye") .eq(2) .click(); cy.findAllByRole("option") - .contains("Allowed") + .contains("Unrestricted") .click(); cy.findByText("Change access to this database to limited?"); @@ -650,11 +649,11 @@ describe("smoketest > admin_setup", () => { cy.findByText("Sample Dataset").click(); // Turn on data access for Marketing users to Products - cy.icon("close") + cy.icon("eye") .eq(1) .click(); cy.findAllByRole("option") - .contains("Allowed") + .contains("Unrestricted") .click(); cy.findByText("Are you sure you want to do this?"); @@ -680,7 +679,7 @@ describe("smoketest > admin_setup", () => { cy.icon("check") .eq(1) .click(); - cy.findByText("No access").click(); + cy.findByText("No").click(); cy.findByText("Save changes").click(); @@ -801,7 +800,6 @@ describe("smoketest > admin_setup", () => { cy.findByText("Ask a question").click(); cy.findByText("Simple question"); - cy.findByText("Native query").should("not.exist"); cy.signOut(); cy.signIn("nocollection");