Remember me function for staffs (#866)

* fix warning in checbox in form field.

* Add remember me function for staffs.

* Add staff instance in session cookie.

* Add result data staff in get user data in auto login.

* Fix remember me function for user.

* Fix login test rb and add remember me function test in login rb.

* Resolve github maxi comments.
This commit is contained in:
LautaroCesso 2020-08-19 23:33:40 -03:00 committed by GitHub
parent 1c5d156723
commit 4077dac8c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 130 additions and 30 deletions

View File

@ -54,11 +54,11 @@ export default {
data: {
userId: rememberData.userId,
rememberToken: rememberData.token,
staff: rememberData.isStaff,
remember: 1,
isAutomatic: 1
}
}).then((result) => {
store.dispatch(this.getUserData(result.data.userId, result.data.token));
store.dispatch(this.getUserData(result.data.userId, result.data.token, result.data.staff));
return result;
})

View File

@ -49,11 +49,32 @@ class AdminLoginPage extends React.Component {
<div>
<Widget className="admin-login-page__content">
<div className="admin-login-page__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Admin Panel"/></div>
<div className="admin-login-page__login-form">
<Form onSubmit={this.onLoginFormSubmit.bind(this)} loading={this.props.session.pending}>
<FormField name="email" label={i18n('EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required />
<FormField name="password" label={i18n('PASSWORD')} field="input" fieldProps={{password:true, size:'large'}} />
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
<div className="admin-login-page__login-form-container">
<Form {...this.getLoginFormProps()}>
<div className="admin-login-page__login-form-container__login-form__fields">
<FormField
name="email"
label={i18n('EMAIL')}
className="admin-login-page__login-form-container__login-form__fields__email"
field="input"
validation="EMAIL"
fieldProps={{size:'large'}}
required />
<FormField
name="password"
label={i18n('PASSWORD')}
className="admin-login-page__login-form-container__login-form__fields__password"
field="input"
fieldProps={{password:true, size:'large'}} />
<FormField
name="remember"
label={i18n('REMEMBER_ME')}
className="admin-login-page__login-form-container__login-form__fields__remember"
field="checkbox" />
</div>
<div className="admin-login-page__login-form-container__login-form__submit-button">
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
</div>
</Form>
</div>
{this.renderRecoverStatus()}
@ -68,7 +89,7 @@ class AdminLoginPage extends React.Component {
renderPasswordRecovery() {
return (
<div>
<div className="admin-login-page__recovery-form-container">
<PasswordRecovery recoverSent={this.state.recoverSent} formProps={this.getRecoverFormProps()} onBackToLoginClick={this.onBackToLoginClick.bind(this)} renderLogo={true}/>
</div>
);
@ -105,7 +126,7 @@ class AdminLoginPage extends React.Component {
getLoginFormProps() {
return {
loading: this.props.session.pending,
className: 'admin-login-page__form',
className: 'admin-login-page__login-form-container__login-form',
ref: 'loginForm',
onSubmit: this.onLoginFormSubmit.bind(this),
errors: this.getLoginFormErrors(),
@ -114,12 +135,17 @@ class AdminLoginPage extends React.Component {
}
getRecoverFormProps() {
const {
loadingRecover,
recoverFormErrors
} = this.state;
return {
loading: this.state.loadingRecover,
className: 'admin-login-page__form',
loading: loadingRecover,
className: 'admin-login-page__recovery-form-container__recovery-form',
ref: 'recoverForm',
onSubmit: this.onForgotPasswordSubmit.bind(this),
errors: this.state.recoverFormErrors,
errors: recoverFormErrors,
onValidateErrors: this.onRecoverFormErrorsValidation.bind(this)
};
}

View File

@ -19,9 +19,13 @@
margin-bottom: 30px;
}
&__login-form {
&__login-form-container {
margin: 0 auto;
display: inline-block;
&__login-form__fields {
padding: 10px 0;
}
}
&__error {

View File

@ -194,8 +194,12 @@ class FormField extends React.Component {
if(field === 'autocomplete') {
props.values = value;
}
props.value = value;
if(field === 'checkbox') {
props.value = !!value;
} else {
props.value = value;
}
return props;
}

View File

@ -48,9 +48,10 @@ class SessionStore {
return JSON.parse(this.getItem('departments'));
}
storeRememberData({token, userId, expiration}) {
storeRememberData({token, userId, expiration, isStaff}) {
this.setItem('rememberData-token', token);
this.setItem('rememberData-userId', userId);
this.setItem('rememberData-isStaff', isStaff);
this.setItem('rememberData-expiration', expiration);
}
@ -106,6 +107,7 @@ class SessionStore {
return {
token: this.getItem('rememberData-token'),
userId: this.getItem('rememberData-userId'),
isStaff: this.getItem('rememberData-isStaff'),
expiration: this.getItem('rememberData-expiration')
};
}
@ -113,6 +115,7 @@ class SessionStore {
clearRememberData() {
this.removeItem('rememberData-token');
this.removeItem('rememberData-userId');
this.removeItem('rememberData-isStaff');
this.removeItem('rememberData-expiration');
}

View File

@ -95,7 +95,7 @@ class SessionReducer extends Reducer {
sessionStore.storeRememberData({
token: resultData.rememberToken,
userId: resultData.userId,
staff: resultData.staff,
isStaff: resultData.staff ? 1 : 0,
expiration: resultData.rememberExpiration
});
}

View File

@ -61,6 +61,7 @@ class LoginController extends Controller {
$this->createUserSession();
$this->createRememberToken();
if(Controller::request('staff')) {
$this->userInstance->lastLogin = Date::getCurrentDate();
$this->userInstance->store();
@ -116,13 +117,18 @@ class LoginController extends Controller {
$rememberToken = Controller::request('rememberToken');
$userInstance = new NullDataStore();
if ($rememberToken) {
if($rememberToken) {
$sessionCookie = SessionCookie::getDataStore($rememberToken, 'token');
$userId = Controller::request('userId');
$isStaff = !!Controller::request('staff');
if (!$sessionCookie->isNull() && $userId === $sessionCookie->user->id) {
$userInstance = $sessionCookie->user;
$sessionCookie->delete();
if(!$sessionCookie->isNull()) {
$loggedInstance = $isStaff ? $sessionCookie->staff : $sessionCookie->user;
if(($userId == $loggedInstance->id) && ($isStaff == $sessionCookie->isStaff)) {
$userInstance = $loggedInstance;
$sessionCookie->delete();
}
}
}
@ -140,13 +146,15 @@ class LoginController extends Controller {
private function createRememberToken() {
$remember = Controller::request('remember');
if (!Controller::request('staff') && $remember) {
if($remember) {
$this->rememberToken = Hashing::generateRandomToken();
$this->rememberExpiration = Date::getNextDate(30);
$sessionCookie = new SessionCookie();
$sessionCookie->setProperties(array(
'user' => $this->userInstance,
'isStaff' => !!Controller::request('staff'),
'user' => $this->userInstance instanceof User ? $this->userInstance : null,
'staff' => $this->userInstance instanceof Staff ? $this->userInstance : null,
'token' => $this->rememberToken,
'ip' => $_SERVER['REMOTE_ADDR'],
'creationDate' => Date::getCurrentDate(),

View File

@ -1,10 +1,24 @@
<?php
/**
* @api {OBJECT} SessionCookie SessionCookie
* @apiVersion 4.8.0
* @apiGroup Data Structures
* @apiParam {Boolean} isStaff Indicates if it wants to login a staff or a regular user.
* @apiParam {Object} user The user.
* @apiParam {Object} staff The staff.
* @apiParam {String} token Token of the session, used to verify the session when making other requests.
* @apiParam {String} ip The ip.
* @apiParam {String} creationDate The creationDate.
* @apiParam {String} expirationDate The expirationDate.
*/
class SessionCookie extends DataStore {
const TABLE = 'sessioncookie';
public static function getProps() {
return array (
'isStaff',
'staff',
'user',
'token',
'ip',

View File

@ -6,6 +6,7 @@ describe '/user/ban' do
password: 'staff',
staff: true
})
(result['status']).should.equal('success')
$csrf_userid = result['data']['userId']
$csrf_token = result['data']['token']

View File

@ -9,7 +9,6 @@ describe '/user/login' do
email: @loginEmail,
password: 'some_incorrect_password'
})
(result['status']).should.equal('fail')
end
@ -18,7 +17,6 @@ describe '/user/login' do
email: @loginEmail,
password: @loginPass
})
(result['status']).should.equal('success')
end
@ -27,7 +25,6 @@ describe '/user/login' do
email: @loginEmail,
password: @loginPass
})
(result['status']).should.equal('success')
end
@ -36,21 +33,20 @@ describe '/user/login' do
result = request('/user/login', {
email: $staff[:email],
password: $staff[:password],
staff: true
staff: 1
})
(result['status']).should.equal('success')
(result['data']['staff']).should.equal(true)
end
it 'should work with remember token' do
it 'should work autologin user with remember token' do
request('/user/logout', {})
result = request('/user/login', {
email: @loginEmail,
password: @loginPass,
staff: 0,
remember: 1
})
(result['status']).should.equal('success')
@rememberToken = result['data']['rememberToken']
@ -60,12 +56,15 @@ describe '/user/login' do
result = request('/user/login', {
userId: @userId,
rememberToken: '12abc',
staff: 0,
remember: 1
})
(result['status']).should.equal('fail')
result = request('/user/login', {
userId: 1,
rememberToken: @rememberToken,
staff: 0,
remember: 1
})
(result['status']).should.equal('fail')
@ -73,8 +72,49 @@ describe '/user/login' do
result = request('/user/login', {
userId: @userId,
rememberToken: @rememberToken,
staff: 0,
remember: 1
})
(result['status']).should.equal('success')
end
it 'should work autologin staff with remember token' do
request('/user/logout', {})
result = request('/user/login', {
email: $staff[:email],
password: $staff[:password],
staff: 1,
remember: 1
})
(result['status']).should.equal('success')
@rememberToken = result['data']['rememberToken']
@staffId = result['data']['userId']
request('/user/logout', {})
result = request('/user/login', {
userId: @staffId,
rememberToken: '12abc',
staff: 1,
remember: 1
})
(result['status']).should.equal('fail')
result = request('/user/login', {
userId: 3,
rememberToken: @rememberToken,
staff: 1,
remember: 1
})
(result['status']).should.equal('fail')
result = request('/user/login', {
userId: @staffId,
rememberToken: @rememberToken,
staff: 1,
remember: 1
})
(result['status']).should.equal('success')
end
end