mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-23 17:34:17 +03:00
Refactorin exceptions, types, constants, ...
This commit is contained in:
parent
d4ea0ad8c6
commit
29ff0081a4
@ -112,34 +112,50 @@ class Login(Handler):
|
||||
raise AccessDenied('Too many fails')
|
||||
|
||||
try:
|
||||
if (
|
||||
'auth_id' not in self._params
|
||||
and 'authId' not in self._params
|
||||
and 'auth_id' not in self._params
|
||||
and 'authSmallName' not in self._params
|
||||
and 'authLabel' not in self._params
|
||||
and 'auth_label' not in self._params
|
||||
and 'auth' not in self._params
|
||||
# if (
|
||||
# 'auth_id' not in self._params
|
||||
# and 'authId' not in self._params
|
||||
# and 'auth_id' not in self._params
|
||||
# and 'authSmallName' not in self._params
|
||||
# and 'authLabel' not in self._params
|
||||
# and 'auth_label' not in self._params
|
||||
# and 'auth' not in self._params
|
||||
# ):
|
||||
# raise RequestError('Invalid parameters (no auth)')
|
||||
|
||||
# Check if we have a valid auth
|
||||
if not any(
|
||||
i in self._params
|
||||
for i in ('auth_id', 'authId', 'authSmallName', 'authLabel', 'auth_label', 'auth')
|
||||
):
|
||||
raise RequestError('Invalid parameters (no auth)')
|
||||
|
||||
scrambler: str = ''.join(
|
||||
random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(32)
|
||||
authId: typing.Optional[str] = self._params.get(
|
||||
'auth_id',
|
||||
self._params.get('authId', None), # Old compat, alias
|
||||
)
|
||||
authId: typing.Optional[str] = self._params.get('authId', self._params.get('auth_id', None))
|
||||
authLabel: typing.Optional[str] = self._params.get(
|
||||
'auth_label',
|
||||
self._params.get('authSmallName', None),
|
||||
self._params.get(
|
||||
'authSmallName', # Old compat name
|
||||
self._params.get('authLabel', None), # Old compat name
|
||||
),
|
||||
)
|
||||
authName: typing.Optional[str] = self._params.get('auth', None)
|
||||
platform: str = self._params.get('platform', self._request.os.os.value[0])
|
||||
|
||||
username: str
|
||||
password: str
|
||||
|
||||
username, password = self._params['username'], self._params['password']
|
||||
username: str = self._params['username']
|
||||
password: str = self._params['password']
|
||||
locale: str = self._params.get('locale', 'en')
|
||||
if authName == 'admin' or authLabel == 'admin' or authId == '00000000-0000-0000-0000-000000000000':
|
||||
|
||||
# Generate a random scrambler
|
||||
scrambler: str = CryptoManager.manager().randomString(32)
|
||||
if (
|
||||
authName == 'admin'
|
||||
or authLabel == 'admin'
|
||||
or authId == '00000000-0000-0000-0000-000000000000'
|
||||
or (not authId and not authName and not authLabel)
|
||||
):
|
||||
if GlobalConfig.SUPER_USER_LOGIN.get(True) == username and CryptoManager().checkHash(
|
||||
password, GlobalConfig.SUPER_USER_PASS.get(True)
|
||||
):
|
||||
@ -147,13 +163,6 @@ class Login(Handler):
|
||||
return Login.result(result='ok', token=self.getAuthToken())
|
||||
return Login.result(error='Invalid credentials')
|
||||
|
||||
# invalid login
|
||||
if (
|
||||
functools.reduce(lambda a, b: (a << 4) + b, [i for i in username.encode()])
|
||||
== 474216907296766572900491101513
|
||||
):
|
||||
return Login.result(result=bytes([i ^ 64 for i in b'\x13(%+(`-!`3()%2!+)`!..)']).decode())
|
||||
|
||||
# Will raise an exception if no auth found
|
||||
if authId:
|
||||
auth = Authenticator.objects.get(uuid=processUuid(authId))
|
||||
@ -162,8 +171,8 @@ class Login(Handler):
|
||||
else:
|
||||
auth = Authenticator.objects.get(small_name=authLabel)
|
||||
|
||||
if not password:
|
||||
password = 'xdaf44tgas4xd5ñasdłe4g€@#½|«ð2' # nosec: Extrange password if credential left empty. Value is not important, just not empty
|
||||
# No matter in fact the password, just not empty (so it can be encrypted, but will be invalid anyway)
|
||||
password = password or CryptoManager().randomString(32)
|
||||
|
||||
logger.debug('Auth obj: %s', auth)
|
||||
authResult = authenticate(username, password, auth, self._request, True)
|
||||
@ -218,19 +227,22 @@ class Auths(Handler):
|
||||
authenticated = False # By default, all handlers needs authentication
|
||||
|
||||
def auths(self) -> typing.Iterable[typing.Dict[str, typing.Any]]:
|
||||
paramAll: bool = self._params.get('all', 'false') == 'true'
|
||||
paramAll: bool = self._params.get('all', 'false').lower() == 'true'
|
||||
auth: Authenticator
|
||||
for auth in Authenticator.objects.all():
|
||||
theType = auth.getType()
|
||||
if paramAll or (theType.isCustom() is False and theType.typeType not in ('IP',)):
|
||||
yield {
|
||||
'authId': auth.uuid,
|
||||
'authId': auth.uuid, # Deprecated, use 'auth_id'
|
||||
'auth_id': auth.uuid,
|
||||
'authSmallName': str(auth.small_name), # Deprecated
|
||||
'authLabel': str(auth.small_name),
|
||||
'authLabel': str(auth.small_name), # Deprecated, use 'auth_label'
|
||||
'auth_label': str(auth.small_name),
|
||||
'auth': auth.name,
|
||||
'type': theType.typeType,
|
||||
'priority': auth.priority,
|
||||
'isCustom': theType.isCustom(),
|
||||
'isCustom': theType.isCustom(), # Deprecated, use 'custom'
|
||||
'custom': theType.isCustom(),
|
||||
}
|
||||
|
||||
def get(self) -> typing.List[typing.Dict[str, typing.Any]]:
|
||||
|
@ -200,7 +200,7 @@ class Services(DetailHandler): # pylint: disable=too-many-public-methods
|
||||
)
|
||||
) from e
|
||||
raise RequestError(_('Element already exists (duplicate key error)')) from e
|
||||
except exceptions.ValidationError as e:
|
||||
except exceptions.validation.ValidationError as e:
|
||||
if (
|
||||
not item and service
|
||||
): # Only remove partially saved element if creating new (if editing, ignore this)
|
||||
|
@ -39,14 +39,13 @@ from django.core.exceptions import ValidationError
|
||||
|
||||
from uds.core.util.state import State
|
||||
|
||||
from uds.core.auths.exceptions import AuthenticatorException
|
||||
from uds.core.auths.user import User as aUser
|
||||
from uds.core.util import log, ensure
|
||||
from uds.core.util.model import processUuid
|
||||
from uds.models import Authenticator, User, Group, ServicePool
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.REST import RequestError
|
||||
from uds.core.consts.images import DEFAULT_THUMB_BASE64
|
||||
from uds.core import consts, exceptions
|
||||
|
||||
from uds.REST.model import DetailHandler
|
||||
|
||||
@ -84,10 +83,9 @@ class Users(DetailHandler):
|
||||
|
||||
def getItems(self, parent: 'Model', item: typing.Optional[str]) -> typing.Any:
|
||||
parent = ensure.is_instance(parent, Authenticator)
|
||||
|
||||
# processes item to change uuid key for id
|
||||
def uuid_to_id(
|
||||
iterable: typing.Iterable[typing.Any] # will get values from a queryset
|
||||
):
|
||||
def uuid_to_id(iterable: typing.Iterable[typing.Any]): # will get values from a queryset
|
||||
for v in iterable:
|
||||
v['id'] = v['uuid']
|
||||
del v['uuid']
|
||||
@ -140,9 +138,7 @@ class Users(DetailHandler):
|
||||
)
|
||||
res['id'] = u.uuid
|
||||
res['role'] = (
|
||||
res['staff_member']
|
||||
and (res['is_admin'] and _('Admin') or _('Staff member'))
|
||||
or _('User')
|
||||
res['staff_member'] and (res['is_admin'] and _('Admin') or _('Staff member')) or _('User')
|
||||
)
|
||||
usr = aUser(u)
|
||||
res['groups'] = [g.dbGroup().uuid for g in usr.groups()]
|
||||
@ -155,9 +151,7 @@ class Users(DetailHandler):
|
||||
def getTitle(self, parent: 'Model') -> str:
|
||||
try:
|
||||
return _('Users of {0}').format(
|
||||
Authenticator.objects.get(
|
||||
uuid=processUuid(self._kwargs['parent_id'])
|
||||
).name
|
||||
Authenticator.objects.get(uuid=processUuid(self._kwargs['parent_id'])).name
|
||||
)
|
||||
except Exception:
|
||||
return _('Current users')
|
||||
@ -245,16 +239,12 @@ class Users(DetailHandler):
|
||||
if not auth.isExternalSource and not user.parent:
|
||||
groups = self.readFieldsFromParams(['groups'])['groups']
|
||||
# Save but skip meta groups, they are not real groups, but just a way to group users based on rules
|
||||
user.groups.set(
|
||||
g
|
||||
for g in parent.groups.filter(uuid__in=groups)
|
||||
if g.is_meta is False
|
||||
)
|
||||
user.groups.set(g for g in parent.groups.filter(uuid__in=groups) if g.is_meta is False)
|
||||
except User.DoesNotExist:
|
||||
raise self.invalidItemException() from None
|
||||
except IntegrityError: # Duplicate key probably
|
||||
raise RequestError(_('User already exists (duplicate key error)')) from None
|
||||
except AuthenticatorException as e:
|
||||
except exceptions.auth.AuthenticatorException as e:
|
||||
raise RequestError(str(e)) from e
|
||||
except ValidationError as e:
|
||||
raise RequestError(str(e.message)) from e
|
||||
@ -310,9 +300,7 @@ class Users(DetailHandler):
|
||||
{
|
||||
'id': i.uuid,
|
||||
'name': i.name,
|
||||
'thumb': i.image.thumb64
|
||||
if i.image is not None
|
||||
else DEFAULT_THUMB_BASE64,
|
||||
'thumb': i.image.thumb64 if i.image is not None else consts.images.DEFAULT_THUMB_BASE64,
|
||||
'user_services_count': i.userServices.exclude(
|
||||
state__in=(State.REMOVED, State.ERROR)
|
||||
).count(),
|
||||
@ -367,9 +355,7 @@ class Groups(DetailHandler):
|
||||
'meta_if_any': i.meta_if_any,
|
||||
}
|
||||
if i.is_meta:
|
||||
val['groups'] = list(
|
||||
x.uuid for x in i.groups.all().order_by('name')
|
||||
)
|
||||
val['groups'] = list(x.uuid for x in i.groups.all().order_by('name'))
|
||||
res.append(val)
|
||||
if multi:
|
||||
return res
|
||||
@ -475,9 +461,7 @@ class Groups(DetailHandler):
|
||||
if is_meta:
|
||||
# Do not allow to add meta groups to meta groups
|
||||
group.groups.set(
|
||||
i
|
||||
for i in parent.groups.filter(uuid__in=self._params['groups'])
|
||||
if i.is_meta is False
|
||||
i for i in parent.groups.filter(uuid__in=self._params['groups']) if i.is_meta is False
|
||||
)
|
||||
|
||||
if pools:
|
||||
@ -489,7 +473,7 @@ class Groups(DetailHandler):
|
||||
raise self.invalidItemException() from None
|
||||
except IntegrityError: # Duplicate key probably
|
||||
raise RequestError(_('User already exists (duplicate key error)')) from None
|
||||
except AuthenticatorException as e:
|
||||
except exceptions.auth.AuthenticatorException as e:
|
||||
raise RequestError(str(e)) from e
|
||||
except RequestError: # pylint: disable=try-except-raise
|
||||
raise # Re-raise
|
||||
@ -506,9 +490,7 @@ class Groups(DetailHandler):
|
||||
except Exception:
|
||||
raise self.invalidItemException() from None
|
||||
|
||||
def servicesPools(
|
||||
self, parent: 'Model', item: str
|
||||
) -> typing.List[typing.Mapping[str, typing.Any]]:
|
||||
def servicesPools(self, parent: 'Model', item: str) -> typing.List[typing.Mapping[str, typing.Any]]:
|
||||
parent = ensure.is_instance(parent, Authenticator)
|
||||
uuid = processUuid(item)
|
||||
group = parent.groups.get(uuid=processUuid(uuid))
|
||||
@ -518,9 +500,7 @@ class Groups(DetailHandler):
|
||||
{
|
||||
'id': i.uuid,
|
||||
'name': i.name,
|
||||
'thumb': i.image.thumb64
|
||||
if i.image is not None
|
||||
else DEFAULT_THUMB_BASE64,
|
||||
'thumb': i.image.thumb64 if i.image is not None else consts.images.DEFAULT_THUMB_BASE64,
|
||||
'user_services_count': i.userServices.exclude(
|
||||
state__in=(State.REMOVED, State.ERROR)
|
||||
).count(),
|
||||
@ -530,9 +510,7 @@ class Groups(DetailHandler):
|
||||
|
||||
return res
|
||||
|
||||
def users(
|
||||
self, parent: 'Model', item: str
|
||||
) -> typing.List[typing.Mapping[str, typing.Any]]:
|
||||
def users(self, parent: 'Model', item: str) -> typing.List[typing.Mapping[str, typing.Any]]:
|
||||
uuid = processUuid(item)
|
||||
parent = ensure.is_instance(parent, Authenticator)
|
||||
group = parent.groups.get(uuid=processUuid(uuid))
|
||||
|
@ -1156,7 +1156,7 @@ class ModelHandler(BaseModelHandler):
|
||||
raise exceptions.NotFound('Item not found') from None
|
||||
except IntegrityError: # Duplicate key probably
|
||||
raise exceptions.RequestError('Element already exists (duplicate key error)') from None
|
||||
except (exceptions.SaveException, udsExceptions.ValidationError) as e:
|
||||
except (exceptions.SaveException, udsexceptions.validation.ValidationError) as e:
|
||||
raise exceptions.RequestError(str(e)) from e
|
||||
except (exceptions.RequestError, exceptions.ResponseError):
|
||||
raise
|
||||
|
@ -104,12 +104,12 @@ class IPAuth(auths.Authenticator):
|
||||
credentials: str, # pylint: disable=unused-argument
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
# If credentials is a dict, that can't be sent directly from web interface, we allow entering
|
||||
if username == self.getIp(request):
|
||||
self.getGroups(username, groupsManager)
|
||||
return auths.SUCCESS_AUTH
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def isAccesibleFrom(self, request: 'ExtendedHttpRequest'):
|
||||
"""
|
||||
@ -127,15 +127,15 @@ class IPAuth(auths.Authenticator):
|
||||
credentials: str, # pylint: disable=unused-argument
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
# In fact, username does not matter, will get IP from request
|
||||
username = self.getIp(request) # Override provided username and use source IP
|
||||
self.getGroups(username, groupsManager)
|
||||
if groupsManager.hasValidGroups() and self.dbObj().isValidUser(
|
||||
username, True
|
||||
):
|
||||
return auths.SUCCESS_AUTH
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
@staticmethod
|
||||
def test(env, data): # pylint: disable=unused-argument
|
||||
|
@ -39,7 +39,7 @@ import dns.resolver
|
||||
import dns.reversename
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import auths, types
|
||||
from uds.core import auths, types, exceptions, consts
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.ui import gui
|
||||
@ -147,25 +147,25 @@ class InternalDBAuth(auths.Authenticator):
|
||||
credentials: str,
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
username = username.lower()
|
||||
dbAuth = self.dbObj()
|
||||
try:
|
||||
user: 'models.User' = dbAuth.users.get(name=username, state=State.ACTIVE)
|
||||
except Exception:
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid user')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
if user.parent: # Direct auth not allowed for "derived" users
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Internal Db Auth has its own groups. (That is, no external source). If a group is active it is valid
|
||||
if CryptoManager().checkHash(credentials, user.password):
|
||||
groupsManager.validate([g.name for g in user.groups.all()])
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid password')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def getGroups(self, username: str, groupsManager: 'auths.GroupsManager'):
|
||||
dbAuth = self.dbObj()
|
||||
|
@ -375,7 +375,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
def _processToken(
|
||||
self, userInfo: typing.Mapping[str, typing.Any], gm: 'auths.GroupsManager'
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
# After this point, we don't mind about the token, we only need to authenticate user
|
||||
# and get some basic info from it
|
||||
|
||||
@ -398,11 +398,11 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
# We don't mind about the token, we only need to authenticate user
|
||||
# and if we are here, the user is authenticated, so we can return SUCCESS_AUTH
|
||||
return auths.AuthenticationResult(auths.AuthenticationState.SUCCESS, username=username)
|
||||
return types.auth.AuthenticationResult(types.auth.AuthenticationState.SUCCESS, username=username)
|
||||
|
||||
def _processTokenOpenId(
|
||||
self, token_id: str, nonce: str, gm: 'auths.GroupsManager'
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
# Get token headers, to extract algorithm
|
||||
info = jwt.get_unverified_header(token_id)
|
||||
logger.debug('Token headers: %s', info)
|
||||
@ -428,19 +428,19 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error('Error decoding token: %s', e)
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# All keys tested, none worked
|
||||
logger.error('Invalid token received on OAuth2 callback')
|
||||
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None:
|
||||
if not values:
|
||||
return
|
||||
|
||||
if ' ' in values['name']:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('This kind of Authenticator does not support white spaces on field NAME')
|
||||
)
|
||||
|
||||
@ -449,9 +449,9 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
if self.responseType.value in ('code', 'pkce', 'openid+code'):
|
||||
if self.commonGroups.value.strip() == '':
|
||||
raise exceptions.ValidationError(gettext('Common groups is required for "code" response types'))
|
||||
raise exceptions.validation.ValidationError(gettext('Common groups is required for "code" response types'))
|
||||
if self.tokenEndpoint.value.strip() == '':
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('Token endpoint is required for "code" response types')
|
||||
)
|
||||
# infoEndpoint will not be necesary if the response of tokenEndpoint contains the user info
|
||||
@ -459,7 +459,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
if self.responseType.value == 'openid+token_id':
|
||||
# Ensure we have a public key
|
||||
if self.publicKey.value.strip() == '':
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('Public key is required for "openid+token_id" response type')
|
||||
)
|
||||
|
||||
@ -473,7 +473,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
match self.responseType.value:
|
||||
case 'code' | 'pkce':
|
||||
return self.authCallbackCode(parameters, gm, request)
|
||||
@ -491,8 +491,8 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
self,
|
||||
request: 'types.request.ExtendedHttpRequest', # pylint: disable=unused-argument
|
||||
username: str, # pylint: disable=unused-argument
|
||||
) -> auths.AuthenticationResult:
|
||||
return auths.SUCCESS_AUTH
|
||||
) -> types.auth.AuthenticationResult:
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
def getJavascript(self, request: 'HttpRequest') -> typing.Optional[str]:
|
||||
"""
|
||||
@ -517,7 +517,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""Process the callback for code authorization flow"""
|
||||
state = parameters.get_params.get('state', '')
|
||||
# Remove state from cache
|
||||
@ -526,13 +526,13 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
if not state or not code_verifier:
|
||||
logger.error('Invalid state received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Get the code
|
||||
code = parameters.get_params.get('code', '')
|
||||
if code == '':
|
||||
logger.error('Invalid code received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Restore code_verifier "none" to None
|
||||
if code_verifier == 'none':
|
||||
@ -546,7 +546,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""Process the callback for PKCE authorization flow"""
|
||||
state = parameters.get_params.get('state', '')
|
||||
stateValue = self.cache.get(state)
|
||||
@ -554,7 +554,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
if not state or not stateValue:
|
||||
logger.error('Invalid state received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Get the token, token_type, expires
|
||||
token = TokenInfo(
|
||||
@ -574,7 +574,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""Process the callback for OpenID authorization flow"""
|
||||
state = parameters.post_params.get('state', '')
|
||||
nonce = self.cache.get(state)
|
||||
@ -582,20 +582,20 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
if not state or not nonce:
|
||||
logger.error('Invalid state received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Get the code
|
||||
code = parameters.post_params.get('code', '')
|
||||
if code == '':
|
||||
logger.error('Invalid code received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Get the token, token_type, expires
|
||||
token = self._requestToken(code)
|
||||
|
||||
if not token.id_token:
|
||||
logger.error('No id_token received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
return self._processTokenOpenId(token.id_token, nonce, gm)
|
||||
|
||||
@ -604,7 +604,7 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""Process the callback for OpenID authorization flow"""
|
||||
state = parameters.post_params.get('state', '')
|
||||
nonce = self.cache.get(state)
|
||||
@ -612,12 +612,12 @@ class OAuth2Authenticator(auths.Authenticator):
|
||||
|
||||
if not state or not nonce:
|
||||
logger.error('Invalid state received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Get the id_token
|
||||
id_token = parameters.post_params.get('id_token', '')
|
||||
if id_token == '':
|
||||
logger.error('Invalid id_token received on OAuth2 callback')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
return self._processTokenOpenId(id_token, nonce, gm)
|
||||
|
@ -35,7 +35,7 @@ import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import auths, types
|
||||
from uds.core import auths, types, consts
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.ui import gui
|
||||
@ -146,7 +146,7 @@ class RadiusAuth(auths.Authenticator):
|
||||
credentials: str,
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
try:
|
||||
connection = self.radiusClient()
|
||||
groups, mfaCode, state = connection.authenticate(username=username, password=credentials, mfaField=self.mfaAttr.value.strip())
|
||||
@ -167,7 +167,7 @@ class RadiusAuth(auths.Authenticator):
|
||||
username,
|
||||
'Access denied by Raiuds',
|
||||
)
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
if self.globalGroup.value.strip():
|
||||
groups.append(self.globalGroup.value.strip())
|
||||
@ -179,7 +179,7 @@ class RadiusAuth(auths.Authenticator):
|
||||
# Validate groups
|
||||
groupsManager.validate(groups)
|
||||
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
def getGroups(self, username: str, groupsManager: 'auths.GroupsManager') -> None:
|
||||
with self.storage.map() as storage:
|
||||
|
@ -38,7 +38,7 @@ import typing
|
||||
import ldap
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import auths, exceptions, types
|
||||
from uds.core import auths, exceptions, types, consts
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import ldaputil, auth as auth_utils
|
||||
@ -61,7 +61,6 @@ LDAP_RESULT_LIMIT = 100
|
||||
|
||||
|
||||
class RegexLdap(auths.Authenticator):
|
||||
|
||||
host = gui.TextField(
|
||||
length=64,
|
||||
label=_('Host'),
|
||||
@ -80,9 +79,7 @@ class RegexLdap(auths.Authenticator):
|
||||
ssl = gui.CheckBoxField(
|
||||
label=_('Use SSL'),
|
||||
order=3,
|
||||
tooltip=_(
|
||||
'If checked, the connection will be ssl, using port 636 instead of 389'
|
||||
),
|
||||
tooltip=_('If checked, the connection will be ssl, using port 636 instead of 389'),
|
||||
)
|
||||
username = gui.TextField(
|
||||
length=64,
|
||||
@ -113,9 +110,7 @@ class RegexLdap(auths.Authenticator):
|
||||
label=_('Verify SSL'),
|
||||
default=True,
|
||||
order=11,
|
||||
tooltip=_(
|
||||
'If checked, SSL verification will be enforced. If not, SSL verification will be disabled'
|
||||
),
|
||||
tooltip=_('If checked, SSL verification will be enforced. If not, SSL verification will be disabled'),
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
certificate = gui.TextField(
|
||||
@ -184,9 +179,7 @@ class RegexLdap(auths.Authenticator):
|
||||
label=_('Alt. class'),
|
||||
default='',
|
||||
order=25,
|
||||
tooltip=_(
|
||||
'Class for LDAP objects that will be also checked for groups retrieval (normally empty)'
|
||||
),
|
||||
tooltip=_('Class for LDAP objects that will be also checked for groups retrieval (normally empty)'),
|
||||
required=False,
|
||||
tab=_('Advanced'),
|
||||
)
|
||||
@ -327,9 +320,9 @@ class RegexLdap(auths.Authenticator):
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
vals = data.decode('utf8').split('\t')
|
||||
|
||||
self._verifySsl = False # Backward compatibility
|
||||
self._mfaAttr = '' # Backward compatibility
|
||||
self._certificate = '' # Backward compatibility
|
||||
self._verifySsl = False # Backward compatibility
|
||||
self._mfaAttr = '' # Backward compatibility
|
||||
self._certificate = '' # Backward compatibility
|
||||
|
||||
# Common
|
||||
logger.debug('Common: %s', vals[1:11])
|
||||
@ -386,7 +379,7 @@ class RegexLdap(auths.Authenticator):
|
||||
@return: Connection established
|
||||
@raise exception: If connection could not be established
|
||||
"""
|
||||
if self._connection is None: # If connection is not established, try to connect
|
||||
if self._connection is None: # If connection is not established, try to connect
|
||||
self._connection = ldaputil.connection(
|
||||
self._username,
|
||||
self._password,
|
||||
@ -485,7 +478,7 @@ class RegexLdap(auths.Authenticator):
|
||||
credentials: str,
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
Must authenticate the user.
|
||||
We can have to different situations here:
|
||||
@ -500,21 +493,15 @@ class RegexLdap(auths.Authenticator):
|
||||
usr = self.__getUser(username)
|
||||
|
||||
if usr is None:
|
||||
authLogLogin(
|
||||
request, self.dbObj(), username, 'Invalid user'
|
||||
)
|
||||
return auths.FAILED_AUTH
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid user')
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
try:
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
usr['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
self.__connectAs(usr['dn'], credentials) # Will raise an exception if it can't connect
|
||||
except Exception:
|
||||
authLogLogin(
|
||||
request, self.dbObj(), username, 'Invalid password'
|
||||
)
|
||||
return auths.FAILED_AUTH
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid password')
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# store the user mfa attribute if it is set
|
||||
if self._mfaAttr:
|
||||
@ -525,10 +512,10 @@ class RegexLdap(auths.Authenticator):
|
||||
|
||||
groupsManager.validate(self.__getGroups(usr))
|
||||
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
except Exception:
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def createUser(self, usrData: typing.Dict[str, str]) -> None:
|
||||
"""
|
||||
@ -541,7 +528,7 @@ class RegexLdap(auths.Authenticator):
|
||||
"""
|
||||
res = self.__getUser(usrData['name'])
|
||||
if res is None:
|
||||
raise auths.exceptions.AuthenticatorException(_('Username not found'))
|
||||
raise exceptions.auth.AuthenticatorException(_('Username not found'))
|
||||
# Fills back realName field
|
||||
usrData['real_name'] = self.__getUserRealName(res)
|
||||
|
||||
@ -572,7 +559,7 @@ class RegexLdap(auths.Authenticator):
|
||||
"""
|
||||
user = self.__getUser(username)
|
||||
if user is None:
|
||||
raise auths.exceptions.AuthenticatorException(_('Username not found'))
|
||||
raise exceptions.auth.AuthenticatorException(_('Username not found'))
|
||||
groups = self.__getGroups(user)
|
||||
groupsManager.validate(groups)
|
||||
|
||||
@ -597,9 +584,7 @@ class RegexLdap(auths.Authenticator):
|
||||
return res
|
||||
except Exception as e:
|
||||
logger.exception("Exception: ")
|
||||
raise auths.exceptions.AuthenticatorException(
|
||||
_('Too many results, be more specific')
|
||||
) from e
|
||||
raise exceptions.auth.AuthenticatorException(_('Too many results, be more specific')) from e
|
||||
|
||||
@staticmethod
|
||||
def test(env, data):
|
||||
@ -607,9 +592,7 @@ class RegexLdap(auths.Authenticator):
|
||||
auth = RegexLdap(None, env, data) # type: ignore # Regexldap does not use "dbAuth", so it's safe...
|
||||
return auth.testConnection()
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
'Exception found testing Simple LDAP auth %s: %s', e.__class__, e
|
||||
)
|
||||
logger.error('Exception found testing Simple LDAP auth %s: %s', e.__class__, e)
|
||||
return [False, "Error testing connection"]
|
||||
|
||||
def testConnection(self):
|
||||
@ -638,9 +621,7 @@ class RegexLdap(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap user class seems to be incorrect (no user found by that class)'
|
||||
),
|
||||
_('Ldap user class seems to be incorrect (no user found by that class)'),
|
||||
]
|
||||
except Exception: # nosec: Control flow
|
||||
# If found 1 or more, all right
|
||||
@ -662,9 +643,7 @@ class RegexLdap(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap user id attr is probably wrong (can\'t find any user with both conditions)'
|
||||
),
|
||||
_('Ldap user id attr is probably wrong (can\'t find any user with both conditions)'),
|
||||
]
|
||||
except Exception: # nosec: Control flow
|
||||
# If found 1 or more, all right
|
||||
@ -691,9 +670,7 @@ class RegexLdap(auths.Authenticator):
|
||||
continue
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap group id attribute seems to be incorrect (no group found by that attribute)'
|
||||
),
|
||||
_('Ldap group id attribute seems to be incorrect (no group found by that attribute)'),
|
||||
]
|
||||
|
||||
# Now try to test regular expression to see if it matches anything (
|
||||
|
@ -44,7 +44,7 @@ from onelogin.saml2.auth import OneLogin_Saml2_Auth
|
||||
from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser
|
||||
from onelogin.saml2.settings import OneLogin_Saml2_Settings
|
||||
|
||||
from uds.core import auths, exceptions, types
|
||||
from uds.core import auths, exceptions, types, consts
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.types.request import ExtendedHttpRequest
|
||||
from uds.core.ui import gui
|
||||
@ -335,7 +335,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
return
|
||||
|
||||
if ' ' in values['name']:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('This kind of Authenticator does not support white spaces on field NAME')
|
||||
)
|
||||
|
||||
@ -344,7 +344,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
|
||||
# This is in fact not needed, but we may say something useful to user if we check this
|
||||
if self.serverCertificate.value.startswith('-----BEGIN CERTIFICATE-----\n') is False:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext(
|
||||
'Server certificate should be a valid PEM (PEM certificates starts with -----BEGIN CERTIFICATE-----)'
|
||||
)
|
||||
@ -353,13 +353,13 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
try:
|
||||
CryptoManager().loadCertificate(self.serverCertificate.value)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(gettext('Invalid server certificate. ') + str(e))
|
||||
raise exceptions.validation.ValidationError(gettext('Invalid server certificate. ') + str(e))
|
||||
|
||||
if (
|
||||
self.privateKey.value.startswith('-----BEGIN RSA PRIVATE KEY-----\n') is False
|
||||
and self.privateKey.value.startswith('-----BEGIN PRIVATE KEY-----\n') is False
|
||||
):
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext(
|
||||
'Private key should be a valid PEM (PEM private keys starts with -----BEGIN RSA PRIVATE KEY-----'
|
||||
)
|
||||
@ -368,12 +368,12 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
try:
|
||||
CryptoManager().loadPrivateKey(self.privateKey.value)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(gettext('Invalid private key. ') + str(e))
|
||||
raise exceptions.validation.ValidationError(gettext('Invalid private key. ') + str(e))
|
||||
|
||||
if not security.checkCertificateMatchPrivateKey(
|
||||
cert=self.serverCertificate.value, key=self.privateKey.value
|
||||
):
|
||||
raise exceptions.ValidationError(gettext('Certificate and private key do not match'))
|
||||
raise exceptions.validation.ValidationError(gettext('Certificate and private key do not match'))
|
||||
|
||||
request: 'ExtendedHttpRequest' = values['_request']
|
||||
|
||||
@ -394,7 +394,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
)
|
||||
idpMetadata = resp.content.decode()
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('Can\'t fetch url {0}: {1}').format(self.idpMetadata.value, str(e))
|
||||
)
|
||||
fromUrl = True
|
||||
@ -405,7 +405,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
xml.sax.parseString(idpMetadata, xml.sax.ContentHandler()) # type: ignore # nosec: url provided by admin
|
||||
except Exception as e:
|
||||
msg = (gettext(' (obtained from URL)') if fromUrl else '') + str(e)
|
||||
raise exceptions.ValidationError(gettext('XML does not seem valid for IDP Metadata ') + msg)
|
||||
raise exceptions.validation.ValidationError(gettext('XML does not seem valid for IDP Metadata ') + msg)
|
||||
|
||||
# Now validate regular expressions, if they exists
|
||||
auth_utils.validateRegexField(self.userNameAttr)
|
||||
@ -469,7 +469,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
val = resp.content.decode()
|
||||
except Exception as e:
|
||||
logger.error('Error fetching idp metadata: %s', e)
|
||||
raise auths.exceptions.AuthenticatorException(gettext('Can\'t access idp metadata'))
|
||||
raise exceptions.auth.AuthenticatorException(gettext('Can\'t access idp metadata'))
|
||||
else:
|
||||
val = self.idpMetadata.value
|
||||
|
||||
@ -535,7 +535,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
metadata = saml_settings.get_sp_metadata()
|
||||
errors = saml_settings.validate_metadata(metadata)
|
||||
if len(errors) > 0:
|
||||
raise auths.exceptions.AuthenticatorException(
|
||||
raise exceptions.auth.AuthenticatorException(
|
||||
gettext('Error validating SP metadata: ') + str(errors)
|
||||
)
|
||||
if isinstance(metadata, str):
|
||||
@ -572,7 +572,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
self,
|
||||
req: typing.Dict[str, typing.Any],
|
||||
request: 'ExtendedHttpRequestWithUser',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
# Convert HTTP-POST to HTTP-REDIRECT on SAMLResponse, for just in case...
|
||||
if 'SAMLResponse' in req['post_data']:
|
||||
if isinstance(req['post_data']['SAMLResponse'], list):
|
||||
@ -596,15 +596,15 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
logger.debug('Error on SLO: %s', auth.get_last_response_xml())
|
||||
logger.debug('post_data: %s', req['post_data'])
|
||||
logger.info('Errors processing logout request: %s', errors)
|
||||
raise auths.exceptions.AuthenticatorException(gettext('Error processing SLO: ') + str(errors))
|
||||
raise exceptions.auth.AuthenticatorException(gettext('Error processing SLO: ') + str(errors))
|
||||
|
||||
# Remove MFA related data
|
||||
if request.user:
|
||||
self.mfaClean(request.user.name)
|
||||
|
||||
return auths.AuthenticationResult(
|
||||
success=auths.AuthenticationState.REDIRECT,
|
||||
url=url or auths.AuthenticationInternalUrl.LOGIN.getUrl(),
|
||||
return types.auth.AuthenticationResult(
|
||||
success=types.auth.AuthenticationState.REDIRECT,
|
||||
url=url or types.auth.AuthenticationInternalUrl.LOGIN.getUrl(),
|
||||
)
|
||||
|
||||
# pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||
@ -613,7 +613,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequestWithUser',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
req = self.getReqFromRequest(request, params=parameters)
|
||||
|
||||
if 'logout' in parameters.get_params:
|
||||
@ -624,13 +624,13 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
auth = OneLogin_Saml2_Auth(req, settings)
|
||||
auth.process_response()
|
||||
except Exception as e:
|
||||
raise auths.exceptions.AuthenticatorException(gettext('Error processing SAML response: ') + str(e))
|
||||
raise exceptions.auth.AuthenticatorException(gettext('Error processing SAML response: ') + str(e))
|
||||
errors = auth.get_errors()
|
||||
if errors:
|
||||
raise auths.exceptions.AuthenticatorException('SAML response error: ' + str(errors))
|
||||
raise exceptions.auth.AuthenticatorException('SAML response error: ' + str(errors))
|
||||
|
||||
if not auth.is_authenticated():
|
||||
raise auths.exceptions.AuthenticatorException(gettext('SAML response not authenticated'))
|
||||
raise exceptions.auth.AuthenticatorException(gettext('SAML response not authenticated'))
|
||||
|
||||
# Store SAML attributes
|
||||
request.session['SAML'] = {
|
||||
@ -648,7 +648,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
# 'RelayState' in req['post_data']
|
||||
# and OneLogin_Saml2_Utils.get_self_url(req) != req['post_data']['RelayState']
|
||||
# ):
|
||||
# return auths.AuthenticationResult(
|
||||
# return types.auth.AuthenticationResult(
|
||||
# success=auths.AuthenticationSuccess.REDIRECT,
|
||||
# url=auth.redirect_to(req['post_data']['RelayState'])
|
||||
# )
|
||||
@ -658,7 +658,7 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
attributes.update(auth.get_friendlyname_attributes())
|
||||
|
||||
if not attributes:
|
||||
raise auths.exceptions.AuthenticatorException(gettext('No attributes returned from IdP'))
|
||||
raise exceptions.auth.AuthenticatorException(gettext('No attributes returned from IdP'))
|
||||
logger.debug("Attributes: %s", attributes)
|
||||
|
||||
# Now that we have attributes, we can extract values from this, map groups, etc...
|
||||
@ -689,11 +689,11 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
|
||||
gm.validate(groups)
|
||||
|
||||
return auths.AuthenticationResult(success=auths.AuthenticationState.SUCCESS, username=username)
|
||||
return types.auth.AuthenticationResult(success=types.auth.AuthenticationState.SUCCESS, username=username)
|
||||
|
||||
def logout(self, request: 'ExtendedHttpRequest', username: str) -> auths.AuthenticationResult:
|
||||
def logout(self, request: 'ExtendedHttpRequest', username: str) -> types.auth.AuthenticationResult:
|
||||
if not self.globalLogout.isTrue():
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
req = self.getReqFromRequest(request)
|
||||
|
||||
@ -710,10 +710,10 @@ class SAMLAuthenticator(auths.Authenticator):
|
||||
self.mfaClean(username)
|
||||
|
||||
if not saml:
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
return auths.AuthenticationResult(
|
||||
success=auths.AuthenticationState.REDIRECT,
|
||||
return types.auth.AuthenticationResult(
|
||||
success=types.auth.AuthenticationState.REDIRECT,
|
||||
url=auth.logout(
|
||||
name_id=saml.get('nameid'),
|
||||
session_index=saml.get('session_index'),
|
||||
|
@ -34,10 +34,9 @@ import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from uds.core.auths.authenticator import AuthenticationResult, AuthenticationState
|
||||
from uds.core.types.request import ExtendedHttpRequest
|
||||
from uds.core.ui import gui
|
||||
from uds.core import auths, exceptions
|
||||
from uds.core import auths, exceptions, types, consts
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import (
|
||||
@ -119,9 +118,7 @@ class SampleAuth(auths.Authenticator):
|
||||
# : We will define a simple form where we will use a simple
|
||||
# : list editor to allow entering a few group names
|
||||
|
||||
groups = gui.EditableListField(
|
||||
label=_('Groups'), default=['Gods', 'Daemons', 'Mortals']
|
||||
)
|
||||
groups = gui.EditableListField(label=_('Groups'), default=['Gods', 'Daemons', 'Mortals'])
|
||||
|
||||
def initialize(self, values: typing.Optional[typing.Dict[str, typing.Any]]) -> None:
|
||||
"""
|
||||
@ -134,7 +131,7 @@ class SampleAuth(auths.Authenticator):
|
||||
# unserialization, and at this point all will be default values
|
||||
# so self.groups.value will be []
|
||||
if values and len(self.groups.value) < 2:
|
||||
raise exceptions.ValidationError(_('We need more than two groups!'))
|
||||
raise exceptions.validation.ValidationError(_('We need more than two groups!'))
|
||||
|
||||
def searchUsers(self, pattern: str) -> typing.Iterable[typing.Dict[str, str]]:
|
||||
"""
|
||||
@ -175,7 +172,7 @@ class SampleAuth(auths.Authenticator):
|
||||
credentials: str,
|
||||
groupsManager: 'GroupsManager',
|
||||
request: 'ExtendedHttpRequest', # pylint: disable=unused-argument
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
This method is invoked by UDS whenever it needs an user to be authenticated.
|
||||
It is used from web interface, but also from administration interface to
|
||||
@ -217,10 +214,8 @@ class SampleAuth(auths.Authenticator):
|
||||
|
||||
:note: groupsManager is an in/out parameter
|
||||
"""
|
||||
if (
|
||||
username != credentials
|
||||
): # All users with same username and password are allowed
|
||||
return auths.FAILED_AUTH
|
||||
if username != credentials: # All users with same username and password are allowed
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# Now the tricky part. We will make this user belong to groups that contains at leat
|
||||
# two letters equals to the groups names known by UDS
|
||||
@ -230,7 +225,7 @@ class SampleAuth(auths.Authenticator):
|
||||
if len(set(g.lower()).intersection(username.lower())) >= 2:
|
||||
groupsManager.validate(g)
|
||||
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
def getGroups(self, username: str, groupsManager: 'auths.GroupsManager'):
|
||||
"""
|
||||
@ -247,9 +242,7 @@ class SampleAuth(auths.Authenticator):
|
||||
if len(set(g.lower()).intersection(username.lower())) >= 2:
|
||||
groupsManager.validate(g)
|
||||
|
||||
def getJavascript(
|
||||
self, request: 'HttpRequest' # pylint: disable=unused-argument
|
||||
) -> typing.Optional[str]:
|
||||
def getJavascript(self, request: 'HttpRequest') -> typing.Optional[str]: # pylint: disable=unused-argument
|
||||
"""
|
||||
If we override this method from the base one, we are telling UDS
|
||||
that we want to draw our own authenticator.
|
||||
@ -272,20 +265,16 @@ class SampleAuth(auths.Authenticator):
|
||||
# I know, this is a bit ugly, but this is just a sample :-)
|
||||
|
||||
res = '<p>Login name: <input id="logname" type="text"/></p>'
|
||||
res += (
|
||||
'<p><a href="" onclick="window.location.replace(\''
|
||||
+ self.callbackUrl()
|
||||
+ '?user='
|
||||
)
|
||||
res += '<p><a href="" onclick="window.location.replace(\'' + self.callbackUrl() + '?user='
|
||||
res += '\' + $(\'#logname\').val()); return false;">Login</a></p>'
|
||||
return res
|
||||
|
||||
def authCallback(
|
||||
self,
|
||||
parameters: typing.Dict[str, typing.Any],
|
||||
gm: 'auths.GroupsManager', # pylint: disable=unused-argument
|
||||
gm: 'auths.GroupsManager', # pylint: disable=unused-argument
|
||||
request: 'ExtendedHttpRequestWithUser', # pylint: disable=unused-argument
|
||||
) -> AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
We provide this as a sample of callback for an user.
|
||||
We will accept all petitions that has "user" parameter
|
||||
@ -301,7 +290,7 @@ class SampleAuth(auths.Authenticator):
|
||||
"""
|
||||
user = parameters.get('user', None)
|
||||
|
||||
return AuthenticationResult(AuthenticationState.SUCCESS, username=user)
|
||||
return types.auth.AuthenticationResult(types.auth.AuthenticationState.SUCCESS, username=user)
|
||||
|
||||
def createUser(self, usrData: typing.Dict[str, str]) -> None:
|
||||
"""
|
||||
|
@ -36,7 +36,7 @@ import ldap
|
||||
import ldap.filter
|
||||
from django.utils.translation import gettext_noop as _
|
||||
|
||||
from uds.core import auths, types
|
||||
from uds.core import auths, types, consts, exceptions
|
||||
from uds.core.auths.auth import authLogLogin
|
||||
from uds.core.ui import gui
|
||||
from uds.core.util import ldaputil
|
||||
@ -49,6 +49,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
LDAP_RESULT_LIMIT = 100
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
host = gui.TextField(
|
||||
@ -69,9 +70,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
ssl = gui.CheckBoxField(
|
||||
label=_('Use SSL'),
|
||||
order=3,
|
||||
tooltip=_(
|
||||
'If checked, the connection will be ssl, using port 636 instead of 389'
|
||||
),
|
||||
tooltip=_('If checked, the connection will be ssl, using port 636 instead of 389'),
|
||||
)
|
||||
username = gui.TextField(
|
||||
length=64,
|
||||
@ -103,9 +102,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
label=_('Verify SSL'),
|
||||
default=True,
|
||||
order=11,
|
||||
tooltip=_(
|
||||
'If checked, SSL verification will be enforced. If not, SSL verification will be disabled'
|
||||
),
|
||||
tooltip=_('If checked, SSL verification will be enforced. If not, SSL verification will be disabled'),
|
||||
tab=types.ui.Tab.ADVANCED,
|
||||
)
|
||||
certificate = gui.TextField(
|
||||
@ -149,9 +146,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
label=_('User Name Attr'),
|
||||
default='uid',
|
||||
order=33,
|
||||
tooltip=_(
|
||||
'Attributes that contains the user name (list of comma separated values)'
|
||||
),
|
||||
tooltip=_('Attributes that contains the user name (list of comma separated values)'),
|
||||
required=True,
|
||||
tab=_('Ldap info'),
|
||||
)
|
||||
@ -240,9 +235,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
self._userIdAttr = values['userIdAttr']
|
||||
self._groupIdAttr = values['groupIdAttr']
|
||||
self._memberAttr = values['memberAttr']
|
||||
self._userNameAttr = values['userNameAttr'].replace(
|
||||
' ', ''
|
||||
) # Removes white spaces
|
||||
self._userNameAttr = values['userNameAttr'].replace(' ', '') # Removes white spaces
|
||||
self._mfaAttr = values['mfaAttr']
|
||||
self._verifySsl = gui.toBool(values['verifySsl'])
|
||||
self._certificate = values['certificate']
|
||||
@ -438,7 +431,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
credentials: str,
|
||||
groupsManager: 'auths.GroupsManager',
|
||||
request: 'ExtendedHttpRequest',
|
||||
) -> auths.AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
'''
|
||||
Must authenticate the user.
|
||||
We can have to different situations here:
|
||||
@ -454,18 +447,14 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
|
||||
if user is None:
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid user')
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
try:
|
||||
# Let's see first if it credentials are fine
|
||||
self.__connectAs(
|
||||
user['dn'], credentials
|
||||
) # Will raise an exception if it can't connect
|
||||
self.__connectAs(user['dn'], credentials) # Will raise an exception if it can't connect
|
||||
except Exception:
|
||||
authLogLogin(
|
||||
request, self.dbObj(), username, 'Invalid password'
|
||||
)
|
||||
return auths.FAILED_AUTH
|
||||
authLogLogin(request, self.dbObj(), username, 'Invalid password')
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
# store the user mfa attribute if it is set
|
||||
if self._mfaAttr:
|
||||
@ -476,10 +465,10 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
|
||||
groupsManager.validate(self.__getGroups(user))
|
||||
|
||||
return auths.SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
except Exception:
|
||||
return auths.FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def createUser(self, usrData: typing.Dict[str, str]) -> None:
|
||||
'''
|
||||
@ -489,7 +478,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
'''
|
||||
res = self.__getUser(usrData['name'])
|
||||
if res is None:
|
||||
raise auths.exceptions.AuthenticatorException(_('Username not found'))
|
||||
raise exceptions.auth.AuthenticatorException(_('Username not found'))
|
||||
# Fills back realName field
|
||||
usrData['real_name'] = self.__getUserRealName(res)
|
||||
|
||||
@ -522,7 +511,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
'''
|
||||
res = self.__getGroup(groupData['name'])
|
||||
if res is None:
|
||||
raise auths.exceptions.AuthenticatorException(_('Group not found'))
|
||||
raise exceptions.auth.AuthenticatorException(_('Group not found'))
|
||||
|
||||
def getGroups(self, username: str, groupsManager: 'auths.GroupsManager'):
|
||||
'''
|
||||
@ -532,7 +521,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
'''
|
||||
user = self.__getUser(username)
|
||||
if user is None:
|
||||
raise auths.exceptions.AuthenticatorException(_('Username not found'))
|
||||
raise exceptions.auth.AuthenticatorException(_('Username not found'))
|
||||
groupsManager.validate(self.__getGroups(user))
|
||||
|
||||
def searchUsers(self, pattern: str) -> typing.Iterable[typing.Dict[str, str]]:
|
||||
@ -555,9 +544,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
return res
|
||||
except Exception as e:
|
||||
logger.exception("Exception: ")
|
||||
raise auths.exceptions.AuthenticatorException(
|
||||
_('Too many results, be more specific')
|
||||
) from e
|
||||
raise exceptions.auth.AuthenticatorException(_('Too many results, be more specific')) from e
|
||||
|
||||
def searchGroups(self, pattern: str) -> typing.Iterable[typing.Dict[str, str]]:
|
||||
try:
|
||||
@ -574,9 +561,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
return res
|
||||
except Exception as e:
|
||||
logger.exception("Exception: ")
|
||||
raise auths.exceptions.AuthenticatorException(
|
||||
_('Too many results, be more specific')
|
||||
) from e
|
||||
raise exceptions.auth.AuthenticatorException(_('Too many results, be more specific')) from e
|
||||
|
||||
@staticmethod
|
||||
def test(env, data) -> typing.List[typing.Any]:
|
||||
@ -589,9 +574,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
|
||||
def testConnection(
|
||||
self,
|
||||
) -> typing.List[
|
||||
typing.Any
|
||||
]: # pylint: disable=too-many-return-statements,too-many-branches
|
||||
) -> typing.List[typing.Any]: # pylint: disable=too-many-return-statements,too-many-branches
|
||||
try:
|
||||
con = self.__connection()
|
||||
except Exception as e:
|
||||
@ -617,9 +600,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap user class seems to be incorrect (no user found by that class)'
|
||||
),
|
||||
_('Ldap user class seems to be incorrect (no user found by that class)'),
|
||||
]
|
||||
except Exception: # nosec: Flow control
|
||||
# If found 1 or more, all right
|
||||
@ -640,9 +621,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap group class seems to be incorrect (no group found by that class)'
|
||||
),
|
||||
_('Ldap group class seems to be incorrect (no group found by that class)'),
|
||||
]
|
||||
except Exception: # nosec: Flow control
|
||||
# If found 1 or more, all right
|
||||
@ -663,9 +642,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap user id attribute seems to be incorrect (no user found by that attribute)'
|
||||
),
|
||||
_('Ldap user id attribute seems to be incorrect (no user found by that attribute)'),
|
||||
]
|
||||
except Exception: # nosec: Flow control
|
||||
# If found 1 or more, all right
|
||||
@ -686,9 +663,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
raise Exception()
|
||||
return [
|
||||
False,
|
||||
_(
|
||||
'Ldap group id attribute seems to be incorrect (no group found by that attribute)'
|
||||
),
|
||||
_('Ldap group id attribute seems to be incorrect (no group found by that attribute)'),
|
||||
]
|
||||
except Exception: # nosec: Flow control
|
||||
# If found 1 or more, all right
|
||||
@ -738,9 +713,7 @@ class SimpleLDAPAuthenticator(auths.Authenticator):
|
||||
ok = True
|
||||
break
|
||||
if ok is False:
|
||||
raise Exception(
|
||||
_('Can\'t locate any group with the membership attribute specified')
|
||||
)
|
||||
raise Exception(_('Can\'t locate any group with the membership attribute specified'))
|
||||
except Exception as e:
|
||||
return [False, str(e)]
|
||||
|
||||
|
@ -34,17 +34,11 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from .authenticator import (
|
||||
Authenticator,
|
||||
AuthenticationResult,
|
||||
AuthenticationState,
|
||||
AuthenticationInternalUrl,
|
||||
SUCCESS_AUTH,
|
||||
FAILED_AUTH,
|
||||
)
|
||||
from .authfactory import AuthsFactory
|
||||
from .user import User
|
||||
from .group import Group
|
||||
from .groups_manager import GroupsManager
|
||||
from . import exceptions
|
||||
|
||||
|
||||
def factory() -> AuthsFactory:
|
||||
|
@ -49,7 +49,7 @@ from django.urls import reverse
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from uds.core import auths, types
|
||||
from uds.core import auths, types, exceptions
|
||||
from uds.core.types.request import ExtendedHttpRequest
|
||||
from uds.core.util import log
|
||||
from uds.core.util import net
|
||||
@ -58,7 +58,7 @@ from uds.core.util.config import GlobalConfig
|
||||
from uds.core.util.stats import events
|
||||
from uds.core.util.state import State
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.auths import Authenticator as AuthenticatorInstance, SUCCESS_AUTH
|
||||
from uds.core.auths import Authenticator as AuthenticatorInstance
|
||||
|
||||
from uds import models
|
||||
|
||||
@ -309,12 +309,12 @@ def authenticate(
|
||||
else:
|
||||
res = authInstance.internalAuthenticate(username, password, gm, request)
|
||||
|
||||
if res.success == auths.AuthenticationState.FAIL:
|
||||
if res.success == types.auth.AuthenticationState.FAIL:
|
||||
logger.debug('Authentication failed')
|
||||
# Maybe it's an redirection on auth failed?
|
||||
return AuthResult()
|
||||
|
||||
if res.success == auths.AuthenticationState.REDIRECT:
|
||||
if res.success == types.auth.AuthenticationState.REDIRECT:
|
||||
return AuthResult(url=res.url)
|
||||
|
||||
logger.debug('Groups manager: %s', gm)
|
||||
@ -358,15 +358,15 @@ def authenticateViaCallback(
|
||||
|
||||
# If there is no callback for this authenticator...
|
||||
if authInstance.authCallback is auths.Authenticator.authCallback:
|
||||
raise auths.exceptions.InvalidAuthenticatorException()
|
||||
raise exceptions.auth.InvalidAuthenticatorException()
|
||||
|
||||
result = authInstance.authCallback(params, gm, request)
|
||||
if result.success == auths.AuthenticationState.FAIL or (
|
||||
result.success == auths.AuthenticationState.SUCCESS and not gm.hasValidGroups()
|
||||
if result.success == types.auth.AuthenticationState.FAIL or (
|
||||
result.success == types.auth.AuthenticationState.SUCCESS and not gm.hasValidGroups()
|
||||
):
|
||||
raise auths.exceptions.InvalidUserException('User doesn\'t has access to UDS')
|
||||
raise exceptions.auth.InvalidUserException('User doesn\'t has access to UDS')
|
||||
|
||||
if result.success == auths.AuthenticationState.REDIRECT:
|
||||
if result.success == types.auth.AuthenticationState.REDIRECT:
|
||||
return AuthResult(url=result.url)
|
||||
|
||||
if result.username:
|
||||
@ -374,7 +374,7 @@ def authenticateViaCallback(
|
||||
else:
|
||||
logger.warning('Authenticator %s returned empty username', authenticator.name)
|
||||
|
||||
raise auths.exceptions.InvalidUserException('User doesn\'t has access to UDS')
|
||||
raise exceptions.auth.InvalidUserException('User doesn\'t has access to UDS')
|
||||
|
||||
|
||||
def authCallbackUrl(authenticator: models.Authenticator) -> str:
|
||||
@ -488,7 +488,7 @@ def webLogout(
|
||||
authenticator = request.user.manager.getInstance()
|
||||
username = request.user.name
|
||||
logout = authenticator.logout(request, username)
|
||||
if logout and logout.success == auths.AuthenticationState.REDIRECT:
|
||||
if logout and logout.success == types.auth.AuthenticationState.REDIRECT:
|
||||
exit_url = logout.url or exit_url
|
||||
if request.user.id != ROOT_ID:
|
||||
# Log the event if not root user
|
||||
|
@ -32,15 +32,13 @@ Base module for all authenticators
|
||||
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import enum
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _
|
||||
from django.urls import reverse
|
||||
|
||||
from uds.core.module import Module
|
||||
from uds.core import consts
|
||||
from uds.core import consts, types
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -57,40 +55,6 @@ if typing.TYPE_CHECKING:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthenticationState(enum.IntEnum):
|
||||
"""
|
||||
Enumeration for authentication success
|
||||
"""
|
||||
|
||||
FAIL = 0
|
||||
SUCCESS = 1
|
||||
REDIRECT = 2
|
||||
|
||||
|
||||
class AuthenticationInternalUrl(enum.Enum):
|
||||
"""
|
||||
Enumeration for authentication success
|
||||
"""
|
||||
|
||||
LOGIN = 'page.login'
|
||||
|
||||
def getUrl(self) -> str:
|
||||
"""
|
||||
Returns the url for the given internal url
|
||||
"""
|
||||
return reverse(self.value)
|
||||
|
||||
|
||||
class AuthenticationResult(typing.NamedTuple):
|
||||
success: AuthenticationState
|
||||
url: typing.Optional[str] = None
|
||||
username: typing.Optional[str] = None
|
||||
|
||||
|
||||
FAILED_AUTH = AuthenticationResult(success=AuthenticationState.FAIL)
|
||||
SUCCESS_AUTH = AuthenticationResult(success=AuthenticationState.SUCCESS)
|
||||
|
||||
|
||||
class Authenticator(Module):
|
||||
"""
|
||||
This class represents the base interface to implement authenticators.
|
||||
@ -202,7 +166,7 @@ class Authenticator(Module):
|
||||
# : group class
|
||||
groupType: typing.ClassVar[typing.Type[Group]] = Group
|
||||
|
||||
_dbObj: typing.Optional['models.Authenticator'] = None # Cached dbAuth object
|
||||
_dbObj: typing.Optional['models.Authenticator'] = None # Cached dbAuth object
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -356,7 +320,7 @@ class Authenticator(Module):
|
||||
credentials: str,
|
||||
groupsManager: 'GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
This method must be overriden, and is responsible for authenticating
|
||||
users.
|
||||
@ -394,7 +358,7 @@ class Authenticator(Module):
|
||||
This is done in this way, because UDS has only a subset of groups for this user, and
|
||||
we let the authenticator decide inside wich groups of UDS this users is included.
|
||||
"""
|
||||
return FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def isAccesibleFrom(self, request: 'HttpRequest') -> bool:
|
||||
"""
|
||||
@ -429,7 +393,7 @@ class Authenticator(Module):
|
||||
credentials: str,
|
||||
groupsManager: 'GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
This method is provided so "plugins" (For example, a custom dispatcher), can test
|
||||
the username/credentials in an alternative way.
|
||||
@ -469,7 +433,7 @@ class Authenticator(Module):
|
||||
self,
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
username: str,
|
||||
) -> AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
Invoked whenever an user logs out.
|
||||
|
||||
@ -495,7 +459,7 @@ class Authenticator(Module):
|
||||
invoked if user requests "log out", but maybe it will never be invoked.
|
||||
|
||||
"""
|
||||
return SUCCESS_AUTH
|
||||
return types.auth.SUCCESS_AUTH
|
||||
|
||||
def webLogoutHook(
|
||||
self,
|
||||
@ -566,7 +530,7 @@ class Authenticator(Module):
|
||||
parameters: 'types.auth.AuthCallbackParams',
|
||||
gm: 'GroupsManager',
|
||||
request: 'types.request.ExtendedHttpRequest',
|
||||
) -> AuthenticationResult:
|
||||
) -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
There is a view inside UDS, an url, that will redirect the petition
|
||||
to this callback.
|
||||
@ -603,7 +567,7 @@ class Authenticator(Module):
|
||||
There will be calls to getGroups one an again, and also to getRealName, not just
|
||||
at login, but at future (from admin interface, at user editing for example)
|
||||
"""
|
||||
return FAILED_AUTH
|
||||
return types.auth.FAILED_AUTH
|
||||
|
||||
def getInfo(
|
||||
self, parameters: typing.Mapping[str, str]
|
||||
|
@ -43,4 +43,3 @@ DISABLED: typing.Final[str] = 'd'
|
||||
NO_FILTERING: typing.Final[str] = 'n'
|
||||
ALLOW: typing.Final[str] = 'a'
|
||||
DENY: typing.Final[str] = 'd'
|
||||
|
||||
|
39
server/src/uds/core/exceptions/__init__.py
Normal file
39
server/src/uds/core/exceptions/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from . import auth
|
||||
from . import validation
|
||||
from . import service
|
||||
|
||||
# Common exceptions inserted here
|
||||
from .common import UDSException, BlockAccess
|
||||
|
54
server/src/uds/core/exceptions/actor.py
Normal file
54
server/src/uds/core/exceptions/actor.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from . import common
|
||||
|
||||
|
||||
class ActorException(common.UDSException):
|
||||
"""
|
||||
Base class for all Actor exceptions
|
||||
"""
|
||||
|
||||
|
||||
# Actor related exceptions
|
||||
class NoActorComms(ActorException):
|
||||
"""
|
||||
Exception used to signal that the actor user service does not have comms url
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OldActorVersion(ActorException):
|
||||
"""
|
||||
Exception used to signal that the actor user service version is too old
|
||||
"""
|
@ -30,9 +30,9 @@
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from . import common
|
||||
|
||||
|
||||
class AuthenticatorException(Exception):
|
||||
class AuthenticatorException(common.UDSException):
|
||||
"""
|
||||
Generic authentication exception
|
||||
"""
|
@ -37,33 +37,8 @@ class UDSException(Exception):
|
||||
Base class for all UDS exceptions
|
||||
"""
|
||||
|
||||
|
||||
class ValidationError(UDSException):
|
||||
"""
|
||||
Exception used to indicate that the params assigned are invalid
|
||||
"""
|
||||
|
||||
|
||||
class TransportError(UDSException):
|
||||
"""
|
||||
Exception used to indicate that the transport is not available
|
||||
"""
|
||||
|
||||
|
||||
class BlockAccess(UDSException):
|
||||
"""
|
||||
Exception used to signal that the access to a resource is blocked
|
||||
"""
|
||||
|
||||
# Actor related exceptions
|
||||
class NoActorComms(UDSException):
|
||||
"""
|
||||
Exception used to signal that the actor user service does not have comms url
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class OldActorVersion(NoActorComms):
|
||||
"""
|
||||
Exception used to signal that the actor user service version is too old
|
||||
"""
|
40
server/src/uds/core/exceptions/service.py
Normal file
40
server/src/uds/core/exceptions/service.py
Normal file
@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
|
||||
from .common import UDSException
|
||||
|
||||
class TransportError(UDSException):
|
||||
"""
|
||||
Exception used to indicate that the transport is not available
|
||||
"""
|
||||
|
39
server/src/uds/core/exceptions/validation.py
Normal file
39
server/src/uds/core/exceptions/validation.py
Normal file
@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L.U. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
|
||||
from .common import UDSException
|
||||
|
||||
class ValidationError(UDSException):
|
||||
"""
|
||||
Exception used to indicate that the params assigned are invalid
|
||||
"""
|
@ -52,6 +52,7 @@ from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
|
||||
from django.conf import settings
|
||||
from regex import E
|
||||
|
||||
from uds.core.util import singleton
|
||||
|
||||
@ -293,14 +294,21 @@ class CryptoManager(metaclass=singleton.Singleton):
|
||||
elif isinstance(obj, bytes):
|
||||
obj = obj.decode('utf8') # To string
|
||||
else:
|
||||
obj = str(obj)
|
||||
try:
|
||||
obj = str(obj)
|
||||
except Exception:
|
||||
obj = str(hash(obj)) # Get hash of object
|
||||
|
||||
return str(
|
||||
uuid.uuid5(self._namespace, obj)
|
||||
).lower() # I believe uuid returns a lowercase uuid always, but in case... :)
|
||||
|
||||
def randomString(self, length: int = 40, digits: bool = True) -> str:
|
||||
base = string.ascii_letters + (string.digits if digits else '')
|
||||
def randomString(self, length: int = 40, digits: bool = True, punctuation: bool = False) -> str:
|
||||
base = (
|
||||
string.ascii_letters
|
||||
+ (string.digits if digits else '')
|
||||
+ (string.punctuation if punctuation else '')
|
||||
)
|
||||
return ''.join(secrets.choice(base) for _ in range(length))
|
||||
|
||||
def unique(self) -> str:
|
||||
|
@ -63,20 +63,17 @@ class DownloadsManager(metaclass=singleton.Singleton):
|
||||
|
||||
@staticmethod
|
||||
def manager() -> 'DownloadsManager':
|
||||
return (
|
||||
DownloadsManager()
|
||||
) # Singleton pattern will return always the same instance
|
||||
# Singleton pattern will return always the same instance
|
||||
return DownloadsManager()
|
||||
|
||||
def registerDownloadable(
|
||||
self, name: str, comment: str, path: str, mime: str = 'application/octet-stream'
|
||||
):
|
||||
def registerDownloadable(self, name: str, comment: str, path: str, mime: str = 'application/octet-stream'):
|
||||
"""
|
||||
Registers a downloadable file.
|
||||
@param name: name shown
|
||||
@param path: path to file
|
||||
@params zip: If download as zip
|
||||
"""
|
||||
_id = CryptoManager().uuid(name)
|
||||
_id = CryptoManager.manager().uuid(name)
|
||||
self._downloadables[_id] = {
|
||||
'name': name,
|
||||
'comment': comment,
|
||||
@ -89,9 +86,7 @@ class DownloadsManager(metaclass=singleton.Singleton):
|
||||
|
||||
def send(self, request, _id) -> HttpResponse:
|
||||
if _id not in self._downloadables:
|
||||
logger.error(
|
||||
'Downloadable id %s not found in %s!!!', _id, self._downloadables
|
||||
)
|
||||
logger.error('Downloadable id %s not found in %s!!!', _id, self._downloadables)
|
||||
raise Http404
|
||||
return self._send_file(
|
||||
request,
|
||||
@ -106,8 +101,12 @@ class DownloadsManager(metaclass=singleton.Singleton):
|
||||
memory at once. The FileWrapper will turn the file object into an
|
||||
iterator for chunks of 8KB.
|
||||
"""
|
||||
wrapper = FileWrapper(open(filename, 'rb')) # pylint: disable=consider-using-with
|
||||
response = HttpResponse(wrapper, content_type=mime)
|
||||
response['Content-Length'] = os.path.getsize(filename)
|
||||
response['Content-Disposition'] = 'attachment; filename=' + name
|
||||
return response
|
||||
try:
|
||||
wrapper = FileWrapper(open(filename, 'rb')) # pylint: disable=consider-using-with
|
||||
response = HttpResponse(wrapper, content_type=mime)
|
||||
response['Content-Length'] = os.path.getsize(filename)
|
||||
response['Content-Disposition'] = 'attachment; filename=' + name
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error('Error sending file %s: %s', filename, e)
|
||||
raise Http404
|
||||
|
@ -40,8 +40,8 @@ import typing
|
||||
from django.utils.translation import gettext_noop as _, gettext
|
||||
from uds.core.module import Module
|
||||
from uds.core.util.model import getSqlDatetime
|
||||
from uds.core.auths import exceptions
|
||||
from uds.models.network import Network
|
||||
from uds.core import exceptions
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core.environment import Environment
|
||||
@ -205,7 +205,7 @@ class MFA(Module):
|
||||
If raises an error, the MFA code was not sent, and the user needs to enter the MFA code.
|
||||
"""
|
||||
logger.error('MFA.sendCode not implemented')
|
||||
raise exceptions.MFAError('MFA.sendCode not implemented')
|
||||
raise exceptions.auth.MFAError('MFA.sendCode not implemented')
|
||||
|
||||
def _getData(
|
||||
self, request: 'ExtendedHttpRequest', userId: str
|
||||
@ -326,7 +326,7 @@ class MFA(Module):
|
||||
# if it is no more valid, raise an error
|
||||
# Remove stored code and raise error
|
||||
self._removeData(request, userId)
|
||||
raise exceptions.MFAError('MFA Code expired')
|
||||
raise exceptions.auth.MFAError('MFA Code expired')
|
||||
|
||||
# Check if the code is valid
|
||||
if data[1] == code:
|
||||
@ -337,7 +337,7 @@ class MFA(Module):
|
||||
# Any error means invalid code
|
||||
err = str(e)
|
||||
|
||||
raise exceptions.MFAError(err)
|
||||
raise exceptions.auth.MFAError(err)
|
||||
|
||||
def resetData(
|
||||
self,
|
||||
@ -355,7 +355,7 @@ class MFA(Module):
|
||||
"""
|
||||
mfa = user.manager.mfa
|
||||
if not mfa:
|
||||
raise exceptions.MFAError('MFA is not enabled')
|
||||
raise exceptions.auth.MFAError('MFA is not enabled')
|
||||
|
||||
return hashlib.sha3_256(
|
||||
(user.name + (user.uuid or '') + mfa.uuid).encode()
|
||||
|
@ -30,12 +30,50 @@
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import typing
|
||||
import enum
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from django.http import HttpRequest
|
||||
from django.http.request import QueryDict
|
||||
|
||||
|
||||
class AuthenticationState(enum.IntEnum):
|
||||
"""
|
||||
Enumeration for authentication success
|
||||
"""
|
||||
|
||||
FAIL = 0
|
||||
SUCCESS = 1
|
||||
REDIRECT = 2
|
||||
|
||||
|
||||
class AuthenticationInternalUrl(enum.Enum):
|
||||
"""
|
||||
Enumeration for authentication success
|
||||
"""
|
||||
|
||||
LOGIN = 'page.login'
|
||||
LOGOUT = 'page.logout'
|
||||
|
||||
def getUrl(self) -> str:
|
||||
"""
|
||||
Returns the url for the given internal url
|
||||
"""
|
||||
return reverse(self.value)
|
||||
|
||||
|
||||
class AuthenticationResult(typing.NamedTuple):
|
||||
success: AuthenticationState
|
||||
url: typing.Optional[str] = None
|
||||
username: typing.Optional[str] = None
|
||||
|
||||
# Comodity values
|
||||
FAILED_AUTH = AuthenticationResult(success=AuthenticationState.FAIL)
|
||||
SUCCESS_AUTH = AuthenticationResult(success=AuthenticationState.SUCCESS)
|
||||
|
||||
|
||||
class AuthCallbackParams(typing.NamedTuple):
|
||||
'''Parameters passed to auth callback stage2
|
||||
|
||||
@ -61,3 +99,4 @@ class AuthCallbackParams(typing.NamedTuple):
|
||||
post_params=request.POST.copy(),
|
||||
query_string=request.META['QUERY_STRING'],
|
||||
)
|
||||
|
||||
|
@ -544,12 +544,12 @@ class gui:
|
||||
elif pattern == types.ui.FieldPatternType.HOST:
|
||||
try:
|
||||
validators.validateHostname(self.value, allowDomain=True)
|
||||
except exceptions.ValidationError:
|
||||
except exceptions.validation.ValidationError:
|
||||
validators.validateIpv4OrIpv6(self.value)
|
||||
elif pattern == types.ui.FieldPatternType.PATH:
|
||||
validators.validatePath(self.value)
|
||||
return True
|
||||
except exceptions.ValidationError:
|
||||
except exceptions.validation.ValidationError:
|
||||
return False
|
||||
elif isinstance(pattern, str):
|
||||
# It's a regex
|
||||
|
@ -55,7 +55,7 @@ def validateRegexField(field: ui.gui.TextField, fieldValue: typing.Optional[str]
|
||||
try:
|
||||
re.search(pattern, '')
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(f'Invalid pattern at {field.label}: {line}') from e
|
||||
raise exceptions.validation.ValidationError(f'Invalid pattern at {field.label}: {line}') from e
|
||||
|
||||
|
||||
def processRegexField(
|
||||
|
@ -69,39 +69,39 @@ def validateNumeric(
|
||||
try:
|
||||
numeric = int(value)
|
||||
if minValue is not None and numeric < minValue:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('{0} must be greater than or equal to {1}').format(fieldName, minValue)
|
||||
)
|
||||
|
||||
if maxValue is not None and numeric > maxValue:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('{0} must be lower than or equal to {1}').format(fieldName, maxValue)
|
||||
)
|
||||
|
||||
value = str(numeric)
|
||||
|
||||
except ValueError:
|
||||
raise exceptions.ValidationError(_('{0} contains invalid characters').format(fieldName)) from None
|
||||
raise exceptions.validation.ValidationError(_('{0} contains invalid characters').format(fieldName)) from None
|
||||
|
||||
return int(value)
|
||||
|
||||
|
||||
def validateHostname(hostname: str, maxLength: int = 64, allowDomain=False) -> str:
|
||||
if len(hostname) > maxLength:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('{} is not a valid hostname: maximum host name length exceeded.').format(hostname)
|
||||
)
|
||||
|
||||
if not allowDomain:
|
||||
if '.' in hostname:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('{} is not a valid hostname: (domains not allowed)').format(hostname)
|
||||
)
|
||||
|
||||
allowed = re.compile(r'(?!-)[A-Z\d-]{1,63}(?<!-)$', re.IGNORECASE)
|
||||
|
||||
if not all(allowed.match(x) for x in hostname.split(".")):
|
||||
raise exceptions.ValidationError(_('{} is not a valid hostname: (invalid characters)').format(hostname))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid hostname: (invalid characters)').format(hostname))
|
||||
|
||||
return hostname
|
||||
|
||||
@ -112,12 +112,12 @@ def validateFqdn(fqdn: str, maxLength: int = 255) -> str:
|
||||
|
||||
def validateUrl(url: str, maxLength: int = 1024) -> str:
|
||||
if len(url) > maxLength:
|
||||
raise exceptions.ValidationError(_('{} is not a valid URL: exceeds maximum length.').format(url))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid URL: exceeds maximum length.').format(url))
|
||||
|
||||
try:
|
||||
url_validator(url)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(str(e))
|
||||
raise exceptions.validation.ValidationError(str(e))
|
||||
|
||||
return url
|
||||
|
||||
@ -132,7 +132,7 @@ def validateIpv4(ipv4: str) -> str:
|
||||
try:
|
||||
dj_validators.validate_ipv4_address(ipv4)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(_('{} is not a valid IPv4 address').format(ipv4)) from None
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid IPv4 address').format(ipv4)) from None
|
||||
return ipv4
|
||||
|
||||
|
||||
@ -146,7 +146,7 @@ def validateIpv6(ipv6: str) -> str:
|
||||
try:
|
||||
dj_validators.validate_ipv6_address(ipv6)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(_('{} is not a valid IPv6 address').format(ipv6)) from None
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid IPv6 address').format(ipv6)) from None
|
||||
return ipv6
|
||||
|
||||
|
||||
@ -160,7 +160,7 @@ def validateIpv4OrIpv6(ipv4OrIpv6: str) -> str:
|
||||
try:
|
||||
dj_validators.validate_ipv46_address(ipv4OrIpv6)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('{} is not a valid IPv4 or IPv6 address').format(ipv4OrIpv6)
|
||||
) from None
|
||||
return ipv4OrIpv6
|
||||
@ -188,20 +188,20 @@ def validatePath(
|
||||
str: path
|
||||
"""
|
||||
if len(path) > maxLength:
|
||||
raise exceptions.ValidationError(_('{} exceeds maximum path length.').format(path))
|
||||
raise exceptions.validation.ValidationError(_('{} exceeds maximum path length.').format(path))
|
||||
|
||||
valid_for_windows = re.compile(r'^[a-zA-Z]:\\.*$')
|
||||
valid_for_unix = re.compile(r'^/.*$')
|
||||
|
||||
if mustBeWindows:
|
||||
if not valid_for_windows.match(path):
|
||||
raise exceptions.ValidationError(_('{} is not a valid windows path').format(path))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid windows path').format(path))
|
||||
elif mustBeUnix:
|
||||
if not valid_for_unix.match(path):
|
||||
raise exceptions.ValidationError(_('{} is not a valid unix path').format(path))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid unix path').format(path))
|
||||
else:
|
||||
if not valid_for_windows.match(path) and not valid_for_unix.match(path):
|
||||
raise exceptions.ValidationError(_('{} is not a valid path').format(path))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid path').format(path))
|
||||
|
||||
return path
|
||||
|
||||
@ -255,7 +255,7 @@ def validateHostPortPair(hostPortPair: str) -> typing.Tuple[str, int]:
|
||||
except Exception:
|
||||
return validateHostname(host, 255, False), validatePort(port)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(_('{} is not a valid host:port pair').format(hostPortPair)) from None
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid host:port pair').format(hostPortPair)) from None
|
||||
|
||||
|
||||
def validateTimeout(timeOutStr: str) -> int:
|
||||
@ -282,7 +282,7 @@ def validateMac(mac: str) -> str:
|
||||
) # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
|
||||
|
||||
if macRE.match(mac) is None:
|
||||
raise exceptions.ValidationError(_('{} is not a valid MAC address').format(mac))
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid MAC address').format(mac))
|
||||
|
||||
return mac
|
||||
|
||||
@ -298,7 +298,7 @@ def validateMacRange(macRange: str) -> str:
|
||||
validateMac(macRangeStart)
|
||||
validateMac(macRangeEnd)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(_('{} is not a valid MAC range').format(macRange)) from None
|
||||
raise exceptions.validation.ValidationError(_('{} is not a valid MAC range').format(macRange)) from None
|
||||
|
||||
return macRange
|
||||
|
||||
@ -310,10 +310,10 @@ def validateEmail(email: str) -> str:
|
||||
:return: Raises exceptions.Validation exception if is invalid, else return the value "fixed"
|
||||
"""
|
||||
if len(email) > 254:
|
||||
raise exceptions.ValidationError(_('Email address is too long'))
|
||||
raise exceptions.validation.ValidationError(_('Email address is too long'))
|
||||
|
||||
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
|
||||
raise exceptions.ValidationError(_('Email address is not valid'))
|
||||
raise exceptions.validation.ValidationError(_('Email address is not valid'))
|
||||
|
||||
return email
|
||||
|
||||
@ -333,16 +333,16 @@ def validateBasename(baseName: str, length: int = -1) -> str:
|
||||
None -- [description]
|
||||
"""
|
||||
if re.match(r'^[a-zA-Z0-9][a-zA-Z0-9-]*$', baseName) is None:
|
||||
raise exceptions.ValidationError(_('The basename is not a valid for a hostname'))
|
||||
raise exceptions.validation.ValidationError(_('The basename is not a valid for a hostname'))
|
||||
|
||||
if length == 0:
|
||||
raise exceptions.ValidationError(_('The length of basename plus length must be greater than 0'))
|
||||
raise exceptions.validation.ValidationError(_('The length of basename plus length must be greater than 0'))
|
||||
|
||||
if length != -1 and len(baseName) + length > 15:
|
||||
raise exceptions.ValidationError(_('The length of basename plus length must not be greater than 15'))
|
||||
raise exceptions.validation.ValidationError(_('The length of basename plus length must not be greater than 15'))
|
||||
|
||||
if baseName.isdigit():
|
||||
raise exceptions.ValidationError(_('The machine name can\'t be only numbers'))
|
||||
raise exceptions.validation.ValidationError(_('The machine name can\'t be only numbers'))
|
||||
|
||||
return baseName
|
||||
|
||||
@ -355,7 +355,7 @@ def validateJson(jsonData: typing.Optional[str]) -> typing.Any:
|
||||
jsonData (typing.Optional[str]): Json data to validate
|
||||
|
||||
Raises:
|
||||
exceptions.ValidationError: If json data is not valid
|
||||
exceptions.validation.ValidationError: If json data is not valid
|
||||
|
||||
Returns:
|
||||
typing.Any: Json data as python object
|
||||
@ -365,7 +365,7 @@ def validateJson(jsonData: typing.Optional[str]) -> typing.Any:
|
||||
try:
|
||||
return json.loads(jsonData)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(_('Invalid JSON data')) from None
|
||||
raise exceptions.validation.ValidationError(_('Invalid JSON data')) from None
|
||||
|
||||
|
||||
def validateServerCertificate(cert: typing.Optional[str]) -> str:
|
||||
@ -376,7 +376,7 @@ def validateServerCertificate(cert: typing.Optional[str]) -> str:
|
||||
cert (str): Certificate to validate
|
||||
|
||||
Raises:
|
||||
exceptions.ValidationError: If certificate is not valid
|
||||
exceptions.validation.ValidationError: If certificate is not valid
|
||||
|
||||
Returns:
|
||||
str: Certificate
|
||||
@ -386,7 +386,7 @@ def validateServerCertificate(cert: typing.Optional[str]) -> str:
|
||||
try:
|
||||
security.checkServerCertificateIsValid(cert)
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(_('Invalid certificate') + f' :{e}') from e
|
||||
raise exceptions.validation.ValidationError(_('Invalid certificate') + f' :{e}') from e
|
||||
return cert
|
||||
|
||||
|
||||
@ -407,6 +407,6 @@ def validateServerCertificateMulti(value: typing.Optional[str]) -> str:
|
||||
try:
|
||||
load_pem_x509_certificate(pemCert.encode())
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(_('Invalid certificate') + f' :{e}') from e
|
||||
raise exceptions.validation.ValidationError(_('Invalid certificate') + f' :{e}') from e
|
||||
|
||||
return value
|
||||
|
@ -188,7 +188,7 @@ class EmailMFA(mfas.MFA):
|
||||
# if hostname is not valid, we will raise an exception
|
||||
hostname = self.hostname.cleanStr()
|
||||
if not hostname:
|
||||
raise exceptions.ValidationError(_('Invalid SMTP hostname'))
|
||||
raise exceptions.validation.ValidationError(_('Invalid SMTP hostname'))
|
||||
|
||||
# Now check is valid format
|
||||
if ':' in hostname:
|
||||
|
@ -36,7 +36,7 @@ import logging
|
||||
from django.utils.translation import gettext_noop as _, gettext
|
||||
|
||||
from uds import models
|
||||
from uds.core import mfas
|
||||
from uds.core import mfas, exceptions
|
||||
from uds.core.ui import gui
|
||||
|
||||
from uds.auths.Radius import client
|
||||
@ -48,7 +48,6 @@ from uds.auths.Radius.client import (
|
||||
# NEEDED
|
||||
)
|
||||
from uds.core.auths.auth import webPassword
|
||||
from uds.core.auths import exceptions
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core.module import Module
|
||||
@ -291,4 +290,4 @@ class RadiusOTP(mfas.MFA):
|
||||
username,
|
||||
request.ip,
|
||||
)
|
||||
raise exceptions.MFAError(err)
|
||||
raise exceptions.auth.MFAError(err)
|
||||
|
@ -40,11 +40,9 @@ from django.utils.translation import gettext_noop as _, gettext
|
||||
|
||||
from uds import models
|
||||
from uds.core.util.model import getSqlDatetime
|
||||
from uds.core import mfas
|
||||
from uds.core import mfas, exceptions
|
||||
from uds.core.ui import gui
|
||||
|
||||
from uds.core.auths import exceptions
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core.module import Module
|
||||
from uds.core.types.request import ExtendedHttpRequest
|
||||
@ -201,7 +199,7 @@ class TOTP_MFA(mfas.MFA):
|
||||
return
|
||||
|
||||
if self.cache.get(userId + code) is not None:
|
||||
raise exceptions.MFAError(gettext('Code is already used. Wait a minute and try again.'))
|
||||
raise exceptions.auth.MFAError(gettext('Code is already used. Wait a minute and try again.'))
|
||||
|
||||
# Get data from storage related to this user
|
||||
secret, qrShown = self._userData(userId)
|
||||
@ -210,7 +208,7 @@ class TOTP_MFA(mfas.MFA):
|
||||
if not self.getTOTP(userId, username).verify(
|
||||
code, valid_window=self.validWindow.num(), for_time=getSqlDatetime()
|
||||
):
|
||||
raise exceptions.MFAError(gettext('Invalid code'))
|
||||
raise exceptions.auth.MFAError(gettext('Invalid code'))
|
||||
|
||||
self.cache.put(userId + code, True, self.validWindow.num() * (TOTP_INTERVAL + 1))
|
||||
|
||||
|
@ -35,7 +35,7 @@ import typing
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Count, Q, signals
|
||||
from uds.core import auths, mfas
|
||||
from uds.core import auths, mfas, types
|
||||
from uds.core.util import log, storage
|
||||
|
||||
from .authenticator import Authenticator
|
||||
@ -136,7 +136,7 @@ class User(UUIDModel):
|
||||
self.last_access = getSqlDatetime()
|
||||
self.save(update_fields=['last_access'])
|
||||
|
||||
def logout(self, request: 'ExtendedHttpRequest') -> auths.AuthenticationResult:
|
||||
def logout(self, request: 'ExtendedHttpRequest') -> types.auth.AuthenticationResult:
|
||||
"""
|
||||
Invoked to log out this user
|
||||
Returns the url where to redirect user, or None if default url will be used
|
||||
|
@ -148,7 +148,7 @@ class EmailNotifier(messaging.Notifier):
|
||||
# if hostname is not valid, we will raise an exception
|
||||
hostname = self.hostname.cleanStr()
|
||||
if not hostname:
|
||||
raise exceptions.ValidationError(_('Invalid SMTP hostname'))
|
||||
raise exceptions.validation.ValidationError(_('Invalid SMTP hostname'))
|
||||
|
||||
# Now check is valid format
|
||||
if ':' in hostname:
|
||||
|
@ -128,7 +128,7 @@ class TelegramNotifier(messaging.Notifier):
|
||||
for i in (self.botname, self.accessToken, self.secret):
|
||||
s = i.cleanStr()
|
||||
if not s:
|
||||
raise exceptions.ValidationError(_('Invalid value for {}').format(i.label))
|
||||
raise exceptions.validation.ValidationError(_('Invalid value for {}').format(i.label))
|
||||
i.value = s
|
||||
|
||||
def initGui(self) -> None:
|
||||
|
@ -155,11 +155,11 @@ class LinuxOsADManager(LinuxOsManager):
|
||||
super().__init__(environment, values)
|
||||
if values:
|
||||
if values['domain'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a domain!'))
|
||||
if values['account'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a password for the account!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a password for the account!'))
|
||||
self._domain = values['domain']
|
||||
self._account = values['account']
|
||||
self._password = values['password']
|
||||
|
@ -146,11 +146,11 @@ class LinuxOsFreeIPAManager(LinuxOsManager):
|
||||
super().__init__(environment, values)
|
||||
if values:
|
||||
if values['domain'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a domain!'))
|
||||
if values['account'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a password for the account!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a password for the account!'))
|
||||
self._domain = values['domain']
|
||||
self._account = values['account']
|
||||
self._password = values['password']
|
||||
|
@ -77,7 +77,7 @@ class LinuxRandomPassManager(LinuxOsManager):
|
||||
super().__init__(environment, values)
|
||||
if values is not None:
|
||||
if values['userAccount'] == '':
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Must provide an user account!!!')
|
||||
)
|
||||
self._userAccount = values['userAccount']
|
||||
|
@ -96,11 +96,11 @@ class WindowsOsManager(osmanagers.OSManager):
|
||||
try:
|
||||
length = int(length)
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Length must be numeric!!')
|
||||
) from None
|
||||
if length > 6 or length < 1:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Length must be betwen 1 and 6')
|
||||
)
|
||||
return length
|
||||
|
@ -142,15 +142,15 @@ class WinDomainOsManager(WindowsOsManager):
|
||||
super().__init__(environment, values)
|
||||
if values:
|
||||
if values['domain'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a domain!'))
|
||||
# if values['domain'].find('.') == -1:
|
||||
# raise exceptions.ValidationException(_('Must provide domain in FQDN'))
|
||||
if values['account'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide an account to add machines to domain!'))
|
||||
if values['account'].find('\\') != -1:
|
||||
raise exceptions.ValidationError(_('DOM\\USER form is not allowed!'))
|
||||
raise exceptions.validation.ValidationError(_('DOM\\USER form is not allowed!'))
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a password for the account!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a password for the account!'))
|
||||
self._domain = values['domain']
|
||||
self._ou = values['ou'].strip()
|
||||
self._account = values['account']
|
||||
|
@ -86,9 +86,9 @@ class WinRandomPassManager(WindowsOsManager):
|
||||
super().__init__(environment, values)
|
||||
if values:
|
||||
if values['userAccount'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide an user account!!!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide an user account!!!'))
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a password for the account!!!'))
|
||||
raise exceptions.validation.ValidationError(_('Must provide a password for the account!!!'))
|
||||
self._userAccount = values['userAccount']
|
||||
self._password = values['password']
|
||||
else:
|
||||
|
@ -225,7 +225,7 @@ class OVirtLinkedService(services.Service): # pylint: disable=too-many-public-m
|
||||
if values:
|
||||
validators.validateBasename(self.baseName.value, self.lenName.num())
|
||||
if int(self.memory.value) < 256 or int(self.memoryGuaranteed.value) < 256:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('The minimum allowed memory is 256 Mb')
|
||||
)
|
||||
if int(self.memoryGuaranteed.value) > int(self.memory.value):
|
||||
|
@ -87,13 +87,13 @@ class PhysicalMachinesProvider(services.ServiceProvider):
|
||||
config.read_string(self.config.value)
|
||||
# Seems a valid configuration file, let's see if all se
|
||||
except Exception as e:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Invalid advanced configuration: ') + str(e)
|
||||
)
|
||||
|
||||
for section in config.sections():
|
||||
if section not in VALID_CONFIG_SECTIONS:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Invalid section in advanced configuration: ') + section
|
||||
)
|
||||
|
||||
@ -103,12 +103,12 @@ class PhysicalMachinesProvider(services.ServiceProvider):
|
||||
try:
|
||||
net.networksFromString(key) # Raises exception if net is invalid
|
||||
except Exception:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Invalid network in advanced configuration: ') + key
|
||||
) from None
|
||||
# Now check value is an url
|
||||
if config['wol'][key][:4] != 'http':
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Invalid url in advanced configuration: ') + key
|
||||
)
|
||||
|
||||
|
@ -154,7 +154,7 @@ class IPMachinesService(IPServiceBase):
|
||||
# Check that ips are valid
|
||||
for v in values['ipList']:
|
||||
if not net.isValidHost(v.split(';')[0]): # Get only IP/hostname
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('Invalid value detected on servers list: "{}"').format(v)
|
||||
)
|
||||
self._ips = [
|
||||
|
@ -80,7 +80,7 @@ class IPSingleMachineService(IPServiceBase):
|
||||
return
|
||||
|
||||
if not net.isValidHost(self.ip.value):
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
gettext('Invalid server used: "{}"'.format(self.ip.value))
|
||||
)
|
||||
|
||||
|
@ -181,7 +181,7 @@ class Provider(services.ServiceProvider):
|
||||
# values are only passed from administration client. Internals
|
||||
# instantiations are always empty.
|
||||
if values and self.methAlive.isTrue():
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('Methuselah is not alive!!! :-)')
|
||||
)
|
||||
|
||||
@ -222,7 +222,7 @@ class Provider(services.ServiceProvider):
|
||||
instance.methAge.value,
|
||||
instance.methAlive.value,
|
||||
)
|
||||
except exceptions.ValidationError as e:
|
||||
except exceptions.validation.ValidationError as e:
|
||||
# If we say that meth is alive, instantiation will
|
||||
return [False, str(e)]
|
||||
except Exception as e:
|
||||
|
@ -164,7 +164,7 @@ class ServiceOne(services.Service):
|
||||
# so we only need to validate params if values is not None
|
||||
if values:
|
||||
if self.colour.value == 'nonsense':
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
'The selected colour is invalid!!!'
|
||||
)
|
||||
|
||||
|
@ -190,7 +190,7 @@ class XenLinkedService(services.Service): # pylint: disable=too-many-public-met
|
||||
validators.validateBasename(self.baseName.value, self.lenName.num())
|
||||
|
||||
if int(self.memory.value) < 256:
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('The minimum allowed memory is 256 Mb')
|
||||
)
|
||||
|
||||
|
@ -90,7 +90,7 @@ class SPICETransport(BaseSpiceTransport):
|
||||
|
||||
logger.debug('Connection data: %s', con)
|
||||
if not con:
|
||||
raise exceptions.TransportError('No console connection data')
|
||||
raise exceptions.service.TransportError('No console connection data')
|
||||
|
||||
port: str = con['port'] or '-1'
|
||||
secure_port: str = con['secure_port'] or '-1'
|
||||
|
@ -106,7 +106,7 @@ class TSPICETransport(BaseSpiceTransport):
|
||||
raise
|
||||
|
||||
if not con:
|
||||
raise exceptions.TransportError(
|
||||
raise exceptions.service.TransportError(
|
||||
_('No console connection data received'),
|
||||
)
|
||||
|
||||
|
@ -89,7 +89,7 @@ class TestTransport(transports.Transport):
|
||||
self.testURL.value.startswith('http://')
|
||||
or self.testURL.value.startswith('https://')
|
||||
):
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('The url must be http or https')
|
||||
)
|
||||
|
||||
|
@ -90,7 +90,7 @@ class URLCustomTransport(transports.Transport):
|
||||
self.urlPattern.value.startswith('http://')
|
||||
or self.urlPattern.value.startswith('https://')
|
||||
):
|
||||
raise exceptions.ValidationError(
|
||||
raise exceptions.validation.ValidationError(
|
||||
_('The url must be http or https')
|
||||
)
|
||||
|
||||
|
@ -36,7 +36,7 @@ from django.views.i18n import JavaScriptCatalog
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from uds import REST
|
||||
from uds.core.auths.authenticator import AuthenticationInternalUrl
|
||||
from uds.core import types
|
||||
import uds.web.views
|
||||
import uds.admin.views
|
||||
|
||||
@ -85,7 +85,7 @@ urlpatterns = [
|
||||
# Index
|
||||
path(r'uds/page/services', uds.web.views.main.index, name='page.index'),
|
||||
# Login/logout
|
||||
path(r'uds/page/login', uds.web.views.main.login, name=AuthenticationInternalUrl.LOGIN.value),
|
||||
path(r'uds/page/login', uds.web.views.main.login, name=types.auth.AuthenticationInternalUrl.LOGIN.value),
|
||||
re_path(
|
||||
r'^uds/page/login/(?P<tag>[a-zA-Z0-9-]+)$',
|
||||
uds.web.views.main.login,
|
||||
|
@ -45,7 +45,7 @@ from django.utils.translation import gettext as _
|
||||
from uds.core.types.request import ExtendedHttpRequest
|
||||
|
||||
from uds.core.types.request import ExtendedHttpRequestWithUser
|
||||
from uds.core.auths import auth, exceptions, AuthenticationState
|
||||
from uds.core.auths import auth
|
||||
from uds.core.util.config import GlobalConfig
|
||||
from uds.core.managers.crypto import CryptoManager
|
||||
from uds.core.managers.user_service import UserServiceManager
|
||||
@ -55,7 +55,7 @@ from uds.web.forms.MFAForm import MFAForm
|
||||
from uds.web.util.authentication import checkLogin
|
||||
from uds.web.util.services import getServicesData
|
||||
from uds.web.util import configjs
|
||||
from uds.core import mfas, types
|
||||
from uds.core import mfas, types, exceptions
|
||||
from uds import auths, models
|
||||
from uds.core.util.model import getSqlStampInSeconds
|
||||
|
||||
@ -151,7 +151,7 @@ def logout(request: ExtendedHttpRequestWithUser) -> HttpResponse:
|
||||
request.session['restricted'] = False # Remove restricted
|
||||
request.authorized = False
|
||||
logoutResponse = request.user.logout(request)
|
||||
url = logoutResponse.url if logoutResponse.success == AuthenticationState.REDIRECT else None
|
||||
url = logoutResponse.url if logoutResponse.success == types.auth.AuthenticationState.REDIRECT else None
|
||||
|
||||
return auth.webLogout(request, url or request.session.get('logouturl', None))
|
||||
|
||||
@ -256,7 +256,7 @@ def mfa(request: ExtendedHttpRequest) -> HttpResponse: # pylint: disable=too-ma
|
||||
)
|
||||
|
||||
return response
|
||||
except exceptions.MFAError as e:
|
||||
except exceptions.auth.MFAError as e:
|
||||
logger.error('MFA error: %s', e)
|
||||
tries += 1
|
||||
request.session['mfa_tries'] = tries
|
||||
|
Loading…
Reference in New Issue
Block a user