]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15327 Inform admin users that DevOps Platform secrets can be encrypted
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Thu, 26 Aug 2021 09:12:16 +0000 (11:12 +0200)
committersonartech <sonartech@sonarsource.com>
Mon, 30 Aug 2021 20:08:19 +0000 (20:08 +0000)
14 files changed:
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketCloudForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketServerForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormField-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketCloudForm-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketServerForm-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index f07477bc8dfa7d9f5a0b811f8c306a7687f43a0f..07142dc6f7b30be175e7df7ecc84429edfa80794 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import { Link } from 'react-router';
 import { ButtonLink } from '../../../../components/controls/buttons';
+import { Alert } from '../../../../components/ui/Alert';
 import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
 import { translate } from '../../../../helpers/l10n';
 import { AlmBindingDefinitionBase } from '../../../../types/alm-settings';
@@ -35,6 +38,7 @@ export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefiniti
   overwriteOnly?: boolean;
   propKey: keyof B;
   value: string;
+  isSecret?: boolean;
 }
 
 export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase>(
@@ -49,7 +53,8 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase
     optional,
     overwriteOnly = false,
     propKey,
-    value
+    value,
+    isSecret
   } = props;
   const [showField, setShowField] = React.useState(!overwriteOnly);
 
@@ -62,23 +67,23 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase
         {!optional && <MandatoryFieldMarker />}
         {help && <div className="markdown small spacer-top">{help}</div>}
       </div>
