Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Genesys PGR
App Blocks
Commits
3541f7ae
Commit
3541f7ae
authored
May 27, 2017
by
Matija Obreza
Browse files
Account lockout manager
parent
f52fec7d
Changes
3
Hide whitespace changes
Inline
Side-by-side
security/src/main/java/org/genesys/blocks/security/lockout/AccountLockoutManager.java
0 → 100644
View file @
3541f7ae
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.genesys.blocks.security.lockout
;
import
java.io.Serializable
;
import
java.util.ArrayList
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Map
;
import
javax.annotation.Resource
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.genesys.blocks.security.model.BasicUser
;
import
org.genesys.blocks.security.service.BasicUserService
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Component
;
/**
* {#link {@link AccountLockoutManager} keeps track of successive failed login attempts and locks the user account if
* there are more than { {@link #lockAfterXFailures} successive failures.
*
* @author Matija Obreza, matija.obreza@croptrust.org
*
*/
@Component
public
class
AccountLockoutManager
{
private
final
Log
_log
=
LogFactory
.
getLog
(
getClass
());
@Resource
(
name
=
"accountLockoutMap"
)
private
Map
<
String
,
AttemptStatistics
>
loginAttempts
;
@Autowired
private
BasicUserService
<?,
?>
userService
;
private
int
lockAfterXFailures
=
5
;
// Things older than 60minutes=60*60*1000
private
int
lockoutTime
=
60
*
60
*
1000
;
/**
* Set account lockout time
*
* @param lockoutTime
*/
public
void
setLockoutTime
(
int
lockoutTime
)
{
this
.
lockoutTime
=
lockoutTime
;
}
/**
* Set number of successive failed login attempts that result in account lockout
*
* @param lockAfterXFailures
*/
public
void
setLockAfterXFailures
(
int
lockAfterXFailures
)
{
_log
.
info
(
"Will lock user accounts after "
+
lockAfterXFailures
+
" successive failed attempts."
);
this
.
lockAfterXFailures
=
lockAfterXFailures
;
}
/**
* Reset failed attempt statistics on successful login
*
* @param userName
*/
synchronized
void
handleSuccessfulLogin
(
String
userName
)
{
purge
();
if
(
loginAttempts
.
containsKey
(
userName
))
{
final
AttemptStatistics
stats
=
loginAttempts
.
get
(
userName
);
loginAttempts
.
remove
(
userName
);
_log
.
info
(
"Successful login. Removed failed login statistics for "
+
userName
+
" "
+
stats
);
}
}
/**
* Update failed attempt statistics on failed login
*
* @param userName
*/
synchronized
void
handleFailedLogin
(
String
userName
)
{
purge
();
AttemptStatistics
stats
=
null
;
if
(
loginAttempts
.
containsKey
(
userName
))
{
stats
=
loginAttempts
.
get
(
userName
);
}
else
{
final
BasicUser
<?>
user
=
userService
.
getUserByEmail
(
userName
);
if
(
user
!=
null
)
{
stats
=
new
AttemptStatistics
();
stats
.
id
=
user
.
getId
();
loginAttempts
.
put
(
userName
,
stats
);
}
else
{
if
(
_log
.
isDebugEnabled
())
{
_log
.
debug
(
"No such user username="
+
userName
);
}
}
}
if
(
stats
!=
null
)
{
stats
.
count
++;
stats
.
lastAttempt
=
new
Date
();
loginAttempts
.
put
(
userName
,
stats
);
_log
.
info
(
"Updated failed login statistics for username="
+
userName
+
" "
+
stats
);
if
(
stats
.
count
>=
lockAfterXFailures
)
{
_log
.
warn
(
"Too many failed login attempts. Locking account for username="
+
userName
);
userService
.
setAccountLockLocal
(
stats
.
id
,
true
);
}
}
}
/**
* Removes expired statistics
*/
synchronized
void
purge
()
{
if
(
loginAttempts
.
size
()
==
0
)
{
return
;
}
if
(
_log
.
isDebugEnabled
())
{
_log
.
debug
(
"Purging expired entries"
);
}
final
List
<
String
>
userNames
=
new
ArrayList
<
String
>(
loginAttempts
.
keySet
());
final
long
now
=
new
Date
().
getTime
();
for
(
final
String
userName
:
userNames
)
{
final
AttemptStatistics
stats
=
loginAttempts
.
get
(
userName
);
if
(
stats
==
null
)
{
loginAttempts
.
remove
(
userName
);
continue
;
}
if
(
now
-
stats
.
lastAttempt
.
getTime
()
>=
lockoutTime
)
{
loginAttempts
.
remove
(
userName
);
_log
.
info
(
"Removed expired failed login statistics for "
+
userName
+
" "
+
stats
);
}
}
if
(
_log
.
isDebugEnabled
())
{
_log
.
debug
(
"Number of failed login attempts in memory: "
+
loginAttempts
.
size
());
}
}
public
static
class
AttemptStatistics
implements
Serializable
{
private
static
final
long
serialVersionUID
=
-
5966606439944355735L
;
long
id
;
int
count
=
0
;
Date
lastAttempt
=
new
Date
();
@Override
public
String
toString
()
{
return
"count="
+
count
+
" lastAttempt="
+
lastAttempt
;
}
}
}
security/src/main/java/org/genesys/blocks/security/lockout/AuthenticationFailureBadCredentialsListener.java
0 → 100644
View file @
3541f7ae
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.genesys.blocks.security.lockout
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.ApplicationListener
;
import
org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent
;
import
org.springframework.security.web.authentication.WebAuthenticationDetails
;
import
org.springframework.stereotype.Component
;
/**
* Log failed login attempt and notify {@link AccountLockoutManager}.
*
* @author Matija Obreza, matija.obreza@croptrust.org
*/
@Component
public
class
AuthenticationFailureBadCredentialsListener
implements
ApplicationListener
<
AuthenticationFailureBadCredentialsEvent
>
{
private
final
Log
_log
=
LogFactory
.
getLog
(
getClass
());
@Autowired
private
AccountLockoutManager
lockoutManager
;
@Override
public
void
onApplicationEvent
(
AuthenticationFailureBadCredentialsEvent
event
)
{
final
String
userName
=
(
String
)
event
.
getAuthentication
().
getPrincipal
();
final
Object
details
=
event
.
getAuthentication
().
getDetails
();
if
(
details
!=
null
&&
details
instanceof
WebAuthenticationDetails
)
{
final
WebAuthenticationDetails
wad
=
(
WebAuthenticationDetails
)
details
;
// This can be picked up by fail2ban http://www.fail2ban.org/
_log
.
warn
(
"Failed login attempt for username="
+
userName
+
" from IP="
+
wad
.
getRemoteAddress
());
}
lockoutManager
.
handleFailedLogin
(
userName
);
}
}
security/src/main/java/org/genesys/blocks/security/lockout/AuthenticationSuccessListener.java
0 → 100644
View file @
3541f7ae
/*
* Copyright 2017 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.genesys.blocks.security.lockout
;
import
org.apache.commons.logging.Log
;
import
org.apache.commons.logging.LogFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.context.ApplicationListener
;
import
org.springframework.security.authentication.event.AuthenticationSuccessEvent
;
import
org.springframework.security.core.userdetails.User
;
import
org.springframework.security.web.authentication.WebAuthenticationDetails
;
import
org.springframework.stereotype.Component
;
/**
* Log successful login attempt and notify {@link AccountLockoutManager}.
*
* @author Matija Obreza, matija.obreza@croptrust.org
*/
@Component
public
class
AuthenticationSuccessListener
implements
ApplicationListener
<
AuthenticationSuccessEvent
>
{
private
final
Log
_log
=
LogFactory
.
getLog
(
getClass
());
@Autowired
private
AccountLockoutManager
lockoutManager
;
@Override
public
void
onApplicationEvent
(
AuthenticationSuccessEvent
event
)
{
final
Object
principal
=
event
.
getAuthentication
().
getPrincipal
();
String
userName
=
null
;
if
(
principal
instanceof
User
)
{
userName
=
((
User
)
principal
).
getUsername
();
}
else
if
(
principal
instanceof
org
.
springframework
.
security
.
core
.
userdetails
.
User
)
{
userName
=
((
org
.
springframework
.
security
.
core
.
userdetails
.
User
)
principal
).
getUsername
();
}
final
Object
details
=
event
.
getAuthentication
().
getDetails
();
if
(
details
!=
null
&&
details
instanceof
WebAuthenticationDetails
)
{
final
WebAuthenticationDetails
wad
=
(
WebAuthenticationDetails
)
details
;
// This can be picked up by fail2ban http://www.fail2ban.org/
_log
.
info
(
"Successful login attempt for username="
+
userName
+
" from IP="
+
wad
.
getRemoteAddress
());
}
lockoutManager
.
handleSuccessfulLogin
(
userName
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment