Skip to content

Commit

Permalink
Merge pull request #50 from secretin/change_password
Browse files Browse the repository at this point in the history
Add form to change master password in settings
  • Loading branch information
agix authored Sep 21, 2017
2 parents ba86040 + 08f85a0 commit 360ffa6
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 6 deletions.
27 changes: 26 additions & 1 deletion src/actions/OptionsActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,23 @@ class OptionsActions {
'activateShortLoginFailure',
'deactivateShortLoginSuccess',
'deactivateShortLoginFailure',
'showImportKeepass',
'hideImportKeepass',
'showChangePassword',
'hideChangePassword',
'importKeepassProgress',
'importKeepassSuccess',
'importKeepassFailure',
'hideQRCode',
'hideShortLogin',
'changeDelaySuccess',
'changeDelayFailure',
'showRescueCodesSuccess',
'hideRescueCodes'
'hideRescueCodes',
'changeNewPass1',
'changeNewPass2',
'changePasswordSuccess',
'changePasswordFailure'
);
}

Expand Down Expand Up @@ -87,6 +98,20 @@ class OptionsActions {
};
}

changePassword({ newPass }) {
return dispatch => {
dispatch();
secretin
.changePassword(newPass)
.then(() => {
this.changePasswordSuccess();
})
.catch(() => {
this.changePasswordFailure();
});
};
}

