Remove change password auth panel

This commit is contained in:
SleepWalker 2016-08-02 22:13:11 +03:00
parent cb1a4b7d55
commit 6dcf985936
24 changed files with 10 additions and 303 deletions

View File

@ -32,7 +32,6 @@ const changeContextSpringConfig = {stiffness: 500, damping: 20, precision: 0.5};
const contexts = [ const contexts = [
['login', 'password', 'forgotPassword', 'recoverPassword'], ['login', 'password', 'forgotPassword', 'recoverPassword'],
['register', 'activation', 'resendActivation'], ['register', 'activation', 'resendActivation'],
['changePassword'],
['acceptRules'], ['acceptRules'],
['permissions'] ['permissions']
]; ];

View File

@ -10,6 +10,8 @@ To add new panel you need to:
* connect component to `routes` * connect component to `routes`
* whatever else you need * whatever else you need
Commit id with example: f4d315c
# TODO # TODO
This flow must be simplified This flow must be simplified

View File

@ -1,6 +1,6 @@
import { routeActions } from 'react-router-redux'; import { routeActions } from 'react-router-redux';
import { updateUser, logout as logoutUser, changePassword as changeUserPassword, acceptRules as userAcceptRules, authenticate } from 'components/user/actions'; import { updateUser, logout as logoutUser, acceptRules as userAcceptRules, authenticate } from 'components/user/actions';
import authentication from 'services/api/authentication'; import authentication from 'services/api/authentication';
import oauth from 'services/api/oauth'; import oauth from 'services/api/oauth';
import signup from 'services/api/signup'; import signup from 'services/api/signup';
@ -35,17 +35,6 @@ export function login({login = '', password = '', rememberMe = false}) {
); );
} }
export function changePassword({
password = '',
newPassword = '',
newRePassword = ''
}) {
return wrapInLoader((dispatch) =>
dispatch(changeUserPassword({password, newPassword, newRePassword, logoutAll: false}))
.catch(validationErrorsHandler(dispatch))
);
}
export function acceptRules() { export function acceptRules() {
return wrapInLoader((dispatch) => return wrapInLoader((dispatch) =>
dispatch(userAcceptRules()) dispatch(userAcceptRules())

View File

@ -1,9 +0,0 @@
{
"changePasswordTitle": "Change password",
"changePasswordMessage": "To enhance the security of your account, please change your password.",
"skipThisStep": "Skip password changing",
"change": "Change password",
"currentPassword": "Enter current password",
"newPassword": "Enter new password",
"newRePassword": "Repeat new password"
}

View File

@ -1,16 +0,0 @@
import factory from 'components/auth/factory';
import Body from './ChangePasswordBody';
import messages from './ChangePassword.intl.json';
export default factory({
title: messages.changePasswordTitle,
body: Body,
footer: {
color: 'darkBlue',
label: messages.change
},
links: {
label: messages.skipThisStep
}
});

View File

@ -1,57 +0,0 @@
import React from 'react';
import { FormattedMessage as Message } from 'react-intl';
import { Input } from 'components/ui/form';
import icons from 'components/ui/icons.scss';
import BaseAuthBody from 'components/auth/BaseAuthBody';
import styles from './changePassword.scss';
import messages from './ChangePassword.intl.json';
export default class ChangePasswordBody extends BaseAuthBody {
static displayName = 'ChangePasswordBody';
static panelId = 'changePassword';
autoFocusField = 'password';
render() {
return (
<div>
{this.renderErrors()}
<div className={styles.security}>
<span className={icons.lock} />
</div>
<p className={styles.descriptionText}>
<Message {...messages.changePasswordMessage} />
</p>
<Input {...this.bindField('password')}
icon="key"
color="darkBlue"
type="password"
required
placeholder={messages.currentPassword}
/>
<Input {...this.bindField('newPassword')}
icon="key"
color="darkBlue"
type="password"
required
placeholder={messages.newPassword}
/>
<Input {...this.bindField('newRePassword')}
icon="key"
color="darkBlue"
type="password"
required
placeholder={messages.newRePassword}
/>
</div>
);
}
}

View File

@ -1,16 +0,0 @@
@import '~components/ui/colors.scss';
.descriptionText {
font-size: 15px;
line-height: 1.4;
padding-bottom: 8px;
color: #aaa;
}
// TODO: вынести иконки такого типа в какую-то внешнюю структуру?
.security {
color: #fff;
font-size: 90px;
line-height: 1;
margin-bottom: 15px;
}

View File

@ -25,8 +25,6 @@ export default class ForgotPasswordBody extends BaseAuthBody {
const login = user.email || user.username || ''; const login = user.email || user.username || '';
const isLoginEditShown = this.state.isLoginEdit; const isLoginEditShown = this.state.isLoginEdit;
// TODO: нужно парсить инфу о том, какой кд у отправки кода и во сколько точно можно будет повторить
return ( return (
<div> <div>
{this.renderErrors()} {this.renderErrors()}

View File

@ -5,5 +5,8 @@
"messageWasSentTo": "The recovery code was sent to your Email {email}.", "messageWasSentTo": "The recovery code was sent to your Email {email}.",
"enterCodeBelow": "Please enter the code received into the field below:", "enterCodeBelow": "Please enter the code received into the field below:",
"enterNewPasswordBelow": "Enter and repeat new password below:", "enterNewPasswordBelow": "Enter and repeat new password below:",
"change": "Change password",
"newPassword": "Enter new password",
"newRePassword": "Repeat new password",
"enterTheCode": "Enter confirmation code" "enterTheCode": "Enter confirmation code"
} }

View File

@ -1,5 +1,4 @@
import factory from 'components/auth/factory'; import factory from 'components/auth/factory';
import changePassword from 'components/auth/changePassword/ChangePassword.intl.json';
import messages from './RecoverPassword.intl.json'; import messages from './RecoverPassword.intl.json';
import Body from './RecoverPasswordBody'; import Body from './RecoverPasswordBody';
@ -9,7 +8,7 @@ export default factory({
body: Body, body: Body,
footer: { footer: {
color: 'lightViolet', color: 'lightViolet',
label: changePassword.change label: messages.change
}, },
links: { links: {
label: messages.contactSupport label: messages.contactSupport

View File

@ -4,13 +4,11 @@ import { FormattedMessage as Message } from 'react-intl';
import { Input } from 'components/ui/form'; import { Input } from 'components/ui/form';
import BaseAuthBody from 'components/auth/BaseAuthBody'; import BaseAuthBody from 'components/auth/BaseAuthBody';
import changePassword from 'components/auth/changePassword/ChangePassword.intl.json';
import styles from './recoverPassword.scss'; import styles from './recoverPassword.scss';
import messages from './RecoverPassword.intl.json'; import messages from './RecoverPassword.intl.json';
// TODO: activation code field may be decoupled into common component and reused here and in activation panel // TODO: activation code field may be decoupled into common component and reused here and in activation panel
// TODO: new password fields may be decoupled into common component and reused here and in changePassword panel
export default class RecoverPasswordBody extends BaseAuthBody { export default class RecoverPasswordBody extends BaseAuthBody {
static displayName = 'RecoverPasswordBody'; static displayName = 'RecoverPasswordBody';
@ -64,7 +62,7 @@ export default class RecoverPasswordBody extends BaseAuthBody {
color="lightViolet" color="lightViolet"
type="password" type="password"
required required
placeholder={changePassword.newPassword} placeholder={messages.newPassword}
/> />
<Input {...this.bindField('newRePassword')} <Input {...this.bindField('newRePassword')}
@ -72,7 +70,7 @@ export default class RecoverPasswordBody extends BaseAuthBody {
color="lightViolet" color="lightViolet"
type="password" type="password"
required required
placeholder={changePassword.newRePassword} placeholder={messages.newRePassword}
/> />
</div> </div>
); );

View File

@ -5,7 +5,6 @@
"preferencesDescription": "Here you can change the key preferences of your account. Please note that all actions must be confirmed by entering a password.", "preferencesDescription": "Here you can change the key preferences of your account. Please note that all actions must be confirmed by entering a password.",
"mojangPriorityWarning": "A Mojang account with the same nickname was found. According to {rules}, account owner has the right to demand the restoration of control over nickname.", "mojangPriorityWarning": "A Mojang account with the same nickname was found. According to {rules}, account owner has the right to demand the restoration of control over nickname.",
"projectRules": "project rules", "projectRules": "project rules",
"oldHashingAlgoWarning": "Your was hashed with an old hashing algorithm.<br />Please, change password.",
"changedAt": "Changed {at}", "changedAt": "Changed {at}",
"disabled": "Disabled", "disabled": "Disabled",
"nickname": "Nickname", "nickname": "Nickname",

View File

@ -83,9 +83,6 @@ export default class Profile extends Component {
value={<Message {...messages.changedAt} values={{ value={<Message {...messages.changedAt} values={{
at: (<Relative value={user.passwordChangedAt * 1000} updateInterval={1000} />) at: (<Relative value={user.passwordChangedAt * 1000} updateInterval={1000} />)
}} />} }} />}
warningMessage={user.shouldChangePassword ? (
<HTMLMessage {...messages.oldHashingAlgoWarning} />
) : ''}
/> />
<ProfileField <ProfileField

View File

@ -31,7 +31,6 @@ export default class User {
isGuest: true, isGuest: true,
isActive: false, isActive: false,
shouldAcceptRules: false, // whether user need to review updated rules shouldAcceptRules: false, // whether user need to review updated rules
shouldChangePassword: false, // TODO: нужно ещё пробросить причину необходимости смены
passwordChangedAt: null, passwordChangedAt: null,
hasMojangUsernameCollision: false, hasMojangUsernameCollision: false,
}; };

View File

@ -69,26 +69,6 @@ export function fetchUserData() {
}); });
} }
export function changePassword({
password = '',
newPassword = '',
newRePassword = '',
logoutAll = true,
}) {
return (dispatch) =>
accounts.changePassword(
{password, newPassword, newRePassword, logoutAll}
)
.then((resp) => {
dispatch(updateUser({
shouldChangePassword: false
}));
return resp;
})
;
}
export function acceptRules() { export function acceptRules() {
return (dispatch) => return (dispatch) =>
accounts.acceptRules() accounts.acceptRules()

View File

@ -31,8 +31,7 @@ class ChangePasswordPage extends Component {
sendData: () => accounts.changePassword(form.serialize()) sendData: () => accounts.changePassword(form.serialize())
}).then(() => { }).then(() => {
this.props.updateUser({ this.props.updateUser({
passwordChangedAt: Date.now() / 1000, passwordChangedAt: Date.now() / 1000
shouldChangePassword: false
}); });
this.context.goToProfile(); this.context.goToProfile();
}); });

View File

@ -20,7 +20,6 @@ import Permissions from 'components/auth/permissions/Permissions';
import Activation from 'components/auth/activation/Activation'; import Activation from 'components/auth/activation/Activation';
import ResendActivation from 'components/auth/resendActivation/ResendActivation'; import ResendActivation from 'components/auth/resendActivation/ResendActivation';
import Password from 'components/auth/password/Password'; import Password from 'components/auth/password/Password';
import ChangePassword from 'components/auth/changePassword/ChangePassword';
import AcceptRules from 'components/auth/acceptRules/AcceptRules'; import AcceptRules from 'components/auth/acceptRules/AcceptRules';
import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword'; import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword';
import RecoverPassword from 'components/auth/recoverPassword/RecoverPassword'; import RecoverPassword from 'components/auth/recoverPassword/RecoverPassword';
@ -62,7 +61,6 @@ export default function routesFactory(store) {
<Route path="/oauth/permissions" components={new Permissions()} {...startAuthFlow} /> <Route path="/oauth/permissions" components={new Permissions()} {...startAuthFlow} />
<Route path="/oauth/finish" component={Finish} {...startAuthFlow} /> <Route path="/oauth/finish" component={Finish} {...startAuthFlow} />
<Route path="/accept-rules" components={new AcceptRules()} {...startAuthFlow} /> <Route path="/accept-rules" components={new AcceptRules()} {...startAuthFlow} />
<Route path="/change-password" components={new ChangePassword()} {...startAuthFlow} />
<Route path="/forgot-password" components={new ForgotPassword()} {...startAuthFlow} /> <Route path="/forgot-password" components={new ForgotPassword()} {...startAuthFlow} />
<Route path="/recover-password(/:key)" components={new RecoverPassword()} {...startAuthFlow} /> <Route path="/recover-password(/:key)" components={new RecoverPassword()} {...startAuthFlow} />
</Route> </Route>

View File

@ -5,18 +5,6 @@ export default {
return request.get('/api/accounts/current'); return request.get('/api/accounts/current');
}, },
changePassword({
password = '',
newPassword = '',
newRePassword = '',
logoutAll = true
}) {
return request.post(
'/api/accounts/change-password',
{password, newPassword, newRePassword, logoutAll}
);
},
acceptRules() { acceptRules() {
return request.post('/api/accounts/accept-rules'); return request.post('/api/accounts/accept-rules');
}, },

View File

@ -144,7 +144,6 @@ export default class AuthFlow {
case '/login': case '/login':
case '/password': case '/password':
case '/accept-rules': case '/accept-rules':
case '/change-password':
case '/oauth/permissions': case '/oauth/permissions':
case '/oauth/finish': case '/oauth/finish':
this.setState(new LoginState()); this.setState(new LoginState());

View File

@ -1,20 +0,0 @@
import AbstractState from './AbstractState';
import CompleteState from './CompleteState';
export default class ChangePasswordState extends AbstractState {
enter(context) {
context.navigate('/change-password');
}
resolve(context, payload) {
context.run('changePassword', payload)
.then(() => context.setState(new CompleteState()));
}
reject(context) {
context.run('updateUser', {
shouldChangePassword: false
});
context.setState(new CompleteState());
}
}

View File

@ -2,7 +2,6 @@ import AbstractState from './AbstractState';
import LoginState from './LoginState'; import LoginState from './LoginState';
import PermissionsState from './PermissionsState'; import PermissionsState from './PermissionsState';
import ActivationState from './ActivationState'; import ActivationState from './ActivationState';
import ChangePasswordState from './ChangePasswordState';
import AcceptRulesState from './AcceptRulesState'; import AcceptRulesState from './AcceptRulesState';
import FinishState from './FinishState'; import FinishState from './FinishState';
@ -22,8 +21,6 @@ export default class CompleteState extends AbstractState {
context.setState(new ActivationState()); context.setState(new ActivationState());
} else if (user.shouldAcceptRules) { } else if (user.shouldAcceptRules) {
context.setState(new AcceptRulesState()); context.setState(new AcceptRulesState());
} else if (user.shouldChangePassword) {
context.setState(new ChangePasswordState());
} else if (auth.oauth && auth.oauth.clientId) { } else if (auth.oauth && auth.oauth.clientId) {
if (auth.oauth.code) { if (auth.oauth.code) {
context.setState(new FinishState()); context.setState(new FinishState());

View File

@ -12,8 +12,6 @@ import ActivationState from 'services/authFlow/ActivationState';
import ResendActivationState from 'services/authFlow/ResendActivationState'; import ResendActivationState from 'services/authFlow/ResendActivationState';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
// TODO: navigate and state switching
describe('AuthFlow', () => { describe('AuthFlow', () => {
let flow; let flow;
let actions; let actions;
@ -178,7 +176,6 @@ describe('AuthFlow', () => {
'/': LoginState, '/': LoginState,
'/login': LoginState, '/login': LoginState,
'/password': LoginState, '/password': LoginState,
'/change-password': LoginState,
'/accept-rules': LoginState, '/accept-rules': LoginState,
'/oauth/permissions': LoginState, '/oauth/permissions': LoginState,
'/oauth/finish': LoginState, '/oauth/finish': LoginState,

View File

@ -1,86 +0,0 @@
import ChangePasswordState from 'services/authFlow/ChangePasswordState';
import CompleteState from 'services/authFlow/CompleteState';
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
describe('ChangePasswordState', () => {
let state;
let context;
let mock;
beforeEach(() => {
state = new ChangePasswordState();
const data = bootstrap();
context = data.context;
mock = data.mock;
});
afterEach(() => {
mock.verify();
});
describe('#enter', () => {
it('should navigate to /change-password', () => {
context.getState.returns({
user: {isGuest: true}
});
expectNavigate(mock, '/change-password');
state.enter(context);
});
});
describe('#resolve', () => {
it('should call changePassword with payload', () => {
const payload = {};
expectRun(
mock,
'changePassword',
sinon.match.same(payload)
).returns({then() {}});
state.resolve(context, payload);
});
it('should transition to complete state on success', () => {
const promise = Promise.resolve();
mock.expects('run').returns(promise);
expectState(mock, CompleteState);
state.resolve(context);
return promise;
});
it('should NOT transition to complete state on fail', () => {
const promise = Promise.reject();
mock.expects('run').returns(promise);
mock.expects('setState').never();
state.resolve(context);
return promise.catch(mock.verify.bind(mock));
});
});
describe('#reject', () => {
it('should transition to complete state and mark that password should not be changed', () => {
expectRun(
mock,
'updateUser',
sinon.match({
shouldChangePassword: false
})
);
expectState(mock, CompleteState);
state.reject(context);
});
});
});

View File

@ -3,7 +3,6 @@ import expect from 'unexpected';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
import ActivationState from 'services/authFlow/ActivationState'; import ActivationState from 'services/authFlow/ActivationState';
import ChangePasswordState from 'services/authFlow/ChangePasswordState';
import AcceptRulesState from 'services/authFlow/AcceptRulesState'; import AcceptRulesState from 'services/authFlow/AcceptRulesState';
import FinishState from 'services/authFlow/FinishState'; import FinishState from 'services/authFlow/FinishState';
import PermissionsState from 'services/authFlow/PermissionsState'; import PermissionsState from 'services/authFlow/PermissionsState';
@ -68,35 +67,6 @@ describe('CompleteState', () => {
state.enter(context); state.enter(context);
}); });
it('should transition to change-password if shouldChangePassword', () => {
context.getState.returns({
user: {
shouldChangePassword: true,
isActive: true,
isGuest: false
},
auth: {}
});
expectState(mock, ChangePasswordState);
state.enter(context);
});
it('should transition to activation with higher priority than shouldChangePassword', () => {
context.getState.returns({
user: {
shouldChangePassword: true,
isGuest: false
},
auth: {}
});
expectState(mock, ActivationState);
state.enter(context);
});
it('should transition to accept-rules if shouldAcceptRules', () => { it('should transition to accept-rules if shouldAcceptRules', () => {
context.getState.returns({ context.getState.returns({
user: { user: {