From bf67b5132981327b62986d0afa39a6813b22b040 Mon Sep 17 00:00:00 2001
From: Alexander Lesnenko <alxnddr@users.noreply.github.com>
Date: Thu, 2 Sep 2021 09:54:33 +0300
Subject: [PATCH] Block permission front-end (#17638)

* Block permission

* Update frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.jsx

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>

Co-authored-by: Nemanja Glumac <31325167+nemanjaglumac@users.noreply.github.com>
---
 .../DataPermissionsHelp.jsx                   |  66 ++++++
 .../DataPermissionsHelp.styled.jsx            |  71 ++++++
 .../PermissionsEditor/PermissionsEditor.jsx   |   2 +-
 .../PermissionsEditor.styled.js               |   1 +
 .../PermissionsPageLayout.jsx                 |  88 ++++---
 .../PermissionsPageLayout.styled.jsx          |  60 ++++-
 .../PermissionsSelect/PermissionsSelect.jsx   |  26 ++-
 .../PermissionsSelect.styled.jsx              |   7 +-
 .../PermissionsSidebar.styled.jsx             |   1 +
 .../PermissionsTable/PermissionsTable.jsx     |  18 +-
 .../PermissionsTable.styled.jsx               |  14 +-
 .../permissions/constants/data-permissions.js |  20 +-
 .../DataPermissionsPage.jsx                   |   2 +
 .../permissions/selectors/confirmations.js    |   2 +-
 .../permissions/selectors/data-permissions.js |  93 +++++---
 .../selectors/data-permissions.unit.spec.js   |  18 +-
 frontend/src/metabase/lib/permissions.js      |   4 +-
 frontend/src/metabase/plugins/index.js        |   2 +-
 .../admin/permissions/permissions.cy.spec.js  | 216 ++++++++++--------
 .../admin/permissions/sandboxes.cy.spec.js    |   2 +-
 .../smoketest/admin_setup.cy.spec.js          |  12 +-
 21 files changed, 522 insertions(+), 203 deletions(-)
 create mode 100644 frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.jsx
 create mode 100644 frontend/src/metabase/admin/permissions/components/DataPermissionsHelp/DataPermissionsHelp.styled.jsx

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 00000000000..14e248bede3
--- /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 00000000000..c6fcb81d37a
--- /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 7ac2a3bded1..f75e04bb833 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 0ed8b502914..ec317cd3269 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 c5fa092c2a8..ee2963731a4 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 bc35ec14332..9f8c8ced862 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 6f138ba76ef..b19d34c21bf 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 3cdcd906e2c..c196e6de434 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 fcc22f8a039..405988476a9 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 56789518b85..190ef535620 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 5cd1b3ad453..739e25cd9ea 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 40efeadf883..965876b68f6 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 d08b76958e4..a2bafb7e10d 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 2d147b8034b..7814f24f26b 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 67f72e5a717..3bfca57aeea 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 ae17f3cd6ab..beeb698cf7b 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 c1fa25ede14..f6d47b5f208 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 dd74b7ab032..8566956402c 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 5d958fd6815..493155e68d2 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 5c9b8622a1c..1b0ea4a936a 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 773ce333069..74a9221ddc3 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");
-- 
GitLab