Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests groupmembership panel #5801

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ describe('Groups Control Panel Test', () => {

// select first group with name, delete it and search if its exists or not!
cy.get('div[role="listbox"]').first().click();
cy.get('div[role="option"]').should('be.visible');
cy.get('div[role="option"]').first().click();
cy.contains('Delete Group');
cy.get('button.ui.primary.button').should('have.text', 'OK').click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,41 +41,33 @@ describe('User Group Membership Control Panel test for NOT many users and many g
cy.visit('/controlpanel/usergroupmembership');
cy.wait('@usergroup');

cy.get('.usergroupmembership').then(($segmentUsergroupmembership) => {
if ($segmentUsergroupmembership.hasClass('upgrade-info')) {
// Panel not supported.
} else {
cy.get('#source-row-max div.checkbox_cooks input').check({
force: true,
});
cy.reload();
cy.get('#source-row-max div.checkbox_cooks input').should('be.checked');
}
cy.get('.usergroupmembership').then(() => {
cy.get('#source-row-max div.checkbox_cooks input').check({
force: true,
});
cy.reload();
cy.get('#source-row-max div.checkbox_cooks input').should('be.checked');
});
});
it('I can search for a user and show his groups', () => {
cy.visit('/controlpanel/usergroupmembership');
cy.wait('@usergroup');

cy.get('.usergroupmembership').then(($segmentUsergroupmembership) => {
if ($segmentUsergroupmembership.hasClass('upgrade-info')) {
// Panel not supported.
} else {
// Show user
cy.get('#user-search-input').type('fröhlich');
cy.contains('Max');

// Show membership of group "Administrators"
cy.get('input[id="group-search-input"]').type('Adm');
cy.contains('Administrators');
cy.get('.label-options').should('not.contain', 'teachers');

// Show also groups membersip of groups of users
cy.get('input[name="addJoinedGroups"]').check({
force: true,
});
cy.get('.label-options').contains('teachers');
}
cy.get('.usergroupmembership').then(() => {
// Show user
cy.get('#user-search-input').type('fröhlich');
cy.contains('Max');

// Show membership of group "Administrators"
cy.get('input[id="group-search-input"]').type('Adm');
cy.contains('Administrators');
cy.get('.label-options').should('not.contain', 'teachers');

// Show also groups membersip of groups of users
cy.get('input[name="addJoinedGroups"]').check({
force: true,
});
cy.get('.label-options').contains('teachers');
});
});
});
Expand Down Expand Up @@ -160,3 +152,27 @@ describe('User Group Membership Control Panel test for MANY users and MANY group
});
});
});

// TODO Test should not fail with GET @users and @groups unauthorized for user with role "Site Administrator".
describe('User Group Membership Control Panel test for non-manager', () => {
beforeEach(() => {
init();
cy.createUser({
username: 'siteadmin',
fullname: 'Sven Siteadministrator',
roles: ['Site Administrator'],
});
cy.autologin('siteadmin', 'password');
});
it('Non-manager is not allowed to edit managers', () => {
cy.visit('/controlpanel/usergroupmembership');
cy.wait('@usergroup');

// Editing checkboxes for Administrators group are disabled.
cy.get('.usergroupmembership').then(() => {
cy.get('#source-row-max div.checkbox_Administrators input').should(
'be.disabled',
);
});
});
});
1 change: 1 addition & 0 deletions packages/volto/news/5244.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Do not display options for Site Administrator to create, modify, or delete Manager users. @wesleybl
1 change: 1 addition & 0 deletions packages/volto/news/5801.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add test for non-manager user editing group memberships. @ksuess
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import {
listRoles,
updateGroup,
authenticatedRole,
getUser,
} from '@plone/volto/actions';
import jwtDecode from 'jwt-decode';
import {
Icon,
ModalForm,
Expand All @@ -21,7 +23,12 @@ import {
Error,
} from '@plone/volto/components';
import { Link } from 'react-router-dom';
import { Helmet, messages } from '@plone/volto/helpers';
import {
Helmet,
messages,
isManager,
canAssignRole,
} from '@plone/volto/helpers';
import clearSVG from '@plone/volto/icons/clear.svg';
import addUserSvg from '@plone/volto/icons/add-user.svg';
import saveSVG from '@plone/volto/icons/save.svg';
Expand Down Expand Up @@ -75,6 +82,19 @@ class GroupsControlpanel extends Component {
groupname: PropTypes.string,
}),
).isRequired,
user: PropTypes.shape({
'@id': PropTypes.string,
id: PropTypes.string,
description: PropTypes.string,
email: PropTypes.string,
fullname: PropTypes.string,
groups: PropTypes.object,
location: PropTypes.string,
portrait: PropTypes.string,
home_page: PropTypes.string,
roles: PropTypes.arrayOf(PropTypes.string),
username: PropTypes.string,
}).isRequired,
};

