diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-09-09 12:17:13 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-09-09 12:17:13 +0200 |
commit | 6ee2c4f68d6c1667dc829abc4b1604f20f85c15d (patch) | |
tree | 59eb0cbc48b22f4ddd338953af079e449f0a4b98 /server/sonar-web/src/main/js/apps/settings | |
parent | 96fb42e56d1eceaae36808f62b773ce941e3bc68 (diff) | |
download | sonarqube-6ee2c4f68d6c1667dc829abc4b1604f20f85c15d.tar.gz sonarqube-6ee2c4f68d6c1667dc829abc4b1604f20f85c15d.zip |
SONAR-7979 Rewrite the "Encryption" page (#1214)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/settings')
8 files changed, 378 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/app.js b/server/sonar-web/src/main/js/apps/settings/app.js index 02e7181d253..4cf6c73457e 100644 --- a/server/sonar-web/src/main/js/apps/settings/app.js +++ b/server/sonar-web/src/main/js/apps/settings/app.js @@ -24,6 +24,7 @@ import { Router, Route, Redirect, useRouterHistory } from 'react-router'; import { createHistory } from 'history'; import App from './components/App'; import LicensesApp from './licenses/LicensesApp'; +import EncryptionAppContainer from './encryption/EncryptionAppContainer'; import rootReducer from './store/rootReducer'; import configureStore from '../../components/store/configureStore'; @@ -46,6 +47,7 @@ window.sonarqube.appStarted.then(options => { <Redirect from="/index" to="/"/> <Route path="/" component={withComponent(App)}/> <Route path="/licenses" component={LicensesApp}/> + <Route path="/encryption" component={EncryptionAppContainer}/> </Router> </Provider> ), el); diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js new file mode 100644 index 00000000000..fd356a3dd7f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionApp.js @@ -0,0 +1,68 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; +import GenerateSecretKeyForm from './GenerateSecretKeyForm'; +import EncryptionForm from './EncryptionForm'; +import GlobalMessagesContainer from '../components/GlobalMessagesContainer'; +import { translate } from '../../../helpers/l10n'; + +export default class EncryptionApp extends React.Component { + static propTypes = { + loading: React.PropTypes.bool.isRequired, + secretKeyAvailable: React.PropTypes.bool, + secretKey: React.PropTypes.string, + encryptedValue: React.PropTypes.string, + + checkSecretKey: React.PropTypes.func.isRequired, + generateSecretKey: React.PropTypes.func.isRequired, + encryptValue: React.PropTypes.func.isRequired, + startGeneration: React.PropTypes.func.isRequired + }; + + componentDidMount () { + this.props.checkSecretKey(); + } + + render () { + return ( + <div id="encryption-page" className="page page-limited"> + <header className="page-header"> + <h1 className="page-title">{translate('property.category.security.encryption')}</h1> + {this.props.loading && <i className="spinner"/>} + </header> + + <GlobalMessagesContainer/> + + {!this.props.loading && !this.props.secretKeyAvailable && ( + <GenerateSecretKeyForm + secretKey={this.props.secretKey} + generateSecretKey={this.props.generateSecretKey}/> + )} + + {this.props.secretKeyAvailable && ( + <EncryptionForm + encryptedValue={this.props.encryptedValue} + encryptValue={this.props.encryptValue} + generateSecretKey={this.props.startGeneration}/> + )} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js new file mode 100644 index 00000000000..42bd05c788d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionAppContainer.js @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { connect } from 'react-redux'; +import EncryptionApp from './EncryptionApp'; +import { checkSecretKey, generateSecretKey, encryptValue, startGeneration } from '../store/encryptionPage/actions'; +import { getEncryptionState } from '../store/rootReducer'; + +export default connect( + state => getEncryptionState(state), + { checkSecretKey, generateSecretKey, encryptValue, startGeneration } +)(EncryptionApp); diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js new file mode 100644 index 00000000000..f07e5cd601e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/encryption/EncryptionForm.js @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +export default class EncryptionForm extends React.Component { + static propTypes = { + encryptedValue: React.PropTypes.string, + encryptValue: React.PropTypes.func.isRequired, + generateSecretKey: React.PropTypes.func.isRequired + }; + + state = { value: '' }; + + handleEncrypt (e) { + e.preventDefault(); + this.props.encryptValue(this.state.value); + } + + handleGenerateNewKey (e) { + e.preventDefault(); + this.props.generateSecretKey(); + } + + render () { + return ( + <div id="encryption-form-container"> + <div className="spacer-bottom"> + Secret key is registered. You can encrypt any property value with the following form: + </div> + + <form id="encryption-form" className="big-spacer-bottom" onSubmit={e => this.handleEncrypt(e)}> + <input + id="encryption-form-value" + className="input-large" + type="text" + autoFocus={true} + required={true} + value={this.state.value} + onChange={e => this.setState({ value: e.target.value })}/> + <button className="spacer-left">Encrypt</button> + </form> + + {this.props.encryptedValue != null && ( + <div> + Encrypted Value:{' '} + <input + id="encrypted-value" + className="input-super-large" + type="text" + readOnly={true} + value={this.props.encryptedValue}/> + </div> + )} + + <div className="huge-spacer-top bordered-top"> + <div className="big-spacer-top spacer-bottom"> + Note that the secret key can be changed, but all the encrypted properties will have to be updated. + {' '} + <a href="http://redirect.sonarsource.com/doc/settings-encryption.html">More information</a> + </div> + + <form id="encryption-new-key-form" onSubmit={e => this.handleGenerateNewKey(e)}> + <button>Generate New Secret Key</button> + </form> + </div> + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js new file mode 100644 index 00000000000..65b70985d88 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/encryption/GenerateSecretKeyForm.js @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 React from 'react'; + +export default class GenerateSecretKeyForm extends React.Component { + static propTypes = { + secretKey: React.PropTypes.string, + generateSecretKey: React.PropTypes.func.isRequired + }; + + handleSubmit (e) { + e.preventDefault(); + this.props.generateSecretKey(); + } + + render () { + return ( + <div id="generate-secret-key-form-container"> + {this.props.secretKey != null ? ( + <div> + <div className="big-spacer-bottom"> + <h3 className="spacer-bottom">Secret Key</h3> + <input + id="secret-key" + className="input-large" + type="text" + readOnly={true} + value={this.props.secretKey}/> + </div> + + <h3 className="spacer-bottom">How To Use</h3> + + <ul className="list-styled markdown"> + <li className="spacer-bottom"> + Store the secret key in the file <code>~/.sonar/sonar-secret.txt</code> of the server. This file can + be relocated by defining the property <code>sonar.secretKeyPath</code>{' '} + in <code>conf/sonar.properties</code> + </li> + <li className="spacer-bottom"> + Restrict access to this file by making it readable and by owner only + </li> + <li className="spacer-bottom"> + Restart the server if the property <code>sonar.secretKeyPath</code> has been set or changed. + </li> + <li className="spacer-bottom"> + Copy this file on all the machines that execute code inspection. Define the property + {' '}<code>sonar.secretKeyPath</code> on those machines if the path is not + {' '}<code>~/.sonar/sonar-secret.txt</code>. + </li> + <li> + For each property that you want to encrypt, generate the encrypted value and replace the original + value wherever it is stored (configuration files, command lines). + </li> + </ul> + </div> + ) : ( + <div> + <p className="spacer-bottom"> + Secret key is required to be able to encrypt properties. + {' '} + <a href="http://redirect.sonarsource.com/doc/settings-encryption.html">More information</a> + </p> + + <form id="generate-secret-key-form" onSubmit={e => this.handleSubmit(e)}> + <button>Generate Secret Key</button> + </form> + </div> + )} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/actions.js b/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/actions.js new file mode 100644 index 00000000000..54bc6de0cec --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/actions.js @@ -0,0 +1,70 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 api from '../../../../api/settings'; +import { parseError } from '../../../code/utils'; +import { addGlobalErrorMessage, closeAllGlobalMessages } from '../../../../components/store/globalMessages'; + +export const UPDATE_ENCRYPTION = 'UPDATE_ENCRYPTION'; + +const updateEncryption = changes => ({ + type: UPDATE_ENCRYPTION, + changes +}); + +const startLoading = dispatch => { + dispatch(updateEncryption({ loading: true })); + dispatch(closeAllGlobalMessages()); +}; + +const handleError = dispatch => error => { + parseError(error).then(message => { + dispatch(addGlobalErrorMessage(message)); + dispatch(updateEncryption({ loading: false })); + }); +}; + +export const checkSecretKey = () => dispatch => { + startLoading(dispatch); + api.checkSecretKey() + .then(data => dispatch(updateEncryption({ ...data, loading: false }))) + .catch(handleError(dispatch)); +}; + +export const generateSecretKey = () => dispatch => { + startLoading(dispatch); + api.generateSecretKey() + .then(data => dispatch(updateEncryption({ + ...data, + secretKeyAvailable: false, + loading: false + }))) + .catch(handleError(dispatch)); +}; + +export const encryptValue = value => dispatch => { + startLoading(dispatch); + api.encryptValue(value) + .then(data => dispatch(updateEncryption({ ...data, loading: false }))) + .catch(handleError(dispatch)); +}; + +export const startGeneration = () => dispatch => { + dispatch(updateEncryption({ secretKeyAvailable: false, secretKey: undefined })); +}; diff --git a/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/reducer.js b/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/reducer.js new file mode 100644 index 00000000000..8ae88c7c127 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/store/encryptionPage/reducer.js @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 { UPDATE_ENCRYPTION } from './actions'; + +const initialState = { loading: true }; + +const reducer = (state = initialState, action = {}) => { + if (action.type === UPDATE_ENCRYPTION) { + return { ...state, ...action.changes }; + } + return state; +}; + +export default reducer; diff --git a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js b/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js index e886b4616c0..0c3b7dfe214 100644 --- a/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js +++ b/server/sonar-web/src/main/js/apps/settings/store/rootReducer.js @@ -23,12 +23,14 @@ import values, * as fromValues from './values/reducer'; import settingsPage, * as fromSettingsPage from './settingsPage/reducer'; import licenses, * as fromLicenses from './licenses/reducer'; import globalMessages, * as fromGlobalMessages from '../../../components/store/globalMessages'; +import encryptionPage from './encryptionPage/reducer'; const rootReducer = combineReducers({ definitions, values, settingsPage, licenses, + encryptionPage, globalMessages }); @@ -58,4 +60,6 @@ export const getAllLicenseKeys = state => fromLicenses.getAllLicenseKeys(state.l export const getValidationMessage = (state, key) => fromSettingsPage.getValidationMessage(state.settingsPage, key); +export const getEncryptionState = state => state.encryptionPage; + export const getGlobalMessages = state => fromGlobalMessages.getGlobalMessages(state.globalMessages); |