mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-31 01:35:15 +02:00
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:
parent
1c5d156723
commit
4077dac8c7
@ -54,11 +54,11 @@ export default {
|
|||||||
data: {
|
data: {
|
||||||
userId: rememberData.userId,
|
userId: rememberData.userId,
|
||||||
rememberToken: rememberData.token,
|
rememberToken: rememberData.token,
|
||||||
|
staff: rememberData.isStaff,
|
||||||
remember: 1,
|
remember: 1,
|
||||||
isAutomatic: 1
|
|
||||||
}
|
}
|
||||||
}).then((result) => {
|
}).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;
|
return result;
|
||||||
})
|
})
|
||||||
|
@ -49,11 +49,32 @@ class AdminLoginPage extends React.Component {
|
|||||||
<div>
|
<div>
|
||||||
<Widget className="admin-login-page__content">
|
<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__image"><img width="100%" src={API.getURL() + '/images/logo.png'} alt="OpenSupports Admin Panel"/></div>
|
||||||
<div className="admin-login-page__login-form">
|
<div className="admin-login-page__login-form-container">
|
||||||
<Form onSubmit={this.onLoginFormSubmit.bind(this)} loading={this.props.session.pending}>
|
<Form {...this.getLoginFormProps()}>
|
||||||
<FormField name="email" label={i18n('EMAIL')} field="input" validation="EMAIL" fieldProps={{size:'large'}} required />
|
<div className="admin-login-page__login-form-container__login-form__fields">
|
||||||
<FormField name="password" label={i18n('PASSWORD')} field="input" fieldProps={{password:true, size:'large'}} />
|
<FormField
|
||||||
<SubmitButton>{i18n('LOG_IN')}</SubmitButton>
|
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>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
{this.renderRecoverStatus()}
|
{this.renderRecoverStatus()}
|
||||||
@ -68,7 +89,7 @@ class AdminLoginPage extends React.Component {
|
|||||||
|
|
||||||
renderPasswordRecovery() {
|
renderPasswordRecovery() {
|
||||||
return (
|
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}/>
|
<PasswordRecovery recoverSent={this.state.recoverSent} formProps={this.getRecoverFormProps()} onBackToLoginClick={this.onBackToLoginClick.bind(this)} renderLogo={true}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -105,7 +126,7 @@ class AdminLoginPage extends React.Component {
|
|||||||
getLoginFormProps() {
|
getLoginFormProps() {
|
||||||
return {
|
return {
|
||||||
loading: this.props.session.pending,
|
loading: this.props.session.pending,
|
||||||
className: 'admin-login-page__form',
|
className: 'admin-login-page__login-form-container__login-form',
|
||||||
ref: 'loginForm',
|
ref: 'loginForm',
|
||||||
onSubmit: this.onLoginFormSubmit.bind(this),
|
onSubmit: this.onLoginFormSubmit.bind(this),
|
||||||
errors: this.getLoginFormErrors(),
|
errors: this.getLoginFormErrors(),
|
||||||
@ -114,12 +135,17 @@ class AdminLoginPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getRecoverFormProps() {
|
getRecoverFormProps() {
|
||||||
|
const {
|
||||||
|
loadingRecover,
|
||||||
|
recoverFormErrors
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading: this.state.loadingRecover,
|
loading: loadingRecover,
|
||||||
className: 'admin-login-page__form',
|
className: 'admin-login-page__recovery-form-container__recovery-form',
|
||||||
ref: 'recoverForm',
|
ref: 'recoverForm',
|
||||||
onSubmit: this.onForgotPasswordSubmit.bind(this),
|
onSubmit: this.onForgotPasswordSubmit.bind(this),
|
||||||
errors: this.state.recoverFormErrors,
|
errors: recoverFormErrors,
|
||||||
onValidateErrors: this.onRecoverFormErrorsValidation.bind(this)
|
onValidateErrors: this.onRecoverFormErrorsValidation.bind(this)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,13 @@
|
|||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__login-form {
|
&__login-form-container {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
|
&__login-form__fields {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__error {
|
&__error {
|
||||||
|
@ -194,8 +194,12 @@ class FormField extends React.Component {
|
|||||||
if(field === 'autocomplete') {
|
if(field === 'autocomplete') {
|
||||||
props.values = value;
|
props.values = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
props.value = value;
|
if(field === 'checkbox') {
|
||||||
|
props.value = !!value;
|
||||||
|
} else {
|
||||||
|
props.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
@ -48,9 +48,10 @@ class SessionStore {
|
|||||||
return JSON.parse(this.getItem('departments'));
|
return JSON.parse(this.getItem('departments'));
|
||||||
}
|
}
|
||||||
|
|
||||||
storeRememberData({token, userId, expiration}) {
|
storeRememberData({token, userId, expiration, isStaff}) {
|
||||||
this.setItem('rememberData-token', token);
|
this.setItem('rememberData-token', token);
|
||||||
this.setItem('rememberData-userId', userId);
|
this.setItem('rememberData-userId', userId);
|
||||||
|
this.setItem('rememberData-isStaff', isStaff);
|
||||||
this.setItem('rememberData-expiration', expiration);
|
this.setItem('rememberData-expiration', expiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +107,7 @@ class SessionStore {
|
|||||||
return {
|
return {
|
||||||
token: this.getItem('rememberData-token'),
|
token: this.getItem('rememberData-token'),
|
||||||
userId: this.getItem('rememberData-userId'),
|
userId: this.getItem('rememberData-userId'),
|
||||||
|
isStaff: this.getItem('rememberData-isStaff'),
|
||||||
expiration: this.getItem('rememberData-expiration')
|
expiration: this.getItem('rememberData-expiration')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -113,6 +115,7 @@ class SessionStore {
|
|||||||
clearRememberData() {
|
clearRememberData() {
|
||||||
this.removeItem('rememberData-token');
|
this.removeItem('rememberData-token');
|
||||||
this.removeItem('rememberData-userId');
|
this.removeItem('rememberData-userId');
|
||||||
|
this.removeItem('rememberData-isStaff');
|
||||||
this.removeItem('rememberData-expiration');
|
this.removeItem('rememberData-expiration');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class SessionReducer extends Reducer {
|
|||||||
sessionStore.storeRememberData({
|
sessionStore.storeRememberData({
|
||||||
token: resultData.rememberToken,
|
token: resultData.rememberToken,
|
||||||
userId: resultData.userId,
|
userId: resultData.userId,
|
||||||
staff: resultData.staff,
|
isStaff: resultData.staff ? 1 : 0,
|
||||||
expiration: resultData.rememberExpiration
|
expiration: resultData.rememberExpiration
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ class LoginController extends Controller {
|
|||||||
|
|
||||||
$this->createUserSession();
|
$this->createUserSession();
|
||||||
$this->createRememberToken();
|
$this->createRememberToken();
|
||||||
|
|
||||||
if(Controller::request('staff')) {
|
if(Controller::request('staff')) {
|
||||||
$this->userInstance->lastLogin = Date::getCurrentDate();
|
$this->userInstance->lastLogin = Date::getCurrentDate();
|
||||||
$this->userInstance->store();
|
$this->userInstance->store();
|
||||||
@ -116,13 +117,18 @@ class LoginController extends Controller {
|
|||||||
$rememberToken = Controller::request('rememberToken');
|
$rememberToken = Controller::request('rememberToken');
|
||||||
$userInstance = new NullDataStore();
|
$userInstance = new NullDataStore();
|
||||||
|
|
||||||
if ($rememberToken) {
|
if($rememberToken) {
|
||||||
$sessionCookie = SessionCookie::getDataStore($rememberToken, 'token');
|
$sessionCookie = SessionCookie::getDataStore($rememberToken, 'token');
|
||||||
$userId = Controller::request('userId');
|
$userId = Controller::request('userId');
|
||||||
|
$isStaff = !!Controller::request('staff');
|
||||||
|
|
||||||
if (!$sessionCookie->isNull() && $userId === $sessionCookie->user->id) {
|
if(!$sessionCookie->isNull()) {
|
||||||
$userInstance = $sessionCookie->user;
|
$loggedInstance = $isStaff ? $sessionCookie->staff : $sessionCookie->user;
|
||||||
$sessionCookie->delete();
|
|
||||||
|
if(($userId == $loggedInstance->id) && ($isStaff == $sessionCookie->isStaff)) {
|
||||||
|
$userInstance = $loggedInstance;
|
||||||
|
$sessionCookie->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,13 +146,15 @@ class LoginController extends Controller {
|
|||||||
private function createRememberToken() {
|
private function createRememberToken() {
|
||||||
$remember = Controller::request('remember');
|
$remember = Controller::request('remember');
|
||||||
|
|
||||||
if (!Controller::request('staff') && $remember) {
|
if($remember) {
|
||||||
$this->rememberToken = Hashing::generateRandomToken();
|
$this->rememberToken = Hashing::generateRandomToken();
|
||||||
$this->rememberExpiration = Date::getNextDate(30);
|
$this->rememberExpiration = Date::getNextDate(30);
|
||||||
|
|
||||||
$sessionCookie = new SessionCookie();
|
$sessionCookie = new SessionCookie();
|
||||||
$sessionCookie->setProperties(array(
|
$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,
|
'token' => $this->rememberToken,
|
||||||
'ip' => $_SERVER['REMOTE_ADDR'],
|
'ip' => $_SERVER['REMOTE_ADDR'],
|
||||||
'creationDate' => Date::getCurrentDate(),
|
'creationDate' => Date::getCurrentDate(),
|
||||||
|
@ -1,10 +1,24 @@
|
|||||||
<?php
|
<?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 {
|
class SessionCookie extends DataStore {
|
||||||
const TABLE = 'sessioncookie';
|
const TABLE = 'sessioncookie';
|
||||||
|
|
||||||
public static function getProps() {
|
public static function getProps() {
|
||||||
return array (
|
return array (
|
||||||
|
'isStaff',
|
||||||
|
'staff',
|
||||||
'user',
|
'user',
|
||||||
'token',
|
'token',
|
||||||
'ip',
|
'ip',
|
||||||
|
@ -6,6 +6,7 @@ describe '/user/ban' do
|
|||||||
password: 'staff',
|
password: 'staff',
|
||||||
staff: true
|
staff: true
|
||||||
})
|
})
|
||||||
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
$csrf_userid = result['data']['userId']
|
$csrf_userid = result['data']['userId']
|
||||||
$csrf_token = result['data']['token']
|
$csrf_token = result['data']['token']
|
||||||
|
@ -9,7 +9,6 @@ describe '/user/login' do
|
|||||||
email: @loginEmail,
|
email: @loginEmail,
|
||||||
password: 'some_incorrect_password'
|
password: 'some_incorrect_password'
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('fail')
|
(result['status']).should.equal('fail')
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ describe '/user/login' do
|
|||||||
email: @loginEmail,
|
email: @loginEmail,
|
||||||
password: @loginPass
|
password: @loginPass
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -27,7 +25,6 @@ describe '/user/login' do
|
|||||||
email: @loginEmail,
|
email: @loginEmail,
|
||||||
password: @loginPass
|
password: @loginPass
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -36,21 +33,20 @@ describe '/user/login' do
|
|||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
email: $staff[:email],
|
email: $staff[:email],
|
||||||
password: $staff[:password],
|
password: $staff[:password],
|
||||||
staff: true
|
staff: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
(result['data']['staff']).should.equal(true)
|
(result['data']['staff']).should.equal(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should work with remember token' do
|
it 'should work autologin user with remember token' do
|
||||||
request('/user/logout', {})
|
request('/user/logout', {})
|
||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
email: @loginEmail,
|
email: @loginEmail,
|
||||||
password: @loginPass,
|
password: @loginPass,
|
||||||
|
staff: 0,
|
||||||
remember: 1
|
remember: 1
|
||||||
})
|
})
|
||||||
|
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
|
|
||||||
@rememberToken = result['data']['rememberToken']
|
@rememberToken = result['data']['rememberToken']
|
||||||
@ -60,12 +56,15 @@ describe '/user/login' do
|
|||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
userId: @userId,
|
userId: @userId,
|
||||||
rememberToken: '12abc',
|
rememberToken: '12abc',
|
||||||
|
staff: 0,
|
||||||
remember: 1
|
remember: 1
|
||||||
})
|
})
|
||||||
(result['status']).should.equal('fail')
|
(result['status']).should.equal('fail')
|
||||||
|
|
||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
userId: 1,
|
userId: 1,
|
||||||
rememberToken: @rememberToken,
|
rememberToken: @rememberToken,
|
||||||
|
staff: 0,
|
||||||
remember: 1
|
remember: 1
|
||||||
})
|
})
|
||||||
(result['status']).should.equal('fail')
|
(result['status']).should.equal('fail')
|
||||||
@ -73,8 +72,49 @@ describe '/user/login' do
|
|||||||
result = request('/user/login', {
|
result = request('/user/login', {
|
||||||
userId: @userId,
|
userId: @userId,
|
||||||
rememberToken: @rememberToken,
|
rememberToken: @rememberToken,
|
||||||
|
staff: 0,
|
||||||
remember: 1
|
remember: 1
|
||||||
})
|
})
|
||||||
(result['status']).should.equal('success')
|
(result['status']).should.equal('success')
|
||||||
end
|
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
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user