]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17228 tabs for Authentication settings
authorJeremy Davis <jeremy.davis@sonarsource.com>
Wed, 24 Aug 2022 09:39:23 +0000 (11:39 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 25 Aug 2022 20:03:09 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/settings/constants.ts
server/sonar-web/src/main/js/apps/settings/styles.css
server/sonar-web/src/main/js/apps/settings/utils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 417758c89320e5a439f27af1dd090d2acfd561c7..f71d457f4cc22b138ee17d773a727bc9666bb395 100644 (file)
@@ -100,7 +100,7 @@ describe('buildSettingLink', () => {
       {
         hash: '#sonar.auth.gitlab.name',
         pathname: '/admin/settings',
-        search: '?category=foo+category&alm=gitlab'
+        search: '?category=foo+category&tab=gitlab'
       }
     ],
     [
@@ -109,7 +109,16 @@ describe('buildSettingLink', () => {
       {
         hash: '#sonar.auth.github.token',
         pathname: '/admin/settings',
-        search: '?category=foo+category&alm=github'
+        search: '?category=foo+category&tab=github'
+      }
+    ],
+    [
+      mockDefinition({ key: 'sonar.auth.bitbucket.token' }),
+      undefined,
+      {
+        hash: '#sonar.auth.bitbucket.token',
+        pathname: '/admin/settings',
+        search: '?category=foo+category&tab=bitbucket'
       }
     ],
     [
index 1737728d02c3643c0239e4c05e6b56839d7ee4eb..6476bb8f1bba5b1b3d63a495b5dbb55df7cfc129 100644 (file)
@@ -24,12 +24,14 @@ import { Component } from '../../../types/types';
 import {
   ALM_INTEGRATION_CATEGORY,
   ANALYSIS_SCOPE_CATEGORY,
+  AUTHENTICATION_CATEGORY,
   LANGUAGES_CATEGORY,
   NEW_CODE_PERIOD_CATEGORY,
   PULL_REQUEST_DECORATION_BINDING_CATEGORY
 } from '../constants';
 import AlmIntegration from './almIntegration/AlmIntegration';
 import { AnalysisScope } from './AnalysisScope';
+import Authentication from './authentication/Authentication';
 import Languages from './Languages';
 import NewCodePeriod from './NewCodePeriod';
 import PullRequestDecorationBinding from './pullRequestDecorationBinding/PRDecorationBinding';
@@ -92,6 +94,14 @@ export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [
     availableForProject: true,
     displayTab: true,
     requiresBranchesEnabled: true
+  },
+  {
+    key: AUTHENTICATION_CATEGORY,
+    name: translate('property.category.authentication'),
+    renderComponent: getAuthenticationComponent,
+    availableGlobally: true,
+    availableForProject: false,
+    displayTab: false
   }
 ];
 
@@ -111,6 +121,10 @@ function getAlmIntegrationComponent(props: AdditionalCategoryComponentProps) {
   return <AlmIntegration {...props} />;
 }
 
+function getAuthenticationComponent(props: AdditionalCategoryComponentProps) {
+  return <Authentication {...props} />;
+}
+
 function getPullRequestDecorationBindingComponent(props: AdditionalCategoryComponentProps) {
   return props.component && <PullRequestDecorationBinding component={props.component} />;
 }
index cb5c37892f548369867e237ce3a0e2dc03d6c94e..8a48cd3c24df9e0d30d80cb1e8fa53971d6df0e6 100644 (file)
@@ -118,3 +118,33 @@ exports[`should render additional categories component correctly 5`] = `
   }
 />
 `;
+
+exports[`should render additional categories component correctly 6`] = `
+<Authentication
+  categories={Array []}
+  component={
+    Object {
+      "breadcrumbs": Array [],
+      "key": "my-project",
+      "name": "MyProject",
+      "qualifier": "TRK",
+      "qualityGate": Object {
+        "isDefault": true,
+        "key": "30",
+        "name": "Sonar way",
+      },
+      "qualityProfiles": Array [
+        Object {
+          "deleted": false,
+          "key": "my-qp",
+          "language": "ts",
+          "name": "Sonar way",
+        },
+      ],
+      "tags": Array [],
+    }
+  }
+  definitions={Array []}
+  selectedCategory="TEST"
+/>
+`;
index 0ed12fa6221eff67ff1dabcf1d964a65c5fbebeb..9405d10f639f6786903fc71c29a76195d740c130 100644 (file)
@@ -128,7 +128,8 @@ export default function AlmTabRenderer(props: AlmTabRendererProps) {
               link: (
                 <Link
                   to={{
-                    pathname: '/admin/settings?category=authentication'
+                    pathname: '/admin/settings',
+                    search: `category=authentication&tab=${almTab}`
                   }}>
                   {translate('property.category.authentication')}
                 </Link>
index 4a8a83d00098e734d229104f76837f6b4dd84a92..5061a09cc58e3907905d6a723e11311b0aa6f30d 100644 (file)
@@ -551,7 +551,8 @@ exports[`should render correctly with validation: pass the correct key for bitbu
           "link": <Link
             to={
               Object {
-                "pathname": "/admin/settings?category=authentication",
+                "pathname": "/admin/settings",
+                "search": "category=authentication&tab=bitbucket",
               }
             }
           >
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
new file mode 100644 (file)
index 0000000..39fae5f
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { FormattedMessage } from 'react-intl';
+import { Link, useSearchParams } from 'react-router-dom';
+import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper';
+import BoxedTabs from '../../../../components/controls/BoxedTabs';
+import { Alert } from '../../../../components/ui/Alert';
+import { translate } from '../../../../helpers/l10n';
+import { getBaseUrl } from '../../../../helpers/system';
+import { searchParamsToQuery } from '../../../../helpers/urls';
+import { AlmKeys } from '../../../../types/alm-settings';
+import { ExtendedSettingDefinition } from '../../../../types/settings';
+import { AUTHENTICATION_CATEGORY } from '../../constants';
+import CategoryDefinitionsList from '../CategoryDefinitionsList';
+
+interface Props {
+  definitions: ExtendedSettingDefinition[];
+}
+
+// We substract the footer height with padding (80) and the main layout padding (20)
+const HEIGHT_ADJUSTMENT = 100;
+
+const SAML = 'saml';
+export type AuthenticationTabs =
+  | typeof SAML
+  | AlmKeys.GitHub
+  | AlmKeys.GitLab
+  | AlmKeys.BitbucketServer;
+
+const DOCUMENTATION_LINKS = {
+  [SAML]: '/documentation/instance-administration/delegated-auth/#saml-authentication',
+  [AlmKeys.GitHub]: '/documentation/analysis/github-integration/#authenticating-with-github',
+  [AlmKeys.GitLab]: '/documentation/analysis/gitlab-integration/#authenticating-with-gitlab',
+  [AlmKeys.BitbucketServer]:
+    '/documentation/analysis/bitbucket-cloud-integration/#authenticating-with-bitbucket-cloud'
+};
+
+function renderDevOpsIcon(key: string) {
+  return (
+    <img
+      alt={key}
+      className="spacer-right"
+      height={16}
+      src={`${getBaseUrl()}/images/alm/${key}.svg`}
+    />
+  );
+}
+
+export default function Authentication(props: Props) {
+  const { definitions } = props;
+
+  const [query, setSearchParams] = useSearchParams();
+
+  const currentTab = (query.get('tab') || SAML) as AuthenticationTabs;
+
+  const tabs = [
+    {
+      key: SAML,
+      label: 'SAML'
+    },
+    {
+      key: AlmKeys.GitHub,
+      label: (
+        <>
+          {renderDevOpsIcon(AlmKeys.GitHub)}
+          GitHub
+        </>
+      )
+    },
+    {
+      key: AlmKeys.BitbucketServer,
+      label: (
+        <>
+          {renderDevOpsIcon(AlmKeys.BitbucketServer)}
+          Bitbucket
+        </>
+      )
+    },
+    {
+      key: AlmKeys.GitLab,
+      label: (
+        <>
+          {renderDevOpsIcon(AlmKeys.GitLab)}
+          GitLab
+        </>
+      )
+    }
+  ];
+
+  return (
+    <>
+      <header className="page-header">
+        <h1 className="page-title">{translate('settings.authentication.title')}</h1>
+      </header>
+
+      <div className="spacer-top huge-spacer-bottom">
+        <p>{translate('settings.authentication.description')}</p>
+      </div>
+
+      <BoxedTabs
+        onSelect={(tab: AuthenticationTabs) => {
+          setSearchParams({ ...searchParamsToQuery(query), tab });
+        }}
+        selected={currentTab}
+        tabs={tabs}
+      />
+
+      <ScreenPositionHelper>
+        {({ top }) => (
+          <div
+            style={{
+              maxHeight: `calc(100vh - ${top + HEIGHT_ADJUSTMENT}px)`
+            }}
+            className="bordered overflow-y-auto tabbed-definitions">
+            <div className="big-padded">
+              <Alert variant="info">
+                <FormattedMessage
+                  id="settings.authentication.help"
+                  defaultMessage={translate('settings.authentication.help')}
+                  values={{
+                    link: (
+                      <Link
+                        to={DOCUMENTATION_LINKS[currentTab]}
+                        rel="noopener noreferrer"
+                        target="_blank">
+                        {translate('settings.authentication.help.link')}
+                      </Link>
+                    )
+                  }}
+                />
+              </Alert>
+              <CategoryDefinitionsList
+                category={AUTHENTICATION_CATEGORY}
+                definitions={definitions}
+                subCategory={currentTab}
+              />
+            </div>
+          </div>
+        )}
+      </ScreenPositionHelper>
+    </>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/__tests__/Authentication-test.tsx
new file mode 100644 (file)
index 0000000..954ef52
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import React from 'react';
+import { renderComponent } from '../../../../../helpers/testReactTestingUtils';
+import Authentication from '../Authentication';
+
+it('should render tabs and allow navigation', async () => {
+  const user = userEvent.setup();
+  renderAuthentication();
+
+  expect(screen.getAllByRole('button')).toHaveLength(4);
+
+  expect(screen.getByRole('button', { name: 'SAML' })).toHaveAttribute('aria-current', 'true');
+
+  await user.click(screen.getByRole('button', { name: 'github GitHub' }));
+
+  expect(screen.getByRole('button', { name: 'SAML' })).toHaveAttribute('aria-current', 'false');
+  expect(screen.getByRole('button', { name: 'github GitHub' })).toHaveAttribute(
+    'aria-current',
+    'true'
+  );
+});
+
+function renderAuthentication() {
+  renderComponent(<Authentication definitions={[]} />);
+}
index d285466cbd070cd1cad3f8b0ec9e64cefa096afd..de0f66f52f80a626bfde0b5ce65c59aa98d0a80c 100644 (file)
@@ -20,6 +20,7 @@
 import { Dict } from '../../types/types';
 
 export const ALM_INTEGRATION_CATEGORY = 'almintegration';
+export const AUTHENTICATION_CATEGORY = 'authentication';
 export const ANALYSIS_SCOPE_CATEGORY = 'exclusions';
 export const LANGUAGES_CATEGORY = 'languages';
 export const NEW_CODE_PERIOD_CATEGORY = 'new_code_period';
index 443d919110b32af1478573671d8bb7d76f6f1ccf..3c1a6948e14a2eafdbaea3f2c8795f446f8e717c 100644 (file)
   align-items: stretch;
 }
 
+.tabbed-definitions .settings-definition {
+  margin: 0 -16px;
+  padding: 10px 16px;
+}
+
 .settings-definition-changed {
   border-top: 1px solid var(--alertBorderWarning);
   border-bottom: 1px solid var(--alertBorderWarning);
index fd26233e62dcb7ab668bd6291653fb0f5739c760..864459c3596a810b669dab3a4d7aaecea32a7ed4 100644 (file)
@@ -225,9 +225,11 @@ export function buildSettingLink(
   const query: Dict<string> = {};
 
   if (key.startsWith('sonar.auth.gitlab')) {
-    query.alm = 'gitlab';
+    query.tab = 'gitlab';
   } else if (key.startsWith('sonar.auth.github')) {
-    query.alm = 'github';
+    query.tab = 'github';
+  } else if (key.startsWith('sonar.auth.bitbucket')) {
+    query.tab = 'bitbucket';
   } else if (key.startsWith('sonar.almintegration')) {
     query.alm = key.split('.').pop() || '';
   }
index 6e857004fa2f09550871b0bb3ef677469db9cdca..40386d870713711f0356665975dfbba6534f1efc 100644 (file)
@@ -1252,6 +1252,11 @@ settings.almintegration.feature.alm_repo_import.disabled.multiple=This feature i
 settings.almintegration.feature.alm_repo_import.disabled.no_url=This feature is disabled because your configured instance has no URL.
 settings.almintegration.tabs.authentication_moved=You can delegate authentication to this DevOps Platform. The relevant settings are under the {link} section.
 
+settings.authentication.title=Authentication
+settings.authentication.description=The following settings allow you to delegate authentication via SAML, or any of the following DevOps Platforms: GitHub, GitLab, and Bitbucket.
+settings.authentication.help=If you need help setting up authentication, read our dedicated {link}.
+settings.authentication.help.link=documentation
+
 settings.pr_decoration.binding.category=DevOps Platform Integration
 settings.pr_decoration.binding.no_bindings=A system administrator needs to enable this feature in the global settings.
 settings.pr_decoration.binding.no_bindings.admin=Set up a {link} first before you and your team can enable Pull Request Decoration.
@@ -1299,14 +1304,10 @@ property.category.general.qualityGate=Quality Gate
 property.category.general.subProjects=Sub-projects
 property.category.almintegration=DevOps Platform Integrations
 property.category.authentication=Authentication
-property.category.authentication.github=GitHub Authentication
-property.category.authentication.github.description=In order to enable authentication on GitHub.com or GitHub Enterprise:<ul><li>SonarQube must be publicly accessible through HTTPS only</li><li>The property 'sonar.core.serverBaseURL' must be set to this public HTTPS URL</li><li>In your GitHub profile, you need to create a Developer Application for which the 'Authorization callback URL' must be set to <code>'&lt;value_of_sonar.core.serverBaseURL_property&gt;/oauth2/callback'</code>.</li></ul>
-property.category.authentication.gitlab=GitLab Authentication
-property.category.authentication.gitlab.description=In order to enable GitLab authentication, the property 'sonar.core.serverBaseURL' must be set to the public URL
-property.category.authentication.bitbucket=Bitbucket Cloud Authentication
-property.category.authentication.bitbucket.description=In order to enable authentication on Bitbucket.org: <ul><li>SonarQube must be publicly accessible through HTTPS only</li><li>The property 'sonar.core.serverBaseURL' must be set to this public HTTPS URL</li><li>In your Bitbucket OAuth Consumer settings, 'Callback URL' must be set to <code>'&lt;value_of_sonar.core.serverBaseURL_property&gt;'</code>.</li></ul>
-property.category.authentication.saml=SAML
-property.category.authentication.saml.description=In order to enable SAML authentication, the property 'sonar.core.serverBaseURL' must be set to the public URL
+property.category.authentication.github= 
+property.category.authentication.gitlab= 
+property.category.authentication.bitbucket= 
+property.category.authentication.saml= 
 property.category.organizations=Organizations
 property.category.security=Security
 property.category.security.encryption=Encryption