Skip to content

Commit

Permalink
feat: introduce Advanced Access Control Layer (AACL) (#2576)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrians5j authored Jun 23, 2023
1 parent 351420f commit 811e972
Show file tree
Hide file tree
Showing 267 changed files with 6,279 additions and 2,730 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="./static/webiny-logo.svg" width="250">
<img src="./docs/static/webiny-logo.svg" width="250">
<br><br>
<strong>Open-Source Serverless Enterprise CMS</strong>
</p>
Expand Down
2 changes: 1 addition & 1 deletion apps/admin/src/okta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const oktaFactory: OktaFactory = ({ clientId }) => {
baseUrl: oktaDomain,
clientId,
redirectUri,
logo: "https://raw.githubusercontent.com/webiny/webiny-js/next/static/webiny-logo.svg",
logo: "https://raw.githubusercontent.com/webiny/webiny-js/next/docs/static/webiny-logo.svg",
authParams: {
scopes
}
Expand Down
1 change: 0 additions & 1 deletion apps/api/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import scaffoldsPlugins from "./plugins/scaffolds";
import { createBenchmarkEnablePlugin } from "~/plugins/benchmarkEnable";

const debug = process.env.DEBUG === "true";

const documentClient = new DocumentClient({
convertEmptyValues: true,
region: process.env.AWS_REGION
Expand Down
1 change: 0 additions & 1 deletion apps/api/graphql/src/plugins/scaffolds/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
// This file is automatically updated via various scaffolding utilities.

export default () => [];
7 changes: 3 additions & 4 deletions apps/api/graphql/src/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import { createStorageOperations as securityStorageOperations } from "@webiny/ap
import { authenticateUsingHttpHeader } from "@webiny/api-security/plugins/authenticateUsingHttpHeader";
import apiKeyAuthentication from "@webiny/api-security/plugins/apiKeyAuthentication";
import apiKeyAuthorization from "@webiny/api-security/plugins/apiKeyAuthorization";
import groupAuthorization from "@webiny/api-security/plugins/groupAuthorization";
import parentTenantGroupAuthorization from "@webiny/api-security/plugins/parentTenantGroupAuthorization";
import cognitoAuthentication from "@webiny/api-security-cognito";
import anonymousAuthorization from "@webiny/api-security/plugins/anonymousAuthorization";
import tenantLinkAuthorization from "@webiny/api-security/plugins/tenantLinkAuthorization";
import createAdminUsersApp from "@webiny/api-admin-users-cognito";
import { syncWithCognito } from "@webiny/api-admin-users-cognito/syncWithCognito";
import { createStorageOperations as createAdminUsersStorageOperations } from "@webiny/api-admin-users-cognito-so-ddb";
Expand Down Expand Up @@ -87,12 +86,12 @@ export default ({ documentClient }: { documentClient: DocumentClient }) => [
/**
* Authorization plugin to fetch permissions from a security group associated with the identity.
*/
groupAuthorization({ identityType: "admin" }),
tenantLinkAuthorization({ identityType: "admin" }),

/**
* Authorization plugin to fetch permissions from the parent tenant.
*/
parentTenantGroupAuthorization({ identityType: "admin" }),
tenantLinkAuthorization({ identityType: "admin", parent: true }),

/**
* Authorization plugin to load permissions for anonymous requests.
Expand Down
43 changes: 0 additions & 43 deletions apps/theme/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,6 @@ const theme = createTheme({
b: { fontWeight: "bold" },
i: { fontStyle: "italic" }
},
quote: {
"blockquote > q": {
quotes: "auto",
"&:before": { content: "open-quote" },
"&:after": { content: "close-quote" }
}
},
button: {
default: buttons({ background: colors.color5, color: colors.color3 }),
primary: buttons({ background: colors.color1, color: colors.color6 }),
Expand All @@ -176,42 +169,6 @@ const theme = createTheme({
color: colors.color1,
"&:hover": { transform: "translateY(-1px)" }
})
},

list: {
"ul, ol": {
li: {
marginBottom: "12px",
marginLeft: "1.875rem",
position: "relative"
}
},
ul: {
li: {
"&:before,&:after": {
position: "absolute",
content: '""',
borderRadius: "50%"
},
"&:before": {
backgroundColor: "#90c418",
height: "1.25rem",
width: "1.25rem",
left: "-1.875rem",
top: "0.125rem"
},
"&:after": {
backgroundColor: "#ffffff",
height: "0.5rem",
left: "-1.5rem",
top: "0.5rem",
width: "0.5rem"
}
}
},
ol: {
listStyleType: "decimal"
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions cypress/integration/admin/login/login.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ context("Login Page", () => {
const password = "12345678";

// eslint-disable-next-line jest/valid-expect-in-promise
cy.securityReadGroup({ slug: "full-access" }).then(group => {
cy.securityReadRole({ slug: "full-access" }).then(group => {
return cy
.securityCreateUser({
data: {
Expand Down Expand Up @@ -76,7 +76,7 @@ context("Login Page", () => {
const password = "12345678";

// eslint-disable-next-line jest/valid-expect-in-promise
cy.securityReadGroup({ slug: "full-access" }).then(group => {
cy.securityReadRole({ slug: "full-access" }).then(group => {
return cy
.securityCreateUser({
data: {
Expand Down Expand Up @@ -121,7 +121,7 @@ context("Login Page", () => {
const newPassword = "12345678910";

// eslint-disable-next-line jest/valid-expect-in-promise
cy.securityReadGroup({ slug: "full-access" }).then(group => {
cy.securityReadRole({ slug: "full-access" }).then(group => {
return cy
.securityCreateUser({
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ context("Security -> API Key -> GraphQL API Access", () => {
});

it("should able to read from GraphQL API using API key", () => {
return cy.securityReadGroup({ slug: "full-access" }, apiKey.token).then(group => {
return cy.securityReadRole({ slug: "full-access" }, apiKey.token).then(group => {
assert.equal(group.name, "Full Access");
assert.equal(group.slug, "full-access");
assert.equal(group.description, "Grants full access to all apps.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const selectPermission = (name, accessLevel) => {
});
};

const deleteGroup = slug => {
const deleteRole = slug => {
cy.findByTestId("default-data-list").within(() => {
cy.get(".mdc-list-item")
.first()
Expand All @@ -21,27 +21,27 @@ const deleteGroup = slug => {
cy.findByTestId("default-data-list.delete-dialog").within(() => {
cy.findAllByTestId("dialog-accept").next().click();
});
cy.findByText(`Group "${slug}" deleted.`).should("exist");
cy.findByText(`Role "${slug}" deleted.`).should("exist");
};

context("Security -> Group", () => {
context("Security -> Role", () => {
beforeEach(() => {
cy.login();
});

it("should create, update and delete group", () => {
cy.visit(`/access-management/groups`);
it("should create, update and delete role", () => {
cy.visit(`/access-management/roles`);

// Create a group
// Create a role
const [name, slug, description] = [
uniqid("name-"),
uniqid("slug-"),
uniqid("description-")
];

// Open group details form
// Open role details form
cy.findAllByTestId("new-record-button").first().click();
// Add group's detail
// Add role's detail
cy.findByTestId("admin.am.group.new.name").type(name);
cy.findByTestId("admin.am.group.new.slug").type(slug);
cy.findByTestId("admin.am.group.new.description").type(description);
Expand All @@ -53,14 +53,14 @@ context("Security -> Group", () => {
// Wait for loading to finish
cy.get(".react-spinner-material").should("not.exist");
// Verify success message
cy.findByText("Group saved successfully!").should("exist");
cy.findByText("Role saved successfully!").should("exist");

// Update group
// Update role

const [newName, newDescription] = [uniqid("new-name-"), uniqid("new-description-")];

cy.findByTestId("admin.am.group.new.name").should("have.value", name);
// Add group's detail
// Add role's detail
cy.findByTestId("admin.am.group.new.name").clear().type(newName);
cy.findByTestId("admin.am.group.new.description").clear().type(newDescription);
// Update permissions
Expand All @@ -77,29 +77,29 @@ context("Security -> Group", () => {
// Wait for loading to finish
cy.get(".react-spinner-material").should("not.exist");
// Verify success message
cy.findByText("Group saved successfully!").should("exist");
// Updated group should be in list
cy.findByText("Role saved successfully!").should("exist");
// Updated role should be in list
cy.findByTestId("default-data-list").within(() => {
cy.findByText(newName).should("exist");
});

// Delete fist group from the list
deleteGroup(slug);
// Delete fist role from the list
deleteRole(slug);
});

it("should create group with all permissions, verify and delete it", () => {
cy.visit(`/access-management/groups`);
it("should create role with all permissions, verify and delete it", () => {
cy.visit(`/access-management/roles`);

// Create a group
// Create a role
const [name, slug, description] = [
uniqid("name-"),
uniqid("slug-"),
uniqid("description-")
];

// Open group details form
// Open role details form
cy.findAllByTestId("new-record-button").first().click();
// Add group's detail
// Add role's detail
cy.findByTestId("admin.am.group.new.name").type(name);
cy.findByTestId("admin.am.group.new.slug").type(slug);
cy.findByTestId("admin.am.group.new.description").type(description);
Expand All @@ -118,11 +118,11 @@ context("Security -> Group", () => {
// Wait for loading to finish
cy.get(".react-spinner-material").should("not.exist");
// Verify success message
cy.findByText("Group saved successfully!").should("exist");
cy.findByText("Role saved successfully!").should("exist");

// Verify group permissions
// Verify role permissions
// eslint-disable-next-line jest/valid-expect-in-promise
cy.securityReadGroup({ slug }).then(group => {
cy.securityReadRole({ slug }).then(group => {
// eslint-disable-next-line jest/valid-expect
expect(group.permissions).to.deep.eq([
{
Expand Down Expand Up @@ -154,6 +154,6 @@ context("Security -> Group", () => {
});

// Delete first group from the list
deleteGroup(slug);
deleteRole(slug);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ const sort = {
NAME_Z_TO_A: "name_DESC"
};

context("Search and Sort Security Groups", () => {
context("Search and Sort Security Roles", () => {
const total = 3;
const groups = [];

before(() => {
for (let i = 0; i < total; i++) {
cy.securityCreateGroup({
cy.securityCreateRole({
data: {
name: uniqid(`${i}-`, "-group"),
description: uniqid("description-"),
Expand All @@ -33,44 +33,44 @@ context("Search and Sort Security Groups", () => {
after(() => {
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
cy.securityDeleteGroup({ id: group.id });
cy.securityDeleteRole({ id: group.id });
}
});

it("should be able to search group", () => {
cy.visit(`/access-management/groups`);
it("should be able to search role", () => {
cy.visit(`/access-management/roles`);

// Searching for a non existing user should result in "no records found"
cy.findByTestId("default-data-list.search").within(() => {
cy.findByPlaceholderText(/Search groups/i).type("NON_EXISTING_USER");
cy.findByPlaceholderText(/Search roles/i).type("NON_EXISTING_USER");
});
cy.findByTestId("ui.list.data-list").within(() => {
cy.findByText(/no records found./i).should("exist");
});

// Searching for a particular group by "slug"
// Searching for a particular role by "slug"
cy.findByTestId("default-data-list.search").within(() => {
cy.findByPlaceholderText(/Search groups/i)
cy.findByPlaceholderText(/Search roles/i)
.clear()
.type(groups[0].slug);
});
cy.findByTestId("ui.list.data-list").within(() => {
cy.findByText(groups[0].name).should("exist");
});

// Searching for a particular group by "name"
// Searching for a particular role by "name"
cy.findByTestId("default-data-list.search").within(() => {
cy.findByPlaceholderText(/Search groups/i)
cy.findByPlaceholderText(/Search roles/i)
.clear()
.type(groups[0].name);
});
cy.findByTestId("ui.list.data-list").within(() => {
cy.findByText(groups[0].name).should("exist");
});

// Searching for a particular group by "description"
// Searching for a particular role by "description"
cy.findByTestId("default-data-list.search").within(() => {
cy.findByPlaceholderText(/Search groups/i)
cy.findByPlaceholderText(/Search roles/i)
.clear()
.type(groups[0].description);
});
Expand All @@ -79,10 +79,10 @@ context("Search and Sort Security Groups", () => {
});
});

it("should be able to sort groups", () => {
cy.visit(`/access-management/groups`);
it("should be able to sort roles", () => {
cy.visit(`/access-management/roles`);

// Sort groups from "login A -> Z"
// Sort roles from "login A -> Z"
cy.findByTestId("default-data-list.filter").click();
cy.findByTestId("ui.list.data-list").within(() => {
cy.get("select").select(sort.NAME_A_TO_Z);
Expand All @@ -97,13 +97,13 @@ context("Search and Sort Security Groups", () => {
});
});

// Sort groups from "name Z -> A"
// Sort roles from "name Z -> A"
cy.findByTestId("default-data-list.filter").click();
cy.findByTestId("ui.list.data-list").within(() => {
cy.get("select").select(sort.NAME_Z_TO_A);
cy.findByTestId("default-data-list.filter").click();
});
// We're testing it against the third item because the first two will be system groups
// We're testing it against the third item because the first two will be system roles
cy.findByTestId("default-data-list").within(() => {
cy.get(".mdc-list-item")
.first()
Expand All @@ -114,13 +114,13 @@ context("Search and Sort Security Groups", () => {
});
});

// Sort groups from "Oldest to Newest"
// Sort roles from "Oldest to Newest"
cy.findByTestId("default-data-list.filter").click();
cy.findByTestId("ui.list.data-list").within(() => {
cy.get("select").select(sort.OLDEST_TO_NEWEST);
cy.findByTestId("default-data-list.filter").click();
});
// We're testing it against the third item because the first two will be system groups
// We're testing it against the third item because the first two will be system roles
cy.findByTestId("default-data-list").within(() => {
cy.get(".mdc-list-item")
.first()
Expand All @@ -131,7 +131,7 @@ context("Search and Sort Security Groups", () => {
});
});

// Sort groups from "Newest to Oldest"
// Sort roles from "Newest to Oldest"
cy.findByTestId("default-data-list.filter").click();
cy.findByTestId("ui.list.data-list").within(() => {
cy.get("select").select(sort.NEWEST_TO_OLDEST);
Expand Down
Loading

0 comments on commit 811e972

Please sign in to comment.