]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19790 allow local admins to restore access on github projects
authorViktor Vorona <viktor.vorona@sonarsource.com>
Mon, 17 Jul 2023 13:30:42 +0000 (15:30 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 18 Jul 2023 20:03:22 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectManagementApp-it.tsx

index 9a4c207e4656523644394a16483dca77fba5c4fb..d2acb3e932b798ecf5482d79a49e3aeb48bb4ea2 100644 (file)
@@ -29,6 +29,7 @@ import { translate, translateWithParameters } from '../../helpers/l10n';
 import { getComponentOverviewUrl } from '../../helpers/urls';
 import { ComponentQualifier } from '../../types/component';
 import { LoggedInUser } from '../../types/users';
+import { useGithubStatusQuery } from '../settings/components/authentication/queries/identity-provider';
 import './ProjectRow.css';
 import ProjectRowActions from './ProjectRowActions';
 
@@ -41,6 +42,7 @@ interface Props {
 
 export default function ProjectRow(props: Props) {
   const { currentUser, project, selected } = props;
+  const { data: githubProvisioningEnabled } = useGithubStatusQuery();
 
   const handleProjectCheck = (checked: boolean) => {
     props.onProjectCheck(project, checked);
@@ -68,9 +70,9 @@ export default function ProjectRow(props: Props) {
             <span>{project.name}</span>
           </Tooltip>
         </Link>
-        {project.qualifier === ComponentQualifier.Project && !project.managed && (
-          <span className="badge sw-ml-1">{translate('local')}</span>
-        )}
+        {project.qualifier === ComponentQualifier.Project &&
+          githubProvisioningEnabled &&
+          !project.managed && <span className="badge sw-ml-1">{translate('local')}</span>}
       </td>
 
       <td className="thin nowrap">
index fa9064adf5497988f82a710fa7a7431119603209..032e81f83f2b1dad442269d32eed324e1d372383 100644 (file)
@@ -26,10 +26,11 @@ import { translate, translateWithParameters } from '../../helpers/l10n';
 import { getComponentPermissionsUrl } from '../../helpers/urls';
 import { LoggedInUser } from '../../types/users';
 import ApplyTemplate from '../permissions/project/components/ApplyTemplate';
+import { useGithubStatusQuery } from '../settings/components/authentication/queries/identity-provider';
 import RestoreAccessModal from './RestoreAccessModal';
 
 export interface Props {
-  currentUser: Pick<LoggedInUser, 'login'>;
+  currentUser: Pick<LoggedInUser, 'login' | 'local'>;
   project: Project;
 }
 
@@ -38,6 +39,7 @@ export default function ProjectRowActions({ currentUser, project }: Props) {
   const [hasAccess, setHasAccess] = useState<boolean | undefined>(undefined);
   const [loading, setLoading] = useState(false);
   const [restoreAccessModal, setRestoreAccessModal] = useState(false);
+  const { data: githubProvisioningEnabled } = useGithubStatusQuery();
 
   const fetchPermissions = () => {
     setLoading(true);
@@ -87,14 +89,15 @@ export default function ProjectRowActions({ currentUser, project }: Props) {
               </ActionsDropdownItem>
             )}
 
-            {hasAccess === false && !project.managed && (
-              <ActionsDropdownItem
-                className="js-restore-access"
-                onClick={() => setRestoreAccessModal(true)}
-              >
-                {translate('global_permissions.restore_access')}
-              </ActionsDropdownItem>
-            )}
+            {hasAccess === false &&
+              (!project.managed || currentUser.local || !githubProvisioningEnabled) && (
+                <ActionsDropdownItem
+                  className="js-restore-access"
+                  onClick={() => setRestoreAccessModal(true)}
+                >
+                  {translate('global_permissions.restore_access')}
+                </ActionsDropdownItem>
+              )}
           </>
         )}
 
index f4631ddc7db3028802433c06d64d713e2a3e7395..6cbc90bcb765fa7c250cb6ccb1862b6cd6700c5a 100644 (file)
@@ -367,7 +367,7 @@ it('should create project', async () => {
   expect(ui.successMsg.get(dialog)).toBeInTheDocument();
   await user.click(ui.close.get(dialog));
   expect(ui.row.getAll()).toHaveLength(6);
-  expect(ui.row.getAll()[1]).toHaveTextContent('qualifier.TRKa Testlocalvisibility.privatetest—');
+  expect(ui.row.getAll()[1]).toHaveTextContent('qualifier.TRKa Testvisibility.privatetest—');
 });
 
 it('should edit permissions of single project', async () => {
@@ -401,7 +401,7 @@ it('should apply template for single object', async () => {
 
 it('should restore access to admin', async () => {
   const user = userEvent.setup();
-  renderProjectManagementApp({}, { login: 'gooduser2' });
+  renderProjectManagementApp({}, { login: 'gooduser2', local: true });
   await act(async () => user.click(await ui.firstProjectActions.find()));
   expect(await ui.restoreAccess.find()).toBeInTheDocument();
   expect(ui.editPermissions.query()).not.toBeInTheDocument();
@@ -414,6 +414,44 @@ it('should restore access to admin', async () => {
   expect(ui.editPermissions.get()).toBeInTheDocument();
 });
 
+it('should restore access for github project', async () => {
+  const user = userEvent.setup();
+  authHandler.githubProvisioningStatus = true;
+  renderProjectManagementApp(
+    {},
+    { login: 'gooduser2', local: true },
+    { featureList: [Feature.GithubProvisioning] }
+  );
+  await waitFor(() => expect(ui.row.getAll()).toHaveLength(5));
+  const rows = ui.row.getAll();
+  await act(async () => user.click(await ui.projectActions.find(rows[4])));
+  expect(await ui.restoreAccess.find()).toBeInTheDocument();
+  expect(ui.showPermissions.query()).not.toBeInTheDocument();
+  await user.click(ui.restoreAccess.get());
+  expect(ui.restoreAccessDialog.get()).toBeInTheDocument();
+  await act(() => user.click(ui.restore.get(ui.restoreAccessDialog.get())));
+  expect(ui.restoreAccessDialog.query()).not.toBeInTheDocument();
+  await act(async () => user.click(await ui.projectActions.find(rows[4])));
+  expect(ui.restoreAccess.query()).not.toBeInTheDocument();
+  expect(ui.showPermissions.get()).toBeInTheDocument();
+});
+
+it('should not allow to restore access on github project for GH user', async () => {
+  const user = userEvent.setup();
+  authHandler.githubProvisioningStatus = true;
+  renderProjectManagementApp(
+    {},
+    { login: 'gooduser2', local: false },
+    { featureList: [Feature.GithubProvisioning] }
+  );
+  await waitFor(() => expect(ui.row.getAll()).toHaveLength(5));
+  const rows = ui.row.getAll();
+  await act(async () => user.click(await ui.projectActions.find(rows[4])));
+  expect(ui.restoreAccess.query()).not.toBeInTheDocument();
+  await act(async () => user.click(await ui.projectActions.find(rows[1])));
+  expect(ui.restoreAccess.get()).toBeInTheDocument();
+});
+
 it('should show github warning on changing default visibility to admin', async () => {
   const user = userEvent.setup();
   authHandler.githubProvisioningStatus = true;
@@ -428,10 +466,6 @@ it('should not allow apply permissions for managed projects', async () => {
   renderProjectManagementApp();
   await waitFor(() => expect(ui.row.getAll()).toHaveLength(5));
   const rows = ui.row.getAll();
-  expect(rows[1]).toHaveTextContent('local');
-  expect(rows[2]).toHaveTextContent('local');
-  expect(rows[3]).toHaveTextContent('local');
-  expect(rows[4]).not.toHaveTextContent('local');
   expect(ui.checkbox.get(rows[4])).toHaveAttribute('aria-disabled', 'true');
   expect(ui.checkbox.get(rows[1])).not.toHaveAttribute('aria-disabled');
   await user.click(ui.checkAll.get());
@@ -439,7 +473,6 @@ it('should not allow apply permissions for managed projects', async () => {
   expect(ui.checkbox.get(rows[1])).toBeChecked();
   await act(() => user.click(ui.projectActions.get(rows[4])));
   expect(ui.applyPermissionTemplate.query()).not.toBeInTheDocument();
-  expect(ui.restoreAccess.query()).not.toBeInTheDocument();
   expect(ui.editPermissions.query()).not.toBeInTheDocument();
   expect(ui.showPermissions.get()).toBeInTheDocument();
   await act(() => user.click(ui.projectActions.get(rows[1])));
@@ -449,7 +482,8 @@ it('should not allow apply permissions for managed projects', async () => {
 });
 
 it('should not show local badge for applications and portfolios', async () => {
-  renderProjectManagementApp();
+  authHandler.githubProvisioningStatus = true;
+  renderProjectManagementApp({}, {}, { featureList: [Feature.GithubProvisioning] });
   await waitFor(() => expect(screen.getAllByText('local')).toHaveLength(3));
 
   await selectEvent.select(ui.qualifierFilter.get(), 'qualifiers.VW');
@@ -459,6 +493,12 @@ it('should not show local badge for applications and portfolios', async () => {
   expect(screen.queryByText('local')).not.toBeInTheDocument();
 });
 
+it('should not show local badge if provisioning is not enabled', async () => {
+  renderProjectManagementApp();
+  await waitFor(() => expect(ui.row.getAll()).toHaveLength(5));
+  expect(screen.queryByText('local')).not.toBeInTheDocument();
+});
+
 function renderProjectManagementApp(
   overrides: Partial<AppState> = {},
   user: Partial<LoggedInUser> = {},