Commit 13ed1049 by Hugo Häggmark Committed by Leonard Gram

teams: hide tabs settings and groupsync for non team admins

parent d1481cac
......@@ -74,86 +74,4 @@ describe('Functions', () => {
expect(instance.props.addTeamMember).toHaveBeenCalledWith(1);
});
describe('isSignedInUserTeamAdmin', () => {
describe('when feature toggle editorsCanAdmin is turned off', () => {
it('should return true', () => {
const { instance } = setup({ editorsCanAdmin: false });
const result = instance.isSignedInUserTeamAdmin();
expect(result).toBe(true);
});
});
describe('when feature toggle editorsCanAdmin is turned on', () => {
it('should return true if signed in user is grafanaAdmin', () => {
const members = getMockTeamMembers(5, 5);
const { instance } = setup({
members,
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: true,
orgRole: OrgRole.Viewer,
},
});
const result = instance.isSignedInUserTeamAdmin();
expect(result).toBe(true);
});
it('should return true if signed in user is org admin', () => {
const members = getMockTeamMembers(5, 5);
const { instance } = setup({
members,
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Admin,
},
});
const result = instance.isSignedInUserTeamAdmin();
expect(result).toBe(true);
});
it('should return true if signed in user is team admin', () => {
const members = getMockTeamMembers(5, signedInUserId);
const { instance } = setup({
members,
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
},
});
const result = instance.isSignedInUserTeamAdmin();
expect(result).toBe(true);
});
it('should return false if signed in user is not grafanaAdmin, org admin or team admin', () => {
const members = getMockTeamMembers(5, 5);
const { instance } = setup({
members,
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
},
});
const result = instance.isSignedInUserTeamAdmin();
expect(result).toBe(false);
});
});
});
});
......@@ -3,9 +3,9 @@ import { connect } from 'react-redux';
import SlideDown from 'app/core/components/Animations/SlideDown';
import { UserPicker } from 'app/core/components/Select/UserPicker';
import { TagBadge } from 'app/core/components/TagFilter/TagBadge';
import { TeamMember, User, TeamPermissionLevel, OrgRole } from 'app/types';
import { TeamMember, User } from 'app/types';
import { loadTeamMembers, addTeamMember, setSearchMemberQuery } from './state/actions';
import { getSearchMemberQuery, getTeamMembers } from './state/selectors';
import { getSearchMemberQuery, getTeamMembers, isSignedInUserTeamAdmin } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
import { WithFeatureToggle } from 'app/core/components/WithFeatureToggle';
import { config } from 'app/core/config';
......@@ -69,19 +69,11 @@ export class TeamMembers extends PureComponent<Props, State> {
);
}
isSignedInUserTeamAdmin = (): boolean => {
const { members, editorsCanAdmin, signedInUser } = this.props;
const userInMembers = members.find(m => m.userId === signedInUser.id);
const isAdmin = signedInUser.isGrafanaAdmin || signedInUser.orgRole === OrgRole.Admin;
const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin;
const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin;
return isSignedInUserTeamAdmin || !editorsCanAdmin;
};
render() {
const { isAdding } = this.state;
const { searchMemberQuery, members, syncEnabled, editorsCanAdmin } = this.props;
const { searchMemberQuery, members, syncEnabled, editorsCanAdmin, signedInUser } = this.props;
const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser });
return (
<div>
<div className="page-action-bar">
......@@ -100,7 +92,7 @@ export class TeamMembers extends PureComponent<Props, State> {
<button
className="btn btn-primary pull-right"
onClick={this.onToggleAdding}
disabled={isAdding || !this.isSignedInUserTeamAdmin()}
disabled={isAdding || !isTeamAdmin}
>
Add member
</button>
......@@ -145,7 +137,7 @@ export class TeamMembers extends PureComponent<Props, State> {
member={member}
syncEnabled={syncEnabled}
editorsCanAdmin={editorsCanAdmin}
signedInUserIsTeamAdmin={this.isSignedInUserTeamAdmin()}
signedInUserIsTeamAdmin={isTeamAdmin}
/>
))}
</tbody>
......
import React from 'react';
import { shallow } from 'enzyme';
import { TeamPages, Props } from './TeamPages';
import { NavModel, Team } from '../../types';
import { NavModel, Team, TeamMember, OrgRole } from '../../types';
import { getMockTeam } from './__mocks__/teamMocks';
import { User } from 'app/core/services/context_srv';
jest.mock('app/core/config', () => ({
buildInfo: { isEnterprise: true },
......@@ -15,6 +16,13 @@ const setup = (propOverrides?: object) => {
loadTeam: jest.fn(),
pageName: 'members',
team: {} as Team,
members: [] as TeamMember[],
editorsCanAdmin: false,
signedInUser: {
id: 1,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
} as User,
};
Object.assign(props, propOverrides);
......@@ -65,4 +73,46 @@ describe('Render', () => {
expect(wrapper).toMatchSnapshot();
});
describe('when feature toggle editorsCanAdmin is turned on', () => {
it('should render settings page if user is team admin', () => {
const { wrapper } = setup({
team: getMockTeam(),
pageName: 'settings',
preferences: {
homeDashboardId: 1,
theme: 'Default',
timezone: 'Default',
},
editorsCanAdmin: true,
signedInUser: {
id: 1,
isGrafanaAdmin: false,
orgRole: OrgRole.Admin,
} as User,
});
expect(wrapper).toMatchSnapshot();
});
it('should not render settings page if user is team member', () => {
const { wrapper } = setup({
team: getMockTeam(),
pageName: 'settings',
preferences: {
homeDashboardId: 1,
theme: 'Default',
timezone: 'Default',
},
editorsCanAdmin: true,
signedInUser: {
id: 1,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
} as User,
});
expect(wrapper).toMatchSnapshot();
});
});
});
......@@ -7,12 +7,13 @@ import Page from 'app/core/components/Page/Page';
import TeamMembers from './TeamMembers';
import TeamSettings from './TeamSettings';
import TeamGroupSync from './TeamGroupSync';
import { NavModel, Team } from 'app/types';
import { NavModel, Team, TeamMember } from 'app/types';
import { loadTeam } from './state/actions';
import { getTeam } from './state/selectors';
import { getTeam, getTeamMembers, isSignedInUserTeamAdmin } from './state/selectors';
import { getTeamLoadingNav } from './state/navModel';
import { getNavModel } from 'app/core/selectors/navModel';
import { getRouteParamsId, getRouteParamsPage } from '../../core/selectors/location';
import { contextSrv, User } from 'app/core/services/context_srv';
export interface Props {
team: Team;
......@@ -20,6 +21,9 @@ export interface Props {
teamId: number;
pageName: string;
navModel: NavModel;
members?: TeamMember[];
editorsCanAdmin?: boolean;
signedInUser?: User;
}
interface State {
......@@ -61,7 +65,15 @@ export class TeamPages extends PureComponent<Props, State> {
return _.includes(pages, currentPage) ? currentPage : pages[0];
}
renderPage() {
hideTabsFromNonTeamAdmin = (navModel: NavModel, isSignedInUserTeamAdmin: boolean) => {
if (!isSignedInUserTeamAdmin && navModel.main && navModel.main.children) {
navModel.main.children = navModel.main.children.filter(navItem => navItem.text === 'Members');
}
return navModel;
};
renderPage(isSignedInUserTeamAdmin: boolean) {
const { isSyncEnabled } = this.state;
const currentPage = this.getCurrentPage();
......@@ -70,21 +82,22 @@ export class TeamPages extends PureComponent<Props, State> {
return <TeamMembers syncEnabled={isSyncEnabled} />;
case PageTypes.Settings:
return <TeamSettings />;
return isSignedInUserTeamAdmin && <TeamSettings />;
case PageTypes.GroupSync:
return isSyncEnabled && <TeamGroupSync />;
return isSignedInUserTeamAdmin && isSyncEnabled && <TeamGroupSync />;
}
return null;
}
render() {
const { team, navModel } = this.props;
const { team, navModel, members, editorsCanAdmin, signedInUser } = this.props;
const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser });
return (
<Page navModel={navModel}>
<Page navModel={this.hideTabsFromNonTeamAdmin(navModel, isTeamAdmin)}>
<Page.Contents isLoading={this.state.isLoading}>
{team && Object.keys(team).length !== 0 && this.renderPage()}
{team && Object.keys(team).length !== 0 && this.renderPage(isTeamAdmin)}
</Page.Contents>
</Page>
);
......@@ -101,6 +114,9 @@ function mapStateToProps(state) {
teamId: teamId,
pageName: pageName,
team: getTeam(state.team, teamId),
members: getTeamMembers(state.team),
editorsCanAdmin: config.editorsCanAdmin, // this makes the feature toggle mockable/controllable from tests,
signedInUser: contextSrv.user, // this makes the feature toggle mockable/controllable from tests,
};
}
......
......@@ -47,3 +47,25 @@ exports[`Render should render settings and preferences page 1`] = `
</PageContents>
</Page>
`;
exports[`Render when feature toggle editorsCanAdmin is turned on should not render settings page if user is team member 1`] = `
<Page
navModel={Object {}}
>
<PageContents
isLoading={true}
/>
</Page>
`;
exports[`Render when feature toggle editorsCanAdmin is turned on should render settings page if user is team admin 1`] = `
<Page
navModel={Object {}}
>
<PageContents
isLoading={true}
>
<Connect(TeamSettings) />
</PageContents>
</Page>
`;
import { getTeam, getTeamMembers, getTeams } from './selectors';
import { getTeam, getTeamMembers, getTeams, isSignedInUserTeamAdmin, Config } from './selectors';
import { getMockTeam, getMockTeamMembers, getMultipleMockTeams } from '../__mocks__/teamMocks';
import { Team, TeamGroup, TeamsState, TeamState } from '../../../types';
import { Team, TeamGroup, TeamsState, TeamState, OrgRole } from '../../../types';
import { User } from 'app/core/services/context_srv';
describe('Teams selectors', () => {
describe('Get teams', () => {
......@@ -55,3 +56,94 @@ describe('Team selectors', () => {
});
});
});
const signedInUserId = 1;
const setup = (configOverrides?: Partial<Config>) => {
const defaultConfig: Config = {
editorsCanAdmin: false,
members: getMockTeamMembers(5, 5),
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
} as User,
};
return { ...defaultConfig, ...configOverrides };
};
describe('isSignedInUserTeamAdmin', () => {
describe('when feature toggle editorsCanAdmin is turned off', () => {
it('should return true', () => {
const config = setup();
const result = isSignedInUserTeamAdmin(config);
expect(result).toBe(true);
});
});
describe('when feature toggle editorsCanAdmin is turned on', () => {
it('should return true if signed in user is grafanaAdmin', () => {
const config = setup({
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: true,
orgRole: OrgRole.Viewer,
} as User,
});
const result = isSignedInUserTeamAdmin(config);
expect(result).toBe(true);
});
it('should return true if signed in user is org admin', () => {
const config = setup({
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Admin,
} as User,
});
const result = isSignedInUserTeamAdmin(config);
expect(result).toBe(true);
});
it('should return true if signed in user is team admin', () => {
const config = setup({
members: getMockTeamMembers(5, signedInUserId),
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
} as User,
});
const result = isSignedInUserTeamAdmin(config);
expect(result).toBe(true);
});
it('should return false if signed in user is not grafanaAdmin, org admin or team admin', () => {
const config = setup({
editorsCanAdmin: true,
signedInUser: {
id: signedInUserId,
isGrafanaAdmin: false,
orgRole: OrgRole.Viewer,
} as User,
});
const result = isSignedInUserTeamAdmin(config);
expect(result).toBe(false);
});
});
});
import { Team, TeamsState, TeamState } from 'app/types';
import { Team, TeamsState, TeamState, TeamMember, OrgRole, TeamPermissionLevel } from 'app/types';
import { User } from 'app/core/services/context_srv';
export const getSearchQuery = (state: TeamsState) => state.searchQuery;
export const getSearchMemberQuery = (state: TeamState) => state.searchMemberQuery;
......@@ -28,3 +29,18 @@ export const getTeamMembers = (state: TeamState) => {
return regex.test(member.login) || regex.test(member.email);
});
};
export interface Config {
members: TeamMember[];
editorsCanAdmin: boolean;
signedInUser: User;
}
export const isSignedInUserTeamAdmin = (config: Config): boolean => {
const userInMembers = config.members.find(m => m.userId === config.signedInUser.id);
const isAdmin = config.signedInUser.isGrafanaAdmin || config.signedInUser.orgRole === OrgRole.Admin;
const userIsTeamAdmin = userInMembers && userInMembers.permission === TeamPermissionLevel.Admin;
const isSignedInUserTeamAdmin = isAdmin || userIsTeamAdmin;
return isSignedInUserTeamAdmin || !config.editorsCanAdmin;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment