From 1b2f074c04cb8d38738bcac16ad0eaf572b2705f Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Thu, 6 Jun 2024 12:00:27 +1000 Subject: [PATCH] feat: organization admin support --- README.md | 1 + cypress/cypress.config.ts | 1 + cypress/e2e/rbac/organizations/orgAdmin.cy.ts | 170 ++++++++++++++++++ .../AddUserToOrganization/index.js | 17 +- src/components/Organizations/Manage/index.js | 14 +- .../Organizations/Organization/Styles.tsx | 8 + .../Organizations/Organization/index.js | 12 +- .../query/organizations/OrganizationByID.js | 1 + .../query/organizations/OrganizationByName.js | 1 + .../organizations/UsersByOrganization.js | 2 + test/keycloak/configure-keycloak.sh | 2 +- 11 files changed, 219 insertions(+), 10 deletions(-) create mode 100644 cypress/e2e/rbac/organizations/orgAdmin.cy.ts diff --git a/README.md b/README.md index b4f9f429..c89932bf 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ A couple of environment variables are required: - user_owner - user with owner role - user_orguser - Organization user - user_orgviewer - Organization viewer +- user_orgadmin - Organization admin - user_orgowner - Organization owner - user_platformowner - Platform owner diff --git a/cypress/cypress.config.ts b/cypress/cypress.config.ts index 9954393f..a1a9e411 100644 --- a/cypress/cypress.config.ts +++ b/cypress/cypress.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ // orgs user_orguser: 'orguser@example.com', user_orgviewer: 'orgviewer@example.com', + user_orgadmin: 'orgadmin@example.com', user_orgowner: 'orgowner@example.com', // top level user for all default tests user_platformowner: 'platformowner@example.com', diff --git a/cypress/e2e/rbac/organizations/orgAdmin.cy.ts b/cypress/e2e/rbac/organizations/orgAdmin.cy.ts new file mode 100644 index 00000000..78533124 --- /dev/null +++ b/cypress/e2e/rbac/organizations/orgAdmin.cy.ts @@ -0,0 +1,170 @@ +import { testData } from 'cypress/fixtures/variables'; +import GroupAction from 'cypress/support/actions/organizations/GroupsAction'; +import NotificationsAction from 'cypress/support/actions/organizations/NotificationsAction'; +import OverviewAction from 'cypress/support/actions/organizations/OverviewAction'; +import ProjectsActions from 'cypress/support/actions/organizations/ProjectsActions'; +import { aliasMutation, aliasQuery, registerIdleHandler } from 'cypress/utils/aliasQuery'; + +const overview = new OverviewAction(); +const group = new GroupAction(); +const project = new ProjectsActions(); +const notifications = new NotificationsAction(); + +const orgAdmin = [Cypress.env('user_orgadmin')]; + +orgAdmin.forEach(admin => { + const desc = { + [Cypress.env('user_orgadmin')]: 'Org admin', + }; + + describe(`Organizations ${desc[admin]} journey`, () => { + beforeEach(() => { + // register interceptors/idle handler + cy.intercept('POST', Cypress.env('api'), req => { + aliasQuery(req, 'getOrganization'); + aliasMutation(req, 'updateOrganizationFriendlyName'); + aliasMutation(req, 'addUserToGroup'); + aliasMutation(req, 'addGroupToOrganization'); + }); + + registerIdleHandler('idle'); + + cy.login(admin, admin); + cy.visit(`${Cypress.env('url')}/organizations/lagoon-demo-organization`); + }); + + if (admin === Cypress.env('user_orgadmin')) { + it('Fails to change org name and desc - no permission for ORGADMIN', () => { + overview.doFailedChangeOrgFriendlyname(testData.organizations.overview.friendlyName); + overview.closeModal(); + overview.doFailedChangeOrgDescription(testData.organizations.overview.description); + overview.closeModal(); + }); + } else { + it('Changes org name and desc', () => { + overview.changeOrgFriendlyname(testData.organizations.overview.friendlyName); + overview.changeOrgDescription(testData.organizations.overview.description); + }); + } + + it('Navigates to groups and creates', () => { + cy.waitForNetworkIdle('@idle', 500); + + const group1 = testData.organizations.groups.newGroupName; + const group2 = testData.organizations.groups.newGroupName2; + + cy.get('.groups').click(); + cy.location('pathname').should('equal', '/organizations/lagoon-demo-organization/groups'); + + group.doAddGroup(group1, group2); + registerIdleHandler('groupQuery'); + group.doAddMemberToGroup(testData.organizations.users.email, group1); + }); + + it('Navigates to projects and creates a new one', () => { + registerIdleHandler('projectsQuery'); + cy.intercept('POST', Cypress.env('api'), req => { + aliasMutation(req, 'addProjectToOrganization'); + }); + + cy.waitForNetworkIdle('@idle', 500); + + cy.get('.projects').click(); + cy.location('pathname').should('equal', '/organizations/lagoon-demo-organization/projects'); + cy.waitForNetworkIdle('@projectsQuery', 1000); + + project.doAddProject(testData.organizations.project); + }); + + it('Navigates to notifications and creates a couple', () => { + cy.intercept('POST', Cypress.env('api'), req => { + aliasMutation(req, 'addNotificationSlack'); + aliasMutation(req, 'UpdateNotificationSlack'); + aliasMutation(req, 'addNotificationRocketChat'); + aliasMutation(req, 'addNotificationMicrosoftTeams'); + aliasMutation(req, 'addNotificationEmail'); + aliasMutation(req, 'addNotificationWebhook'); + }); + + registerIdleHandler('notificationsQuery'); + + cy.waitForNetworkIdle('@idle', 500); + cy.get('.notifications').click(); + cy.location('pathname').should('equal', '/organizations/lagoon-demo-organization/notifications'); + cy.waitForNetworkIdle('@notificationsQuery', 1000); + + const { slack: slackData, email: emailData, webhook: webhookData } = testData.organizations.notifications; + + notifications.doAddNotification('slack', slackData); + notifications.doAddNotification('email', emailData); + notifications.doAddNotification('webhook', webhookData); + }); + + it('Navigates to a project, adds a group and notifications', () => { + cy.visit( + `${Cypress.env('url')}/organizations/lagoon-demo-organization/projects/${ + testData.organizations.project.projectName + }` + ); + + cy.getBySel('addGroupToProject').click(); + + cy.get('.react-select__indicator').click({ force: true }); + cy.get('#react-select-2-option-0').click(); + + cy.getBySel('addGroupToProjectConfirm').click(); + + cy.log('add notifications'); + + cy.getBySel('addNotificationToProject').click(); + + cy.get('[class$=control]').click({ force: true }); + cy.get('#react-select-3-option-0').click(); + + cy.getBySel('addNotificationToProjectConfirm').click(); + }); + + // cleanup + after(() => { + registerIdleHandler('projectsQuery'); + registerIdleHandler('groupQuery'); + cy.intercept('POST', Cypress.env('api'), req => { + aliasMutation(req, 'removeNotification'); + aliasMutation(req, 'deleteGroup'); + aliasMutation(req, 'deleteProject'); + }); + + cy.waitForNetworkIdle('@idle', 500); + cy.get('.groups').click(); + + group.doDeleteGroup(testData.organizations.groups.newGroupName); + cy.wait('@gqldeleteGroupMutation'); + + group.doDeleteGroup(testData.organizations.groups.newGroupName2); + cy.wait('@gqldeleteGroupMutation'); + + cy.waitForNetworkIdle('@idle', 500); + cy.get('.projects').click(); + + cy.waitForNetworkIdle('@projectsQuery', 1000); + + project.doDeleteProject(testData.organizations.project.projectName); + + cy.get('.notifications').click(); + + cy.waitForNetworkIdle('@idle', 500); + + const { + webhook: { name: webhooknName }, + email: { name: emailName }, + slack: { name: slackName }, + } = testData.organizations.notifications; + + notifications.doDeleteNotification(webhooknName); + cy.wait('@gqlremoveNotificationMutation'); // wait for a delete mutation instead + notifications.doDeleteNotification(emailName); + cy.wait('@gqlremoveNotificationMutation'); + notifications.doDeleteNotification(slackName); + }); + }); +}); diff --git a/src/components/Organizations/AddUserToOrganization/index.js b/src/components/Organizations/AddUserToOrganization/index.js index 895415f8..06b2c859 100644 --- a/src/components/Organizations/AddUserToOrganization/index.js +++ b/src/components/Organizations/AddUserToOrganization/index.js @@ -11,8 +11,8 @@ import { Footer } from '../SharedStyles'; import { NewUser } from './Styles'; export const ADD_USER_MUTATION = gql` - mutation AddUserToOrganization($email: String!, $organization: Int!, $owner: Boolean) { - addUserToOrganization(input: { user: { email: $email }, organization: $organization, owner: $owner }) { + mutation AddUserToOrganization($email: String!, $organization: Int!, $owner: Boolean, $admin: Boolean) { + addUserToOrganization(input: { user: { email: $email }, organization: $organization, owner: $owner, admin: $admin }) { id } } @@ -28,6 +28,8 @@ export const AddUserToOrganization = ({ setInputValue, checkboxValueOwner, setCheckboxValueOwner, + checkboxValueAdmin, + setCheckboxValueAdmin, onAddUser, users, }) => { @@ -70,6 +72,16 @@ export const AddUserToOrganization = ({ onChange={setCheckboxValueOwner} /> +
diff --git a/src/lib/query/organizations/OrganizationByID.js b/src/lib/query/organizations/OrganizationByID.js index 14c29b39..160cede7 100644 --- a/src/lib/query/organizations/OrganizationByID.js +++ b/src/lib/query/organizations/OrganizationByID.js @@ -29,6 +29,7 @@ export default gql` lastName email owner + admin } projects { id diff --git a/src/lib/query/organizations/OrganizationByName.js b/src/lib/query/organizations/OrganizationByName.js index 90d074cc..7180efff 100644 --- a/src/lib/query/organizations/OrganizationByName.js +++ b/src/lib/query/organizations/OrganizationByName.js @@ -29,6 +29,7 @@ export default gql` lastName email owner + admin } projects { id diff --git a/src/lib/query/organizations/UsersByOrganization.js b/src/lib/query/organizations/UsersByOrganization.js index 75afe120..97a1c2f5 100644 --- a/src/lib/query/organizations/UsersByOrganization.js +++ b/src/lib/query/organizations/UsersByOrganization.js @@ -31,6 +31,7 @@ export const getOrganization = gql` lastName email owner + admin } } } @@ -52,6 +53,7 @@ export const getOrganizationByName = gql` lastName email owner + admin } } } diff --git a/test/keycloak/configure-keycloak.sh b/test/keycloak/configure-keycloak.sh index 0aa788c0..aee9bf06 100755 --- a/test/keycloak/configure-keycloak.sh +++ b/test/keycloak/configure-keycloak.sh @@ -10,7 +10,7 @@ function is_keycloak_running { function configure_user_passwords { LAGOON_DEMO_USERS=("guest@example.com" "reporter@example.com" "developer@example.com" "maintainer@example.com" "owner@example.com") - LAGOON_DEMO_ORG_USERS=("orguser@example.com" "orgviewer@example.com" "orgowner@example.com" "platformowner@example.com") + LAGOON_DEMO_ORG_USERS=("orguser@example.com" "orgviewer@example.com" "orgadmin@example.com" "orgowner@example.com" "platformowner@example.com") for i in ${LAGOON_DEMO_USERS[@]} do