Code owners
Assign users and groups as approvers for specific file changes. Learn more.
download-permissions.cy.spec.js 8.34 KiB
import { SAMPLE_DB_ID, USER_GROUPS } from "e2e/support/cypress_data";
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import {
ORDERS_DASHBOARD_ID,
ORDERS_QUESTION_ID,
} from "e2e/support/cypress_sample_instance_data";
import {
restore,
modal,
describeEE,
assertPermissionForItem,
modifyPermission,
downloadAndAssert,
assertSheetRowsCount,
sidebar,
visitQuestion,
visitDashboard,
popover,
setTokenFeatures,
} from "e2e/support/helpers";
const { ALL_USERS_GROUP, COLLECTION_GROUP, DATA_GROUP } = USER_GROUPS;
const {
PRODUCTS_ID,
ORDERS_ID,
PEOPLE_ID,
REVIEWS_ID,
ACCOUNTS_ID,
ANALYTIC_EVENTS_ID,
FEEDBACK_ID,
INVOICES_ID,
} = SAMPLE_DATABASE;
const DATA_ACCESS_PERMISSION_INDEX = 0;
const DOWNLOAD_PERMISSION_INDEX = 2;
describeEE("scenarios > admin > permissions > data > downloads", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
setTokenFeatures("all");
// Restrict downloads for Collection and Data groups before each test so that they don't override All Users
cy.updatePermissionsGraph({
[COLLECTION_GROUP]: {
[SAMPLE_DB_ID]: {
download: { schemas: "none" },
},
},
[DATA_GROUP]: {
[SAMPLE_DB_ID]: {
download: { schemas: "none" },
},
},
});
});
it("setting downloads permission UI flow should work", () => {
cy.log("allows changing download results permission for a database");
cy.visit(`/admin/permissions/data/database/${SAMPLE_DB_ID}`);
modifyPermission("All Users", DOWNLOAD_PERMISSION_INDEX, "No");
cy.button("Save changes").click();
modal().within(() => {
cy.findByText("Save permissions?");
cy.findByText("Are you sure you want to do this?");
cy.button("Yes").click();
});
assertPermissionForItem("All Users", DOWNLOAD_PERMISSION_INDEX, "No");
cy.log("Make sure we can change download results permission for a table");
sidebar().contains("Orders").click();
modifyPermission("All Users", DOWNLOAD_PERMISSION_INDEX, "1 million rows");
cy.button("Save changes").click();
modal().within(() => {
cy.findByText("Save permissions?");
cy.findByText("Are you sure you want to do this?");
cy.button("Yes").click();
});
assertPermissionForItem(
"All Users",
DOWNLOAD_PERMISSION_INDEX,
"1 million rows",
);
});
it("respects 'no download' permissions when 'All users' group data permissions are set to `Blocked` (metabase#22408)", () => {
cy.visit(`/admin/permissions/data/database/${SAMPLE_DB_ID}`);
modifyPermission("All Users", DATA_ACCESS_PERMISSION_INDEX, "Blocked");
cy.button("Save changes").click();
modal().within(() => {
cy.findByText("Save permissions?");
cy.findByText("Are you sure you want to do this?");
cy.button("Yes").click();
});
// When data permissions are set to `Blocked`, download permissions are automatically revoked
assertPermissionForItem("All Users", DOWNLOAD_PERMISSION_INDEX, "No");
// Normal user belongs to both "data" and "collection" groups.
// They both have restricted downloads so this user shouldn't have the right to download anything.
cy.signInAsNormalUser();
visitQuestion(ORDERS_QUESTION_ID);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Showing first 2,000 rows");
cy.icon("download").should("not.exist");
});
it("restricts users from downloading questions", () => {
// Restrict downloads for All Users
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: {
download: { schemas: "none" },
},
},
});
cy.signInAsNormalUser();
visitQuestion(ORDERS_QUESTION_ID);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Showing first 2,000 rows");
cy.icon("download").should("not.exist");
visitDashboard(ORDERS_DASHBOARD_ID);
cy.findByTestId("dashcard").within(() => {
cy.findByTestId("legend-caption").realHover();
cy.findByTestId("dashcard-menu").click();
});
popover().within(() => {
cy.findByText("Edit question").should("be.visible");
cy.findByText("Download results").should("not.exist");
});
});
it("limits users from downloading all results", () => {
// Restrict downloads for All Users
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: {
download: { schemas: "limited" },
},
},
});
cy.signInAsNormalUser();
visitQuestion(ORDERS_QUESTION_ID);
downloadAndAssert(
{ fileType: "xlsx", questionId: ORDERS_QUESTION_ID },
assertSheetRowsCount(10000),
);
});
describe("native questions", () => {
beforeEach(() => {
cy.intercept("POST", "/api/dataset").as("dataset");
cy.createNativeQuestion(
{
name: "Native Orders",
native: {
query: "select * from orders",
},
},
{ wrapId: true, idAlias: "nativeQuestionId" },
);
});
it("lets user download results from native queries", () => {
cy.signInAsNormalUser();
cy.get("@nativeQuestionId").then(id => {
visitQuestion(id);
downloadAndAssert(
{ fileType: "xlsx", questionId: id },
assertSheetRowsCount(18760),
);
// Make sure we can download results from an ad-hoc nested query based on a native question
cy.findByText("Explore results").click();
cy.wait("@dataset");
downloadAndAssert({ fileType: "xlsx" }, assertSheetRowsCount(18760));
// Make sure we can download results from a native model
cy.request("PUT", `/api/card/${id}`, { name: "Native Model" });
visitQuestion(id);
downloadAndAssert(
{ fileType: "xlsx", questionId: id },
assertSheetRowsCount(18760),
);
});
});
it("prevents user from downloading a native question even if only one table doesn't have download permissions", () => {
setDownloadPermissionsForProductsTable("none");
cy.signInAsNormalUser();
cy.get("@nativeQuestionId").then(id => {
visitQuestion(id);
cy.findByText("Showing first 2,000 rows");
cy.icon("download").should("not.exist");
// Ad-hoc nested query also shouldn't be downloadable
cy.findByText("Explore results").click();
cy.wait("@dataset");
cy.findByText("Showing first 2,000 rows");
cy.icon("download").should("not.exist");
// Convert question to a model, which also shouldn't be downloadable
cy.request("PUT", `/api/card/${id}`, { name: "Native Model" });
visitQuestion(id);
cy.findByText("Showing first 2,000 rows");
cy.icon("download").should("not.exist");
});
});
it("limits download results for a native question even if only one table has `limited` download permissions", () => {
setDownloadPermissionsForProductsTable("limited");
cy.signInAsNormalUser();
cy.get("@nativeQuestionId").then(id => {
visitQuestion(id);
downloadAndAssert(
{ fileType: "xlsx", questionId: id },
assertSheetRowsCount(10000),
);
// Ad-hoc nested query based on a native question should also have a download row limit
cy.findByText("Explore results").click();
cy.wait("@dataset");
downloadAndAssert({ fileType: "xlsx" }, assertSheetRowsCount(10000));
// Convert question to a model, which should also have a download row limit
cy.request("PUT", `/api/card/${id}`, { name: "Native Model" });
visitQuestion(id);
downloadAndAssert(
{ fileType: "xlsx", questionId: id },
assertSheetRowsCount(10000),
);
});
});
});
});
function setDownloadPermissionsForProductsTable(permission) {
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: {
download: {
schemas: {
PUBLIC: {
[PRODUCTS_ID]: permission,
[ORDERS_ID]: "full",
[PEOPLE_ID]: "full",
[REVIEWS_ID]: "full",
[ACCOUNTS_ID]: "full",
[ANALYTIC_EVENTS_ID]: "full",
[FEEDBACK_ID]: "full",
[INVOICES_ID]: "full",
},
},
},
},
},
});
}