2016-09-02 17:00:47 -07:00
// This class handles the Account Lockout Policy settings.
2016-11-24 15:47:41 -05:00
import Parse from 'parse/node' ;
2016-09-02 17:00:47 -07:00
export class AccountLockout {
constructor ( user , config ) {
this . _user = user ;
this . _config = config ;
}
/ * *
* set _failed _login _count to value
* /
_setFailedLoginCount ( value ) {
let query = {
2016-11-24 15:47:41 -05:00
username : this . _user . username
2016-09-02 17:00:47 -07:00
} ;
const updateFields = {
_failed _login _count : value
} ;
return this . _config . database . update ( '_User' , query , updateFields ) ;
}
/ * *
* check if the _failed _login _count field has been set
* /
_isFailedLoginCountSet ( ) {
return new Promise ( ( resolve , reject ) => {
const query = {
username : this . _user . username ,
_failed _login _count : { $exists : true }
} ;
this . _config . database . find ( '_User' , query )
. then ( users => {
if ( Array . isArray ( users ) && users . length > 0 ) {
resolve ( true ) ;
} else {
resolve ( false ) ;
}
} )
. catch ( err => {
reject ( err ) ;
} ) ;
} ) ;
}
/ * *
* if _failed _login _count is NOT set then set it to 0
* else do nothing
* /
_initFailedLoginCount ( ) {
return new Promise ( ( resolve , reject ) => {
this . _isFailedLoginCountSet ( )
. then ( failedLoginCountIsSet => {
if ( ! failedLoginCountIsSet ) {
return this . _setFailedLoginCount ( 0 ) ;
} else {
return Promise . resolve ( ) ;
}
} )
. then ( ( ) => {
resolve ( ) ;
} )
. catch ( err => {
reject ( err ) ;
} ) ;
} ) ;
}
/ * *
* increment _failed _login _count by 1
* /
_incrementFailedLoginCount ( ) {
const query = {
2016-11-24 15:47:41 -05:00
username : this . _user . username
2016-09-02 17:00:47 -07:00
} ;
const updateFields = { _failed _login _count : { _ _op : 'Increment' , amount : 1 } } ;
return this . _config . database . update ( '_User' , query , updateFields ) ;
}
/ * *
* if the failed login count is greater than the threshold
* then sets lockout expiration to 'currenttime + accountPolicy.duration' , i . e . , account is locked out for the next 'accountPolicy.duration' minutes
* else do nothing
* /
_setLockoutExpiration ( ) {
return new Promise ( ( resolve , reject ) => {
const query = {
username : this . _user . username ,
2016-11-24 15:47:41 -05:00
_failed _login _count : { $gte : this . _config . accountLockout . threshold }
2016-09-02 17:00:47 -07:00
} ;
const now = new Date ( ) ;
const updateFields = {
_account _lockout _expires _at : Parse . _encode ( new Date ( now . getTime ( ) + this . _config . accountLockout . duration * 60 * 1000 ) )
} ;
this . _config . database . update ( '_User' , query , updateFields )
. then ( ( ) => {
resolve ( ) ;
} )
. catch ( err => {
if ( err && err . code && err . message && err . code === 101 && err . message === 'Object not found.' ) {
resolve ( ) ; // nothing to update so we are good
} else {
reject ( err ) ; // unknown error
}
} ) ;
} ) ;
}
/ * *
* if _account _lockout _expires _at > current _time and _failed _login _count > threshold
* reject with account locked error
* else
* resolve
* /
_notLocked ( ) {
return new Promise ( ( resolve , reject ) => {
const query = {
username : this . _user . username ,
_account _lockout _expires _at : { $gt : Parse . _encode ( new Date ( ) ) } ,
_failed _login _count : { $gte : this . _config . accountLockout . threshold }
} ;
this . _config . database . find ( '_User' , query )
. then ( users => {
if ( Array . isArray ( users ) && users . length > 0 ) {
reject ( new Parse . Error ( Parse . Error . OBJECT _NOT _FOUND , 'Your account is locked due to multiple failed login attempts. Please try again after ' + this . _config . accountLockout . duration + ' minute(s)' ) ) ;
} else {
resolve ( ) ;
}
} )
. catch ( err => {
reject ( err ) ;
} ) ;
} ) ;
}
/ * *
* set and / or increment _failed _login _count
* if _failed _login _count > threshold
* set the _account _lockout _expires _at to current _time + accountPolicy . duration
* else
* do nothing
* /
_handleFailedLoginAttempt ( ) {
return new Promise ( ( resolve , reject ) => {
this . _initFailedLoginCount ( )
. then ( ( ) => {
return this . _incrementFailedLoginCount ( ) ;
} )
. then ( ( ) => {
return this . _setLockoutExpiration ( ) ;
} )
. then ( ( ) => {
resolve ( ) ;
} )
. catch ( err => {
reject ( err ) ;
} ) ;
} ) ;
}
/ * *
* handle login attempt if the Account Lockout Policy is enabled
* /
handleLoginAttempt ( loginSuccessful ) {
if ( ! this . _config . accountLockout ) {
return Promise . resolve ( ) ;
}
return new Promise ( ( resolve , reject ) => {
this . _notLocked ( )
. then ( ( ) => {
if ( loginSuccessful ) {
return this . _setFailedLoginCount ( 0 ) ;
} else {
return this . _handleFailedLoginAttempt ( ) ;
}
} )
. then ( ( ) => {
resolve ( ) ;
} )
. catch ( err => {
reject ( err ) ;
} ) ;
} ) ;
}
}
export default AccountLockout ;