we are using an auth script to lock user accounts on multiple invalid login attempts...
attributes:
invalid_login_count_attribute : oxCountInvalidLogin
login_unlock_time_millis : oxUnlockTimeMillis
maximum_invalid_login_attempts : 3
maximum_daily_login_lockouts : 3
daily_lockout_count_attribute : oxCountDailyLockout
login_lockout_time_millis_1 : 60000
login_lockout_time_millis_2 : 180000
login_lockout_time_millis_3 : 300000
daily_lock_period_end_millis : oxDailyLockEndMillis
exempt_user : gridadmin
Script Content:
# oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
# Copyright (c) 2016, Gluu
#
# Author: Yuriy Movchan
#
# Added timeout functionality - Emily Hennessy
from org.jboss.seam.security import Identity
from org.jboss.seam.faces import FacesMessages
from org.jboss.seam.international import StatusMessage
from org.jboss.seam.contexts import Context, Contexts
from org.xdi.model.custom.script.type.auth import PersonAuthenticationType
from org.xdi.oxauth.service import UserService
from org.xdi.service import MailService
from org.xdi.util import StringHelper
from org.gluu.site.ldap.persistence.exception import AuthenticationException
import java
from java.lang import System as javasystem
class PersonAuthentication(PersonAuthenticationType):
def __init__(self, currentTimeMillis):
self.currentTimeMillis = currentTimeMillis
def init(self, configurationAttributes):
print "TimeoutLockAccount. Init"
# ox attributes
self.invalidLoginCountAttribute = "oxCountInvalidLogin"
if configurationAttributes.containsKey("invalid_login_count_attribute"):
self.invalidLoginCountAttribute = configurationAttributes.get("invalid_login_count_attribute").getValue2()
else:
print "TimeoutLockAccount. Init. Using default invalid login count attribute"
self.loginUnlockTimeMillis = "oxUnlockTimeMillis"
if configurationAttributes.containsKey("login_unlock_time_millis"):
self.loginUnlockTimeMillis = configurationAttributes.get("login_unlock_time_millis").getValue2()
else:
print "TimeoutLockAccount. Init. Using default unlock time attribute"
self.dailyLockoutCountAttribute = "oxCountDailyLockout"
if configurationAttributes.containsKey("daily_lockout_count_attribute"):
self.dailyLockoutCountAttribute = configurationAttributes.get("daily_lockout_count_attribute").getValue2()
else:
print "TimeoutLockAccount. Init. Using default daily lockout attribute."
self.dailyLockPeriodEndMillis = "oxDailyLockEndMillis"
if configurationAttributes.containsKey("daily_lock_period_end_millis"):
self.dailyLockPeriodEndMillis = configurationAttributes.get("daily_lock_period_end_millis").getValue2()
else:
print "TimeoutLockAccount. Init. Using default daily lock end period attribute."
print "TimeoutLockAccount. Init success. invalid_login_count_attribute: '%s', login_unlock_time_millis: '%s', daily_lockout_count_attribute: '%s', daily_lock_period_end_millis: '%s'" % (
self.invalidLoginCountAttribute, self.loginUnlockTimeMillis, self.dailyLockoutCountAttribute,
self.dailyLockPeriodEndMillis)
# script custom maximum properties
self.maximumInvalidLoginAttempts = 3
if configurationAttributes.containsKey("maximum_invalid_login_attempts"):
self.maximumInvalidLoginAttempts = StringHelper.toInteger(
configurationAttributes.get("maximum_invalid_login_attempts").getValue2())
else:
print "TimeoutLockAccount. Init. Using default number attempts"
self.maximumDailyLoginLockouts = 3
if configurationAttributes.containsKey("maximum_daily_login_lockouts"):
self.maximumDailyLoginLockouts = StringHelper.toInteger(
configurationAttributes.get("maximum_daily_login_lockouts").getValue2())
else:
print "TimeoutLockAccount. Init. Using default number lockouts"
print "TimeoutLockAccount. Init success. maximum_invalid_login_attempts: '%s', maximum_daily_login_lockouts: '%s'" % (
self.maximumInvalidLoginAttempts, self.maximumDailyLoginLockouts)
# script custom lockout time properties
self.loginLockoutTimeMillis1 = 60000
if configurationAttributes.containsKey("login_lockout_time_millis_1"):
self.loginLockoutTimeMillis1 = StringHelper.toInteger(
configurationAttributes.get("login_lockout_time_millis_1").getValue2())
else:
print "TimeoutLockAccount. Init. Using default lockout time 1"
self.loginLockoutTimeMillis2 = 180000
if configurationAttributes.containsKey("login_lockout_time_millis_2"):
self.loginLockoutTimeMillis2 = StringHelper.toInteger(
configurationAttributes.get("login_lockout_time_millis_2").getValue2())
else:
print "TimeoutLockAccount. Init. Using default lockout time 2"
self.loginLockoutTimeMillis3 = 300000
if configurationAttributes.containsKey("login_lockout_time_millis_3"):
self.loginLockoutTimeMillis3 = StringHelper.toInteger(
configurationAttributes.get("login_lockout_time_millis_3").getValue2())
else:
print "TimeoutLockAccount. Init. Using default lockout time 3"
print "TimeoutLockAccount. Init success. login_lockout_time_millis_1: '%s', login_lockout_time_millis_2: '%s', login_lockout_time_millis_3: '%s'" % (
self.loginLockoutTimeMillis1, self.loginLockoutTimeMillis2, self.loginLockoutTimeMillis3)
# user exempt from lockout
self.exemptUser = None
if configurationAttributes.containsKey("exempt_user"):
self.exemptUser = configurationAttributes.get("exempt_user").getValue2()
else:
print "TimeoutLockAccount. Init. No exempt users."
return True
def destroy(self, configurationAttributes):
print "TimeoutLockAccount. Destroy"
print "TimeoutLockAccount. Destroy success"
return True
def getApiVersion(self):
return 1
def isValidAuthenticationMethod(self, usageType, configurationAttributes):
return True
def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes):
return None
# noinspection PyTypeChecker
def authenticate(self, configurationAttributes, requestParameters, step):
if step == 1:
print "TimeoutLockAccount. Authenticate for step 1"
credentials = Identity.instance().getCredentials()
user_name = credentials.getUsername()
user_password = credentials.getPassword()
# exempt user?
if self.exemptUser != None and self.exemptUser == user_name:
return self.login(user_name, user_password)
# is user over max daily lockouts?
# get lockout count
daily_lockout_count = StringHelper.toInteger(
self.getUserAttributeValue(user_name, self.dailyLockoutCountAttribute), 0)
# check if user perm locked
if self.isPermanentlyLockedOut(user_name, daily_lockout_count):
print "TimeoutLockAccount. Authenticate. User '%s' is permanently locked out." % user_name
return False
# is user currently locked out?
# get current time & unlock time
current_time_millis = javasystem.currentTimeMillis()
unlock_time_millis = self.getUserAttributeValue(user_name, self.loginUnlockTimeMillis)
# check unlock time for type conversion
if unlock_time_millis == None:
unlock_time_millis = 0
else:
unlock_time_millis = long(unlock_time_millis)
# check if user locked
if self.isLockedOut(user_name, current_time_millis, unlock_time_millis):
return False
# was user previously locked out?
# get invalid login count
invalid_login_count = StringHelper.toInteger(self.getUserAttributeValue(user_name,
self.invalidLoginCountAttribute), 0)
# check prev locked
if unlock_time_millis != 0:
invalid_login_count = 0
unlock_time_millis = 0
self.setUserAttributeValue(user_name, self.loginUnlockTimeMillis, StringHelper.toString(0))
self.setUserAttributeValue(user_name, self.invalidLoginCountAttribute, StringHelper.toString(0))
self.toggleUserLock(user_name, False)
# finally attempt to login
logged_in = self.login(user_name, user_password)
# authentication failed, determine status
if (not logged_in):
# if user hasn't reached lock point
if invalid_login_count < self.maximumInvalidLoginAttempts:
invalid_login_count += 1
self.setUserAttributeValue(user_name, self.invalidLoginCountAttribute,
StringHelper.toString(invalid_login_count))
# user has reached lock point (after 3rd lockout, user only has 1 try)
if invalid_login_count >= self.maximumInvalidLoginAttempts or daily_lockout_count == 3:
self.handleLockout(user_name, current_time_millis, daily_lockout_count)
return False
# successful login = reset invalid login attempts
self.setUserAttributeValue(user_name, self.invalidLoginCountAttribute, StringHelper.toString(0))
return True
else:
return False
def prepareForStep(self, configurationAttributes, requestParameters, step):
if step == 1:
print "TimeoutLockAccount. Prepare for Step 1"
return True
else:
return False
def getClientId(self):
context = Contexts.getEventContext()
if context is None:
return None
client_id = context.get("client_id")
if client_id is None:
return None
return client_id
def getExtraParametersForStep(self, configurationAttributes, step):
return None
def getCountAuthenticationSteps(self, configurationAttributes):
return 1
def getPageForStep(self, configurationAttributes, step):
if step == 1:
client_id = self.getClientId()
if (client_id is None):
return "/login.xhtml"
if (client_id == "@!828C.076E.9EF6.A8B2!0001!D8E0.38DF!0008!1C25.9BCC"):
return "/admin-login.xhtml"
if (client_id == "@!828C.076E.9EF6.A8B2!0001!D8E0.38DF!0008!2844.345E"):
return "/dcxm-login.xhtml"
if (client_id == "@!828C.076E.9EF6.A8B2!0001!D8E0.38DF!0008!DE82.6066"):
return "/patient-login.xhtml"
return "/login.xhtml"
else:
return ""
def logout(self, configurationAttributes, requestParameters):
return True
# determine if permanently locked out
def isPermanentlyLockedOut(self, user_name, daily_lockout_count):
if daily_lockout_count > self.maximumDailyLoginLockouts:
self.displayLockoutError(True)
return True
return False
def isLockedOut(self, user_name, current_millis, unlock_millis):
print "TimeoutLockAccount. IsLockedOut. Comparing current time: '%s' to unlock time: '%s'." % (
str(current_millis), str(unlock_millis))
# check if user currently locked out
if unlock_millis != 0 and current_millis <= unlock_millis:
print "TimeoutLockAccount. IsLockedOut. User '%s' is currently locked out." % user_name
self.displayLockoutError(False)
return True
return False
def login(self, user_name, user_password):
login_success = False
if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)):
userService = UserService.instance()
try:
login_success = userService.authenticate(user_name, user_password)
except AuthenticationException:
print "TimeoutLockAccount. Login. Failed to authenticate user '%s'" % user_name
return login_success
def handleLockout(self, user_name, current_millis, lockout_count):
perm_lock = False
# get end of lockout period & type check
period_end_millis = self.getUserAttributeValue(user_name, self.dailyLockPeriodEndMillis)
if period_end_millis == None:
period_end_millis = 0
else:
period_end_millis = long(period_end_millis)
# set lock period end on first lockout
if (lockout_count == 0):
self.setDailyLockPeriodEnd(user_name, current_millis)
# check if need to reset lockout
elif (lockout_count <= self.maximumDailyLoginLockouts) and self.isDailyLockPeriodExpired(
current_millis, period_end_millis):
lockout_count = 0
self.setDailyLockPeriodEnd(user_name, current_millis)
# increment daily lockout
lockout_count += 1
self.setUserAttributeValue(user_name, self.dailyLockoutCountAttribute,
StringHelper.toString(lockout_count))
# increment unlock time
loginUnlockTimeMillisString = str(self.getUnlockTime(current_millis, lockout_count))
self.setUserAttributeValue(user_name, self.loginUnlockTimeMillis, loginUnlockTimeMillisString)
# lock user
self.toggleUserLock(user_name, True)
if lockout_count > self.maximumDailyLoginLockouts:
perm_lock = True
self.sendLockoutEmail(user_name)
self.displayLockoutError(perm_lock)
return
# sets the daily lock period to 24 hrs after first lockout
def setDailyLockPeriodEnd(self, user_name, current_millis):
dailyLockPeriodEndMillisLong = current_millis + 86400000
dailyLockPeriodEndMillisString = str(dailyLockPeriodEndMillisLong)
self.setUserAttributeValue(user_name, self.dailyLockPeriodEndMillis, dailyLockPeriodEndMillisString)
return
# determines if the 24 hr daily lock period has ended
def isDailyLockPeriodExpired(self, current_millis, daily_lock_period_end_millis):
if current_millis <= daily_lock_period_end_millis:
return False
return True
# returns unlock time based on current time + appropriate lockout time
def getUnlockTime(self, current_millis, daily_lockout_count):
lockout_time = 0
if daily_lockout_count == 1:
lockout_time = self.loginLockoutTimeMillis1
elif daily_lockout_count == 2:
lockout_time = self.loginLockoutTimeMillis2
elif daily_lockout_count == 3:
lockout_time = self.loginLockoutTimeMillis3
unlockTimeLong = current_millis + long(lockout_time)
return unlockTimeLong
# sends email to inform user they are permanently locked out
def sendLockoutEmail(self, user_name):
email_address = self.getUserAttributeValue(user_name, "mail")
if email_address == None:
print "TimeoutLockAccount. SendEmail. No user email."
return
print "TimeoutLockAccount. SendEmail. Sending email."
mailService = MailService.instance()
subject = "Vitality Account Locked"
body = "You are permanently locked out of Vitality. Please contact your system administrator to unlock your account."
mailService.sendMail(email_address, subject, body)
return
# display lockout error
def displayLockoutError(self, is_perm_lock):
error_msg = "You are currently locked out. Please try again later."
if is_perm_lock:
error_msg = "You are permanently locked out. Please contact your system administrator."
facesMessages = FacesMessages.instance()
facesMessages.add(StatusMessage.Severity.ERROR, error_msg, None)
# get a custom user attribute
def getUserAttributeValue(self, user_name, attribute_name):
if StringHelper.isEmpty(user_name):
return None
userService = UserService.instance()
find_user_by_uid = userService.getUser(user_name, attribute_name)
if find_user_by_uid == None:
print "TimeoutLockAccount. GetUserAttributeValue. User '%s' for attribute '%s' not found." % (
user_name, attribute_name)
return None
custom_attribute_value = userService.getCustomAttribute(find_user_by_uid, attribute_name)
if custom_attribute_value == None:
return None
attribute_value = custom_attribute_value.getValue()
print "TimeoutLockAccount. GetUserAttributeValue. User's '%s' attribute '%s' = '%s'" % (
user_name, attribute_name, attribute_value)
return attribute_value
# set a custom user attribute
def setUserAttributeValue(self, user_name, attribute_name, attribute_value):
if StringHelper.isEmpty(user_name):
return None
userService = UserService.instance()
find_user_by_uid = userService.getUser(user_name)
if find_user_by_uid == None:
print "TimeoutLockAccount. SetUserAttributeValue. User '%s' for attribute '%s' not found." % (
user_name, attribute_name)
return None
userService.setCustomAttribute(find_user_by_uid, attribute_name, attribute_value)
updated_user = userService.updateUser(find_user_by_uid)
print "TimeoutLockAccount. SetUserAttributeValue. User's '%s' attribute '%s' = '%s'" % (
user_name, attribute_name, attribute_value)
return updated_user
# changes user status to active or inactive based on it unlocking or locking
def toggleUserLock(self, user_name, locking):
# determine vars by locking/unlocking
actionString = "unlock"
statusString = "active"
if (locking):
actionString = "lock"
statusString = "inactive"
if StringHelper.isEmpty(user_name):
return None
userService = UserService.instance()
find_user_by_uid = userService.getUser(user_name)
if (find_user_by_uid == None):
return None
status_attribute_value = userService.getCustomAttribute(find_user_by_uid, "gluuStatus")
if status_attribute_value != None:
user_status = status_attribute_value.getValue()
if StringHelper.equals(user_status, statusString):
print "TimeoutLockAccount. ToggleUserLock. User '%s' '%s'ed already" % (user_name, actionString)
return
userService.setCustomAttribute(find_user_by_uid, "gluuStatus", statusString)
updated_user = userService.updateUser(find_user_by_uid)
print "TimeoutLockAccount. ToggleUserLock. User '%s' '%s'ed" % (user_name, actionString)
We are using OpenID Connect as the protocol to redirect from Application to Gluu