1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
/*
* SonarQube
* Copyright (C) 2009-2024 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 { Outlet } from 'react-router-dom';
import A11ySkipTarget from '../../components/a11y/A11ySkipTarget';
import { Component } from '../../types/types';
import handleRequiredAuthorization from '../utils/handleRequiredAuthorization';
import withComponentContext from './componentContext/withComponentContext';
interface Props {
component: Component;
}
export class ProjectAdminContainer extends React.PureComponent<Props> {
mounted = false;
componentDidMount() {
// We need to check permissions *after* the parent ComponentContainer is updated.
// This is why we wrap checkPermission() in a setTimeout().
//
// We want to prevent an edge-case where if you navigate from an admin component page
// to another component page where you have "read" access but no admin access, and
// then hit the browser's Back button, you will get redirected to the login page.
//
// The reason is that this component will get mounted *before* the parent
// ComponentContainer is *updated*. The parent component still has the last component
// stored in state, the one where the user might not have admin access. It will detect
// the change in URL and trigger a refresh of its state, but this comes too late; the
// checkPermission() here will already have triggered (because mounts happen before updates)
// and redirected the user. Wrapping it in a setTimeout() allows the parent component
// to refresh, unmounting this component, and only triggering the redirect if it makes sense.
//
// See https://sonarsource.atlassian.net/browse/SONAR-19437
setTimeout(this.checkPermissions, 0);
this.mounted = true;
}
componentDidUpdate() {
this.checkPermissions();
}
componentWillUnmount() {
this.mounted = false;
}
checkPermissions = () => {
if (this.mounted && !this.isProjectAdmin()) {
handleRequiredAuthorization();
}
};
isProjectAdmin = () => {
const { configuration } = this.props.component;
return configuration != null && configuration.showSettings;
};
render() {
if (!this.isProjectAdmin()) {
return null;
}
return (
<main>
<A11ySkipTarget anchor="admin_main" />
<Outlet />
</main>
);
}
}
export default withComponentContext(ProjectAdminContainer);
|