-      <div className="settings-definition-right big-padded-top">
+      <div className="settings-definition-right big-padded-top display-flex-column">
         {!showField && overwriteOnly && (
           <div>
-            <p>{translate('settings.almintegration.form.secret_field')}</p>
+            <p>{translate('settings.almintegration.form.secret.field')}</p>
             <ButtonLink
               onClick={() => {
                 props.onFieldChange(propKey, '');
                 setShowField(true);
               }}>
-              {translate('settings.almintegration.form.update_secret_field')}
+              {translate('settings.almintegration.form.secret.update_field')}
             </ButtonLink>
           </div>
         )}
 
         {showField && isTextArea && (
           <textarea
-            className="settings-large-input"
+            className="width-100"
             id={id}
             maxLength={maxLength || 2000}
             onChange={e => props.onFieldChange(propKey, e.currentTarget.value)}
@@ -90,8 +95,8 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase
 
         {showField && !isTextArea && (
           <input
+            className="width-100"
             autoFocus={autoFocus}
-            className="settings-large-input"
             id={id}
             maxLength={maxLength || 100}
             name={id}
@@ -101,6 +106,27 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinitionBase
             value={value}
           />
         )}
+
+        {showField && isSecret && (
+          <Alert variant="info" className="spacer-top">
+            <FormattedMessage
+              id="settings.almintegration.form.secret.can_encrypt"
+              defaultMessage={translate('settings.almintegration.form.secret.can_encrypt')}
+              values={{
+                learn_more: (
+                  <Link
+                    target="_blank"
+                    to={{
+                      pathname:
+                        '/documentation/instance-administration/security/#settings-encryption'
+                    }}>
+                    {translate('learn_more')}
+                  </Link>
+                )
+              }}
+            />
+          </Alert>
+        )}
       </div>
     </div>
   );
index a2e852833cc702a5d6814b554c7f7654b631632a..30835e23826a1d17c9de5e14c9c8364e04f1ac36 100644 (file)
@@ -93,6 +93,7 @@ export default function AzureForm(props: AzureFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="personalAccessToken"
         value={formData.personalAccessToken}
+        isSecret={true}
       />
     </>
   );
index 92d0809be7bc72cd86f2f13009b927b39a0ab62b..928163cf8e01fd53d5f82dd0f2a182e1b6c0e823 100644 (file)
@@ -68,7 +68,7 @@ export default function BitbucketCloudForm(props: BitbucketCloudFormProps) {
         propKey="workspace"
         value={formData.workspace || ''}
       />
-      <Alert className="big-spacer-top big-spacer-right" variant="info">
+      <Alert className="big-spacer-top" variant="info">
         <FormattedMessage
           defaultMessage={translate(`settings.almintegration.bitbucketcloud.info`)}
           id="settings.almintegration.bitbucketcloud.info"
@@ -104,6 +104,7 @@ export default function BitbucketCloudForm(props: BitbucketCloudFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="clientSecret"
         value={formData.clientSecret || ''}
+        isSecret={true}
       />
     </>
   );
index 952136933e554a706ad930b916fbf1d568edadd8..b9e8721fb2c74cc27602ce61678094e92b4d39b7 100644 (file)
@@ -92,6 +92,7 @@ export default function BitbucketServerForm(props: BitbucketServerFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="personalAccessToken"
         value={formData.personalAccessToken || ''}
+        isSecret={true}
       />
     </>
   );
index 7e635251a585ccee8e1ab7bad8e4120f18ad7696..d279943900641280f39009e872b26e5e80a31585 100644 (file)
@@ -63,7 +63,7 @@ export default function GithubForm(props: GithubFormProps) {
         propKey="url"
         value={formData.url}
       />
-      <Alert className="big-spacer-top big-spacer-right" variant="info">
+      <Alert className="big-spacer-top" variant="info">
         <FormattedMessage
           defaultMessage={translate(`settings.almintegration.github.info`)}
           id="settings.almintegration.github.info"
@@ -100,6 +100,7 @@ export default function GithubForm(props: GithubFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="clientSecret"
         value={formData.clientSecret}
+        isSecret={true}
       />
       <AlmBindingDefinitionFormField
         id="private_key"
@@ -109,6 +110,7 @@ export default function GithubForm(props: GithubFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="privateKey"
         value={formData.privateKey}
+        isSecret={true}
       />
     </>
   );
index 4514b840364b5eaccb2070f1dd9f2fab13ed65ad..6f938ef6f299fbf5d846954874cbfcbaee0a89af 100644 (file)
@@ -89,6 +89,7 @@ export default function GitlabForm(props: GitlabFormProps) {
         overwriteOnly={Boolean(formData.key)}
         propKey="personalAccessToken"
         value={formData.personalAccessToken}
+        isSecret={true}
       />
     </>
   );
index 180abec48168047a57efddbbda19b3f49f319794..2eb517c348dc621f410961300839b9e83e7444e6 100644 (file)
@@ -33,6 +33,7 @@ it('should render correctly', () => {
   expect(shallowRender({ isTextArea: true })).toMatchSnapshot('textarea');
   expect(shallowRender({ optional: true })).toMatchSnapshot('optional');
   expect(shallowRender({ overwriteOnly: true })).toMatchSnapshot('secret');
+  expect(shallowRender({ isSecret: true })).toMatchSnapshot('encryptable');
 });
 
 it('should call onFieldChange', () => {
index 63b65f54f6887c26e5ebc4ac0f8a74beb27a0cf4..2cb687fd3f5f0c4b38d432ed39e984c78b77d9a4 100644 (file)
@@ -16,10 +16,10 @@ exports[`should render correctly: default 1`] = `
     <MandatoryFieldMarker />
   </div>
   <div
-    className="settings-definition-right big-padded-top"
+    className="settings-definition-right big-padded-top display-flex-column"
   >
     <input
-      className="settings-large-input"
+      className="width-100"
       id="key"
       maxLength={40}
       name="key"
@@ -32,6 +32,63 @@ exports[`should render correctly: default 1`] = `
 </div>
 `;
 
+exports[`should render correctly: encryptable 1`] = `
+<div
+  className="settings-definition"
+>
+  <div
+    className="settings-definition-left"
+  >
+    <label
+      className="h3"
+      htmlFor="key"
+    >
+      settings.almintegration.form.key
+    </label>
+    <MandatoryFieldMarker />
+  </div>
+  <div
+    className="settings-definition-right big-padded-top display-flex-column"
+  >
+    <input
+      className="width-100"
+      id="key"
+      maxLength={40}
+      name="key"
+      onChange={[Function]}
+      size={50}
+      type="text"
+      value="key"
+    />
+    <Alert
+      className="spacer-top"
+      variant="info"
+    >
+      <FormattedMessage
+        defaultMessage="settings.almintegration.form.secret.can_encrypt"
+        id="settings.almintegration.form.secret.can_encrypt"
+        values={
+          Object {
+            "learn_more": <Link
+              onlyActiveOnIndex={false}
+              style={Object {}}
+              target="_blank"
+              to={
+                Object {
+                  "pathname": "/documentation/instance-administration/security/#settings-encryption",
+                }
+              }
+            >
+              learn_more
+            </Link>,
+          }
+        }
+      />
+    </Alert>
+  </div>
+</div>
+`;
+
 exports[`should render correctly: optional 1`] = `
 <div
   className="settings-definition"
@@ -47,10 +104,10 @@ exports[`should render correctly: optional 1`] = `
     </label>
   </div>
   <div
-    className="settings-definition-right big-padded-top"
+    className="settings-definition-right big-padded-top display-flex-column"
   >
     <input
-      className="settings-large-input"
+      className="width-100"
       id="key"
       maxLength={40}
       name="key"
@@ -79,16 +136,16 @@ exports[`should render correctly: secret 1`] = `
     <MandatoryFieldMarker />
   </div>
   <div
-    className="settings-definition-right big-padded-top"
+    className="settings-definition-right big-padded-top display-flex-column"
   >
     <div>
       <p>
-        settings.almintegration.form.secret_field
+        settings.almintegration.form.secret.field
       </p>
       <ButtonLink
         onClick={[Function]}
       >
-        settings.almintegration.form.update_secret_field
+        settings.almintegration.form.secret.update_field
       </ButtonLink>
     </div>
   </div>
@@ -111,10 +168,10 @@ exports[`should render correctly: textarea 1`] = `
     <MandatoryFieldMarker />
   </div>
   <div
-    className="settings-definition-right big-padded-top"
+    className="settings-definition-right big-padded-top display-flex-column"
   >
     <textarea
-      className="settings-large-input"
+      className="width-100"
       id="key"
       maxLength={40}
       onChange={[Function]}
@@ -147,10 +204,10 @@ exports[`should render correctly: with help 1`] = `
     </div>
   </div>
   <div
-    className="settings-definition-right big-padded-top"
+    className="settings-definition-right big-padded-top display-flex-column"
   >
     <input
-      className="settings-large-input"
+      className="width-100"
       id="key"
       maxLength={40}
       name="key"
index 8ed346ed3402d6bf02b0e9f85e9a16e31846f74f..6120baaa2426f60c33072cef2add648290696811 100644 (file)
@@ -63,6 +63,7 @@ exports[`should render correctly: create 1`] = `
       />
     }
     id="personal_access_token"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={false}
@@ -135,6 +136,7 @@ exports[`should render correctly: edit 1`] = `
       />
     }
     id="personal_access_token"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
index bd5e9d5635ce1763332f555faddbf86cb48865eb..02b2f021223dfb04c8a72cc38a9b89de98706ed4 100644 (file)
@@ -36,7 +36,7 @@ exports[`should render correctly 1`] = `
     value="workspace"
   />
   <Alert
-    className="big-spacer-top big-spacer-right"
+    className="big-spacer-top"
     variant="info"
   >
     <FormattedMessage
@@ -76,6 +76,7 @@ exports[`should render correctly 1`] = `
   <AlmBindingDefinitionFormField
     help="settings.almintegration.form.oauth_secret.bitbucketcloud.help"
     id="client_secret.bitbucketcloud"
+    isSecret={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
     propKey="clientSecret"
index 6541018d021e1da4c713611587d289cd353fa78c..086b09f83cdad607442e5b421212c1cbdd87e5a2 100644 (file)
@@ -59,6 +59,7 @@ exports[`should render correctly 1`] = `
       />
     }
     id="personal_access_token"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
index 5cf7401d642b8472d859956f668818f1d2d44a75..4499dc5e2a1eef72fa5c7b833f4d55d028d6b558 100644 (file)
@@ -34,7 +34,7 @@ exports[`should render correctly 1`] = `
     value=""
   />
   <Alert
-    className="big-spacer-top big-spacer-right"
+    className="big-spacer-top"
     variant="info"
   >
     <FormattedMessage
@@ -73,6 +73,7 @@ exports[`should render correctly 1`] = `
   <AlmBindingDefinitionFormField
     help="settings.almintegration.form.client_secret.github.help"
     id="client_secret.github"
+    isSecret={true}
     maxLength={80}
     onFieldChange={[MockFunction]}
     overwriteOnly={false}
@@ -82,6 +83,7 @@ exports[`should render correctly 1`] = `
   <AlmBindingDefinitionFormField
     help="settings.almintegration.form.private_key.github.help"
     id="private_key"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={false}
@@ -125,7 +127,7 @@ exports[`should render correctly 2`] = `
     value="http://github.enterprise.com"
   />
   <Alert
-    className="big-spacer-top big-spacer-right"
+    className="big-spacer-top"
     variant="info"
   >
     <FormattedMessage
@@ -164,6 +166,7 @@ exports[`should render correctly 2`] = `
   <AlmBindingDefinitionFormField
     help="settings.almintegration.form.client_secret.github.help"
     id="client_secret.github"
+    isSecret={true}
     maxLength={80}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
@@ -173,6 +176,7 @@ exports[`should render correctly 2`] = `
   <AlmBindingDefinitionFormField
     help="settings.almintegration.form.private_key.github.help"
     id="private_key"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
index 591c37bd876024e6d7230937380ef688d86cb52d..f8b95437d64320775529a5459aa3359f2d5fe0d2 100644 (file)
@@ -59,6 +59,7 @@ exports[`should render correctly 1`] = `
       />
     }
     id="personal_access_token"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={false}
@@ -127,6 +128,7 @@ exports[`should render correctly 2`] = `
       />
     }
     id="personal_access_token"
+    isSecret={true}
     isTextArea={true}
     onFieldChange={[MockFunction]}
     overwriteOnly={true}
index 99e5a57b313667e1b5953cb5cad7ce4b545ecde8..f7b92e4fc460d34281ace25acda960498ad69d62 100644 (file)
@@ -1180,7 +1180,7 @@ settings.almintegration.form.client_id.bitbucketcloud=OAuth Key
 settings.almintegration.form.client_secret.bitbucketcloud=OAuth Secret
 settings.almintegration.form.private_key=Private Key
 settings.almintegration.form.private_key.github.help=Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. Copy and paste the whole contents of the file here.
-settings.almintegration.form.personal_access_token=Personal Access token
+settings.almintegration.form.personal_access_token=Personal Access Token
 settings.almintegration.form.personal_access_token.azure.help=SonarQube needs a {pat} to report the Quality Gate status on Pull Requests in Azure DevOps. To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. The token itself needs {permission} permission. {doc_link}
 settings.almintegration.form.personal_access_token.azure.help.url=Personal Access Token
 settings.almintegration.form.personal_access_token.gitlab.help=SonarQube needs a {pat} to report the Quality Gate status on Merge Requests in GitLab. To create this token, we recommend using a dedicated GitLab account with {permission} permission to all target projects. The token itself needs the {scope} scope. {doc_link}
@@ -1189,8 +1189,9 @@ settings.almintegration.form.personal_access_token.bitbucket.help=SonarQube need
 settings.almintegration.form.personal_access_token.bitbucket.help.url=Personal Access Token
 settings.almintegration.form.save=Save configuration
 settings.almintegration.form.cancel=Cancel
-settings.almintegration.form.secret_field=This field is hidden for security reasons.
-settings.almintegration.form.update_secret_field=Update field value
+settings.almintegration.form.secret.field=This field is hidden for security reasons.
+settings.almintegration.form.secret.update_field=Update field value
+settings.almintegration.form.secret.can_encrypt=You can encrypt this value. {learn_more}
 settings.almintegration.feature.status_reporting.title=Quality Gate status reporting
 settings.almintegration.feature.status_reporting.description_pr=Add analysis and a Quality Gate to your Pull Requests directly in your ALM provider's interface.
 settings.almintegration.feature.status_reporting.description_pr_and_commits=Add the Quality Gate status to your Pull Requests and on analyzed commits directly in your ALM provider's interface.