diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-09-25 16:00:06 +0200 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-10-02 17:18:15 +0200 |
commit | 77d3e365acbfd9095efe4458c643a9e8ccdada13 (patch) | |
tree | 8b19a0ace2cb8b2ce57005b62eec498c17d72866 /server/sonar-web/src/main/js/apps/quality-profiles/details | |
parent | 7c42d81e08599b315076118e961361a96df62323 (diff) | |
download | sonarqube-77d3e365acbfd9095efe4458c643a9e8ccdada13.tar.gz sonarqube-77d3e365acbfd9095efe4458c643a9e8ccdada13.zip |
SONAR-1330 Allow only authorized actions on the Quality Profiles page
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles/details')
8 files changed, 210 insertions, 33 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx index 59566996434..524a148dadd 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.tsx @@ -26,7 +26,6 @@ import ProfilePermissions from './ProfilePermissions'; import { Exporter, Profile } from '../types'; interface Props { - canAdmin: boolean; exporters: Exporter[]; onRequestFail: (reasong: any) => void; organization: string | null; @@ -36,18 +35,17 @@ interface Props { } export default function ProfileDetails(props: Props) { + const { profile } = props; return ( <div> <div className="quality-profile-grid"> <div className="quality-profile-grid-left"> <ProfileRules {...props} /> <ProfileExporters {...props} /> - {props.canAdmin && - !props.profile.isBuiltIn && ( - <ProfilePermissions - organization={props.organization || undefined} - profile={props.profile} - /> + {profile.actions && + profile.actions.edit && + !profile.isBuiltIn && ( + <ProfilePermissions organization={props.organization || undefined} profile={profile} /> )} </div> <div className="quality-profile-grid-right"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx index d0091653d08..5b04d60fdd4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.tsx @@ -33,7 +33,6 @@ import { import { Profile } from '../types'; interface Props { - canAdmin: boolean; onRequestFail: (reasong: any) => void; profile: Profile; organization: string | null; @@ -113,7 +112,6 @@ export default class ProfileHeader extends React.PureComponent<Props> { {translate('actions')} <i className="icon-dropdown" /> </button> <ProfileActions - canAdmin={this.props.canAdmin} onRequestFail={this.props.onRequestFail} organization={organization} profile={profile} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx index 1e71f1bd612..77f8a92a7f1 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.tsx @@ -26,7 +26,6 @@ import { getProfileInheritance } from '../../../api/quality-profiles'; import { Profile } from '../types'; interface Props { - canAdmin: boolean; onRequestFail: (reason: any) => void; organization: string | null; profile: Profile; @@ -120,8 +119,9 @@ export default class ProfileInheritance extends React.PureComponent<Props, State return ( <div className="boxed-group quality-profile-inheritance"> - {this.props.canAdmin && - !this.props.profile.isBuiltIn && ( + {profile.actions && + profile.actions.edit && + !profile.isBuiltIn && ( <div className="boxed-group-actions"> <button className="pull-right js-change-parent" onClick={this.handleChangeParentClick}> {translate('quality_profiles.change_parent')} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx index ca284571441..b809abd6a6f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.tsx @@ -26,7 +26,6 @@ import { translate } from '../../../helpers/l10n'; import { Profile } from '../types'; interface Props { - canAdmin: boolean; organization: string | null; profile: Profile; updateProfiles: () => Promise<void>; @@ -127,10 +126,12 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { } render() { + const { profile } = this.props; return ( <div className="boxed-group quality-profile-projects"> - {this.props.canAdmin && - !this.props.profile.isDefault && ( + {profile.actions && + profile.actions.edit && + !profile.isDefault && ( <div className="boxed-group-actions"> <button className="js-change-projects" onClick={this.handleChangeClick}> {translate('quality_profiles.change_projects')} @@ -145,7 +146,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { <div className="boxed-group-inner"> {this.state.loading ? ( <i className="spinner" /> - ) : this.props.profile.isDefault ? ( + ) : profile.isDefault ? ( this.renderDefault() ) : ( this.renderProjects() @@ -156,7 +157,7 @@ export default class ProfileProjects extends React.PureComponent<Props, State> { <ChangeProjectsForm onClose={this.closeForm} organization={this.props.organization} - profile={this.props.profile} + profile={profile} /> )} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx index b9ee6c91fac..a036a636f62 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.tsx @@ -33,7 +33,6 @@ import { Profile } from '../types'; const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL']; interface Props { - canAdmin: boolean; organization: string | null; profile: Profile; } @@ -181,7 +180,8 @@ export default class ProfileRules extends React.PureComponent<Props, State> { </tbody> </table> - {this.props.canAdmin && + {profile.actions && + profile.actions.edit && !profile.isBuiltIn && ( <div className="text-right big-spacer-top"> <Link to={activateMoreUrl} className="button js-activate-rules"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx new file mode 100644 index 00000000000..f799c8e3f28 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileDetails-test.tsx @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import ProfileDetails from '../ProfileDetails'; +import { Profile } from '../../types'; + +it('renders without permissions', () => { + expect( + shallow( + <ProfileDetails + exporters={[]} + onRequestFail={jest.fn()} + organization="org" + profile={{} as Profile} + profiles={[]} + updateProfiles={jest.fn()} + /> + ) + ).toMatchSnapshot(); +}); + +it('renders with edit permission', () => { + expect( + shallow( + <ProfileDetails + exporters={[]} + onRequestFail={jest.fn()} + organization="org" + profile={{ actions: { edit: true } } as Profile} + profiles={[]} + updateProfiles={jest.fn()} + /> + ) + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx index 9b282b2e2a1..95a3665df08 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx @@ -40,6 +40,8 @@ const PROFILE = { rulesUpdatedAt: '2017-06-28T12:58:44+0000' }; +const EDITABLE_PROFILE = { ...PROFILE, actions: { edit: true } }; + const apiResponseAll = { total: 243, facets: [ @@ -81,7 +83,7 @@ const apiResponseActive = { }); it('should render the quality profiles rules with sonarway comparison', () => { - const wrapper = shallow(<ProfileRules canAdmin={false} organization="foo" profile={PROFILE} />); + const wrapper = shallow(<ProfileRules organization="foo" profile={PROFILE} />); const instance = wrapper.instance() as any; instance.mounted = true; instance.loadRules(); @@ -93,16 +95,15 @@ it('should render the quality profiles rules with sonarway comparison', () => { }); it('should show a button to activate more rules for admins', () => { - const wrapper = shallow(<ProfileRules canAdmin={true} organization="foo" profile={PROFILE} />); + const wrapper = shallow(<ProfileRules organization="foo" profile={EDITABLE_PROFILE} />); expect(wrapper.find('.js-activate-rules')).toMatchSnapshot(); }); it('should show a deprecated rules warning message', () => { const wrapper = shallow( <ProfileRules - canAdmin={true} organization="foo" - profile={{ ...PROFILE, activeDeprecatedRuleCount: 8 }} + profile={{ ...EDITABLE_PROFILE, activeDeprecatedRuleCount: 8 }} /> ); expect(wrapper.find('ProfileRulesDeprecatedWarning')).toMatchSnapshot(); @@ -110,14 +111,7 @@ it('should show a deprecated rules warning message', () => { it('should not show a button to activate more rules on built in profiles', () => { const wrapper = shallow( - <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} /> - ); - expect(wrapper.find('.js-activate-rules')).toHaveLength(0); -}); - -it('should not show a button to activate more rules on built in profiles', () => { - const wrapper = shallow( - <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} /> + <ProfileRules organization={null} profile={{ ...EDITABLE_PROFILE, isBuiltIn: true }} /> ); expect(wrapper.find('.js-activate-rules')).toHaveLength(0); }); @@ -125,7 +119,7 @@ it('should not show a button to activate more rules on built in profiles', () => it('should not show sonarway comparison for built in profiles', () => { (apiQP as any).getQualityProfile = jest.fn(() => Promise.resolve()); const wrapper = shallow( - <ProfileRules canAdmin={true} organization={null} profile={{ ...PROFILE, isBuiltIn: true }} /> + <ProfileRules organization={null} profile={{ ...PROFILE, isBuiltIn: true }} /> ); const instance = wrapper.instance() as any; instance.mounted = true; @@ -147,7 +141,7 @@ it('should not show sonarway comparison if there is no missing rules', () => { } }) ); - const wrapper = shallow(<ProfileRules canAdmin={true} organization={null} profile={PROFILE} />); + const wrapper = shallow(<ProfileRules organization={null} profile={PROFILE} />); const instance = wrapper.instance() as any; instance.mounted = true; instance.loadRules(); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap new file mode 100644 index 00000000000..85f0f92c7a5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileDetails-test.tsx.snap @@ -0,0 +1,133 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders with edit permission 1`] = ` +<div> + <div + className="quality-profile-grid" + > + <div + className="quality-profile-grid-left" + > + <ProfileRules + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={ + Object { + "actions": Object { + "edit": true, + }, + } + } + profiles={Array []} + updateProfiles={[Function]} + /> + <ProfileExporters + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={ + Object { + "actions": Object { + "edit": true, + }, + } + } + profiles={Array []} + updateProfiles={[Function]} + /> + <ProfilePermissions + organization="org" + profile={ + Object { + "actions": Object { + "edit": true, + }, + } + } + /> + </div> + <div + className="quality-profile-grid-right" + > + <ProfileInheritance + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={ + Object { + "actions": Object { + "edit": true, + }, + } + } + profiles={Array []} + updateProfiles={[Function]} + /> + <ProfileProjects + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={ + Object { + "actions": Object { + "edit": true, + }, + } + } + profiles={Array []} + updateProfiles={[Function]} + /> + </div> + </div> +</div> +`; + +exports[`renders without permissions 1`] = ` +<div> + <div + className="quality-profile-grid" + > + <div + className="quality-profile-grid-left" + > + <ProfileRules + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={Object {}} + profiles={Array []} + updateProfiles={[Function]} + /> + <ProfileExporters + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={Object {}} + profiles={Array []} + updateProfiles={[Function]} + /> + </div> + <div + className="quality-profile-grid-right" + > + <ProfileInheritance + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={Object {}} + profiles={Array []} + updateProfiles={[Function]} + /> + <ProfileProjects + exporters={Array []} + onRequestFail={[Function]} + organization="org" + profile={Object {}} + profiles={Array []} + updateProfiles={[Function]} + /> + </div> + </div> +</div> +`; |