mirror of
https://github.com/dkmstr/openuds.git
synced 2025-03-22 14:50:29 +03:00
Advancing on new v2 actor
This commit is contained in:
parent
45b827e9e9
commit
e967d994b1
@ -237,6 +237,8 @@ class Actor(Handler):
|
||||
return 'ok'
|
||||
raise Exception('Unknown message {} for an user service without os manager'.format(message))
|
||||
res = osmanager.process(service, message, data, options={'scramble': False})
|
||||
if not res:
|
||||
raise Exception('Old Actors not supported by this os Manager!')
|
||||
except Exception as e:
|
||||
logger.exception("Exception processing from OS Manager")
|
||||
return Actor.result(str(e), ERR_OSMANAGER_ERROR)
|
||||
|
@ -34,13 +34,28 @@ import secrets
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from uds.models import getSqlDatetimeAsUnix, getSqlDatetime, ActorToken
|
||||
from uds.models import (
|
||||
getSqlDatetimeAsUnix,
|
||||
getSqlDatetime,
|
||||
ActorToken,
|
||||
UserService
|
||||
)
|
||||
|
||||
from uds.core import VERSION
|
||||
from ..handlers import Handler
|
||||
from uds.core.util.state import State
|
||||
from uds.core.util.cache import Cache
|
||||
from uds.core.util.config import GlobalConfig
|
||||
|
||||
from ..handlers import Handler, AccessDenied, RequestError
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core import osmanagers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ALLOWED_FAILS = 5
|
||||
|
||||
def actorResult(result: typing.Any = None, error: typing.Optional[str] = None) -> typing.MutableMapping[str, typing.Any]:
|
||||
result = result or ''
|
||||
res = {'result': result, 'stamp': getSqlDatetimeAsUnix()}
|
||||
@ -48,6 +63,19 @@ def actorResult(result: typing.Any = None, error: typing.Optional[str] = None) -
|
||||
res['error'] = error
|
||||
return res
|
||||
|
||||
def checkBlockedIp(ip: str)-> None:
|
||||
cache = Cache('actorv2')
|
||||
fails = cache.get(ip) or 0
|
||||
if fails > ALLOWED_FAILS:
|
||||
logger.info('Access to actor from %s is blocked for %s seconds since last fail', ip, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
raise Exception()
|
||||
|
||||
def incFailedIp(ip: str) -> None:
|
||||
cache = Cache('actorv2')
|
||||
fails = (cache.get(ip) or 0) + 1
|
||||
cache.put(ip, fails, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
|
||||
|
||||
# Enclosed methods under /actor path
|
||||
class ActorV2(Handler):
|
||||
"""
|
||||
@ -70,7 +98,7 @@ class ActorV2Action(Handler):
|
||||
path = 'actor/v2'
|
||||
|
||||
def get(self):
|
||||
return actorResult('')
|
||||
return actorResult(VERSION)
|
||||
|
||||
class ActorV2Register(ActorV2Action):
|
||||
"""
|
||||
@ -115,15 +143,92 @@ class ActorV2Initiialize(ActorV2Action):
|
||||
Information about machine action.
|
||||
Also returns the id used for the rest of the actions. (Only this one will use actor key)
|
||||
"""
|
||||
name = 'initiaize'
|
||||
name = 'initialize'
|
||||
|
||||
def post(self):
|
||||
def get(self) -> typing.MutableMapping[str, typing.Any]:
|
||||
"""
|
||||
Processes get requests. Basically checks if this is a "postThoughGet" for OpenGnsys or similar
|
||||
"""
|
||||
if self._args[0] == 'PostThoughGet':
|
||||
self._args = self._args[1:] # Remove first argument
|
||||
return self.post()
|
||||
|
||||
raise RequestError('Invalid request')
|
||||
|
||||
def post(self) -> typing.MutableMapping[str, typing.Any]:
|
||||
"""
|
||||
Initialize method expect a json POST with this fields:
|
||||
* version: str -> Actor version
|
||||
* token: str -> Valid Actor Token (if invalid, will return an error)
|
||||
* id: List[dict] -> List of dictionary containing id and mac:
|
||||
Will return on field "result" a dictinary with:
|
||||
* own_token: Optional[str] -> Personal uuid for the service (That, on service, will be used from now onwards). If None, there is no own_token
|
||||
* unique_id: Optional[str] -> If not None, unique id for the service
|
||||
* max_idle: Optional[int] -> If not None, max configured Idle for the vm
|
||||
* os: Optional[dict] -> Data returned by os manager for setting up this service.
|
||||
On error, will return Empty (None) result, and error field
|
||||
Example:
|
||||
{
|
||||
'version': '3.0',
|
||||
'token': 'asbdasdf',
|
||||
'maxIdle': 99999 or None,
|
||||
'id': [
|
||||
{
|
||||
'mac': 'xxxxx',
|
||||
'ip': 'vvvvvvvv'
|
||||
}, ...
|
||||
]
|
||||
}
|
||||
"""
|
||||
# First, validate token...
|
||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||
return actorResult('ok')
|
||||
try:
|
||||
checkBlockedIp(self._request.ip) # Raises an exception if ip is temporarily blocked
|
||||
ActorToken.objects.get(token=self._params['token']) # Not assigned, because only needs check
|
||||
# Valid actor token, now validate access allowed. That is, look for a valid mac from the ones provided.
|
||||
try:
|
||||
userService: UserService = next(
|
||||
iter(UserService.objects.filter(
|
||||
unique_id__in=[i['mac'] for i in self._params.get('id')[:5]],
|
||||
state__in=[State.USABLE, State.PREPARING]
|
||||
))
|
||||
)
|
||||
except Exception as e:
|
||||
logger.info('Unmanaged host request: %s, %s', self._params, e)
|
||||
return actorResult({
|
||||
'own_token': None,
|
||||
'max_idle': None,
|
||||
'unique_id': None,
|
||||
'os': None
|
||||
})
|
||||
|
||||
# Managed by UDS, get initialization data from osmanager and return it
|
||||
# Set last seen actor version
|
||||
userService.setProperty('actor_version', self._params['version'])
|
||||
maxIdle = None
|
||||
osData: typing.MutableMapping[str, typing.Any] = {}
|
||||
if userService.deployed_service.osmanager:
|
||||
osManager: 'osmanagers.OSManager' = userService.deployed_service.osmanager.getInstance()
|
||||
maxIdle = osManager.maxIdle()
|
||||
logger.debug('Max idle: %s', maxIdle)
|
||||
osData = osManager.actorData(userService)
|
||||
|
||||
return actorResult({
|
||||
'own_token': userService.uuid,
|
||||
'unique_id': userService.unique_id,
|
||||
'max_idle': maxIdle,
|
||||
'os': osData
|
||||
})
|
||||
except ActorToken.DoesNotExist:
|
||||
incFailedIp(self._request.ip) # For blocking attacks
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
raise AccessDenied('Access denied')
|
||||
|
||||
class ActorV2Login(ActorV2Action):
|
||||
"""
|
||||
Information about machine
|
||||
Notifies user logged out
|
||||
"""
|
||||
name = 'login'
|
||||
|
||||
@ -133,7 +238,7 @@ class ActorV2Login(ActorV2Action):
|
||||
|
||||
class ActorV2Logout(ActorV2Action):
|
||||
"""
|
||||
Information about machine
|
||||
Notifies user logged in
|
||||
"""
|
||||
name = 'logout'
|
||||
|
||||
@ -143,7 +248,7 @@ class ActorV2Logout(ActorV2Action):
|
||||
|
||||
class ActorV2Log(ActorV2Action):
|
||||
"""
|
||||
Information about machine
|
||||
Sends a log from the service
|
||||
"""
|
||||
name = 'log'
|
||||
|
||||
@ -153,7 +258,7 @@ class ActorV2Log(ActorV2Action):
|
||||
|
||||
class ActorV2IpChange(ActorV2Action):
|
||||
"""
|
||||
Information about machine
|
||||
Notifies an IP change
|
||||
"""
|
||||
name = 'ipchange'
|
||||
|
||||
@ -163,10 +268,20 @@ class ActorV2IpChange(ActorV2Action):
|
||||
|
||||
class ActorV2Ready(ActorV2Action):
|
||||
"""
|
||||
Information about machine
|
||||
Notifies the service is ready
|
||||
"""
|
||||
name = 'ready'
|
||||
|
||||
def post(self):
|
||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||
return actorResult('ok')
|
||||
|
||||
class ActorV2Ticket(ActorV2Action):
|
||||
"""
|
||||
Gets an stored ticket
|
||||
"""
|
||||
name = 'ticket'
|
||||
|
||||
def post(self):
|
||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||
return actorResult('ok')
|
||||
|
@ -95,14 +95,47 @@ class OSManager(Module):
|
||||
@return nothing
|
||||
"""
|
||||
|
||||
# These methods must be overriden
|
||||
def process(self, userService: 'UserService', message: str, data: typing.Any, options: typing.Optional[typing.Dict[str, typing.Any]] = None) -> str:
|
||||
"""
|
||||
This method must be overriden so your so manager can manage requests and responses from agent.
|
||||
@param userService: Service that sends the request (virtual machine or whatever)
|
||||
@param message: message to process (os manager dependent)
|
||||
@param data: Data for this message
|
||||
|
||||
Note: this method is deprecated and will be removed on a future release, when pre 3.0 actors support will be drop
|
||||
For now, this method will be kept on exising os managers for compatibility with old actors, but is not required for
|
||||
new os managers (that will only be available on actor 3.0) anymore
|
||||
"""
|
||||
return ''
|
||||
|
||||
# These methods must be overriden
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
"""
|
||||
This method provides information to actor, so actor can complete os configuration.
|
||||
Currently exists 3 types of os managers
|
||||
* rename vm and do NOT ADD to AD
|
||||
{
|
||||
'action': 'rename',
|
||||
'name': 'xxxxxx'
|
||||
}
|
||||
* rename vm and ADD to AD
|
||||
{
|
||||
'action': 'renameAD',
|
||||
'name': 'xxxxxxx',
|
||||
'ad': 'domain.xxx'
|
||||
'ou': 'ou' # or '' if default ou
|
||||
'username': 'userwithaddmachineperms@domain.xxxx'
|
||||
'password': 'passwordForTheUserWithPerms',
|
||||
}
|
||||
* rename vm, do NOT ADD to AD, and change password for an user
|
||||
{
|
||||
'action': 'rename_and_pw'
|
||||
'name': 'xxxxx'
|
||||
'username': 'username to change pass'
|
||||
'password': 'current password for username to change password'
|
||||
'newpassword': 'new password to be set for the username'
|
||||
}
|
||||
"""
|
||||
return {}
|
||||
|
||||
def checkState(self, userService: 'UserService') -> str:
|
||||
"""
|
||||
@ -240,6 +273,17 @@ class OSManager(Module):
|
||||
|
||||
log.useLog('logout', uniqueId, serviceIp, userName, knownUserIP, fullUserName, userService.friendly_name, userService.deployed_service.name)
|
||||
|
||||
def loginNotified(self, userService: 'UserService', userName: typing.Optional[str] = None) -> None:
|
||||
self.loggedIn(userService, userName)
|
||||
|
||||
def logoutNotified(self, userService: 'UserService', userName: typing.Optional[str] = None) -> None:
|
||||
self.loggedOut(userService, userName)
|
||||
|
||||
def readyNotified(self, userService: 'UserService') -> None:
|
||||
"""
|
||||
Invoked by actor v2 whenever a service is set as "ready"
|
||||
"""
|
||||
|
||||
def isPersistent(self) -> bool:
|
||||
"""
|
||||
When a publication if finished, old assigned machines will be removed if this value is True.
|
||||
|
@ -48,4 +48,4 @@ class ActorToken(models.Model):
|
||||
stamp = models.DateTimeField() # Date creation or validation of this entry
|
||||
|
||||
def __str__(self):
|
||||
return '<ActorToken {} created on {} by {} from {}/{}>'.format(self.token, self.stamp, self.username, self.ip_from, self.ip)
|
||||
return '<ActorToken {} created on {} by {} from {}/{}>'.format(self.token, self.stamp, self.username, self.hostname, self.ip_from)
|
||||
|
@ -45,7 +45,7 @@ class UUIDModel(models.Model):
|
||||
"""
|
||||
uuid = models.CharField(max_length=50, default=None, null=True, unique=True)
|
||||
|
||||
class Meta:
|
||||
class Meta: # pylint: disable=too-few-public-methods
|
||||
abstract = True
|
||||
|
||||
def genUuid(self) -> str:
|
||||
@ -53,7 +53,7 @@ class UUIDModel(models.Model):
|
||||
|
||||
# Override default save to add uuid
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
if self.uuid is None or self.uuid == '':
|
||||
if not self.uuid:
|
||||
self.uuid = self.genUuid()
|
||||
elif self.uuid != self.uuid.lower():
|
||||
self.uuid = self.uuid.lower() # If we modify uuid elsewhere, ensure that it's stored in lower case
|
||||
|
@ -143,6 +143,28 @@ class LinuxOsManager(osmanagers.OSManager):
|
||||
except Exception:
|
||||
log.doLog(service, log.ERROR, "do not understand {0}".format(data), origin)
|
||||
|
||||
# default "ready received" does nothing
|
||||
def readyReceived(self, userService, data):
|
||||
pass
|
||||
|
||||
def loginNotified(self, userService, userName=None):
|
||||
if '\\' not in userName:
|
||||
self.loggedIn(userService, userName)
|
||||
|
||||
def logoutNotified(self, userService, userName=None):
|
||||
self.loggedOut(userService, userName)
|
||||
if self.isRemovableOnLogout(userService):
|
||||
userService.release()
|
||||
|
||||
def readyNotified(self, userService):
|
||||
return
|
||||
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename',
|
||||
'name': userService.getName()
|
||||
}
|
||||
|
||||
def process(self, userService: 'UserService', message: str, data: typing.Any, options: typing.Optional[typing.Dict[str, typing.Any]] = None) -> str:
|
||||
"""
|
||||
We understand this messages:
|
||||
|
@ -30,6 +30,8 @@
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import random
|
||||
import string
|
||||
import logging
|
||||
import typing
|
||||
|
||||
@ -74,8 +76,6 @@ class LinuxRandomPassManager(LinuxOsManager):
|
||||
return username, password
|
||||
|
||||
def genPassword(self, service):
|
||||
import random
|
||||
import string
|
||||
randomPass = service.recoverValue('linOsRandomPass')
|
||||
if randomPass is None:
|
||||
randomPass = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16))
|
||||
@ -90,6 +90,15 @@ class LinuxRandomPassManager(LinuxOsManager):
|
||||
def infoValue(self, service):
|
||||
return 'rename\r{0}\t{1}\t\t{2}'.format(self.getName(service), self._userAccount, self.genPassword(service))
|
||||
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename_and_pw',
|
||||
'name': userService.getName(),
|
||||
'username': self._userAccount,
|
||||
'password': '', # On linux, user password is not needed so we provide an empty one
|
||||
'newpassword': self.genPassword(userService)
|
||||
}
|
||||
|
||||
def marshal(self) -> bytes:
|
||||
"""
|
||||
Serializes the os manager data so we can store it in database
|
||||
|
@ -109,9 +109,6 @@ class WindowsOsManager(osmanagers.OSManager):
|
||||
pass
|
||||
|
||||
def getName(self, userService: 'UserService') -> str:
|
||||
"""
|
||||
gets name from deployed
|
||||
"""
|
||||
return userService.getName()
|
||||
|
||||
def infoVal(self, userService: 'UserService') -> str:
|
||||
@ -153,6 +150,24 @@ class WindowsOsManager(osmanagers.OSManager):
|
||||
def readyReceived(self, userService, data):
|
||||
pass
|
||||
|
||||
def loginNotified(self, userService, userName=None):
|
||||
if '\\' not in userName:
|
||||
self.loggedIn(userService, userName)
|
||||
|
||||
def logoutNotified(self, userService, userName=None):
|
||||
self.loggedOut(userService, userName)
|
||||
if self.isRemovableOnLogout(userService):
|
||||
userService.release()
|
||||
|
||||
def readyNotified(self, userService):
|
||||
return
|
||||
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename',
|
||||
'name': userService.getName()
|
||||
}
|
||||
|
||||
def process(self, userService: 'UserService', message: str, data: typing.Any, options: typing.Optional[typing.Dict[str, typing.Any]] = None) -> str: # pylint: disable=too-many-branches
|
||||
"""
|
||||
We understand this messages:
|
||||
|
@ -156,9 +156,10 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
|
||||
raise ldaputil.LDAPError(_str)
|
||||
|
||||
def __getGroup(self, ldapConnection: typing.Any) -> str:
|
||||
def __getGroup(self, ldapConnection: typing.Any) -> typing.Optional[str]:
|
||||
base = ','.join(['DC=' + i for i in self._domain.split('.')])
|
||||
group = ldaputil.escape(self._group)
|
||||
obj: typing.Optional[typing.MutableMapping[str, typing.Any]]
|
||||
try:
|
||||
obj = next(ldaputil.getAsDict(ldapConnection, base, "(&(objectClass=group)(|(cn={0})(sAMAccountName={0})))".format(group), ['dn'], sizeLimit=50))
|
||||
except StopIteration:
|
||||
@ -176,6 +177,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
base = ','.join(['DC=' + i for i in self._domain.split('.')])
|
||||
|
||||
fltr = '(&(objectClass=computer)(sAMAccountName={}$))'.format(ldaputil.escape(machineName))
|
||||
obj: typing.Optional[typing.MutableMapping[str, typing.Any]]
|
||||
try:
|
||||
obj = next(ldaputil.getAsDict(ldapConnection, base, fltr, ['dn'], sizeLimit=50))
|
||||
except StopIteration:
|
||||
@ -186,7 +188,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
|
||||
return obj['dn'] # Returns the DN
|
||||
|
||||
def readyReceived(self, userService: 'UserService', data: str) -> None:
|
||||
def readyNotified(self, userService: 'UserService') -> None:
|
||||
# No group to add
|
||||
if self._group == '':
|
||||
return
|
||||
@ -227,7 +229,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
log.doLog(userService, log.WARN, error, log.OSMANAGER)
|
||||
logger.error(error)
|
||||
|
||||
def release(self, userService: 'UserSrevice') -> None:
|
||||
def release(self, userService: 'UserService') -> None:
|
||||
super().release(userService)
|
||||
|
||||
# If no removal requested, just return
|
||||
@ -266,7 +268,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
try:
|
||||
ldapConnection = self.__connectLdap()
|
||||
except ldaputil.LDAPError as e:
|
||||
return _('Check error: {}').format(self.__getLdapError(e))
|
||||
return _('Check error: {}').format(e)
|
||||
except dns.resolver.NXDOMAIN:
|
||||
return _('Could not find server parameters (_ldap._tcp.{0} can\'t be resolved)').format(self._domain)
|
||||
except Exception as e:
|
||||
@ -276,7 +278,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
try:
|
||||
ldapConnection.search_st(self._ou, ldap.SCOPE_BASE) # @UndefinedVariable
|
||||
except ldaputil.LDAPError as e:
|
||||
return _('Check error: {}').format(self.__getLdapError(e))
|
||||
return _('Check error: {}').format(e)
|
||||
|
||||
# Group
|
||||
if self._group != '':
|
||||
@ -289,14 +291,13 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
@staticmethod
|
||||
def test(env: 'Environment', data: typing.Dict[str, str]) -> typing.List[typing.Any]:
|
||||
logger.debug('Test invoked')
|
||||
wd: typing.Optional[WinDomainOsManager] = None
|
||||
try:
|
||||
wd = WinDomainOsManager(env, data)
|
||||
wd: WinDomainOsManager = WinDomainOsManager(env, data)
|
||||
logger.debug(wd)
|
||||
try:
|
||||
ldapConnection = wd.__connectLdap()
|
||||
except ldaputil.LDAPError as e:
|
||||
return [False, _('Could not access AD using LDAP ({0})').format(wd.__getLdapError(e))]
|
||||
return [False, _('Could not access AD using LDAP ({0})').format(e)]
|
||||
|
||||
ou = wd._ou
|
||||
if ou == '':
|
||||
@ -318,6 +319,16 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
|
||||
return [True, _("All parameters seem to work fine.")]
|
||||
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename_and_pw',
|
||||
'name': userService.getName(),
|
||||
'ad': self._domain,
|
||||
'ou': self._ou,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
}
|
||||
|
||||
def infoVal(self, userService: 'UserService') -> str:
|
||||
return 'domain:{0}\t{1}\t{2}\t{3}\t{4}'.format(self.getName(userService), self._domain, self._ou, self._account, self._password)
|
||||
|
||||
@ -354,7 +365,7 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
self._serverHint = values[7]
|
||||
else:
|
||||
self._serverHint = ''
|
||||
|
||||
|
||||
if values[0] == 'v4':
|
||||
self._ssl = values[8]
|
||||
self._removeOnExit = values[9]
|
||||
@ -373,5 +384,5 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
dct['grp'] = self._group
|
||||
dct['serverHint'] = self._serverHint
|
||||
dct['ssl'] = self._ssl == 'y'
|
||||
dct['removeOnExit'] = self._removeOnExit == 'y'
|
||||
dct['removeOnExit'] = self._removeOnExit == 'y'
|
||||
return dct
|
||||
|
@ -96,6 +96,15 @@ class WinRandomPassManager(WindowsOsManager):
|
||||
log.doLog(userService, log.INFO, "Password set to \"{}\"".format(randomPass), log.OSMANAGER)
|
||||
return randomPass
|
||||
|
||||
def actorData(self, userService: 'UserService') -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename_and_pw',
|
||||
'name': userService.getName(),
|
||||
'username': self._userAccount,
|
||||
'password': self._password,
|
||||
'newpassword': self.genPassword(userService)
|
||||
}
|
||||
|
||||
def infoVal(self, userService: 'UserService') -> str:
|
||||
return 'rename:{0}\t{1}\t{2}\t{3}'.format(self.getName(userService), self._userAccount, self._password, self.genPassword(userService))
|
||||
|
||||
|
@ -90,10 +90,10 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
|
||||
cache = Cache('auth')
|
||||
cacheKey = str(authenticator.id) + userName
|
||||
tries = cache.get(cacheKey)
|
||||
if tries is None:
|
||||
tries = 0
|
||||
if authenticator.getInstance().blockUserOnLoginFailures is True and tries >= GlobalConfig.MAX_LOGIN_TRIES.getInt():
|
||||
tries = cache.get(cacheKey) or 0
|
||||
triesByIp = cache.get(request.ip) or 0
|
||||
maxTries = GlobalConfig.MAX_LOGIN_TRIES.getInt()
|
||||
if (authenticator.getInstance().blockUserOnLoginFailures is True and (tries >= maxTries) or triesByIp >= maxTries):
|
||||
authLogLogin(request, authenticator, userName, 'Temporarily blocked')
|
||||
return (None, _('Too many authentication errrors. User temporarily blocked'))
|
||||
|
||||
@ -106,8 +106,8 @@ def checkLogin( # pylint: disable=too-many-branches, too-many-statements
|
||||
|
||||
if user is None:
|
||||
logger.debug("Invalid user %s (access denied)", userName)
|
||||
tries += 1
|
||||
cache.put(cacheKey, tries, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
cache.put(cacheKey, tries+1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
cache.put(request.ip, triesByIp+1, GlobalConfig.LOGIN_BLOCK.getInt())
|
||||
authLogLogin(request, authenticator, userName, 'Access denied (user not allowed by UDS)')
|
||||
return (None, _('Access denied'))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user