From: Stephen Smith Date: Tue, 8 Feb 2022 16:43:05 +0000 (-0500) Subject: 18284: Add update vm login dialog, remove default group, update tests X-Git-Tag: 2.4.0~12^2~2 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/b178ea37c7a247a6399ecf1112d5f8c8aae12136?hp=c2a580be1484629e557b967524473a75b2d02274 18284: Add update vm login dialog, remove default group, update tests Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- diff --git a/cypress/integration/virtual-machine-admin.spec.js b/cypress/integration/virtual-machine-admin.spec.js index d057ce49..73804b20 100644 --- a/cypress/integration/virtual-machine-admin.spec.js +++ b/cypress/integration/virtual-machine-admin.spec.js @@ -41,10 +41,10 @@ describe('Virtual machine login manage tests', function() { cy.get('button[title="Add Login Permission"]').click(); }); cy.get('[data-cy=form-dialog]') - .should('contain', 'Add login permissions') + .should('contain', 'Add login permission') .within(() => { cy.get('label') - .contains('Search for users') + .contains('Search for user') .parent() .within(() => { cy.get('input').type('VMAdmin'); @@ -52,13 +52,13 @@ describe('Virtual machine login manage tests', function() { }); cy.get('[role=tooltip]').click(); cy.get('[data-cy=form-dialog]') - .should('contain', 'Add login permissions') + .should('contain', 'Add login permission') .within(() => { cy.get('label') .contains('Add groups') .parent() .within(() => { - cy.get('input').type('sudo{enter}'); + cy.get('input').type('docker sudo{enter}'); }) }); cy.get('[data-cy=form-dialog]').within(() => { @@ -79,10 +79,10 @@ describe('Virtual machine login manage tests', function() { cy.get('button[title="Add Login Permission"]').click(); }); cy.get('[data-cy=form-dialog]') - .should('contain', 'Add login permissions') + .should('contain', 'Add login permission') .within(() => { cy.get('label') - .contains('Search for users') + .contains('Search for user') .parent() .within(() => { cy.get('input').type('VMActive user'); @@ -118,12 +118,100 @@ describe('Virtual machine login manage tests', function() { cy.get('header button[title="Account Management"]').click(); cy.get('#account-menu').contains('Virtual Machines').click(); + cy.get('[data-cy=vm-user-table]') + .contains(vmHost) + .parents('tr') + .within(() => { + cy.get('td').contains('user'); + cy.get('td').should('not.contain', 'docker'); + cy.get('td').should('not.contain', 'sudo'); + cy.get('td').contains('ssh user@' + vmHost); + }); + + // Edit login permissions + cy.loginAs(adminUser); + cy.get('header button[title="Admin Panel"]').click(); + cy.get('#admin-menu').contains('Virtual Machines').click(); + + cy.get('[data-cy=vm-admin-table]') + .contains('admin'); // Wait for page to finish + + cy.get('[data-cy=vm-admin-table]') + .contains(vmHost) + .parents('tr') + .contains('admin') + .click(); + + cy.get('[data-cy=form-dialog]') + .should('contain', 'Update login permission') + .within(() => { + cy.get('label') + .contains('Add groups') + .parent() + .as('groupInput'); + }); + + cy.get('@groupInput').within(() => { + cy.get('div[role=button]').contains('sudo').parent().find('svg').click(); + cy.get('div[role=button]').contains('docker').parent().find('svg').click(); + }); + + cy.get('[data-cy=form-dialog]').within(() => { + cy.get('[data-cy=form-submit-btn]').click(); + }); + + cy.get('[data-cy=vm-admin-table]') + .contains('user'); // Wait for page to finish + + cy.get('[data-cy=vm-admin-table]') + .contains(vmHost) + .parents('tr') + .contains('user') + .click(); + + cy.get('[data-cy=form-dialog]') + .should('contain', 'Update login permission') + .within(() => { + cy.get('label') + .contains('Add groups') + .parent() + .within(() => { + cy.get('input').type('docker{enter}'); + }) + }); + + cy.get('[data-cy=form-dialog]').within(() => { + cy.get('[data-cy=form-submit-btn]').click(); + }); + + // Verify new login permissions + // Check admin's vm page for login + cy.get('header button[title="Account Management"]').click(); + cy.get('#account-menu').contains('Virtual Machines').click(); + + cy.get('[data-cy=vm-user-table]') + .contains(vmHost) + .parents('tr') + .within(() => { + cy.get('td').contains('admin'); + cy.get('td').should('not.contain', 'docker'); + cy.get('td').should('not.contain', 'sudo'); + cy.get('td').contains('ssh admin@' + vmHost); + }); + + // Verify new login permissions + // Check activeUser's vm page for login + cy.loginAs(activeUser); + cy.get('header button[title="Account Management"]').click(); + cy.get('#account-menu').contains('Virtual Machines').click(); + cy.get('[data-cy=vm-user-table]') .contains(vmHost) .parents('tr') .within(() => { cy.get('td').contains('user'); cy.get('td').contains('docker'); + cy.get('td').should('not.contain', 'sudo'); cy.get('td').contains('ssh user@' + vmHost); }); diff --git a/src/store/virtual-machines/virtual-machines-actions.ts b/src/store/virtual-machines/virtual-machines-actions.ts index 08654a44..e2cf6fd4 100644 --- a/src/store/virtual-machines/virtual-machines-actions.ts +++ b/src/store/virtual-machines/virtual-machines-actions.ts @@ -18,6 +18,7 @@ import { PermissionLevel } from "models/permission"; import { deleteResources, updateResources } from 'store/resources/resources-actions'; import { Participant } from "views-components/sharing-dialog/participant-select"; import { initialize, reset } from "redux-form"; +import { getUserDisplayName } from "models/user"; export const virtualMachinesActions = unionize({ SET_REQUESTED_DATE: ofType(), @@ -35,6 +36,7 @@ export const VIRTUAL_MACHINE_ADD_LOGIN_DIALOG = 'virtualMachineAddLoginDialog'; export const VIRTUAL_MACHINE_ADD_LOGIN_FORM = 'virtualMachineAddLoginForm'; export const VIRTUAL_MACHINE_REMOVE_LOGIN_DIALOG = 'virtualMachineRemoveLoginDialog'; +export const VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD = 'uuid'; export const VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD = 'vmUuid'; export const VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD = 'user'; export const VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD = 'groups'; @@ -111,42 +113,67 @@ export const loadVirtualMachinesUserData = () => dispatch(virtualMachinesActions.SET_LINKS(links)); }; -export const openAddVirtualMachineLoginDialog = (uuid: string) => +export const openAddVirtualMachineLoginDialog = (vmUuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - dispatch(initialize(VIRTUAL_MACHINE_ADD_LOGIN_FORM, {[VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: uuid, [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: ['docker']})); + dispatch(initialize(VIRTUAL_MACHINE_ADD_LOGIN_FORM, {[VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: vmUuid, [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: []})); dispatch(dialogActions.OPEN_DIALOG( {id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, data: {}} )); } +export const openEditVirtualMachineLoginDialog = (permissionUuid: string) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const login = await services.permissionService.get(permissionUuid); + const user = await services.userService.get(login.tailUuid); + dispatch(initialize(VIRTUAL_MACHINE_ADD_LOGIN_FORM, { + [VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD]: permissionUuid, + [VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD]: {name: getUserDisplayName(user, true), uuid: login.tailUuid}, + [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: login.properties.groups, + })); + dispatch(dialogActions.OPEN_DIALOG( {id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, data: {updating: true}} )); + } + export interface AddLoginFormData { + [VIRTUAL_MACHINE_UPDATE_LOGIN_UUID_FIELD]: string; [VIRTUAL_MACHINE_ADD_LOGIN_VM_FIELD]: string; [VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD]: Participant; [VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD]: string[]; } -export const addVirtualMachineLogin = ({vmUuid, user, groups}: AddLoginFormData) => +export const addUpdateVirtualMachineLogin = ({uuid, vmUuid, user, groups}: AddLoginFormData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { try { // Get user const userResource = await services.userService.get(user.uuid); - const permission = await services.permissionService.create({ + if (uuid) { + const permission = await services.permissionService.update(uuid, { + tailUuid: userResource.uuid, + name: PermissionLevel.CAN_LOGIN, + properties: { + username: userResource.username, + groups, + } + }); + dispatch(updateResources([permission])); + } else { + const permission = await services.permissionService.create({ headUuid: vmUuid, - tailUuid: userResource.uuid, - name: PermissionLevel.CAN_LOGIN, - properties: { - username: userResource.username, - groups, - } - }); - dispatch(updateResources([permission])); + tailUuid: userResource.uuid, + name: PermissionLevel.CAN_LOGIN, + properties: { + username: userResource.username, + groups, + } + }); + dispatch(updateResources([permission])); + } dispatch(reset(VIRTUAL_MACHINE_ADD_LOGIN_FORM)); dispatch(dialogActions.CLOSE_DIALOG({ id: VIRTUAL_MACHINE_ADD_LOGIN_DIALOG })); dispatch(loadVirtualMachinesAdminData()); dispatch(snackbarActions.OPEN_SNACKBAR({ - message: `Permissions updated`, + message: `Permission updated`, kind: SnackbarKind.SUCCESS })); } catch (e) { diff --git a/src/views-components/virtual-machines-dialog/add-login-dialog.tsx b/src/views-components/virtual-machines-dialog/add-login-dialog.tsx index 8d543406..bfc04716 100644 --- a/src/views-components/virtual-machines-dialog/add-login-dialog.tsx +++ b/src/views-components/virtual-machines-dialog/add-login-dialog.tsx @@ -7,7 +7,7 @@ import { compose } from "redux"; import { reduxForm, InjectedFormProps, WrappedFieldProps, Field } from 'redux-form'; import { withDialog, WithDialogProps } from "store/dialog/with-dialog"; import { FormDialog } from 'components/form-dialog/form-dialog'; -import { VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, VIRTUAL_MACHINE_ADD_LOGIN_FORM, addVirtualMachineLogin, AddLoginFormData, VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD } from 'store/virtual-machines/virtual-machines-actions'; +import { VIRTUAL_MACHINE_ADD_LOGIN_DIALOG, VIRTUAL_MACHINE_ADD_LOGIN_FORM, addUpdateVirtualMachineLogin, AddLoginFormData, VIRTUAL_MACHINE_ADD_LOGIN_USER_FIELD, VIRTUAL_MACHINE_ADD_LOGIN_GROUPS_FIELD } from 'store/virtual-machines/virtual-machines-actions'; import { ParticipantSelect } from 'views-components/sharing-dialog/participant-select'; import { GroupArrayInput } from 'views-components/virtual-machines-dialog/group-array-input'; @@ -16,27 +16,27 @@ export const VirtualMachineAddLoginDialog = compose( reduxForm({ form: VIRTUAL_MACHINE_ADD_LOGIN_FORM, onSubmit: (data, dispatch) => { - dispatch(addVirtualMachineLogin(data)); + dispatch(addUpdateVirtualMachineLogin(data)); } }) )( (props: CreateGroupDialogComponentProps) => ); -type CreateGroupDialogComponentProps = WithDialogProps<{}> & InjectedFormProps; +type CreateGroupDialogComponentProps = WithDialogProps<{updating: boolean}> & InjectedFormProps; const AddLoginFormFields = () => <> ; @@ -50,7 +50,7 @@ const UserField = () => const UserSelect = ({ input, meta }: WrappedFieldProps) => (input.onChange(''))} />; diff --git a/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx b/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx index bfa6be26..468ef35a 100644 --- a/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx +++ b/src/views/virtual-machine-panel/virtual-machine-admin-panel.tsx @@ -8,7 +8,7 @@ import { Grid, Card, Chip, CardContent, TableBody, TableCell, TableHead, TableRo import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles'; import { ArvadosTheme } from 'common/custom-theme'; import { compose, Dispatch } from 'redux'; -import { loadVirtualMachinesAdminData, openAddVirtualMachineLoginDialog, openRemoveVirtualMachineLoginDialog } from 'store/virtual-machines/virtual-machines-actions'; +import { loadVirtualMachinesAdminData, openAddVirtualMachineLoginDialog, openRemoveVirtualMachineLoginDialog, openEditVirtualMachineLoginDialog } from 'store/virtual-machines/virtual-machines-actions'; import { RootState } from 'store/store'; import { ListResults } from 'services/common-service/common-service'; import { MoreOptionsIcon, AddUserIcon } from 'components/icon/icon'; @@ -40,7 +40,7 @@ const mapStateToProps = (state: RootState) => { }; }; -const mapDispatchToProps = (dispatch: Dispatch): Pick => ({ +const mapDispatchToProps = (dispatch: Dispatch): Pick => ({ loadVirtualMachinesData: () => dispatch(loadVirtualMachinesAdminData()), onOptionsMenuOpen: (event, virtualMachine) => { dispatch(openVirtualMachinesContextMenu(event, virtualMachine)); @@ -51,6 +51,9 @@ const mapDispatchToProps = (dispatch: Dispatch): Pick { dispatch(openRemoveVirtualMachineLoginDialog(uuid)); }, + onLoginEdit: (uuid: string) => { + dispatch(openEditVirtualMachineLoginDialog(uuid)); + }, }); interface VirtualMachinesPanelDataProps { @@ -65,6 +68,7 @@ interface VirtualMachinesPanelActionProps { onOptionsMenuOpen: (event: React.MouseEvent, virtualMachine: VirtualMachinesResource) => void; onAddLogin: (uuid: string) => void; onDeleteLogin: (uuid: string) => void; + onLoginEdit: (uuid: string) => void; } type VirtualMachineProps = VirtualMachinesPanelActionProps & VirtualMachinesPanelDataProps & WithStyles; @@ -117,7 +121,7 @@ const virtualMachinesTable = (props: VirtualMachineProps) => {props.links.items.filter((link) => (link.headUuid === machine.uuid)).map((permission, i) => ( - } onDelete={event => props.onDeleteLogin(permission.uuid)} /> + } onDelete={event => props.onDeleteLogin(permission.uuid)} onClick={event => props.onLoginEdit(permission.uuid)} /> ))}