Newer
Older
import { SAMPLE_DB_ID, USER_GROUPS } from "e2e/support/cypress_data";
import {
ORDERS_QUESTION_ID,
ORDERS_DASHBOARD_ID,
} from "e2e/support/cypress_sample_instance_data";
import {
restore,
modal,
describeEE,
assertPermissionOptions,
modifyPermission,
selectSidebarItem,
assertSidebarItems,
visitQuestion,
visitDashboard,
selectPermissionRow,
} from "e2e/support/helpers";
github-automation-metabase
committed
const { ALL_USERS_GROUP, ADMIN_GROUP, COLLECTION_GROUP, DATA_GROUP } =
USER_GROUPS;
Alexander Lesnenko
committed
const COLLECTION_ACCESS_PERMISSION_INDEX = 0;
const NATIVE_QUERIES_PERMISSION_INDEX = 0;
describe("scenarios > admin > permissions", { tags: "@OSS" }, () => {
Nemanja Glumac
committed
beforeEach(() => {
Nemanja Glumac
committed
restore();
Nemanja Glumac
committed
});
it("shows hidden tables", () => {
cy.visit(`/admin/datamodel/database/${SAMPLE_DB_ID}`);
cy.icon("eye_crossed_out").eq(0).click();
cy.visit(
`admin/permissions/data/group/${ALL_USERS_GROUP}/database/${SAMPLE_DB_ID}`,
);
assertPermissionTable([
["Accounts", "No"],
["Analytic Events", "No"],
["Feedback", "No"],
["Invoices", "No"],
["Orders", "No"],
["People", "No"],
["Products", "No"],
["Reviews", "No"],
it("should not show view data column on OSS", () => {
cy.visit(`/admin/permissions/data/group/${ALL_USERS_GROUP}`);
cy.findByTestId("permission-table").within(() => {
cy.findByText("Database name").should("exist");
cy.findByText("View data").should("not.exist");
cy.findByText("Create queries").should("exist");
});
});
it("should display error on failed save", () => {
// revoke some permissions
cy.visit(`/admin/permissions/data/group/${ALL_USERS_GROUP}`);
cy.icon("close").first().click();
cy.findAllByRole("option").contains("Query builder and native").click();
// stub out the PUT and save
cy.intercept("PUT", "/api/permissions/graph", req => {
req.reply(500, "Server error");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("button", "Yes").click();
// see error modal
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.contains("There was an error saving");
});
context("collection permissions", () => {
it("warns about leaving with unsaved changes", () => {
cy.visit("/admin/permissions/collections");
selectSidebarItem("First collection");
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
"View",
true,
);
// Navigation to other collection should not show any warnings
selectSidebarItem("Our analytics");
modal().should("not.exist");
// Switching to data permissions page
cy.get("label").contains("Data").click();
Maz Ameli
committed
cy.findByText("Discard your changes?");
Maz Ameli
committed
"Your changes haven't been saved, so you'll lose them if you navigate away.",
);
cy.button("Cancel").click();
});
cy.url().should("include", "/admin/permissions/collections/root");
// Switching to data permissions page again
cy.get("label").contains("Data").click();
Maz Ameli
committed
modal().button("Discard changes").click();
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
cy.url().should("include", "/admin/permissions/data/group");
});
it("allows to view and edit permissions", () => {
cy.visit("/admin/permissions/collections");
const collections = ["Our analytics", "First collection"];
assertSidebarItems(collections);
selectSidebarItem("First collection");
assertSidebarItems([...collections, "Second collection"]);
selectSidebarItem("Second collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "No access"],
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
"View",
true,
);
// Navigate to children
selectSidebarItem("Third collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "View"], // Check permission has been propagated
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
// Navigate to parent
selectSidebarItem("First collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "No access"],
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
"Curate",
false,
);
selectSidebarItem("Second collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "View"], // Check permission has not been propagated
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
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();
});
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Save changes").should("not.exist");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "View"],
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
});
});
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
it("don't propagate permissions by checkbox without access select", () => {
cy.visit("/admin/permissions/collections");
const collections = ["Our analytics", "First collection"];
assertSidebarItems(collections);
selectSidebarItem("First collection");
assertSidebarItems([...collections, "Second collection"]);
selectSidebarItem("Second collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "No access"],
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
"View",
false,
);
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
null,
true,
);
// Navigate to children
selectSidebarItem("Third collection");
assertPermissionTable([
["Administrators", "Curate"],
["All Users", "No access"], // Check permission hasn't been propagated
["collection", "Curate"],
["data", "No access"],
["nosql", "No access"],
["readonly", "View"],
]);
});
it("show selected option for the collection with children", () => {
cy.visit("/admin/permissions/collections");
const collections = ["Our analytics", "First collection"];
assertSidebarItems(collections);
selectSidebarItem("First collection");
assertSidebarItems([...collections, "Second collection"]);
selectSidebarItem("Second collection");
selectPermissionRow("All Users", COLLECTION_ACCESS_PERMISSION_INDEX);
assertPermissionOptions(["Curate", "View", "No access"]);
selectSidebarItem("Third collection");
selectPermissionRow("All Users", COLLECTION_ACCESS_PERMISSION_INDEX);
assertPermissionOptions(["Curate", "View"]);
});
context("data permissions", () => {
it("warns about leaving with unsaved changes", () => {
cy.visit("/admin/permissions");
selectSidebarItem("All Users");
modifyPermission(
NATIVE_QUERIES_PERMISSION_INDEX,
"Query builder and native",
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("You've made changes to permissions.");
// Switching to databases focus should not show any warnings
cy.get("label").contains("Databases").click();
cy.url().should("include", "/admin/permissions/data/database");
modal().should("not.exist");
// Switching to collection permissions page
cy.get("label").contains("Collection").click();
Maz Ameli
committed
cy.findByText("Discard your changes?");
Maz Ameli
committed
"Your changes haven't been saved, so you'll lose them if you navigate away.",
);
cy.button("Cancel").click();
});
cy.url().should("include", "/admin/permissions/data/database");
// Switching to collection permissions page again
cy.get("label").contains("Collection").click();
Maz Ameli
committed
modal().button("Discard changes").click();
cy.url().should("include", "/admin/permissions/collections");
});
context("group focused view", () => {
it("shows filterable list of groups", () => {
cy.visit("/admin/permissions");
// no groups selected initially and it shows an empty state
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Select a group to see its data permissions");
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
const groups = [
"Administrators",
"All Users",
"collection",
"data",
"nosql",
"readonly",
];
assertSidebarItems(groups);
// filter groups
cy.findByPlaceholderText("Search for a group").type("a");
const filteredGroups = [
"Administrators",
"All Users",
"data",
"readonly",
];
// client filter debounce
cy.wait(300);
assertSidebarItems(filteredGroups);
});
it("allows to only view Administrators permissions", () => {
cy.visit("/admin/permissions");
selectSidebarItem("Administrators");
cy.url().should(
"include",
`/admin/permissions/data/group/${ADMIN_GROUP}`,
);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Permissions for the Administrators group");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("1 person");
assertPermissionTable([
["Sample Database", "Query builder and native"],
]);
// Drill down to tables permissions
cy.findByTextEnsureVisible("Sample Database").click();
["Accounts", "Query builder and native"],
["Analytic Events", "Query builder and native"],
["Feedback", "Query builder and native"],
["Invoices", "Query builder and native"],
["Orders", "Query builder and native"],
["People", "Query builder and native"],
["Products", "Query builder and native"],
["Reviews", "Query builder and native"],
it("should show a modal when a revision changes while an admin is editing", () => {
cy.intercept("/api/permissions/graph/group/1").as("graph");
cy.visit("/admin/permissions");
selectSidebarItem("collection");
modifyPermission(
"Sample Database",
NATIVE_QUERIES_PERMISSION_INDEX,
"Query builder and native",
);
cy.get("@graph").then(data => {
cy.request("PUT", "/api/permissions/graph", {
groups: {},
revision: data.response.body.revision,
}).then(() => {
selectSidebarItem("data");
modal().findByText("Someone just changed permissions");
});
});
});
});
context("database focused view", () => {
it("should show a modal when a revision changes while an admin is editing", () => {
cy.intercept("/api/permissions/graph/group/1").as("graph");
cy.visit("/admin/permissions/");
selectSidebarItem("collection");
modifyPermission(
"Sample Database",
NATIVE_QUERIES_PERMISSION_INDEX,
"Query builder and native",
);
cy.get("@graph").then(data => {
cy.request("PUT", "/api/permissions/graph", {
groups: {},
revision: data.response.body.revision,
}).then(() => {
cy.get("label").contains("Databases").click();
selectSidebarItem("Sample Database");
modal().findByText("Someone just changed permissions");
});
});
});
describeEE("scenarios > admin > permissions", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
setTokenFeatures("all");
Alexander Lesnenko
committed
it("Visualization and Settings query builder buttons are not visible for questions that use blocked data sources", () => {
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: {
"view-data": "blocked",
},
},
[COLLECTION_GROUP]: {
[SAMPLE_DB_ID]: {
"view-data": "blocked",
Alexander Lesnenko
committed
},
},
});
cy.signIn("nodata");
visitQuestion(ORDERS_QUESTION_ID);
Alexander Lesnenko
committed
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
Alexander Lesnenko
committed
cy.findByText("There was a problem with your question");
cy.findByTestId("viz-settings-button").should("not.exist");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
Alexander Lesnenko
committed
cy.findByText("Visualization").should("not.exist");
});
it("shows permission error for cards that use blocked data sources", () => {
cy.updatePermissionsGraph({
[ALL_USERS_GROUP]: {
[SAMPLE_DB_ID]: {
"view-data": "blocked",
},
},
[COLLECTION_GROUP]: {
[SAMPLE_DB_ID]: {
"view-data": "blocked",
},
},
});
cy.signIn("nodata");
visitDashboard(ORDERS_DASHBOARD_ID);
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Sorry, you don't have permission to see this card.");
});
describe("scenarios > admin > permissions", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
it("shows permissions help", () => {
cy.visit("/admin/permissions");
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
// Data permissions w/o `legacy-no-self-service` in graph
cy.get("main").within(() => {
cy.findByText("Permissions help").as("permissionHelpButton").click();
cy.get("@permissionHelpButton").should("not.exist");
});
cy.findByLabelText("Permissions help reference").within(() => {
cy.findByText("Data permissions");
cy.findByText("Database ‘View data’ levels").click();
cy.findByTestId("database-view-data-level").should(
"not.contain",
/No self-service/,
);
cy.findByText("Database ‘View data’ levels").click();
cy.findByText(/Schema or table ‘View data’ levels/).click();
cy.findByTestId("schema-table-level").should(
"not.contain",
/No self-service/,
);
cy.findByText(/Schema or table ‘View data’ levels/).click();
cy.findByText("‘Create queries’ levels");
cy.findByLabelText("Close").click();
});
// Data permissions w/ `legacy-no-self-service` in graph
cy.visit("/admin/permissions");
cy.intercept("GET", `/api/permissions/graph/group/${ALL_USERS_GROUP}`, {
statusCode: 200,
body: {
revision: 1,
groups: {
1: {
1: {
"view-data": "legacy-no-self-service",
"create-queries": "query-builder-and-native",
download: { schemas: "full" },
},
},
},
},
});
cy.findByText("Permissions help").as("permissionHelpButton").click();
cy.get("@permissionHelpButton").should("not.exist");
});
cy.findByLabelText("Permissions help reference")
.as("permissionsHelpContent")
.within(() => {
cy.findByText("Database ‘View data’ levels").click();
cy.findAllByText(/No self-service/);
cy.findByLabelText("Close").click();
});
cy.get("main").within(() => {
cy.findByText("Collections").click();
cy.get("@permissionHelpButton").click();
});
// Collection permissions
cy.get("@permissionsHelpContent").within(() => {
cy.findByText("Collection permissions");
cy.findByText("Collections Permission Levels");
});
// The help reference keeps being open when switching tabs
cy.get("main").within(() => {
cy.findByText("Data").click();
});
cy.get("@permissionsHelpContent").within(() => {
cy.findByText("Data permissions");
});
});
github-automation-metabase
committed
it("should show a dismissable modal and banner showing split permisson changes (#metabase#45073", () => {
github-automation-metabase
committed
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
// We need a way to pass true values for these settings in CI. Generally in CI, these values will always be false
// because we always start with a fresh instance. However, to test the flow of someone who has upgraded from 49 -> current
// we set them to false and ensure a modal is shown explaining the new permissions structure
const tempState = {
"show-updated-permission-modal": true,
"show-updated-permission-banner": true,
};
// When the app calls for session properties, give them a modified API response
cy.intercept("/api/session/properties", req => {
req.continue(res => {
res.body = { ...res.body, ...tempState };
});
}).as("sessionProps");
// These calls are setting the permission to false, so update the local state. When the settings are refreshed
// from the browser, they will get the new values from local state
cy.intercept("api/setting/show-updated-permission-modal", () => {
tempState["show-updated-permission-modal"] = false;
});
cy.intercept("api/setting/show-updated-permission-banner", () => {
tempState["show-updated-permission-banner"] = false;
});
cy.visit("/admin/permissions/");
//Both the command palette and the admin app call refresh settings
cy.wait(["@sessionProps", "@sessionProps"]);
cy.findByRole("dialog", { name: /permissions may look different/ })
.findByRole("button", { name: "Got it" })
.click();
cy.wait("@sessionProps");
cy.findByRole("menuitem", { name: "All Users" }).click();
cy.findByRole("alert").should(
"contain.text",
"Your data permissions may look different",
);
cy.findByRole("alert").findByRole("button").click();
//Cypress will error if the page refreshes while it's also intercepting a request
cy.wait("@sessionProps");
cy.reload();
cy.findByRole("dialog", { name: /permissions may look different/ }).should(
"not.exist",
);
cy.findByRole("alert").should("not.exist");
});
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
it("split permisson change modal should dismiss even if network request fails", () => {
// We need a way to pass true values for these settings in CI. Generally in CI, these values will always be false
// because we always start with a fresh instance. However, to test the flow of someone who has upgraded from 49 -> current
// we set them to false and ensure a modal is shown explaining the new permissions structure
const tempState = {
"show-updated-permission-modal": true,
};
// When the app calls for session properties, give them a modified API response
cy.intercept("/api/session/properties", req => {
req.continue(res => {
res.body = { ...res.body, ...tempState };
});
}).as("sessionProps");
// These calls are setting the permission to false, so update the local state. When the settings are refreshed
// from the browser, they will get the new values from local state
cy.intercept("api/setting/show-updated-permission-modal", {
statusCode: 500,
});
cy.visit("/admin/permissions/");
//Both the command palette and the admin app call refresh settings
cy.wait(["@sessionProps", "@sessionProps"]);
cy.findByRole("dialog", { name: /permissions may look different/ })
.findByRole("button", { name: "Got it" })
.click();
cy.wait("@sessionProps");
cy.findByRole("menuitem", { name: "All Users" }).click();
});
github-automation-metabase
committed
describe("scenarios > admin > permissions", () => {
beforeEach(() => {
restore();
cy.signInAsAdmin();
});
github-automation-metabase
committed
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
context("partial updates", () => {
it("partial data permission updates should not remove permissions from other unmodified groups", () => {
// check the we have an expected initial state
cy.visit(`admin/permissions/data/group/${DATA_GROUP}`);
assertPermissionTable([["Sample Database", "Query builder and native"]]);
// make a change to the permissions of another group
selectSidebarItem("nosql");
assertPermissionTable([["Sample Database", "Query builder only"]]);
modifyPermission(
"Sample Database",
NATIVE_QUERIES_PERMISSION_INDEX,
"No",
);
// observe the save change request and assert that we don't get back
// values for groups we did not modify
cy.intercept("PUT", "/api/permissions/graph").as("updateGraph");
// save changes
cy.button("Save changes").click();
modal().within(() => {
cy.button("Yes").click();
});
cy.wait("@updateGraph").then(interception => {
const requestGroupIds = Object.keys(interception.request.body.groups);
const responseGroupIds = Object.keys(interception.response.body.groups);
expect(requestGroupIds).to.deep.equal(responseGroupIds);
});
github-automation-metabase
committed
github-automation-metabase
committed
// make sure that our other group's permission data did not get changed
selectSidebarItem("data");
assertPermissionTable([["Sample Database", "Query builder and native"]]);
github-automation-metabase
committed
});
github-automation-metabase
committed
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
it("partial collection permission updates should not prevent user from making further changes", () => {
cy.visit("/admin/permissions/collections");
selectSidebarItem("First collection");
modifyPermission(
"All Users",
COLLECTION_ACCESS_PERMISSION_INDEX,
"View",
true,
);
cy.intercept("PUT", "/api/collection/graph").as("updateGraph");
cy.button("Save changes").click();
modal().within(() => {
cy.button("Yes").click();
});
cy.wait("@updateGraph").then(interception => {
cy.log("should skip graph in request and response");
expect(interception.request.body.skip_graph).to.equal(true);
expect(interception.response.body).to.not.haveOwnProperty("groups");
});
selectSidebarItem("First collection");
modifyPermission(
"nosql",
COLLECTION_ACCESS_PERMISSION_INDEX,
"Curate",
true,
);
cy.button("Save changes").click();
modal().within(() => {
cy.button("Yes").click();
});
cy.wait("@updateGraph").then(interception => {
cy.log("should not send previously saved edits");
expect(interception.request.body.groups).to.not.haveOwnProperty(
USER_GROUPS.ALL_USERS_GROUP,
);
cy.log("should not fail when making multiple rounds of edits");
expect(interception.response.statusCode).to.equal(200);
});
});