Browse Source

SONAR-15702 Prevent displaying App if no access to child projects

tags/9.3.0.51899
Jeremy Davis 2 years ago
parent
commit
3991002357

+ 58
- 0
server/sonar-web/src/main/js/app/components/NonAdminPagesContainer.tsx View File

@@ -0,0 +1,58 @@
/*
* SonarQube
* Copyright (C) 2009-2021 SonarSource SA
* mailto:info 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 { Alert } from '../../components/ui/Alert';
import { translate } from '../../helpers/l10n';
import { BranchLike } from '../../types/branch-like';
import { isApplication } from '../../types/component';

export interface NonAdminPagesContainerProps {
children: JSX.Element;
branchLike?: BranchLike;
branchLikes: BranchLike[];
component: T.Component;
isInProgress?: boolean;
isPending?: boolean;
onBranchesChange: () => void;
onComponentChange: (changes: {}) => void;
}

export default function NonAdminPagesContainer(props: NonAdminPagesContainerProps) {
const { children, component } = props;

/*
* Catch Applications for which the user does not have access to all child projects
* and prevent displaying whatever page was requested.
* This doesn't apply to admin pages (those are not within this container)
*/
if (isApplication(component.qualifier) && !component.canBrowseAllChildProjects) {
return (
<div className="page page-limited display-flex-justify-center">
<Alert className="max-width-60 huge-spacer-top" display="block" variant="error">
<p>{translate('application.cannot_access_all_child_projects1')}</p>
<br />
<p>{translate('application.cannot_access_all_child_projects2')}</p>
</Alert>
</div>
);
}

return React.cloneElement(children, props);
}

+ 69
- 0
server/sonar-web/src/main/js/app/components/__tests__/NonAdminPagesContainer-test.tsx View File