toggleTotp({ checked }) {
if (checked) {
return true;
Expand Down
138 changes: 138 additions & 0 deletions src/components/options/ChangePasswordShow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import connectToStores from 'alt-utils/lib/connectToStores';

import Modal from 'components/utilities/Modal';
import Button from 'components/utilities/Button';
import Input from 'components/utilities/Input';

import OptionsStore from 'stores/OptionsStore';
import OptionsActions from 'actions/OptionsActions';

class ChangePasswordShow extends Component {
static propTypes = {
shown: PropTypes.bool,
loading: PropTypes.bool,
error: PropTypes.string,
newPass1: PropTypes.string,
newPass2: PropTypes.string,
};

static defaultProps = {
shown: false,
loading: false,
newPass1: '',
newPass2: '',
error: '',
};

static getStores() {
return [OptionsStore];
}

static getPropsFromStores() {
const state = OptionsStore.getNewPass();

return {
errors: state.get('error'),
shown: state.get('shown'),
loading: state.get('loading'),
newPass1: state.get('newPass1'),
newPass2: state.get('newPass2'),
};
}

constructor(props) {
super(props);

this.state = {
success: false,
};
this.handleChangePassword = this.handleChangePassword.bind(this);
}

handleChangePassword() {
OptionsActions.changePassword({
newPass: this.props.newPass1,
});
}

componentWillReceiveProps(nextProps) {
if (this.props.loading && !nextProps.loading && nextProps.error === '') {
this.setState({
success: true,
});
} else {
this.setState({
success: false,
});
}
}

render() {
return (
<Modal
show={this.props.shown}
onClose={OptionsActions.hideChangePassword}
>
<Modal.Header>
<span className="text">Change master password</span>
</Modal.Header>

{!this.state.success
? <Modal.Body>
<Input
name="newPass1"
label="New password"
value={this.props.newPass1}
onChange={OptionsActions.changeNewPass1}
type="password"
disabled={this.props.loading}
/>
{this.props.newPass1.length > 0 &&
<span className="options-changepassword">
<Input
name="newPass2"
label="Retype"
value={this.props.newPass2}
onChange={OptionsActions.changeNewPass2}
type="password"
disabled={this.props.loading}
/>
</span>}
<div className="options-changepassword-infos">
{this.props.error === '' &&
this.props.newPass1.length > 0 &&
this.props.newPass1 !== this.props.newPass2 &&
'Passwords mismatch'}
{this.props.error !== '' && this.props.error}
</div>
</Modal.Body>
: <Modal.Body>
<div className="options-changepassword-success">Success</div>
</Modal.Body>}

<Modal.Footer>
<Button
type="reset"
buttonStyle="default"
onClick={OptionsActions.hideChangePassword}
>
Close
</Button>
{this.props.newPass1.length > 0 &&
<Button
type="button"
buttonStyle="primary"
onClick={this.handleChangePassword}
disabled={this.props.newPass1 !== this.props.newPass2}
>
Change it
</Button>}
</Modal.Footer>
</Modal>
);
}
}

export default connectToStores(ChangePasswordShow);
16 changes: 13 additions & 3 deletions src/components/options/OptionsContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import AppUIStore from 'stores/AppUIStore';
import ShortLoginShow from 'components/options/ShortLoginShow';
import QRCodeShow from 'components/options/QRCodeShow';
import RescueCodesShow from 'components/options/RescueCodesShow';
import ChangePasswordShow from 'components/options/ChangePasswordShow';
import Title from 'components/utilities/Title';
import Checkbox from 'components/utilities/Checkbox';
import Input from 'components/utilities/Input';
Expand All @@ -19,6 +20,7 @@ import OptionsStore from 'stores/OptionsStore';
class OptionsContainer extends Component {
static propTypes = {
options: PropTypes.instanceOf(Immutable.Map),
newPass: PropTypes.instanceOf(Immutable.Map),
};

static getStores() {
Expand All @@ -39,7 +41,6 @@ class OptionsContainer extends Component {

render() {
const { options } = this.props;

return (
<div className="page">
<div className="page-header">
Expand Down Expand Up @@ -117,8 +118,17 @@ class OptionsContainer extends Component {
</div>
</div>
<div className="options-section">
<h3 className="options-section-title">Password generation</h3>
<div className="options-section-item">SOON BY @dqms</div>
<div className="options-section-item">
<ChangePasswordShow />
<Button
type="button"
buttonStyle="warning"
onClick={OptionsActions.showChangePassword}
disabled={!AppUIStore.isOnline()}
>
Change master password
</Button>
</div>
</div>
</div>
</div>
Expand Down
8 changes: 7 additions & 1 deletion src/components/utilities/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ class Button extends Component {
form: PropTypes.string,
disabled: PropTypes.bool,
size: PropTypes.string,
buttonStyle: PropTypes.oneOf(['default', 'primary', 'icon']),
buttonStyle: PropTypes.oneOf([
'default',
'primary',
'icon',
'success',
'warning',
]),
to: PropTypes.string,
onClick: PropTypes.func,
children: PropTypes.oneOfType([
Expand Down
67 changes: 67 additions & 0 deletions src/stores/OptionsStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ const OptionsState = new Record({
showQRCode: false,
showShortLogin: false,
showRescueCodes: false,
newPass: new Immutable.Map({
shown: false,
newPass1: '',
newPass2: '',
error: '',
loading: false,
}),
rescueCodes: new Immutable.List(),
loading: false,
});
Expand Down Expand Up @@ -108,6 +115,62 @@ class OptionsStore {
this.setState(this.state.setIn(['options', 'timeToClose'], timeToClose));
}

onChangeNewPass1(newPass1) {
this.setState(this.state.setIn(['newPass', 'newPass1'], newPass1.value));
}

onChangeNewPass2(newPass2) {
this.setState(this.state.setIn(['newPass', 'newPass2'], newPass2.value));
}

onShowChangePassword() {
this.setState(
this.state
.setIn(['newPass', 'newPass1'], '')
.setIn(['newPass', 'newPass2'], '')
.setIn(['newPass', 'error'], '')
.setIn(['newPass', 'loading'], false)
.setIn(['newPass', 'shown'], true)
);
}

onHideChangePassword() {
this.setState(
this.state
.setIn(['newPass', 'newPass1'], '')
.setIn(['newPass', 'newPass2'], '')
.setIn(['newPass', 'error'], '')
.setIn(['newPass', 'loading'], false)
.setIn(['newPass', 'shown'], false)
);
}

onChangePassword() {
this.setState(
this.state
.setIn(['newPass', 'loading'], true)
.setIn(['newPass', 'error'], '')
);
}

onChangePasswordSuccess() {
this.setState(
this.state
.setIn(['newPass', 'newPass1'], '')
.setIn(['newPass', 'newPass2'], '')
.setIn(['newPass', 'error'], '')
.setIn(['newPass', 'loading'], false)
);
}

onChangePasswordFailure() {
this.setState(
this.state
.setIn(['newPass', 'error'], 'Password change failed')
.setIn(['newPass', 'loading'], false)
);
}

onShowRescueCodesSuccess({ rescueCodes }) {
this.setState(
this.state.merge({
Expand All @@ -129,6 +192,10 @@ class OptionsStore {
static getOptions() {
return this.getState().get('options');
}

static getNewPass() {
return this.getState().get('newPass');
}
}

export default alt.createStore(makeImmutable(OptionsStore), 'OptionsStore');
1 change: 1 addition & 0 deletions src/stylesheets/application.sass
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
@import 'components/users/UserConnect'

@import 'components/options/OptionsContainer'
@import 'components/options/ChangePassword'
@import 'components/options/OptionsTOTP'
@import 'components/options/ImportKeepass'
10 changes: 10 additions & 0 deletions src/stylesheets/components/options/ChangePassword.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.options-changepassword
margin-left: 20px

.options-changepassword-infos
margin-top: 10px

.options-changepassword-success
color: $color-nephritis
text-align: center
font-size: 20px
11 changes: 10 additions & 1 deletion src/stylesheets/components/utilities/Button.sass
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,17 @@
background-color: $color-peter-river
color: white

.button--style-warning
@extend .button--style-primary
background-color: $color-pomegranate

.button--style-success
@extend .button--style-primary
background-color: $color-nephritis

.button--style-default,
.button--style-primary
.button--style-primary,
.button--style-warning
&.button--size-base
.icon
margin-left: -12px
Expand Down

0 comments on commit 360ffa6

Please sign in to comment.