From 81cb7e6cd9d42e81d2f26db8eff9a8fcc44e7bf8 Mon Sep 17 00:00:00 2001 From: agix Date: Wed, 12 Jul 2017 20:04:49 +0200 Subject: [PATCH 1/9] Implement export and "generic" import --- src/actions/ImportActions.js | 57 ++++++++ src/actions/OptionsActions.js | 23 ---- .../FileChooser.js | 0 src/components/Import/Special.js | 48 +++++++ src/components/Import/SpecialField.js | 42 ++++++ src/components/Import/index.js | 124 +++++++++++++++++ src/components/Layout.js | 2 + src/components/Sidebar.js | 36 +++++ .../options/ImportKeepassShow/index.js | 127 ------------------ src/components/options/OptionsContainer.js | 13 +- src/icons.svg | 10 ++ src/stores/ImportStore.js | 100 ++++++++++++++ src/stores/OptionsStore.js | 67 --------- src/utils/import/index.js | 9 ++ src/utils/{import.js => import/keepass.js} | 26 +++- src/utils/import/secretin.js | 38 ++++++ 16 files changed, 490 insertions(+), 232 deletions(-) create mode 100644 src/actions/ImportActions.js rename src/components/{options/ImportKeepassShow => Import}/FileChooser.js (100%) create mode 100644 src/components/Import/Special.js create mode 100644 src/components/Import/SpecialField.js create mode 100644 src/components/Import/index.js delete mode 100644 src/components/options/ImportKeepassShow/index.js create mode 100644 src/stores/ImportStore.js create mode 100644 src/utils/import/index.js rename src/utils/{import.js => import/keepass.js} (89%) create mode 100644 src/utils/import/secretin.js diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js new file mode 100644 index 0000000..064148a --- /dev/null +++ b/src/actions/ImportActions.js @@ -0,0 +1,57 @@ +import alt from 'utils/alt'; +import Import from 'utils/import'; + +class ImportActions { + constructor() { + this.generateActions( + 'importProgress', + 'importSuccess', + 'importFailure', + 'detectTypeSuccess', + 'detectTypeFailure', + 'defaultStore', + 'changeSpecial' + ); + } + + detectType({ file }) { + const reader = new FileReader(); + reader.readAsText(file); + reader.onload = readedFile => { + const file = readedFile.target.result; + let importType = ''; + const importTypes = Object.keys(Import); + for (let i = 0; i < importTypes.length; i++) { + if (Import[importTypes[i]].detect(file)) { + importType = importTypes[i]; + break; + } + } + if (importType !== '') { + const special = Import[importType].needSpecial(); + this.detectTypeSuccess({ file, importType, special }); + } else { + this.detectTypeFailure({ error: 'Invalid type' }); + } + }; + return reader; + } + + importSecrets({ file, type, special }) { + return dispatch => { + dispatch(); + Import[type] + .parse(file, special.toJS(), (importStatus, importTotal) => + this.importProgress({ importStatus, importTotal }) + ) + .then(() => { + this.importSuccess(); + }) + .catch(error => { + this.importFailure({ error }); + }); + }; + } +} + +export default alt.createActions(ImportActions); diff --git a/src/actions/OptionsActions.js b/src/actions/OptionsActions.js index 2e22337..df1e03b 100644 --- a/src/actions/OptionsActions.js +++ b/src/actions/OptionsActions.js @@ -1,6 +1,5 @@ import alt from 'utils/alt'; import secretin from 'utils/secretin'; -import { parseKeepass } from 'utils/import'; import uuid from 'uuid'; class OptionsActions { @@ -14,11 +13,6 @@ class OptionsActions { 'activateShortLoginFailure', 'deactivateShortLoginSuccess', 'deactivateShortLoginFailure', - 'showImportKeepass', - 'hideImportKeepass', - 'importKeepassProgress', - 'importKeepassSuccess', - 'importKeepassFailure', 'hideQRCode', 'hideShortLogin', 'changeDelaySuccess', @@ -101,23 +95,6 @@ class OptionsActions { return this.deactivateTotp(); } - importKeepass({ file }) { - const reader = new FileReader(); - reader.readAsText(file); - reader.onload = readedFile => - parseKeepass(readedFile.target.result, (importStatus, importTotal) => - this.importKeepassProgress({ importStatus, importTotal }) - ) - .then(() => { - this.importKeepassSuccess(); - }) - .catch(error => { - this.importKeepassFailure({ error }); - }); - - return reader; - } - showRescueCodes() { return dispatch => { dispatch(); diff --git a/src/components/options/ImportKeepassShow/FileChooser.js b/src/components/Import/FileChooser.js similarity index 100% rename from src/components/options/ImportKeepassShow/FileChooser.js rename to src/components/Import/FileChooser.js diff --git a/src/components/Import/Special.js b/src/components/Import/Special.js new file mode 100644 index 0000000..31bf5c1 --- /dev/null +++ b/src/components/Import/Special.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Immutable from 'immutable'; +import connectToStores from 'alt-utils/lib/connectToStores'; +import ImportStore from 'stores/ImportStore'; +import SpecialField from './SpecialField'; + +import ImportActions from 'actions/ImportActions'; + +class ImportSpecial extends Component { + static propTypes = { + special: PropTypes.instanceOf(Immutable.Map), + }; + + static getStores() { + return [ImportStore]; + } + + static getPropsFromStores() { + const state = ImportStore.getState(); + + return { + special: state.get('special'), + }; + } + + render() { + const keys = Object.keys(this.props.special.toJS()); + const inputs = []; + keys.forEach(key => { + inputs.push( + + ); + }); + return ( +
+ {inputs} +
+ ); + } +} + +export default connectToStores(ImportSpecial); diff --git a/src/components/Import/SpecialField.js b/src/components/Import/SpecialField.js new file mode 100644 index 0000000..624fa8e --- /dev/null +++ b/src/components/Import/SpecialField.js @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Input from 'components/utilities/Input'; + +class SpecialField extends Component { + static propTypes = { + name: PropTypes.string, + value: PropTypes.string, + onChange: PropTypes.func, + }; + + constructor(props) { + super(props); + this.handleChange = this.handleChange.bind(this); + } + + handleChange({ value }) { + const params = { + name: this.props.name, + value, + }; + + this.props.onChange(params); + } + + render() { + return ( + { + this.input = ref; + }} + label={this.props.name} + name={this.props.name} + value={this.props.value} + onChange={this.handleChange} + type="password" + /> + ); + } +} + +export default SpecialField; diff --git a/src/components/Import/index.js b/src/components/Import/index.js new file mode 100644 index 0000000..67142ab --- /dev/null +++ b/src/components/Import/index.js @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Immutable from 'immutable'; +import connectToStores from 'alt-utils/lib/connectToStores'; + +import Title from 'components/utilities/Title'; +import Button from 'components/utilities/Button'; +import Icon from 'components/utilities/Icon'; +import Spinner from 'components/utilities/Spinner'; + +import ImportStore from 'stores/ImportStore'; +import ImportActions from 'actions/ImportActions'; +import MetadataActions from 'actions/MetadataActions'; + +import FileChooser from './FileChooser'; +import ImportSpecial from './Special'; + +class ImportContainer extends Component { + static propTypes = { + importType: PropTypes.string, + importing: PropTypes.bool, + importStatus: PropTypes.number, + importTotal: PropTypes.number, + success: PropTypes.bool, + file: PropTypes.string, + error: PropTypes.string, + special: PropTypes.instanceOf(Immutable.Map), + }; + + static getStores() { + return [ImportStore]; + } + + static getPropsFromStores() { + const state = ImportStore.getState(); + + return { + error: state.get('error'), + importType: state.get('importType'), + importing: state.get('importing'), + importStatus: state.get('importStatus'), + importTotal: state.get('importTotal'), + success: state.get('success'), + file: state.get('file'), + special: state.get('special'), + }; + } + + constructor(props) { + super(props); + + this.handleFileChoosen = this.handleFileChoosen.bind(this); + this.handleStartParsing = this.handleStartParsing.bind(this); + } + + handleFileChoosen(file) { + ImportActions.detectType({ file }); + } + + handleStartParsing() { + ImportActions.importSecrets({ + file: this.props.file, + special: this.props.special, + type: this.props.importType, + }); + } + + render() { + if (this.props.success) { + MetadataActions.loadMetadata(); + setTimeout(function() { + ImportActions.defaultStore(); + }, 1500); + } + return ( +
+
+
+ + </div> + </div> + + <div className="page-content options"> + <span>Supported type are <i>secret-in</i>, <i>keepass</i></span> + {((this.props.success || this.props.importing) && + ((this.props.success && + <div className="import-progress"> + <Icon id="done" size={120} /> + <div className="import-progress-title"> + Done! + </div> + </div>) || + <div className="import-progress"> + <Spinner /> + {this.props.importTotal !== 0 && + <div className="import-progress-title"> + Importing... + {' '} + {this.props.importStatus} + {' '} + / + {' '} + {this.props.importTotal} + </div>} + </div>)) || + <FileChooser onFileChoosen={this.handleFileChoosen} />} + {this.props.error !== '' && <span>{this.props.error}</span>} + {this.props.special.size > 0 && + <ImportSpecial special={this.props.special} />} + {this.props.importType !== '' && + <Button + buttonStyle="primary" + className="button button--style-default button--size-base" + onClick={this.handleStartParsing} + > + Import from {this.props.importType} + </Button>} + </div> + </div> + ); + } +} + +export default connectToStores(ImportContainer); diff --git a/src/components/Layout.js b/src/components/Layout.js index 882ec4e..e366731 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -7,6 +7,7 @@ import Sidebar from 'components/Sidebar'; import SecretShow from 'components/secrets/SecretShow'; import SecretListContainer from 'components/secrets/SecretListContainer'; import OptionsContainer from 'components/options/OptionsContainer'; +import ImportContainer from 'components/Import'; function Layout() { return ( @@ -32,6 +33,7 @@ function Layout() { render={props => <SecretListContainer {...props} />} /> <Route path="/settings/" component={OptionsContainer} /> + <Route path="/import/" component={ImportContainer} /> <Redirect to="/secrets/" /> </Switch> </div> diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index 2ae5c52..5105865 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -7,6 +7,22 @@ import secretin from 'utils/secretin'; import AppUIStore from 'stores/AppUIStore'; import Icon from 'components/utilities/Icon'; +function download(filename, text) { + var element = document.createElement('a'); + element.setAttribute( + 'href', + `data:application/json;charset=utf-8,${encodeURIComponent(text)}` + ); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); +} + function SidebarMenuLink({ children, ...props }) { return ( <li className="sidebar-menu-item"> @@ -28,6 +44,15 @@ SidebarMenuLink.propTypes = { ]), }; +function exportDb() { + secretin.getDb().then(db => { + download( + `Secret-in_${secretin.currentUser.username}_${Date.now()}.json`, + db + ); + }); +} + function Sidebar() { const currentUser = AppUIStore.getCurrentUser(); return ( @@ -61,6 +86,17 @@ function Sidebar() { <Icon id="gear" size="base" /> Settings </SidebarMenuLink> + <div className="sidebar-separator" /> + <li className="sidebar-menu-item"> + <a className="sidebar-menu-link" onClick={exportDb}> + <Icon id="export" size="base" /> + Export secrets + </a> + </li> + <SidebarMenuLink to="/import/"> + <Icon id="import" size="base" /> + Import secrets + </SidebarMenuLink> </ul> </div> </div> diff --git a/src/components/options/ImportKeepassShow/index.js b/src/components/options/ImportKeepassShow/index.js deleted file mode 100644 index 4c2f6f7..0000000 --- a/src/components/options/ImportKeepassShow/index.js +++ /dev/null @@ -1,127 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Immutable from 'immutable'; -import connectToStores from 'alt-utils/lib/connectToStores'; - -import Modal from 'components/utilities/Modal'; -import Button from 'components/utilities/Button'; -import Icon from 'components/utilities/Icon'; -import Spinner from 'components/utilities/Spinner'; - -import OptionsStore from 'stores/OptionsStore'; -import OptionsActions from 'actions/OptionsActions'; -import MetadataActions from 'actions/MetadataActions'; - -import FileChooser from './FileChooser'; - -class ImportKeepassShow extends Component { - static propTypes = { - shown: PropTypes.bool, - importing: PropTypes.bool, - importStatus: PropTypes.number, - importTotal: PropTypes.number, - success: PropTypes.bool, - }; - - static defaultProps = { - shown: false, - importing: false, - success: false, - errors: new Immutable.Map(), - }; - - static getStores() { - return [OptionsStore]; - } - - static getPropsFromStores() { - const state = OptionsStore.getState().getIn(['import', 'keepass']); - - return { - errors: state.get('errors'), - shown: state.get('shown'), - importing: state.get('importing'), - importStatus: state.get('importStatus'), - importTotal: state.get('importTotal'), - success: state.get('success'), - }; - } - - constructor(props) { - super(props); - - this.handleFileChoosen = this.handleFileChoosen.bind(this); - } - - componentWillUnmount() { - OptionsActions.hideImportKeepass(); - } - - handleFileChoosen(file) { - OptionsActions.importKeepass({ file }); - } - - render() { - return ( - <Modal - show={this.props.shown} - onClose={ - !this.props.importing ? OptionsActions.hideImportKeepass : undefined - } - > - <Modal.Header> - <span className="text"> - Import from Keepass - </span> - </Modal.Header> - - <Modal.Body> - {((this.props.success || this.props.importing) && - ((this.props.success && - <div className="import-progress"> - <Icon id="done" size={120} /> - <div className="import-progress-title"> - Done! - </div> - </div>) || - <div className="import-progress"> - <Spinner /> - {this.props.importTotal !== 0 && - <div className="import-progress-title"> - Importing... - {' '} - {this.props.importStatus} - {' '} - / - {' '} - {this.props.importTotal} - </div>} - </div>)) || - <FileChooser onFileChoosen={this.handleFileChoosen} />} - </Modal.Body> - - <Modal.Footer> - {this.props.success - ? <Button - buttonStyle="primary" - className="button button--style-default button--size-base" - to="/" - onClick={MetadataActions.loadMetadata} - > - Return to Home - </Button> - : <Button - type="reset" - buttonStyle="default" - onClick={OptionsActions.hideImportKeepass} - disabled={this.props.importing} - > - Close - </Button>} - </Modal.Footer> - </Modal> - ); - } -} - -export default connectToStores(ImportKeepassShow); diff --git a/src/components/options/OptionsContainer.js b/src/components/options/OptionsContainer.js index a697c16..051878e 100644 --- a/src/components/options/OptionsContainer.js +++ b/src/components/options/OptionsContainer.js @@ -7,7 +7,6 @@ import AppUIStore from 'stores/AppUIStore'; import ShortLoginShow from 'components/options/ShortLoginShow'; import QRCodeShow from 'components/options/QRCodeShow'; import RescueCodesShow from 'components/options/RescueCodesShow'; -import ImportKeepassShow from 'components/options/ImportKeepassShow'; import Title from 'components/utilities/Title'; import Checkbox from 'components/utilities/Checkbox'; import Input from 'components/utilities/Input'; @@ -121,18 +120,10 @@ class OptionsContainer extends Component { </div> <div className="options-section"> <h3 className="options-section-title"> - Imports + Password generation </h3> <div className="options-section-item"> - <ImportKeepassShow /> - <Button - type="button" - buttonStyle="primary" - onClick={OptionsActions.showImportKeepass} - disabled={!AppUIStore.isOnline()} - > - Import from Keepass - </Button> + SOON BY @dqms </div> </div> </div> diff --git a/src/icons.svg b/src/icons.svg index dc63199..9470670 100644 --- a/src/icons.svg +++ b/src/icons.svg @@ -134,6 +134,16 @@ <path d="M0 0h24v24H0z" fill="none"/> </symbol> + <symbol id="export" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"/> + </symbol> + + <symbol id="import" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/> + </symbol> + <symbol id="done" viewBox="0 0 24 24"> <path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/> <path d="M0 0h24v24H0z" fill="none"/> diff --git a/src/stores/ImportStore.js b/src/stores/ImportStore.js new file mode 100644 index 0000000..db97f98 --- /dev/null +++ b/src/stores/ImportStore.js @@ -0,0 +1,100 @@ +import Immutable, { Record } from 'immutable'; +import alt from 'utils/alt'; +import makeImmutable from 'utils/makeImmutable'; + +import ImportActions from 'actions/ImportActions'; + +const ImportState = new Record({ + importType: '', + importing: false, + importStatus: 0, + importTotal: 0, + success: false, + file: '', + error: '', + special: new Immutable.Map(), +}); + +class ImportStore { + constructor() { + this.bindActions(ImportActions); + this.state = new ImportState(); + } + + onImportSecrets() { + this.setState( + this.state.merge({ + importing: true, + success: false, + error: '', + }) + ); + } + + onDetectTypeFailure({ error }) { + this.setState( + this.state.merge({ + error, + importType: '', + special: new Immutable.Map(), + }) + ); + } + + onDetectTypeSuccess({ file, importType, special }) { + this.setState( + this.state.merge({ + importType, + error: '', + file, + special: new Immutable.Map(special), + }) + ); + } + + onImportProgress({ importStatus, importTotal }) { + this.setState( + this.state.merge({ + importStatus, + importTotal, + error: '', + }) + ); + } + + onImportSuccess() { + this.setState( + this.state.merge({ + importing: false, + success: true, + importType: '', + error: '', + importStatus: 0, + importTotal: 0, + special: new Immutable.Map(), + }) + ); + } + + onImportFailure({ error }) { + this.setState( + this.state.merge({ + importing: false, + success: false, + importType: '', + error: error, + special: new Immutable.Map(), + }) + ); + } + + onDefaultStore() { + this.setState(new ImportState()); + } + + onChangeSpecial({ name, value }) { + this.setState(this.state.setIn(['special', name], value)); + } +} + +export default alt.createStore(makeImmutable(ImportStore), 'ImportStore'); diff --git a/src/stores/OptionsStore.js b/src/stores/OptionsStore.js index cbc28c5..f17a921 100644 --- a/src/stores/OptionsStore.js +++ b/src/stores/OptionsStore.js @@ -13,16 +13,6 @@ const OptionsState = new Record({ showRescueCodes: false, rescueCodes: new Immutable.List(), loading: false, - import: Immutable.fromJS({ - keepass: { - shown: false, - importing: false, - importStatus: 0, - importTotal: 0, - success: false, - errors: new Immutable.Map(), - }, - }), }); class OptionsStore { @@ -53,63 +43,6 @@ class OptionsStore { this.setState(this.state.set('showShortLogin', showShortLogin)); } - onShowImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'shown'], true) - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onHideImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'shown'], false) - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], true) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onImportKeepassProgress({ importStatus, importTotal }) { - this.setState( - this.state - .setIn(['import', 'keepass', 'importStatus'], importStatus) - .setIn(['import', 'keepass', 'importTotal'], importTotal) - ); - } - - onImportKeepassSuccess() { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], true) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - .setIn(['import', 'keepass', 'importStatus'], 0) - .setIn(['import', 'keepass', 'importTotal'], 0) - ); - } - - onImportKeepassFailure({ error }) { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map(error)) - ); - } - onActivateTotpFailure({ error }) { this.setState( this.state.merge({ diff --git a/src/utils/import/index.js b/src/utils/import/index.js new file mode 100644 index 0000000..3628063 --- /dev/null +++ b/src/utils/import/index.js @@ -0,0 +1,9 @@ +import keepass from 'utils/import/keepass'; +import secretinDB from 'utils/import/secretin'; + +const Import = { + keepass, + secretin: secretinDB, +}; + +export default Import; diff --git a/src/utils/import.js b/src/utils/import/keepass.js similarity index 89% rename from src/utils/import.js rename to src/utils/import/keepass.js index f2486fb..ae53ee9 100644 --- a/src/utils/import.js +++ b/src/utils/import/keepass.js @@ -147,7 +147,19 @@ function count(group) { return nb; } -export function parseKeepass(xml, progress = defaultProgress) { +export function detect(file) { + const parser = new DOMParser(); + let isKeepass = false; + try { + const xmlDoc = parser.parseFromString(file, 'application/xml'); + isKeepass = xmlDoc.getElementsByTagName('KeePassFile').length === 1; + } catch (e) { + isKeepass = false; + } + return isKeepass; +} + +export function parse(xml, special, progress = defaultProgress) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xml, 'application/xml'); const root = xmlDoc.getElementsByTagName('Root')[0].children[0]; @@ -155,8 +167,14 @@ export function parseKeepass(xml, progress = defaultProgress) { return parseGroup(root, currentProgress); } -const Import = { - parseKeepass, +export function needSpecial() { + return {}; +} + +const keepass = { + parse, + detect, + needSpecial, }; -export default Import; +export default keepass; diff --git a/src/utils/import/secretin.js b/src/utils/import/secretin.js new file mode 100644 index 0000000..1ad933c --- /dev/null +++ b/src/utils/import/secretin.js @@ -0,0 +1,38 @@ +import secretin from 'utils/secretin'; + +function defaultProgress(status, total) { + // eslint-disable-next-line no-console + console.log(`${status}/${total}`); +} + +export function detect(file) { + let isSecretin = false; + try { + const db = JSON.parse(file); + isSecretin = 'users' in db && 'secrets' in db; + } catch (e) { + isSecretin = false; + } + return isSecretin; +} + +export function parse(db, { username, password }, progress = defaultProgress) { + return secretin.importDb(username, password, db, function(status) { + progress(status.state, status.total); + }); +} + +export function needSpecial() { + return { + username: '', + password: '', + }; +} + +const secretinDB = { + parse, + detect, + needSpecial, +}; + +export default secretinDB; From 4aeba83eaa6c499b75dc2f610727afa7ba8d50bf Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Wed, 12 Jul 2017 20:24:37 +0200 Subject: [PATCH 2/9] Prevent anonym function by returning status object to progress --- src/actions/ImportActions.js | 7 +++++-- src/utils/import/keepass.js | 2 +- src/utils/import/secretin.js | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js index 064148a..cce6249 100644 --- a/src/actions/ImportActions.js +++ b/src/actions/ImportActions.js @@ -41,8 +41,11 @@ class ImportActions { return dispatch => { dispatch(); Import[type] - .parse(file, special.toJS(), (importStatus, importTotal) => - this.importProgress({ importStatus, importTotal }) + .parse(file, special.toJS(), status => + this.importProgress({ + importStatus: status.state, + importTotal: status.total, + }) ) .then(() => { this.importSuccess(); diff --git a/src/utils/import/keepass.js b/src/utils/import/keepass.js index ae53ee9..5fea1e4 100644 --- a/src/utils/import/keepass.js +++ b/src/utils/import/keepass.js @@ -11,7 +11,7 @@ class InternalProgressBar { update() { this.status += 1; - this.func(this.status, this.total); + this.func({ state: this.status, total: this.total }); } } diff --git a/src/utils/import/secretin.js b/src/utils/import/secretin.js index 1ad933c..ce71749 100644 --- a/src/utils/import/secretin.js +++ b/src/utils/import/secretin.js @@ -17,9 +17,7 @@ export function detect(file) { } export function parse(db, { username, password }, progress = defaultProgress) { - return secretin.importDb(username, password, db, function(status) { - progress(status.state, status.total); - }); + return secretin.importDb(username, password, db, progress); } export function needSpecial() { From 5eec58547f448a0159be9d6f0516cd41ca6b301e Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Wed, 19 Jul 2017 00:47:26 +0200 Subject: [PATCH 3/9] Fix @calyhre suggestions --- src/actions/ImportActions.js | 53 +++++++++---------- src/components/Layout.js | 2 +- src/components/Sidebar.js | 3 +- .../index.js => import/ImportContainer.js} | 27 +++++----- .../ImportFileChooser.js} | 8 +-- .../ImportMandatoryField.js} | 18 +++---- .../ImportMandatoryFields.js} | 19 ++++--- src/stores/ImportStore.js | 24 +++++---- src/utils/import/index.js | 9 ---- src/utils/importers/index.js | 9 ++++ src/utils/{import => importers}/keepass.js | 8 ++- src/utils/{import => importers}/secretin.js | 14 +++-- 12 files changed, 94 insertions(+), 100 deletions(-) rename src/components/{Import/index.js => import/ImportContainer.js} (83%) rename src/components/{Import/FileChooser.js => import/ImportFileChooser.js} (88%) rename src/components/{Import/SpecialField.js => import/ImportMandatoryField.js} (59%) rename src/components/{Import/Special.js => import/ImportMandatoryFields.js} (57%) delete mode 100644 src/utils/import/index.js create mode 100644 src/utils/importers/index.js rename src/utils/{import => importers}/keepass.js (97%) rename src/utils/{import => importers}/secretin.js (68%) diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js index cce6249..8e636be 100644 --- a/src/actions/ImportActions.js +++ b/src/actions/ImportActions.js @@ -1,35 +1,31 @@ +import { findKey } from 'lodash'; + import alt from 'utils/alt'; -import Import from 'utils/import'; +import importers from 'utils/importers'; class ImportActions { constructor() { this.generateActions( - 'importProgress', - 'importSuccess', - 'importFailure', + 'importSecretsProgress', + 'importSecretsSuccess', + 'importSecretsFailure', 'detectTypeSuccess', 'detectTypeFailure', 'defaultStore', - 'changeSpecial' + 'changeMandatoryField' ); } detectType({ file }) { const reader = new FileReader(); reader.readAsText(file); - reader.onload = readedFile => { - const file = readedFile.target.result; - let importType = ''; - const importTypes = Object.keys(Import); - for (let i = 0; i < importTypes.length; i++) { - if (Import[importTypes[i]].detect(file)) { - importType = importTypes[i]; - break; - } - } - if (importType !== '') { - const special = Import[importType].needSpecial(); - this.detectTypeSuccess({ file, importType, special }); + reader.onload = ({ target }) => { + const file = target.result; + const importType = findKey(importers, importer => importer.detect(file)); + + if (typeof importType !== 'undefined') { + const mandatoryFields = importers[importType].mandatoryFields; + this.detectTypeSuccess({ file, importType, mandatoryFields }); } else { this.detectTypeFailure({ error: 'Invalid type' }); } @@ -37,21 +33,24 @@ class ImportActions { return reader; } - importSecrets({ file, type, special }) { + importSecrets({ file, type, mandatoryFields }) { return dispatch => { dispatch(); - Import[type] - .parse(file, special.toJS(), status => - this.importProgress({ - importStatus: status.state, - importTotal: status.total, - }) + importers[type] + .parse( + file, + mandatoryFields.toJS(), + ({ state: importStatus, total: importTotal }) => + this.importSecretsProgress({ + importStatus, + importTotal, + }) ) .then(() => { - this.importSuccess(); + this.importSecretsSuccess(); }) .catch(error => { - this.importFailure({ error }); + this.importSecretsFailure({ error }); }); }; } diff --git a/src/components/Layout.js b/src/components/Layout.js index e366731..5af44ab 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -7,7 +7,7 @@ import Sidebar from 'components/Sidebar'; import SecretShow from 'components/secrets/SecretShow'; import SecretListContainer from 'components/secrets/SecretListContainer'; import OptionsContainer from 'components/options/OptionsContainer'; -import ImportContainer from 'components/Import'; +import ImportContainer from 'components/import/ImportContainer'; function Layout() { return ( diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index 5105865..ee08f98 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Link from 'react-router-dom/Link'; import NavLink from 'react-router-dom/NavLink'; +import moment from 'moment'; import secretin from 'utils/secretin'; import AppUIStore from 'stores/AppUIStore'; @@ -47,7 +48,7 @@ SidebarMenuLink.propTypes = { function exportDb() { secretin.getDb().then(db => { download( - `Secret-in_${secretin.currentUser.username}_${Date.now()}.json`, + `Secret-in_${secretin.currentUser.username}_${moment.format()}.json`, db ); }); diff --git a/src/components/Import/index.js b/src/components/import/ImportContainer.js similarity index 83% rename from src/components/Import/index.js rename to src/components/import/ImportContainer.js index 67142ab..d5e4aff 100644 --- a/src/components/Import/index.js +++ b/src/components/import/ImportContainer.js @@ -12,8 +12,8 @@ import ImportStore from 'stores/ImportStore'; import ImportActions from 'actions/ImportActions'; import MetadataActions from 'actions/MetadataActions'; -import FileChooser from './FileChooser'; -import ImportSpecial from './Special'; +import ImportFileChooser from './ImportFileChooser'; +import ImportMandatoryFields from './ImportMandatoryFields'; class ImportContainer extends Component { static propTypes = { @@ -24,7 +24,7 @@ class ImportContainer extends Component { success: PropTypes.bool, file: PropTypes.string, error: PropTypes.string, - special: PropTypes.instanceOf(Immutable.Map), + mandatoryFields: PropTypes.instanceOf(Immutable.Map), }; static getStores() { @@ -42,7 +42,7 @@ class ImportContainer extends Component { importTotal: state.get('importTotal'), success: state.get('success'), file: state.get('file'), - special: state.get('special'), + mandatoryFields: state.get('mandatoryFields'), }; } @@ -60,7 +60,7 @@ class ImportContainer extends Component { handleStartParsing() { ImportActions.importSecrets({ file: this.props.file, - special: this.props.special, + mandatoryFields: this.props.mandatoryFields, type: this.props.importType, }); } @@ -94,19 +94,16 @@ class ImportContainer extends Component { <Spinner /> {this.props.importTotal !== 0 && <div className="import-progress-title"> - Importing... - {' '} - {this.props.importStatus} - {' '} - / - {' '} - {this.props.importTotal} + {`Importing... ${this.props.importStatus} / ${this.props + .importTotal}`} </div>} </div>)) || - <FileChooser onFileChoosen={this.handleFileChoosen} />} + <ImportFileChooser onFileChoosen={this.handleFileChoosen} />} {this.props.error !== '' && <span>{this.props.error}</span>} - {this.props.special.size > 0 && - <ImportSpecial special={this.props.special} />} + {this.props.mandatoryFields.size > 0 && + <ImportMandatoryFields + mandatoryFields={this.props.mandatoryFields} + />} {this.props.importType !== '' && <Button buttonStyle="primary" diff --git a/src/components/Import/FileChooser.js b/src/components/import/ImportFileChooser.js similarity index 88% rename from src/components/Import/FileChooser.js rename to src/components/import/ImportFileChooser.js index 88b89c0..a7ce521 100644 --- a/src/components/Import/FileChooser.js +++ b/src/components/import/ImportFileChooser.js @@ -5,7 +5,7 @@ import HTML5Backend, { NativeTypes } from 'react-dnd-html5-backend'; import Icon from 'components/utilities/Icon'; -class FileChooser extends Component { +class ImportFileChooser extends Component { static propTypes = { onFileChoosen: PropTypes.func, connectDropTarget: PropTypes.func, @@ -54,10 +54,10 @@ function itemTargetCollect(connect, monitor) { }; } -const FileChooserTarget = new DropTarget( +const ImportFileChooserTarget = new DropTarget( NativeTypes.FILE, itemTarget, itemTargetCollect -)(FileChooser); +)(ImportFileChooser); -export default new DragDropContext(HTML5Backend)(FileChooserTarget); +export default new DragDropContext(HTML5Backend)(ImportFileChooserTarget); diff --git a/src/components/Import/SpecialField.js b/src/components/import/ImportMandatoryField.js similarity index 59% rename from src/components/Import/SpecialField.js rename to src/components/import/ImportMandatoryField.js index 624fa8e..112c7a2 100644 --- a/src/components/Import/SpecialField.js +++ b/src/components/import/ImportMandatoryField.js @@ -1,11 +1,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import Immutable from 'immutable'; import Input from 'components/utilities/Input'; -class SpecialField extends Component { +class ImportMandatoryField extends Component { static propTypes = { - name: PropTypes.string, - value: PropTypes.string, + field: PropTypes.instanceOf(Immutable.Map), onChange: PropTypes.func, }; @@ -16,7 +16,7 @@ class SpecialField extends Component { handleChange({ value }) { const params = { - name: this.props.name, + field: this.props.field, value, }; @@ -29,14 +29,14 @@ class SpecialField extends Component { ref={ref => { this.input = ref; }} - label={this.props.name} - name={this.props.name} - value={this.props.value} + label={this.props.field.get('name')} + name={this.props.field.get('name')} + value={this.props.field.get('value')} onChange={this.handleChange} - type="password" + type={this.props.field.get('type')} /> ); } } -export default SpecialField; +export default ImportMandatoryField; diff --git a/src/components/Import/Special.js b/src/components/import/ImportMandatoryFields.js similarity index 57% rename from src/components/Import/Special.js rename to src/components/import/ImportMandatoryFields.js index 31bf5c1..45103f0 100644 --- a/src/components/Import/Special.js +++ b/src/components/import/ImportMandatoryFields.js @@ -3,13 +3,13 @@ import PropTypes from 'prop-types'; import Immutable from 'immutable'; import connectToStores from 'alt-utils/lib/connectToStores'; import ImportStore from 'stores/ImportStore'; -import SpecialField from './SpecialField'; +import ImportMandatoryField from './ImportMandatoryField'; import ImportActions from 'actions/ImportActions'; -class ImportSpecial extends Component { +class ImportersMandatoryFields extends Component { static propTypes = { - special: PropTypes.instanceOf(Immutable.Map), + mandatoryFields: PropTypes.instanceOf(Immutable.Map), }; static getStores() { @@ -20,20 +20,19 @@ class ImportSpecial extends Component { const state = ImportStore.getState(); return { - special: state.get('special'), + mandatoryFields: state.get('mandatoryFields'), }; } render() { - const keys = Object.keys(this.props.special.toJS()); + const keys = Object.keys(this.props.mandatoryFields.toJS()); const inputs = []; keys.forEach(key => { inputs.push( - <SpecialField + <ImportMandatoryField key={key} - name={key} - value={this.props.special.get(key)} - onChange={ImportActions.changeSpecial} + field={this.props.mandatoryFields.get(key)} + onChange={ImportActions.changeMandatoryField} /> ); }); @@ -45,4 +44,4 @@ class ImportSpecial extends Component { } } -export default connectToStores(ImportSpecial); +export default connectToStores(ImportersMandatoryFields); diff --git a/src/stores/ImportStore.js b/src/stores/ImportStore.js index db97f98..ef77968 100644 --- a/src/stores/ImportStore.js +++ b/src/stores/ImportStore.js @@ -12,7 +12,7 @@ const ImportState = new Record({ success: false, file: '', error: '', - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }); class ImportStore { @@ -36,23 +36,23 @@ class ImportStore { this.state.merge({ error, importType: '', - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } - onDetectTypeSuccess({ file, importType, special }) { + onDetectTypeSuccess({ file, importType, mandatoryFields }) { this.setState( this.state.merge({ importType, error: '', file, - special: new Immutable.Map(special), + mandatoryFields: Immutable.fromJS(mandatoryFields), }) ); } - onImportProgress({ importStatus, importTotal }) { + onImportSecretsProgress({ importStatus, importTotal }) { this.setState( this.state.merge({ importStatus, @@ -62,7 +62,7 @@ class ImportStore { ); } - onImportSuccess() { + onImportSecretsSuccess() { this.setState( this.state.merge({ importing: false, @@ -71,19 +71,19 @@ class ImportStore { error: '', importStatus: 0, importTotal: 0, - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } - onImportFailure({ error }) { + onImportSecretsFailure({ error }) { this.setState( this.state.merge({ importing: false, success: false, importType: '', error: error, - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } @@ -92,8 +92,10 @@ class ImportStore { this.setState(new ImportState()); } - onChangeSpecial({ name, value }) { - this.setState(this.state.setIn(['special', name], value)); + onChangeMandatoryField({ field, value }) { + this.setState( + this.state.setIn(['mandatoryFields', field.get('name'), 'value'], value) + ); } } diff --git a/src/utils/import/index.js b/src/utils/import/index.js deleted file mode 100644 index 3628063..0000000 --- a/src/utils/import/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import keepass from 'utils/import/keepass'; -import secretinDB from 'utils/import/secretin'; - -const Import = { - keepass, - secretin: secretinDB, -}; - -export default Import; diff --git a/src/utils/importers/index.js b/src/utils/importers/index.js new file mode 100644 index 0000000..a90088a --- /dev/null +++ b/src/utils/importers/index.js @@ -0,0 +1,9 @@ +import keepass from 'utils/importers/keepass'; +import secretinDB from 'utils/importers/secretin'; + +const importers = { + keepass, + secretin: secretinDB, +}; + +export default importers; diff --git a/src/utils/import/keepass.js b/src/utils/importers/keepass.js similarity index 97% rename from src/utils/import/keepass.js rename to src/utils/importers/keepass.js index 5fea1e4..6088a72 100644 --- a/src/utils/import/keepass.js +++ b/src/utils/importers/keepass.js @@ -159,7 +159,7 @@ export function detect(file) { return isKeepass; } -export function parse(xml, special, progress = defaultProgress) { +export function parse(xml, mandatoryField, progress = defaultProgress) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xml, 'application/xml'); const root = xmlDoc.getElementsByTagName('Root')[0].children[0]; @@ -167,14 +167,12 @@ export function parse(xml, special, progress = defaultProgress) { return parseGroup(root, currentProgress); } -export function needSpecial() { - return {}; -} +export const mandatoryFields = {}; const keepass = { parse, detect, - needSpecial, + mandatoryFields, }; export default keepass; diff --git a/src/utils/import/secretin.js b/src/utils/importers/secretin.js similarity index 68% rename from src/utils/import/secretin.js rename to src/utils/importers/secretin.js index ce71749..4906d77 100644 --- a/src/utils/import/secretin.js +++ b/src/utils/importers/secretin.js @@ -17,20 +17,18 @@ export function detect(file) { } export function parse(db, { username, password }, progress = defaultProgress) { - return secretin.importDb(username, password, db, progress); + return secretin.importDb(username.value, password.value, db, progress); } -export function needSpecial() { - return { - username: '', - password: '', - }; -} +export const mandatoryFields = { + username: { type: 'text', name: 'username', value: '' }, + password: { type: 'password', name: 'password', value: '' }, +}; const secretinDB = { parse, detect, - needSpecial, + mandatoryFields, }; export default secretinDB; From 329031ab68f9a6546f0c9cbb16d28474c51dfac3 Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Tue, 25 Jul 2017 18:11:59 +0200 Subject: [PATCH 4/9] Fix moment typo --- src/components/Sidebar.js | 2 +- src/components/import/ImportContainer.js | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index ee08f98..714a16d 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -48,7 +48,7 @@ SidebarMenuLink.propTypes = { function exportDb() { secretin.getDb().then(db => { download( - `Secret-in_${secretin.currentUser.username}_${moment.format()}.json`, + `Secret-in_${secretin.currentUser.username}_${moment().format()}.json`, db ); }); diff --git a/src/components/import/ImportContainer.js b/src/components/import/ImportContainer.js index d5e4aff..1600cc5 100644 --- a/src/components/import/ImportContainer.js +++ b/src/components/import/ImportContainer.js @@ -65,13 +65,22 @@ class ImportContainer extends Component { }); } - render() { + shouldComponentUpdate(nextProps, nextState) { + return ( + this.props.success !== true || nextProps.success !== this.props.success + ); + } + + componentDidUpdate() { if (this.props.success) { MetadataActions.loadMetadata(); setTimeout(function() { ImportActions.defaultStore(); }, 1500); } + } + + render() { return ( <div className="page"> <div className="page-header"> @@ -81,14 +90,14 @@ class ImportContainer extends Component { </div> <div className="page-content options"> - <span>Supported type are <i>secret-in</i>, <i>keepass</i></span> + <span> + Supported type are <i>secret-in</i>, <i>keepass</i> + </span> {((this.props.success || this.props.importing) && ((this.props.success && <div className="import-progress"> <Icon id="done" size={120} /> - <div className="import-progress-title"> - Done! - </div> + <div className="import-progress-title">Done!</div> </div>) || <div className="import-progress"> <Spinner /> @@ -99,7 +108,10 @@ class ImportContainer extends Component { </div>} </div>)) || <ImportFileChooser onFileChoosen={this.handleFileChoosen} />} - {this.props.error !== '' && <span>{this.props.error}</span>} + {this.props.error !== '' && + <span> + {this.props.error} + </span>} {this.props.mandatoryFields.size > 0 && <ImportMandatoryFields mandatoryFields={this.props.mandatoryFields} From 2dd15e3db78479f1f4b25e42ff910aa377ef2a96 Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Wed, 12 Jul 2017 20:04:49 +0200 Subject: [PATCH 5/9] Implement export and "generic" import --- src/actions/ImportActions.js | 57 ++++++++ src/actions/OptionsActions.js | 23 ---- .../FileChooser.js | 0 src/components/Import/Special.js | 48 +++++++ src/components/Import/SpecialField.js | 42 ++++++ src/components/Import/index.js | 124 ++++++++++++++++++ src/components/Layout.js | 2 + src/components/Sidebar.js | 36 +++++ .../options/ImportKeepassShow/index.js | 118 ----------------- src/components/options/OptionsContainer.js | 15 +-- src/icons.svg | 10 ++ src/stores/ImportStore.js | 100 ++++++++++++++ src/stores/OptionsStore.js | 67 ---------- src/utils/import/index.js | 9 ++ src/utils/{import.js => import/keepass.js} | 26 +++- src/utils/import/secretin.js | 38 ++++++ 16 files changed, 490 insertions(+), 225 deletions(-) create mode 100644 src/actions/ImportActions.js rename src/components/{options/ImportKeepassShow => Import}/FileChooser.js (100%) create mode 100644 src/components/Import/Special.js create mode 100644 src/components/Import/SpecialField.js create mode 100644 src/components/Import/index.js delete mode 100644 src/components/options/ImportKeepassShow/index.js create mode 100644 src/stores/ImportStore.js create mode 100644 src/utils/import/index.js rename src/utils/{import.js => import/keepass.js} (89%) create mode 100644 src/utils/import/secretin.js diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js new file mode 100644 index 0000000..064148a --- /dev/null +++ b/src/actions/ImportActions.js @@ -0,0 +1,57 @@ +import alt from 'utils/alt'; +import Import from 'utils/import'; + +class ImportActions { + constructor() { + this.generateActions( + 'importProgress', + 'importSuccess', + 'importFailure', + 'detectTypeSuccess', + 'detectTypeFailure', + 'defaultStore', + 'changeSpecial' + ); + } + + detectType({ file }) { + const reader = new FileReader(); + reader.readAsText(file); + reader.onload = readedFile => { + const file = readedFile.target.result; + let importType = ''; + const importTypes = Object.keys(Import); + for (let i = 0; i < importTypes.length; i++) { + if (Import[importTypes[i]].detect(file)) { + importType = importTypes[i]; + break; + } + } + if (importType !== '') { + const special = Import[importType].needSpecial(); + this.detectTypeSuccess({ file, importType, special }); + } else { + this.detectTypeFailure({ error: 'Invalid type' }); + } + }; + return reader; + } + + importSecrets({ file, type, special }) { + return dispatch => { + dispatch(); + Import[type] + .parse(file, special.toJS(), (importStatus, importTotal) => + this.importProgress({ importStatus, importTotal }) + ) + .then(() => { + this.importSuccess(); + }) + .catch(error => { + this.importFailure({ error }); + }); + }; + } +} + +export default alt.createActions(ImportActions); diff --git a/src/actions/OptionsActions.js b/src/actions/OptionsActions.js index 2e22337..df1e03b 100644 --- a/src/actions/OptionsActions.js +++ b/src/actions/OptionsActions.js @@ -1,6 +1,5 @@ import alt from 'utils/alt'; import secretin from 'utils/secretin'; -import { parseKeepass } from 'utils/import'; import uuid from 'uuid'; class OptionsActions { @@ -14,11 +13,6 @@ class OptionsActions { 'activateShortLoginFailure', 'deactivateShortLoginSuccess', 'deactivateShortLoginFailure', - 'showImportKeepass', - 'hideImportKeepass', - 'importKeepassProgress', - 'importKeepassSuccess', - 'importKeepassFailure', 'hideQRCode', 'hideShortLogin', 'changeDelaySuccess', @@ -101,23 +95,6 @@ class OptionsActions { return this.deactivateTotp(); } - importKeepass({ file }) { - const reader = new FileReader(); - reader.readAsText(file); - reader.onload = readedFile => - parseKeepass(readedFile.target.result, (importStatus, importTotal) => - this.importKeepassProgress({ importStatus, importTotal }) - ) - .then(() => { - this.importKeepassSuccess(); - }) - .catch(error => { - this.importKeepassFailure({ error }); - }); - - return reader; - } - showRescueCodes() { return dispatch => { dispatch(); diff --git a/src/components/options/ImportKeepassShow/FileChooser.js b/src/components/Import/FileChooser.js similarity index 100% rename from src/components/options/ImportKeepassShow/FileChooser.js rename to src/components/Import/FileChooser.js diff --git a/src/components/Import/Special.js b/src/components/Import/Special.js new file mode 100644 index 0000000..31bf5c1 --- /dev/null +++ b/src/components/Import/Special.js @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Immutable from 'immutable'; +import connectToStores from 'alt-utils/lib/connectToStores'; +import ImportStore from 'stores/ImportStore'; +import SpecialField from './SpecialField'; + +import ImportActions from 'actions/ImportActions'; + +class ImportSpecial extends Component { + static propTypes = { + special: PropTypes.instanceOf(Immutable.Map), + }; + + static getStores() { + return [ImportStore]; + } + + static getPropsFromStores() { + const state = ImportStore.getState(); + + return { + special: state.get('special'), + }; + } + + render() { + const keys = Object.keys(this.props.special.toJS()); + const inputs = []; + keys.forEach(key => { + inputs.push( + <SpecialField + key={key} + name={key} + value={this.props.special.get(key)} + onChange={ImportActions.changeSpecial} + /> + ); + }); + return ( + <div> + {inputs} + </div> + ); + } +} + +export default connectToStores(ImportSpecial); diff --git a/src/components/Import/SpecialField.js b/src/components/Import/SpecialField.js new file mode 100644 index 0000000..624fa8e --- /dev/null +++ b/src/components/Import/SpecialField.js @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Input from 'components/utilities/Input'; + +class SpecialField extends Component { + static propTypes = { + name: PropTypes.string, + value: PropTypes.string, + onChange: PropTypes.func, + }; + + constructor(props) { + super(props); + this.handleChange = this.handleChange.bind(this); + } + + handleChange({ value }) { + const params = { + name: this.props.name, + value, + }; + + this.props.onChange(params); + } + + render() { + return ( + <Input + ref={ref => { + this.input = ref; + }} + label={this.props.name} + name={this.props.name} + value={this.props.value} + onChange={this.handleChange} + type="password" + /> + ); + } +} + +export default SpecialField; diff --git a/src/components/Import/index.js b/src/components/Import/index.js new file mode 100644 index 0000000..67142ab --- /dev/null +++ b/src/components/Import/index.js @@ -0,0 +1,124 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Immutable from 'immutable'; +import connectToStores from 'alt-utils/lib/connectToStores'; + +import Title from 'components/utilities/Title'; +import Button from 'components/utilities/Button'; +import Icon from 'components/utilities/Icon'; +import Spinner from 'components/utilities/Spinner'; + +import ImportStore from 'stores/ImportStore'; +import ImportActions from 'actions/ImportActions'; +import MetadataActions from 'actions/MetadataActions'; + +import FileChooser from './FileChooser'; +import ImportSpecial from './Special'; + +class ImportContainer extends Component { + static propTypes = { + importType: PropTypes.string, + importing: PropTypes.bool, + importStatus: PropTypes.number, + importTotal: PropTypes.number, + success: PropTypes.bool, + file: PropTypes.string, + error: PropTypes.string, + special: PropTypes.instanceOf(Immutable.Map), + }; + + static getStores() { + return [ImportStore]; + } + + static getPropsFromStores() { + const state = ImportStore.getState(); + + return { + error: state.get('error'), + importType: state.get('importType'), + importing: state.get('importing'), + importStatus: state.get('importStatus'), + importTotal: state.get('importTotal'), + success: state.get('success'), + file: state.get('file'), + special: state.get('special'), + }; + } + + constructor(props) { + super(props); + + this.handleFileChoosen = this.handleFileChoosen.bind(this); + this.handleStartParsing = this.handleStartParsing.bind(this); + } + + handleFileChoosen(file) { + ImportActions.detectType({ file }); + } + + handleStartParsing() { + ImportActions.importSecrets({ + file: this.props.file, + special: this.props.special, + type: this.props.importType, + }); + } + + render() { + if (this.props.success) { + MetadataActions.loadMetadata(); + setTimeout(function() { + ImportActions.defaultStore(); + }, 1500); + } + return ( + <div className="page"> + <div className="page-header"> + <div className="breadcrumb"> + <Title link="/import/" icon="import" title="Import" /> + </div> + </div> + + <div className="page-content options"> + <span>Supported type are <i>secret-in</i>, <i>keepass</i></span> + {((this.props.success || this.props.importing) && + ((this.props.success && + <div className="import-progress"> + <Icon id="done" size={120} /> + <div className="import-progress-title"> + Done! + </div> + </div>) || + <div className="import-progress"> + <Spinner /> + {this.props.importTotal !== 0 && + <div className="import-progress-title"> + Importing... + {' '} + {this.props.importStatus} + {' '} + / + {' '} + {this.props.importTotal} + </div>} + </div>)) || + <FileChooser onFileChoosen={this.handleFileChoosen} />} + {this.props.error !== '' && <span>{this.props.error}</span>} + {this.props.special.size > 0 && + <ImportSpecial special={this.props.special} />} + {this.props.importType !== '' && + <Button + buttonStyle="primary" + className="button button--style-default button--size-base" + onClick={this.handleStartParsing} + > + Import from {this.props.importType} + </Button>} + </div> + </div> + ); + } +} + +export default connectToStores(ImportContainer); diff --git a/src/components/Layout.js b/src/components/Layout.js index 882ec4e..e366731 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -7,6 +7,7 @@ import Sidebar from 'components/Sidebar'; import SecretShow from 'components/secrets/SecretShow'; import SecretListContainer from 'components/secrets/SecretListContainer'; import OptionsContainer from 'components/options/OptionsContainer'; +import ImportContainer from 'components/Import'; function Layout() { return ( @@ -32,6 +33,7 @@ function Layout() { render={props => <SecretListContainer {...props} />} /> <Route path="/settings/" component={OptionsContainer} /> + <Route path="/import/" component={ImportContainer} /> <Redirect to="/secrets/" /> </Switch> </div> diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index 2ae5c52..5105865 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -7,6 +7,22 @@ import secretin from 'utils/secretin'; import AppUIStore from 'stores/AppUIStore'; import Icon from 'components/utilities/Icon'; +function download(filename, text) { + var element = document.createElement('a'); + element.setAttribute( + 'href', + `data:application/json;charset=utf-8,${encodeURIComponent(text)}` + ); + element.setAttribute('download', filename); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); +} + function SidebarMenuLink({ children, ...props }) { return ( <li className="sidebar-menu-item"> @@ -28,6 +44,15 @@ SidebarMenuLink.propTypes = { ]), }; +function exportDb() { + secretin.getDb().then(db => { + download( + `Secret-in_${secretin.currentUser.username}_${Date.now()}.json`, + db + ); + }); +} + function Sidebar() { const currentUser = AppUIStore.getCurrentUser(); return ( @@ -61,6 +86,17 @@ function Sidebar() { <Icon id="gear" size="base" /> Settings </SidebarMenuLink> + <div className="sidebar-separator" /> + <li className="sidebar-menu-item"> + <a className="sidebar-menu-link" onClick={exportDb}> + <Icon id="export" size="base" /> + Export secrets + </a> + </li> + <SidebarMenuLink to="/import/"> + <Icon id="import" size="base" /> + Import secrets + </SidebarMenuLink> </ul> </div> </div> diff --git a/src/components/options/ImportKeepassShow/index.js b/src/components/options/ImportKeepassShow/index.js deleted file mode 100644 index caf878f..0000000 --- a/src/components/options/ImportKeepassShow/index.js +++ /dev/null @@ -1,118 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import Immutable from 'immutable'; -import connectToStores from 'alt-utils/lib/connectToStores'; - -import Modal from 'components/utilities/Modal'; -import Button from 'components/utilities/Button'; -import Icon from 'components/utilities/Icon'; -import Spinner from 'components/utilities/Spinner'; - -import OptionsStore from 'stores/OptionsStore'; -import OptionsActions from 'actions/OptionsActions'; -import MetadataActions from 'actions/MetadataActions'; - -import FileChooser from './FileChooser'; - -class ImportKeepassShow extends Component { - static propTypes = { - shown: PropTypes.bool, - importing: PropTypes.bool, - importStatus: PropTypes.number, - importTotal: PropTypes.number, - success: PropTypes.bool, - }; - - static defaultProps = { - shown: false, - importing: false, - success: false, - errors: new Immutable.Map(), - }; - - static getStores() { - return [OptionsStore]; - } - - static getPropsFromStores() { - const state = OptionsStore.getState().getIn(['import', 'keepass']); - - return { - errors: state.get('errors'), - shown: state.get('shown'), - importing: state.get('importing'), - importStatus: state.get('importStatus'), - importTotal: state.get('importTotal'), - success: state.get('success'), - }; - } - - constructor(props) { - super(props); - - this.handleFileChoosen = this.handleFileChoosen.bind(this); - } - - componentWillUnmount() { - OptionsActions.hideImportKeepass(); - } - - handleFileChoosen(file) { - OptionsActions.importKeepass({ file }); - } - - render() { - return ( - <Modal - show={this.props.shown} - onClose={ - !this.props.importing ? OptionsActions.hideImportKeepass : undefined - } - > - <Modal.Header> - <span className="text">Import from Keepass</span> - </Modal.Header> - - <Modal.Body> - {((this.props.success || this.props.importing) && - ((this.props.success && - <div className="import-progress"> - <Icon id="done" size={120} /> - <div className="import-progress-title">Done!</div> - </div>) || - <div className="import-progress"> - <Spinner /> - {this.props.importTotal !== 0 && - <div className="import-progress-title"> - Importing... {this.props.importStatus} /{' '} - {this.props.importTotal} - </div>} - </div>)) || - <FileChooser onFileChoosen={this.handleFileChoosen} />} - </Modal.Body> - - <Modal.Footer> - {this.props.success - ? <Button - buttonStyle="primary" - className="button button--style-default button--size-base" - to="/" - onClick={MetadataActions.loadMetadata} - > - Return to Home - </Button> - : <Button - type="reset" - buttonStyle="default" - onClick={OptionsActions.hideImportKeepass} - disabled={this.props.importing} - > - Close - </Button>} - </Modal.Footer> - </Modal> - ); - } -} - -export default connectToStores(ImportKeepassShow); diff --git a/src/components/options/OptionsContainer.js b/src/components/options/OptionsContainer.js index eddc698..3c7c5fe 100644 --- a/src/components/options/OptionsContainer.js +++ b/src/components/options/OptionsContainer.js @@ -7,7 +7,6 @@ import AppUIStore from 'stores/AppUIStore'; import ShortLoginShow from 'components/options/ShortLoginShow'; import QRCodeShow from 'components/options/QRCodeShow'; import RescueCodesShow from 'components/options/RescueCodesShow'; -import ImportKeepassShow from 'components/options/ImportKeepassShow'; import Title from 'components/utilities/Title'; import Checkbox from 'components/utilities/Checkbox'; import Input from 'components/utilities/Input'; @@ -118,18 +117,8 @@ class OptionsContainer extends Component { </div> </div> <div className="options-section"> - <h3 className="options-section-title">Imports</h3> - <div className="options-section-item"> - <ImportKeepassShow /> - <Button - type="button" - buttonStyle="primary" - onClick={OptionsActions.showImportKeepass} - disabled={!AppUIStore.isOnline()} - > - Import from Keepass - </Button> - </div> + <h3 className="options-section-title">Password generation</h3> + <div className="options-section-item">SOON BY @dqms</div> </div> </div> </div> diff --git a/src/icons.svg b/src/icons.svg index dc63199..9470670 100644 --- a/src/icons.svg +++ b/src/icons.svg @@ -134,6 +134,16 @@ <path d="M0 0h24v24H0z" fill="none"/> </symbol> + <symbol id="export" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM17 13l-5 5-5-5h3V9h4v4h3z"/> + </symbol> + + <symbol id="import" viewBox="0 0 24 24"> + <path d="M0 0h24v24H0z" fill="none"/> + <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/> + </symbol> + <symbol id="done" viewBox="0 0 24 24"> <path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/> <path d="M0 0h24v24H0z" fill="none"/> diff --git a/src/stores/ImportStore.js b/src/stores/ImportStore.js new file mode 100644 index 0000000..db97f98 --- /dev/null +++ b/src/stores/ImportStore.js @@ -0,0 +1,100 @@ +import Immutable, { Record } from 'immutable'; +import alt from 'utils/alt'; +import makeImmutable from 'utils/makeImmutable'; + +import ImportActions from 'actions/ImportActions'; + +const ImportState = new Record({ + importType: '', + importing: false, + importStatus: 0, + importTotal: 0, + success: false, + file: '', + error: '', + special: new Immutable.Map(), +}); + +class ImportStore { + constructor() { + this.bindActions(ImportActions); + this.state = new ImportState(); + } + + onImportSecrets() { + this.setState( + this.state.merge({ + importing: true, + success: false, + error: '', + }) + ); + } + + onDetectTypeFailure({ error }) { + this.setState( + this.state.merge({ + error, + importType: '', + special: new Immutable.Map(), + }) + ); + } + + onDetectTypeSuccess({ file, importType, special }) { + this.setState( + this.state.merge({ + importType, + error: '', + file, + special: new Immutable.Map(special), + }) + ); + } + + onImportProgress({ importStatus, importTotal }) { + this.setState( + this.state.merge({ + importStatus, + importTotal, + error: '', + }) + ); + } + + onImportSuccess() { + this.setState( + this.state.merge({ + importing: false, + success: true, + importType: '', + error: '', + importStatus: 0, + importTotal: 0, + special: new Immutable.Map(), + }) + ); + } + + onImportFailure({ error }) { + this.setState( + this.state.merge({ + importing: false, + success: false, + importType: '', + error: error, + special: new Immutable.Map(), + }) + ); + } + + onDefaultStore() { + this.setState(new ImportState()); + } + + onChangeSpecial({ name, value }) { + this.setState(this.state.setIn(['special', name], value)); + } +} + +export default alt.createStore(makeImmutable(ImportStore), 'ImportStore'); diff --git a/src/stores/OptionsStore.js b/src/stores/OptionsStore.js index cbc28c5..f17a921 100644 --- a/src/stores/OptionsStore.js +++ b/src/stores/OptionsStore.js @@ -13,16 +13,6 @@ const OptionsState = new Record({ showRescueCodes: false, rescueCodes: new Immutable.List(), loading: false, - import: Immutable.fromJS({ - keepass: { - shown: false, - importing: false, - importStatus: 0, - importTotal: 0, - success: false, - errors: new Immutable.Map(), - }, - }), }); class OptionsStore { @@ -53,63 +43,6 @@ class OptionsStore { this.setState(this.state.set('showShortLogin', showShortLogin)); } - onShowImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'shown'], true) - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onHideImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'shown'], false) - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onImportKeepass() { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], true) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - ); - } - - onImportKeepassProgress({ importStatus, importTotal }) { - this.setState( - this.state - .setIn(['import', 'keepass', 'importStatus'], importStatus) - .setIn(['import', 'keepass', 'importTotal'], importTotal) - ); - } - - onImportKeepassSuccess() { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], true) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map()) - .setIn(['import', 'keepass', 'importStatus'], 0) - .setIn(['import', 'keepass', 'importTotal'], 0) - ); - } - - onImportKeepassFailure({ error }) { - this.setState( - this.state - .setIn(['import', 'keepass', 'importing'], false) - .setIn(['import', 'keepass', 'success'], false) - .setIn(['import', 'keepass', 'errors'], new Immutable.Map(error)) - ); - } - onActivateTotpFailure({ error }) { this.setState( this.state.merge({ diff --git a/src/utils/import/index.js b/src/utils/import/index.js new file mode 100644 index 0000000..3628063 --- /dev/null +++ b/src/utils/import/index.js @@ -0,0 +1,9 @@ +import keepass from 'utils/import/keepass'; +import secretinDB from 'utils/import/secretin'; + +const Import = { + keepass, + secretin: secretinDB, +}; + +export default Import; diff --git a/src/utils/import.js b/src/utils/import/keepass.js similarity index 89% rename from src/utils/import.js rename to src/utils/import/keepass.js index f2486fb..ae53ee9 100644 --- a/src/utils/import.js +++ b/src/utils/import/keepass.js @@ -147,7 +147,19 @@ function count(group) { return nb; } -export function parseKeepass(xml, progress = defaultProgress) { +export function detect(file) { + const parser = new DOMParser(); + let isKeepass = false; + try { + const xmlDoc = parser.parseFromString(file, 'application/xml'); + isKeepass = xmlDoc.getElementsByTagName('KeePassFile').length === 1; + } catch (e) { + isKeepass = false; + } + return isKeepass; +} + +export function parse(xml, special, progress = defaultProgress) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xml, 'application/xml'); const root = xmlDoc.getElementsByTagName('Root')[0].children[0]; @@ -155,8 +167,14 @@ export function parseKeepass(xml, progress = defaultProgress) { return parseGroup(root, currentProgress); } -const Import = { - parseKeepass, +export function needSpecial() { + return {}; +} + +const keepass = { + parse, + detect, + needSpecial, }; -export default Import; +export default keepass; diff --git a/src/utils/import/secretin.js b/src/utils/import/secretin.js new file mode 100644 index 0000000..1ad933c --- /dev/null +++ b/src/utils/import/secretin.js @@ -0,0 +1,38 @@ +import secretin from 'utils/secretin'; + +function defaultProgress(status, total) { + // eslint-disable-next-line no-console + console.log(`${status}/${total}`); +} + +export function detect(file) { + let isSecretin = false; + try { + const db = JSON.parse(file); + isSecretin = 'users' in db && 'secrets' in db; + } catch (e) { + isSecretin = false; + } + return isSecretin; +} + +export function parse(db, { username, password }, progress = defaultProgress) { + return secretin.importDb(username, password, db, function(status) { + progress(status.state, status.total); + }); +} + +export function needSpecial() { + return { + username: '', + password: '', + }; +} + +const secretinDB = { + parse, + detect, + needSpecial, +}; + +export default secretinDB; From cccdc8b49c7df89cc20bbe089986e35ded249c12 Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Wed, 12 Jul 2017 20:24:37 +0200 Subject: [PATCH 6/9] Prevent anonym function by returning status object to progress --- src/actions/ImportActions.js | 7 +++++-- src/utils/import/keepass.js | 2 +- src/utils/import/secretin.js | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js index 064148a..cce6249 100644 --- a/src/actions/ImportActions.js +++ b/src/actions/ImportActions.js @@ -41,8 +41,11 @@ class ImportActions { return dispatch => { dispatch(); Import[type] - .parse(file, special.toJS(), (importStatus, importTotal) => - this.importProgress({ importStatus, importTotal }) + .parse(file, special.toJS(), status => + this.importProgress({ + importStatus: status.state, + importTotal: status.total, + }) ) .then(() => { this.importSuccess(); diff --git a/src/utils/import/keepass.js b/src/utils/import/keepass.js index ae53ee9..5fea1e4 100644 --- a/src/utils/import/keepass.js +++ b/src/utils/import/keepass.js @@ -11,7 +11,7 @@ class InternalProgressBar { update() { this.status += 1; - this.func(this.status, this.total); + this.func({ state: this.status, total: this.total }); } } diff --git a/src/utils/import/secretin.js b/src/utils/import/secretin.js index 1ad933c..ce71749 100644 --- a/src/utils/import/secretin.js +++ b/src/utils/import/secretin.js @@ -17,9 +17,7 @@ export function detect(file) { } export function parse(db, { username, password }, progress = defaultProgress) { - return secretin.importDb(username, password, db, function(status) { - progress(status.state, status.total); - }); + return secretin.importDb(username, password, db, progress); } export function needSpecial() { From 5af735247233825512a7efdc0612f1cd42b1e54c Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Wed, 19 Jul 2017 00:47:26 +0200 Subject: [PATCH 7/9] Fix @calyhre suggestions --- src/actions/ImportActions.js | 53 +++++++++---------- src/components/Layout.js | 2 +- src/components/Sidebar.js | 3 +- .../index.js => import/ImportContainer.js} | 27 +++++----- .../ImportFileChooser.js} | 8 +-- .../ImportMandatoryField.js} | 18 +++---- .../ImportMandatoryFields.js} | 19 ++++--- src/stores/ImportStore.js | 24 +++++---- src/utils/import/index.js | 9 ---- src/utils/importers/index.js | 9 ++++ src/utils/{import => importers}/keepass.js | 8 ++- src/utils/{import => importers}/secretin.js | 14 +++-- 12 files changed, 94 insertions(+), 100 deletions(-) rename src/components/{Import/index.js => import/ImportContainer.js} (83%) rename src/components/{Import/FileChooser.js => import/ImportFileChooser.js} (88%) rename src/components/{Import/SpecialField.js => import/ImportMandatoryField.js} (59%) rename src/components/{Import/Special.js => import/ImportMandatoryFields.js} (57%) delete mode 100644 src/utils/import/index.js create mode 100644 src/utils/importers/index.js rename src/utils/{import => importers}/keepass.js (97%) rename src/utils/{import => importers}/secretin.js (68%) diff --git a/src/actions/ImportActions.js b/src/actions/ImportActions.js index cce6249..8e636be 100644 --- a/src/actions/ImportActions.js +++ b/src/actions/ImportActions.js @@ -1,35 +1,31 @@ +import { findKey } from 'lodash'; + import alt from 'utils/alt'; -import Import from 'utils/import'; +import importers from 'utils/importers'; class ImportActions { constructor() { this.generateActions( - 'importProgress', - 'importSuccess', - 'importFailure', + 'importSecretsProgress', + 'importSecretsSuccess', + 'importSecretsFailure', 'detectTypeSuccess', 'detectTypeFailure', 'defaultStore', - 'changeSpecial' + 'changeMandatoryField' ); } detectType({ file }) { const reader = new FileReader(); reader.readAsText(file); - reader.onload = readedFile => { - const file = readedFile.target.result; - let importType = ''; - const importTypes = Object.keys(Import); - for (let i = 0; i < importTypes.length; i++) { - if (Import[importTypes[i]].detect(file)) { - importType = importTypes[i]; - break; - } - } - if (importType !== '') { - const special = Import[importType].needSpecial(); - this.detectTypeSuccess({ file, importType, special }); + reader.onload = ({ target }) => { + const file = target.result; + const importType = findKey(importers, importer => importer.detect(file)); + + if (typeof importType !== 'undefined') { + const mandatoryFields = importers[importType].mandatoryFields; + this.detectTypeSuccess({ file, importType, mandatoryFields }); } else { this.detectTypeFailure({ error: 'Invalid type' }); } @@ -37,21 +33,24 @@ class ImportActions { return reader; } - importSecrets({ file, type, special }) { + importSecrets({ file, type, mandatoryFields }) { return dispatch => { dispatch(); - Import[type] - .parse(file, special.toJS(), status => - this.importProgress({ - importStatus: status.state, - importTotal: status.total, - }) + importers[type] + .parse( + file, + mandatoryFields.toJS(), + ({ state: importStatus, total: importTotal }) => + this.importSecretsProgress({ + importStatus, + importTotal, + }) ) .then(() => { - this.importSuccess(); + this.importSecretsSuccess(); }) .catch(error => { - this.importFailure({ error }); + this.importSecretsFailure({ error }); }); }; } diff --git a/src/components/Layout.js b/src/components/Layout.js index e366731..5af44ab 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -7,7 +7,7 @@ import Sidebar from 'components/Sidebar'; import SecretShow from 'components/secrets/SecretShow'; import SecretListContainer from 'components/secrets/SecretListContainer'; import OptionsContainer from 'components/options/OptionsContainer'; -import ImportContainer from 'components/Import'; +import ImportContainer from 'components/import/ImportContainer'; function Layout() { return ( diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index 5105865..ee08f98 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Link from 'react-router-dom/Link'; import NavLink from 'react-router-dom/NavLink'; +import moment from 'moment'; import secretin from 'utils/secretin'; import AppUIStore from 'stores/AppUIStore'; @@ -47,7 +48,7 @@ SidebarMenuLink.propTypes = { function exportDb() { secretin.getDb().then(db => { download( - `Secret-in_${secretin.currentUser.username}_${Date.now()}.json`, + `Secret-in_${secretin.currentUser.username}_${moment.format()}.json`, db ); }); diff --git a/src/components/Import/index.js b/src/components/import/ImportContainer.js similarity index 83% rename from src/components/Import/index.js rename to src/components/import/ImportContainer.js index 67142ab..d5e4aff 100644 --- a/src/components/Import/index.js +++ b/src/components/import/ImportContainer.js @@ -12,8 +12,8 @@ import ImportStore from 'stores/ImportStore'; import ImportActions from 'actions/ImportActions'; import MetadataActions from 'actions/MetadataActions'; -import FileChooser from './FileChooser'; -import ImportSpecial from './Special'; +import ImportFileChooser from './ImportFileChooser'; +import ImportMandatoryFields from './ImportMandatoryFields'; class ImportContainer extends Component { static propTypes = { @@ -24,7 +24,7 @@ class ImportContainer extends Component { success: PropTypes.bool, file: PropTypes.string, error: PropTypes.string, - special: PropTypes.instanceOf(Immutable.Map), + mandatoryFields: PropTypes.instanceOf(Immutable.Map), }; static getStores() { @@ -42,7 +42,7 @@ class ImportContainer extends Component { importTotal: state.get('importTotal'), success: state.get('success'), file: state.get('file'), - special: state.get('special'), + mandatoryFields: state.get('mandatoryFields'), }; } @@ -60,7 +60,7 @@ class ImportContainer extends Component { handleStartParsing() { ImportActions.importSecrets({ file: this.props.file, - special: this.props.special, + mandatoryFields: this.props.mandatoryFields, type: this.props.importType, }); } @@ -94,19 +94,16 @@ class ImportContainer extends Component { <Spinner /> {this.props.importTotal !== 0 && <div className="import-progress-title"> - Importing... - {' '} - {this.props.importStatus} - {' '} - / - {' '} - {this.props.importTotal} + {`Importing... ${this.props.importStatus} / ${this.props + .importTotal}`} </div>} </div>)) || - <FileChooser onFileChoosen={this.handleFileChoosen} />} + <ImportFileChooser onFileChoosen={this.handleFileChoosen} />} {this.props.error !== '' && <span>{this.props.error}</span>} - {this.props.special.size > 0 && - <ImportSpecial special={this.props.special} />} + {this.props.mandatoryFields.size > 0 && + <ImportMandatoryFields + mandatoryFields={this.props.mandatoryFields} + />} {this.props.importType !== '' && <Button buttonStyle="primary" diff --git a/src/components/Import/FileChooser.js b/src/components/import/ImportFileChooser.js similarity index 88% rename from src/components/Import/FileChooser.js rename to src/components/import/ImportFileChooser.js index 36c7438..8e8db95 100644 --- a/src/components/Import/FileChooser.js +++ b/src/components/import/ImportFileChooser.js @@ -5,7 +5,7 @@ import HTML5Backend, { NativeTypes } from 'react-dnd-html5-backend'; import Icon from 'components/utilities/Icon'; -class FileChooser extends Component { +class ImportFileChooser extends Component { static propTypes = { onFileChoosen: PropTypes.func, connectDropTarget: PropTypes.func, @@ -52,10 +52,10 @@ function itemTargetCollect(connect, monitor) { }; } -const FileChooserTarget = new DropTarget( +const ImportFileChooserTarget = new DropTarget( NativeTypes.FILE, itemTarget, itemTargetCollect -)(FileChooser); +)(ImportFileChooser); -export default new DragDropContext(HTML5Backend)(FileChooserTarget); +export default new DragDropContext(HTML5Backend)(ImportFileChooserTarget); diff --git a/src/components/Import/SpecialField.js b/src/components/import/ImportMandatoryField.js similarity index 59% rename from src/components/Import/SpecialField.js rename to src/components/import/ImportMandatoryField.js index 624fa8e..112c7a2 100644 --- a/src/components/Import/SpecialField.js +++ b/src/components/import/ImportMandatoryField.js @@ -1,11 +1,11 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; +import Immutable from 'immutable'; import Input from 'components/utilities/Input'; -class SpecialField extends Component { +class ImportMandatoryField extends Component { static propTypes = { - name: PropTypes.string, - value: PropTypes.string, + field: PropTypes.instanceOf(Immutable.Map), onChange: PropTypes.func, }; @@ -16,7 +16,7 @@ class SpecialField extends Component { handleChange({ value }) { const params = { - name: this.props.name, + field: this.props.field, value, }; @@ -29,14 +29,14 @@ class SpecialField extends Component { ref={ref => { this.input = ref; }} - label={this.props.name} - name={this.props.name} - value={this.props.value} + label={this.props.field.get('name')} + name={this.props.field.get('name')} + value={this.props.field.get('value')} onChange={this.handleChange} - type="password" + type={this.props.field.get('type')} /> ); } } -export default SpecialField; +export default ImportMandatoryField; diff --git a/src/components/Import/Special.js b/src/components/import/ImportMandatoryFields.js similarity index 57% rename from src/components/Import/Special.js rename to src/components/import/ImportMandatoryFields.js index 31bf5c1..45103f0 100644 --- a/src/components/Import/Special.js +++ b/src/components/import/ImportMandatoryFields.js @@ -3,13 +3,13 @@ import PropTypes from 'prop-types'; import Immutable from 'immutable'; import connectToStores from 'alt-utils/lib/connectToStores'; import ImportStore from 'stores/ImportStore'; -import SpecialField from './SpecialField'; +import ImportMandatoryField from './ImportMandatoryField'; import ImportActions from 'actions/ImportActions'; -class ImportSpecial extends Component { +class ImportersMandatoryFields extends Component { static propTypes = { - special: PropTypes.instanceOf(Immutable.Map), + mandatoryFields: PropTypes.instanceOf(Immutable.Map), }; static getStores() { @@ -20,20 +20,19 @@ class ImportSpecial extends Component { const state = ImportStore.getState(); return { - special: state.get('special'), + mandatoryFields: state.get('mandatoryFields'), }; } render() { - const keys = Object.keys(this.props.special.toJS()); + const keys = Object.keys(this.props.mandatoryFields.toJS()); const inputs = []; keys.forEach(key => { inputs.push( - <SpecialField + <ImportMandatoryField key={key} - name={key} - value={this.props.special.get(key)} - onChange={ImportActions.changeSpecial} + field={this.props.mandatoryFields.get(key)} + onChange={ImportActions.changeMandatoryField} /> ); }); @@ -45,4 +44,4 @@ class ImportSpecial extends Component { } } -export default connectToStores(ImportSpecial); +export default connectToStores(ImportersMandatoryFields); diff --git a/src/stores/ImportStore.js b/src/stores/ImportStore.js index db97f98..ef77968 100644 --- a/src/stores/ImportStore.js +++ b/src/stores/ImportStore.js @@ -12,7 +12,7 @@ const ImportState = new Record({ success: false, file: '', error: '', - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }); class ImportStore { @@ -36,23 +36,23 @@ class ImportStore { this.state.merge({ error, importType: '', - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } - onDetectTypeSuccess({ file, importType, special }) { + onDetectTypeSuccess({ file, importType, mandatoryFields }) { this.setState( this.state.merge({ importType, error: '', file, - special: new Immutable.Map(special), + mandatoryFields: Immutable.fromJS(mandatoryFields), }) ); } - onImportProgress({ importStatus, importTotal }) { + onImportSecretsProgress({ importStatus, importTotal }) { this.setState( this.state.merge({ importStatus, @@ -62,7 +62,7 @@ class ImportStore { ); } - onImportSuccess() { + onImportSecretsSuccess() { this.setState( this.state.merge({ importing: false, @@ -71,19 +71,19 @@ class ImportStore { error: '', importStatus: 0, importTotal: 0, - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } - onImportFailure({ error }) { + onImportSecretsFailure({ error }) { this.setState( this.state.merge({ importing: false, success: false, importType: '', error: error, - special: new Immutable.Map(), + mandatoryFields: new Immutable.Map(), }) ); } @@ -92,8 +92,10 @@ class ImportStore { this.setState(new ImportState()); } - onChangeSpecial({ name, value }) { - this.setState(this.state.setIn(['special', name], value)); + onChangeMandatoryField({ field, value }) { + this.setState( + this.state.setIn(['mandatoryFields', field.get('name'), 'value'], value) + ); } } diff --git a/src/utils/import/index.js b/src/utils/import/index.js deleted file mode 100644 index 3628063..0000000 --- a/src/utils/import/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import keepass from 'utils/import/keepass'; -import secretinDB from 'utils/import/secretin'; - -const Import = { - keepass, - secretin: secretinDB, -}; - -export default Import; diff --git a/src/utils/importers/index.js b/src/utils/importers/index.js new file mode 100644 index 0000000..a90088a --- /dev/null +++ b/src/utils/importers/index.js @@ -0,0 +1,9 @@ +import keepass from 'utils/importers/keepass'; +import secretinDB from 'utils/importers/secretin'; + +const importers = { + keepass, + secretin: secretinDB, +}; + +export default importers; diff --git a/src/utils/import/keepass.js b/src/utils/importers/keepass.js similarity index 97% rename from src/utils/import/keepass.js rename to src/utils/importers/keepass.js index 5fea1e4..6088a72 100644 --- a/src/utils/import/keepass.js +++ b/src/utils/importers/keepass.js @@ -159,7 +159,7 @@ export function detect(file) { return isKeepass; } -export function parse(xml, special, progress = defaultProgress) { +export function parse(xml, mandatoryField, progress = defaultProgress) { const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xml, 'application/xml'); const root = xmlDoc.getElementsByTagName('Root')[0].children[0]; @@ -167,14 +167,12 @@ export function parse(xml, special, progress = defaultProgress) { return parseGroup(root, currentProgress); } -export function needSpecial() { - return {}; -} +export const mandatoryFields = {}; const keepass = { parse, detect, - needSpecial, + mandatoryFields, }; export default keepass; diff --git a/src/utils/import/secretin.js b/src/utils/importers/secretin.js similarity index 68% rename from src/utils/import/secretin.js rename to src/utils/importers/secretin.js index ce71749..4906d77 100644 --- a/src/utils/import/secretin.js +++ b/src/utils/importers/secretin.js @@ -17,20 +17,18 @@ export function detect(file) { } export function parse(db, { username, password }, progress = defaultProgress) { - return secretin.importDb(username, password, db, progress); + return secretin.importDb(username.value, password.value, db, progress); } -export function needSpecial() { - return { - username: '', - password: '', - }; -} +export const mandatoryFields = { + username: { type: 'text', name: 'username', value: '' }, + password: { type: 'password', name: 'password', value: '' }, +}; const secretinDB = { parse, detect, - needSpecial, + mandatoryFields, }; export default secretinDB; From 2706c5647c82b04f2adf1cb9e12bcf5ef8799216 Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Tue, 25 Jul 2017 18:11:59 +0200 Subject: [PATCH 8/9] Upgrade secretin-lib and fix moment typo --- src/components/Sidebar.js | 2 +- src/components/import/ImportContainer.js | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index ee08f98..714a16d 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -48,7 +48,7 @@ SidebarMenuLink.propTypes = { function exportDb() { secretin.getDb().then(db => { download( - `Secret-in_${secretin.currentUser.username}_${moment.format()}.json`, + `Secret-in_${secretin.currentUser.username}_${moment().format()}.json`, db ); }); diff --git a/src/components/import/ImportContainer.js b/src/components/import/ImportContainer.js index d5e4aff..1600cc5 100644 --- a/src/components/import/ImportContainer.js +++ b/src/components/import/ImportContainer.js @@ -65,13 +65,22 @@ class ImportContainer extends Component { }); } - render() { + shouldComponentUpdate(nextProps, nextState) { + return ( + this.props.success !== true || nextProps.success !== this.props.success + ); + } + + componentDidUpdate() { if (this.props.success) { MetadataActions.loadMetadata(); setTimeout(function() { ImportActions.defaultStore(); }, 1500); } + } + + render() { return ( <div className="page"> <div className="page-header"> @@ -81,14 +90,14 @@ class ImportContainer extends Component { </div> <div className="page-content options"> - <span>Supported type are <i>secret-in</i>, <i>keepass</i></span> + <span> + Supported type are <i>secret-in</i>, <i>keepass</i> + </span> {((this.props.success || this.props.importing) && ((this.props.success && <div className="import-progress"> <Icon id="done" size={120} /> - <div className="import-progress-title"> - Done! - </div> + <div className="import-progress-title">Done!</div> </div>) || <div className="import-progress"> <Spinner /> @@ -99,7 +108,10 @@ class ImportContainer extends Component { </div>} </div>)) || <ImportFileChooser onFileChoosen={this.handleFileChoosen} />} - {this.props.error !== '' && <span>{this.props.error}</span>} + {this.props.error !== '' && + <span> + {this.props.error} + </span>} {this.props.mandatoryFields.size > 0 && <ImportMandatoryFields mandatoryFields={this.props.mandatoryFields} From 0e9f84f16836ab8a86c25c49c445eefd1ab8b792 Mon Sep 17 00:00:00 2001 From: agix <florian.gaultier@gmail.com> Date: Thu, 27 Jul 2017 17:06:31 +0200 Subject: [PATCH 9/9] Bump to 1.8.2 to import secretin json just with password --- package.json | 2 +- src/components/Sidebar.js | 2 +- src/utils/importers/secretin.js | 3 +- yarn.lock | 351 ++++++-------------------------- 4 files changed, 68 insertions(+), 290 deletions(-) diff --git a/package.json b/package.json index 9fde248..eee0f75 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "react-dom": "^15.6.1", "react-overlays": "^0.8.0", "react-router-dom": "^4.1.2", - "secretin": "^1.8.1", + "secretin": "^1.8.3", "url-join": "^2.0.2", "uuid": "^3.1.0" }, diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index 714a16d..f4d5d5f 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -46,7 +46,7 @@ SidebarMenuLink.propTypes = { }; function exportDb() { - secretin.getDb().then(db => { + secretin.exportDb().then(db => { download( `Secret-in_${secretin.currentUser.username}_${moment().format()}.json`, db diff --git a/src/utils/importers/secretin.js b/src/utils/importers/secretin.js index 4906d77..cce88b0 100644 --- a/src/utils/importers/secretin.js +++ b/src/utils/importers/secretin.js @@ -17,11 +17,10 @@ export function detect(file) { } export function parse(db, { username, password }, progress = defaultProgress) { - return secretin.importDb(username.value, password.value, db, progress); + return secretin.importDb(password.value, db, progress); } export const mandatoryFields = { - username: { type: 'text', name: 'username', value: '' }, password: { type: 'password', name: 'password', value: '' }, }; diff --git a/yarn.lock b/yarn.lock index 282fd54..356f860 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,14 +47,10 @@ acorn@^5.0.0, acorn@^5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" -address@1.0.2: +address@1.0.2, address@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/address/-/address-1.0.2.tgz#480081e82b587ba319459fef512f516fe03d58af" -address@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.1.tgz#363f5d3f2be26d0655d8afd5a9562e4fc2194537" - ajv-keywords@^1.0.0, ajv-keywords@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.2.0.tgz#676c4f087bfe1e8b12dca6fda2f3c74f417b099c" @@ -139,13 +135,7 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.0.0.tgz#5404e93a544c4fec7f048262977bebfe3155e0c1" - dependencies: - color-convert "^1.0.0" - -ansi-styles@^3.1.0: +ansi-styles@^3.0.0, ansi-styles@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" dependencies: @@ -324,7 +314,7 @@ async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" -async@2.1.4: +async@2.1.4, async@^2.1.2, async@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" dependencies: @@ -334,18 +324,6 @@ async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/async/-/async-2.1.2.tgz#612a4ab45ef42a70cde806bad86ee6db047e8385" - dependencies: - lodash "^4.14.0" - -async@^2.1.4: - version "2.3.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.3.0.tgz#1013d1051047dd320fe24e494d5c66ecaf6147d9" - dependencies: - lodash "^4.14.0" - async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -402,7 +380,7 @@ babel-code-frame@6.22.0, babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, bab esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@6.25.0: +babel-core@6.25.0, babel-core@^6.0.0, babel-core@^6.24.1: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.25.0.tgz#7dd42b0463c742e9d5296deb3ec67a9322dad729" dependencies: @@ -426,30 +404,6 @@ babel-core@6.25.0: slash "^1.0.0" source-map "^0.5.0" -babel-core@^6.0.0, babel-core@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" - dependencies: - babel-code-frame "^6.22.0" - babel-generator "^6.24.1" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" - convert-source-map "^1.1.0" - debug "^2.1.1" - json5 "^0.5.0" - lodash "^4.2.0" - minimatch "^3.0.2" - path-is-absolute "^1.0.0" - private "^0.1.6" - slash "^1.0.0" - source-map "^0.5.0" - babel-eslint@7.2.3, babel-eslint@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" @@ -459,20 +413,7 @@ babel-eslint@7.2.3, babel-eslint@^7.2.3: babel-types "^6.23.0" babylon "^6.17.0" -babel-generator@^6.18.0, babel-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.2.0" - source-map "^0.5.0" - trim-right "^1.0.1" - -babel-generator@^6.25.0: +babel-generator@^6.18.0, babel-generator@^6.25.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc" dependencies: @@ -1028,17 +969,7 @@ babel-runtime@6.23.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtim core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.16.0, babel-template@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - babylon "^6.11.0" - lodash "^4.2.0" - -babel-template@^6.25.0: +babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.25.0: version "6.25.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" dependencies: @@ -1048,64 +979,56 @@ babel-template@^6.25.0: babylon "^6.17.2" lodash "^4.2.0" -babel-traverse@^6.18.0, babel-traverse@^6.23.1, babel-traverse@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" +babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.24.1" - babylon "^6.15.0" + babel-types "^6.25.0" + babylon "^6.17.2" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-traverse@^6.25.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" +babel-traverse@^6.23.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.25.0" - babylon "^6.17.2" + babel-types "^6.24.1" + babylon "^6.15.0" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" +babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.25.0: + version "6.25.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babel-types@^6.25.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" +babel-types@^6.23.0, babel-types@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.11.0, babylon@^6.15.0: - version "6.16.1" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3" - -babylon@^6.13.0, babylon@^6.17.0: +babylon@^6.13.0, babylon@^6.15.0, babylon@^6.17.0, babylon@^6.17.2: version "6.17.3" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" -babylon@^6.17.2: - version "6.17.4" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" - balanced-match@^0.4.1, balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" @@ -1289,14 +1212,7 @@ browserslist@^1.7.1: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" -browserslist@^2.1.2: - version "2.1.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" - dependencies: - caniuse-lite "^1.0.30000670" - electron-to-chromium "^1.3.11" - -browserslist@^2.1.3: +browserslist@^2.1.2, browserslist@^2.1.3: version "2.2.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.2.2.tgz#e9b4618b8a01c193f9786beea09f6fd10dbe31c3" dependencies: @@ -1561,13 +1477,7 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -color-convert@^1.0.0, color-convert@^1.3.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.8.2.tgz#be868184d7c8631766d54e7078e2672d7c7e3339" - dependencies: - color-name "^1.1.1" - -color-convert@^1.9.0: +color-convert@^1.3.0, color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: @@ -1615,13 +1525,13 @@ combined-stream@~0.0.4, combined-stream@~0.0.5: dependencies: delayed-stream "0.0.5" -commander@2.9.0, commander@2.9.x, commander@^2.8.1, commander@^2.9.0, commander@~2.9.0: +commander@2.9.0, commander@2.9.x, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: graceful-readlink ">= 1.0.0" -commander@~2.11.0: +commander@^2.8.1, commander@~2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" @@ -1983,24 +1893,24 @@ debug@0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" -debug@2.2.0, debug@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.2: +debug@^2.1.1, debug@^2.2.0: version "2.4.5" resolved "https://registry.yarnpkg.com/debug/-/debug-2.4.5.tgz#34c7b12a1ca96674428f41fe92c49b4ce7cd0607" dependencies: ms "0.7.2" -debug@^2.6.0, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: +debug@^2.1.3, debug@^2.3.2, debug@^2.6.0, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2312,11 +2222,7 @@ electron-packager@^8.7.2: sanitize-filename "^1.6.0" semver "^5.3.0" -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.11: - version "1.3.14" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz#64af0f9efd3c3c6acd57d71f83b49ca7ee9c4b43" - -electron-to-chromium@^1.3.16: +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.16: version "1.3.16" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.16.tgz#d0e026735754770901ae301a21664cba45d92f7d" @@ -2544,14 +2450,7 @@ eslint-loader@1.7.1: object-hash "^1.1.4" rimraf "^2.6.1" -eslint-module-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" - dependencies: - debug "2.2.0" - pkg-dir "^1.0.0" - -eslint-module-utils@^2.1.1: +eslint-module-utils@^2.0.0, eslint-module-utils@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" dependencies: @@ -3085,21 +2984,14 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@1.1.2: +fsevents@1.1.2, fsevents@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: nan "^2.3.0" node-pre-gyp "^0.6.36" -fsevents@^1.0.0: - version "1.0.17" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.17.tgz#8537f3f12272678765b4fd6528c0f1f66f8f4558" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.29" - -fstream-ignore@^1.0.5, fstream-ignore@~1.0.5: +fstream-ignore@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" dependencies: @@ -3107,7 +2999,7 @@ fstream-ignore@^1.0.5, fstream-ignore@~1.0.5: inherits "2" minimatch "^3.0.0" -fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: version "1.0.10" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" dependencies: @@ -3116,15 +3008,6 @@ fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: mkdirp ">=0.5 0" rimraf "2" -fstream@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - function-bind@^1.0.2, function-bind@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" @@ -3157,19 +3040,6 @@ gauge@~2.7.1: supports-color "^0.2.0" wide-align "^1.1.0" -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gaze@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" @@ -3312,14 +3182,10 @@ got@^5.0.0: unzip-response "^1.0.2" url-parse-lax "^1.0.0" -graceful-fs@4.1.11, graceful-fs@^4.1.11, graceful-fs@^4.1.3: +graceful-fs@4.1.11, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: - version "4.1.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.10.tgz#f2d720c22092f743228775c75e3612632501f131" - "graceful-readlink@>= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -3629,7 +3495,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -4897,20 +4763,6 @@ node-notifier@^5.0.2: shellwords "^0.1.0" which "^1.2.12" -node-pre-gyp@^0.6.29: - version "0.6.32" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5" - dependencies: - mkdirp "~0.5.1" - nopt "~3.0.6" - npmlog "^4.0.1" - rc "~1.1.6" - request "^2.79.0" - rimraf "~2.5.4" - semver "~5.3.0" - tar "~2.2.1" - tar-pack "~3.3.0" - node-pre-gyp@^0.6.36: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" @@ -4956,7 +4808,7 @@ node-uuid@~1.4.0: version "1.4.7" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" -"nopt@2 || 3", nopt@^3.0.1, nopt@~3.0.6: +"nopt@2 || 3", nopt@^3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -5010,7 +4862,7 @@ normalize-url@^1.4.0: gauge "~2.6.0" set-blocking "~2.0.0" -npmlog@^4.0.0, npmlog@^4.0.1: +npmlog@^4.0.0, npmlog@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" dependencies: @@ -5019,15 +4871,6 @@ npmlog@^4.0.0, npmlog@^4.0.1: gauge "~2.7.1" set-blocking "~2.0.0" -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" @@ -5109,12 +4952,6 @@ once@^1.3.0, once@^1.3.3, once@^1.4.0: dependencies: wrappy "1" -once@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" - dependencies: - wrappy "1" - onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" @@ -5851,23 +5688,23 @@ raw-loader@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" -rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@~1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" +rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" dependencies: deep-extend "~0.4.0" ini "~1.3.0" minimist "^1.2.0" - strip-json-comments "~1.0.4" + strip-json-comments "~2.0.1" -rc@^1.1.7: - version "1.2.1" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" +rc@^1.1.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" dependencies: deep-extend "~0.4.0" ini "~1.3.0" minimist "^1.2.0" - strip-json-comments "~2.0.1" + strip-json-comments "~1.0.4" rcedit@^0.9.0: version "0.9.0" @@ -6116,7 +5953,7 @@ readable-stream@^1.1.8, readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, readable-stream@^2.2.9: +readable-stream@^2.0.0, readable-stream@^2.1.4, readable-stream@^2.2.9: version "2.2.11" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.11.tgz#0796b31f8d7688007ff0b93a8088d34aa17c0f72" dependencies: @@ -6140,18 +5977,6 @@ readable-stream@^2.0.0, readable-stream@^2.2.9: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@^2.1.4: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - readable-stream@~2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" @@ -6163,18 +5988,6 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -6464,7 +6277,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4: +rimraf@2, rimraf@^2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -6518,10 +6331,6 @@ safe-buffer@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - sane@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775" @@ -6558,9 +6367,9 @@ schema-utils@^0.3.0: dependencies: ajv "^5.0.0" -secretin@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/secretin/-/secretin-1.8.1.tgz#69c7ff8d3d99fc554ceb1d06eba9f5e420ba274b" +secretin@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/secretin/-/secretin-1.8.3.tgz#e0f015fc4c9075f779a7d3b90e9229e2a906e7ab" select-hose@^2.0.0: version "2.0.0" @@ -6578,7 +6387,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -6891,12 +6700,6 @@ string_decoder@~1.0.0: dependencies: safe-buffer "~5.0.1" -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -7053,20 +6856,7 @@ tar-pack@^3.4.0: tar "^2.2.1" uid-number "^0.0.6" -tar-pack@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" - dependencies: - debug "~2.2.0" - fstream "~1.0.10" - fstream-ignore "~1.0.5" - once "~1.3.3" - readable-stream "~2.1.4" - rimraf "~2.5.1" - tar "~2.2.1" - uid-number "~0.0.6" - -tar@^2.0.0, tar@^2.2.1, tar@~2.2.1: +tar@^2.0.0, tar@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" dependencies: @@ -7247,11 +7037,11 @@ ua-parser-js@^0.7.9: version "0.7.12" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" -uglify-js@3.0.x: - version "3.0.15" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.15.tgz#aacb323a846b234602270dead8a32441a8806f42" +uglify-js@3.0.x, uglify-js@^3.0.13: + version "3.0.26" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.26.tgz#ba279ca597b13fe6c62c2d87dd5188e57a7a3233" dependencies: - commander "~2.9.0" + commander "~2.11.0" source-map "~0.5.1" uglify-js@^2.6, uglify-js@^2.8.27: @@ -7263,18 +7053,11 @@ uglify-js@^2.6, uglify-js@^2.8.27: optionalDependencies: uglify-to-browserify "~1.0.0" -uglify-js@^3.0.13: - version "3.0.26" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.26.tgz#ba279ca597b13fe6c62c2d87dd5188e57a7a3233" - dependencies: - commander "~2.11.0" - source-map "~0.5.1" - uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" -uid-number@^0.0.6, uid-number@~0.0.6: +uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -7409,11 +7192,7 @@ uuid@^2.0.1, uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -uuid@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" - -uuid@^3.1.0: +uuid@^3.0.0, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"