/**
Expand Down Expand Up @@ -118,6 +138,7 @@ class GroupsControlpanel extends Component {
groupEntries: this.props.groups,
});
}
await this.props.getUser(this.props.userId);
};
/**
* Component did mount
Expand Down Expand Up @@ -375,6 +396,8 @@ class GroupsControlpanel extends Component {
? this.state.groupToDelete.id
: '';

const isUserManager = isManager(this.props.user);

return (
<Container className="users-control-panel">
<Helmet title={this.props.intl.formatMessage(messages.groups)} />
Expand Down Expand Up @@ -460,10 +483,9 @@ class GroupsControlpanel extends Component {
messages.addGroupsFormRolesTitle,
),
type: 'array',
choices: this.props.roles.map((role) => [
role.id,
role.title,
]),
choices: this.props.roles
.filter((role) => canAssignRole(isUserManager, role))
.map((role) => [role.id, role.title]),
noValueOption: false,
description: '',
},
Expand Down Expand Up @@ -553,6 +575,7 @@ class GroupsControlpanel extends Component {
group={group}
updateGroups={this.updateGroupRole}
inheritedRole={this.state.authenticatedRole}
isUserManager={isUserManager}
/>
))}
</Table.Body>
Expand Down Expand Up @@ -640,6 +663,10 @@ export default compose(
injectIntl,
connect(
(state, props) => ({
user: state.users.user,
userId: state.userSession.token
? jwtDecode(state.userSession.token).sub
: '',
roles: state.roles.roles,
groups: state.groups.groups,
description: state.description,
Expand All @@ -661,6 +688,7 @@ export default compose(
createGroup,
updateGroup,
authenticatedRole,
getUser,
},
dispatch,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-intl-redux';
import jwt from 'jsonwebtoken';

import GroupsControlpanel from './GroupsControlpanel';

Expand All @@ -12,6 +13,17 @@ jest.mock('react-portal', () => ({
describe('UsersControlpanel', () => {
it('renders a user control component', () => {
const store = mockStore({
userSession: {
token: jwt.sign({ sub: 'john' }, 'secret'),
},
users: {
users: [],
create: { loading: false },
user: {
roles: ['Manager'],
'@id': 'admin',
},
},
roles: { roles: [] },
groups: {
groups: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
import trashSVG from '@plone/volto/icons/delete.svg';
import ploneSVG from '@plone/volto/icons/plone.svg';
import { Icon } from '@plone/volto/components';
import { canAssignRole } from '@plone/volto/helpers';

/**
* UsersControlpanelGroups class.
Expand Down Expand Up @@ -38,6 +39,7 @@ class RenderGroups extends Component {
).isRequired,
inheritedRole: PropTypes.array,
onDelete: PropTypes.func.isRequired,
isUserManager: PropTypes.bool.isRequired,
};

/**
Expand Down Expand Up @@ -69,6 +71,12 @@ class RenderGroups extends Component {
isAuthGroup = (roleId) => {
return this.props.inheritedRole.includes(roleId);
};

canDeleteGroup() {
if (this.props.isUserManager) return true;
return !this.props.group.roles.includes('Manager');
}

/**
* Render method.
* @method render
Expand Down Expand Up @@ -98,22 +106,25 @@ class RenderGroups extends Component {
}
onChange={this.onChange}
value={`${this.props.group.id}&role=${role.id}`}
disabled={!canAssignRole(this.props.isUserManager, role)}
/>
)}
</Table.Cell>
))}
<Table.Cell textAlign="right">
<Dropdown icon="ellipsis horizontal">
<Dropdown.Menu className="left">
<Dropdown.Item
onClick={this.props.onDelete}
value={this.props.group['@id']}
>
<Icon name={trashSVG} size="15px" />
<FormattedMessage id="Delete" defaultMessage="Delete" />
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
{this.canDeleteGroup() && (
<Dropdown icon="ellipsis horizontal">
<Dropdown.Menu className="left">
<Dropdown.Item
onClick={this.props.onDelete}
value={this.props.group['@id']}
>
<Icon name={trashSVG} size="15px" />
<FormattedMessage id="Delete" defaultMessage="Delete" />
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}
</Table.Cell>
</Table.Row>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,27 @@ describe('UsersControlpanelGroups', () => {
group={testGroups}
roles={testRoles}
onDelete={() => {}}
isUserManager={true}
/>
</Provider>,
);
const json = component.toJSON();
expect(json).toMatchSnapshot();
});
it('renders a UsersControlpanelGroups component with no Manager user', () => {
const store = mockStore({
intl: {
locale: 'en',
messages: {},
},
});
const component = renderer.create(
<Provider store={store}>
<RenderGroups
group={testGroups}
roles={testRoles}
onDelete={() => {}}
isUserManager={false}
/>
</Provider>,
);
Expand Down
Loading
Loading