@@ -0,0 +1,69 @@
/*
* SonarQube
* Copyright (C) 2009-2021 SonarSource SA
* mailto:info 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 { shallow } from 'enzyme';
import * as React from 'react';
import { mockComponent } from '../../../helpers/mocks/component';
import { ComponentQualifier } from '../../../types/component';
import NonAdminPagesContainer, { NonAdminPagesContainerProps } from '../NonAdminPagesContainer';

function Child() {
return <div />;
}

it('should render correctly', () => {
expect(
shallowRender()
.find(Child)
.exists()
).toBe(true);

expect(
shallowRender({
component: mockComponent({
qualifier: ComponentQualifier.Application,
canBrowseAllChildProjects: true
})
})
.find(Child)
.exists()
).toBe(true);

const wrapper = shallowRender({
component: mockComponent({
qualifier: ComponentQualifier.Application
})
});

expect(wrapper.find(Child).exists()).toBe(false);
expect(wrapper).toMatchSnapshot();
});

function shallowRender(props: Partial<NonAdminPagesContainerProps> = {}) {
return shallow<NonAdminPagesContainerProps>(
<NonAdminPagesContainer
branchLikes={[]}
component={mockComponent()}
onBranchesChange={jest.fn()}
onComponentChange={jest.fn()}
{...props}>
<Child />
</NonAdminPagesContainer>
);
}

+ 21
- 0
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/NonAdminPagesContainer-test.tsx.snap View File

@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="page page-limited display-flex-justify-center"
>
<Alert
className="max-width-60 huge-spacer-top"
display="block"
variant="error"
>
<p>
application.cannot_access_all_child_projects1
</p>
<br />
<p>
application.cannot_access_all_child_projects2
</p>
</Alert>
</div>
`;

+ 45
- 39
server/sonar-web/src/main/js/app/utils/startReactApp.tsx View File

@@ -67,6 +67,7 @@ import App from '../components/App';
import GlobalContainer from '../components/GlobalContainer';
import { PageContext } from '../components/indexation/PageUnavailableDueToIndexation';
import MigrationContainer from '../components/MigrationContainer';
import NonAdminPagesContainer from '../components/NonAdminPagesContainer';
import getStore from './getStore';

/*
@@ -156,46 +157,51 @@ function renderRedirects() {
function renderComponentRoutes() {
return (
<Route component={lazyLoadComponent(() => import('../components/ComponentContainer'))}>
<RouteWithChildRoutes path="code" childRoutes={codeRoutes} />
<RouteWithChildRoutes path="component_measures" childRoutes={componentMeasuresRoutes} />
<RouteWithChildRoutes path="dashboard" childRoutes={overviewRoutes} />
<RouteWithChildRoutes path="portfolio" childRoutes={portfolioRoutes} />
<RouteWithChildRoutes path="project/activity" childRoutes={projectActivityRoutes} />
<Route
path="project/extension/:pluginKey/:extensionKey"
component={lazyLoadComponent(() => import('../components/extensions/ProjectPageExtension'))}
/>
<Route
path="project/issues"
component={Issues}
onEnter={({ location: { query } }, replace) => {
if (query.types) {
if (query.types === 'SECURITY_HOTSPOT') {
replace({
pathname: '/security_hotspots',
query: { ...pick(query, ['id', 'branch', 'pullRequest']), assignedToMe: false }
});
} else {
query.types = query.types
.split(',')
.filter((type: string) => type !== 'SECURITY_HOTSPOT')
.join(',');
{/* This container is a catch-all for all non-admin pages */}
<Route component={NonAdminPagesContainer}>
<RouteWithChildRoutes path="code" childRoutes={codeRoutes} />
<RouteWithChildRoutes path="component_measures" childRoutes={componentMeasuresRoutes} />
<RouteWithChildRoutes path="dashboard" childRoutes={overviewRoutes} />
<RouteWithChildRoutes path="portfolio" childRoutes={portfolioRoutes} />
<RouteWithChildRoutes path="project/activity" childRoutes={projectActivityRoutes} />
<Route
path="project/extension/:pluginKey/:extensionKey"
component={lazyLoadComponent(() =>
import('../components/extensions/ProjectPageExtension')
)}
/>
<Route
path="project/issues"
component={Issues}
onEnter={({ location: { query } }, replace) => {
if (query.types) {
if (query.types === 'SECURITY_HOTSPOT') {
replace({
pathname: '/security_hotspots',
query: { ...pick(query, ['id', 'branch', 'pullRequest']), assignedToMe: false }
});
} else {
query.types = query.types
.split(',')
.filter((type: string) => type !== 'SECURITY_HOTSPOT')
.join(',');
}
}
}
}}
/>
<Route
path="security_hotspots"
component={lazyLoadComponent(() =>
import('../../apps/security-hotspots/SecurityHotspotsApp')
)}
/>
<RouteWithChildRoutes path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<RouteWithChildRoutes
path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes}
/>
<RouteWithChildRoutes path="tutorials" childRoutes={tutorialsRoutes} />
}}
/>
<Route
path="security_hotspots"
component={lazyLoadComponent(() =>
import('../../apps/security-hotspots/SecurityHotspotsApp')
)}
/>
<RouteWithChildRoutes path="project/quality_gate" childRoutes={projectQualityGateRoutes} />
<RouteWithChildRoutes
path="project/quality_profiles"
childRoutes={projectQualityProfilesRoutes}
/>
<RouteWithChildRoutes path="tutorials" childRoutes={tutorialsRoutes} />
</Route>
<Route component={lazyLoadComponent(() => import('../components/ProjectAdminContainer'))}>
<Route
path="project/admin/extension/:pluginKey/:extensionKey"

+ 8
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -3979,6 +3979,14 @@ branch_like_navigation.only_one_branch.documentation=Branches documentation
branch_like_navigation.only_one_branch.pr_analysis=Pull Request analysis
branch_like_navigation.tutorial_for_ci=Show me how to set up my CI

#------------------------------------------------------------------------------
#
# APPLICATIONS
#
#------------------------------------------------------------------------------
application.cannot_access_all_child_projects1=You must have access to all the projects within this Application in order to browse it.
application.cannot_access_all_child_projects2=Please contact your project administrator.

#------------------------------------------------------------------------------
#
# PORTFOLIOS

Loading…
Cancel
Save