mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-22 13:34:04 +03:00
* Fixed test error with modified http client
* linting even more, untill all files has been linted * Tested still works after changes :)
This commit is contained in:
parent
a058b61276
commit
8adc3ca40d
@ -3,6 +3,7 @@
|
|||||||
Settings file for uds server (Django)
|
Settings file for uds server (Django)
|
||||||
'''
|
'''
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import django
|
import django
|
||||||
|
|
||||||
# calculated paths for django and the site
|
# calculated paths for django and the site
|
||||||
@ -57,20 +58,23 @@ TIME_ZONE = 'Europe/Madrid'
|
|||||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||||
LANGUAGE_CODE = 'en'
|
LANGUAGE_CODE = 'en'
|
||||||
|
|
||||||
ugettext = lambda s: s
|
# Override for gettext so we can use the same syntax as in django
|
||||||
|
# and we can translate it later with our own function
|
||||||
|
def gettext(s):
|
||||||
|
return s
|
||||||
|
|
||||||
LANGUAGES = (
|
LANGUAGES = (
|
||||||
('es', ugettext('Spanish')),
|
('es', gettext('Spanish')),
|
||||||
('en', ugettext('English')),
|
('en', gettext('English')),
|
||||||
('fr', ugettext('French')),
|
('fr', gettext('French')),
|
||||||
('de', ugettext('German')),
|
('de', gettext('German')),
|
||||||
('pt', ugettext('Portuguese')),
|
('pt', gettext('Portuguese')),
|
||||||
('it', ugettext('Italian')),
|
('it', gettext('Italian')),
|
||||||
('ar', ugettext('Arabic')),
|
('ar', gettext('Arabic')),
|
||||||
('eu', ugettext('Basque')),
|
('eu', gettext('Basque')),
|
||||||
('ar', ugettext('Arabian')),
|
('ar', gettext('Arabian')),
|
||||||
('ca', ugettext('Catalan')),
|
('ca', gettext('Catalan')),
|
||||||
('zh-hans', ugettext('Chinese')),
|
('zh-hans', gettext('Chinese')),
|
||||||
)
|
)
|
||||||
|
|
||||||
LANGUAGE_COOKIE_NAME = 'uds_lang'
|
LANGUAGE_COOKIE_NAME = 'uds_lang'
|
||||||
@ -159,7 +163,7 @@ FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o750
|
|||||||
FILE_UPLOAD_MAX_MEMORY_SIZE = 512 * 1024 # 512 Kb
|
FILE_UPLOAD_MAX_MEMORY_SIZE = 512 * 1024 # 512 Kb
|
||||||
|
|
||||||
# Make this unique, and don't share it with anybody.
|
# Make this unique, and don't share it with anybody.
|
||||||
SECRET_KEY = 's5ky!7b5f#s35!e38xv%e-+iey6yi-#630x)kk3kk5_j8rie2*'
|
SECRET_KEY = 's5ky!7b5f#s35!e38xv%e-+iey6yi-#630x)kk3kk5_j8rie2*' # nosec: sample key, Remember to change it on production!!
|
||||||
# This is a very long string, an RSA KEY (this can be changed, but if u loose it, all encription will be lost)
|
# This is a very long string, an RSA KEY (this can be changed, but if u loose it, all encription will be lost)
|
||||||
RSA_KEY = '-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQC0qe1GlriQbHFYdKYRPBFDSS8Ne/TEKI2mtPKJf36XZTy6rIyH\nvUpT1gMScVjHjOISLNJQqktyv0G+ZGzLDmfkCUBev6JBlFwNeX3Dv/97Q0BsEzJX\noYHiDANUkuB30ukmGvG0sg1v4ccl+xs2Su6pFSc5bGINBcQ5tO0ZI6Q1nQIDAQAB\nAoGBAKA7Octqb+T/mQOX6ZXNjY38wXOXJb44LXHWeGnEnvUNf/Aci0L0epCidfUM\nfG33oKX4BMwwTVxHDrsa/HaXn0FZtbQeBVywZqMqWpkfL/Ho8XJ8Rsq8OfElrwek\nOCPXgxMzQYxoNHw8V97k5qhfupQ+h878BseN367xSyQ8plahAkEAuPgAi6aobwZ5\nFZhx/+6rmQ8sM8FOuzzm6bclrvfuRAUFa9+kMM2K48NAneAtLPphofqI8wDPCYgQ\nTl7O96GXVQJBAPoKtWIMuBHJXKCdUNOISmeEvEzJMPKduvyqnUYv17tM0JTV0uzO\nuDpJoNIwVPq5c3LJaORKeCZnt3dBrdH1FSkCQQC3DK+1hIvhvB0uUvxWlIL7aTmM\nSny47Y9zsc04N6JzbCiuVdeueGs/9eXHl6f9gBgI7eCD48QAocfJVygphqA1AkEA\nrvzZjcIK+9+pJHqUO0XxlFrPkQloaRK77uHUaW9IEjui6dZu4+2T/q7SjubmQgWR\nZy7Pap03UuFZA2wCoqJbaQJAUG0FVrnyUORUnMQvdDjAWps2sXoPvA8sbQY1W8dh\nR2k4TCFl2wD7LutvsdgdkiH0gWdh5tc1c4dRmSX1eQ27nA==\n-----END RSA PRIVATE KEY-----'
|
RSA_KEY = '-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQC0qe1GlriQbHFYdKYRPBFDSS8Ne/TEKI2mtPKJf36XZTy6rIyH\nvUpT1gMScVjHjOISLNJQqktyv0G+ZGzLDmfkCUBev6JBlFwNeX3Dv/97Q0BsEzJX\noYHiDANUkuB30ukmGvG0sg1v4ccl+xs2Su6pFSc5bGINBcQ5tO0ZI6Q1nQIDAQAB\nAoGBAKA7Octqb+T/mQOX6ZXNjY38wXOXJb44LXHWeGnEnvUNf/Aci0L0epCidfUM\nfG33oKX4BMwwTVxHDrsa/HaXn0FZtbQeBVywZqMqWpkfL/Ho8XJ8Rsq8OfElrwek\nOCPXgxMzQYxoNHw8V97k5qhfupQ+h878BseN367xSyQ8plahAkEAuPgAi6aobwZ5\nFZhx/+6rmQ8sM8FOuzzm6bclrvfuRAUFa9+kMM2K48NAneAtLPphofqI8wDPCYgQ\nTl7O96GXVQJBAPoKtWIMuBHJXKCdUNOISmeEvEzJMPKduvyqnUYv17tM0JTV0uzO\nuDpJoNIwVPq5c3LJaORKeCZnt3dBrdH1FSkCQQC3DK+1hIvhvB0uUvxWlIL7aTmM\nSny47Y9zsc04N6JzbCiuVdeueGs/9eXHl6f9gBgI7eCD48QAocfJVygphqA1AkEA\nrvzZjcIK+9+pJHqUO0XxlFrPkQloaRK77uHUaW9IEjui6dZu4+2T/q7SjubmQgWR\nZy7Pap03UuFZA2wCoqJbaQJAUG0FVrnyUORUnMQvdDjAWps2sXoPvA8sbQY1W8dh\nR2k4TCFl2wD7LutvsdgdkiH0gWdh5tc1c4dRmSX1eQ27nA==\n-----END RSA PRIVATE KEY-----'
|
||||||
|
|
||||||
@ -252,7 +256,7 @@ AUTHFILE = 'auth.log'
|
|||||||
USEFILE = 'use.log'
|
USEFILE = 'use.log'
|
||||||
TRACEFILE = 'trace.log'
|
TRACEFILE = 'trace.log'
|
||||||
OPERATIONSFILE = 'operations.log'
|
OPERATIONSFILE = 'operations.log'
|
||||||
LOGLEVEL = DEBUG and 'DEBUG' or 'INFO'
|
LOGLEVEL = 'DEBUG' if DEBUG else 'INFO'
|
||||||
ROTATINGSIZE = 32 * 1024 * 1024 # 32 Megabytes before rotating files
|
ROTATINGSIZE = 32 * 1024 * 1024 # 32 Megabytes before rotating files
|
||||||
|
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
@ -279,7 +283,6 @@ LOGGING = {
|
|||||||
'workers': {
|
'workers': {
|
||||||
'format': 'uds-w[%(process)-5s]: %(levelname)s %(asctime)s %(name)s:%(funcName)s %(lineno)d %(message)s'
|
'format': 'uds-w[%(process)-5s]: %(levelname)s %(asctime)s %(name)s:%(funcName)s %(lineno)d %(message)s'
|
||||||
},
|
},
|
||||||
'database': {'format': '%(levelname)s %(asctime)s Database %(message)s'},
|
|
||||||
'auth': {'format': '%(asctime)s %(message)s'},
|
'auth': {'format': '%(asctime)s %(message)s'},
|
||||||
'use': {'format': '%(asctime)s %(message)s'},
|
'use': {'format': '%(asctime)s %(message)s'},
|
||||||
'trace': {'format': '%(levelname)s %(asctime)s %(message)s'},
|
'trace': {'format': '%(levelname)s %(asctime)s %(message)s'},
|
||||||
|
@ -59,17 +59,17 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
self.client.enable_ipv4()
|
self.client.enable_ipv4()
|
||||||
|
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
request = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
req = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
||||||
# session[AUTHORIZED_KEY] = False, not logged in
|
# session[AUTHORIZED_KEY] = False, not logged in
|
||||||
self.assertEqual(request.session.get(AUTHORIZED_KEY), False)
|
self.assertEqual(req.session.get(AUTHORIZED_KEY), False)
|
||||||
|
|
||||||
# Ensure ip, and ip_proxy are set and both are the same, 127.0.0.1
|
# Ensure ip, and ip_proxy are set and both are the same, 127.0.0.1
|
||||||
self.assertEqual(request.ip, '127.0.0.1')
|
self.assertEqual(req.ip, '127.0.0.1')
|
||||||
self.assertEqual(request.ip_proxy, '127.0.0.1')
|
self.assertEqual(req.ip_proxy, '127.0.0.1')
|
||||||
self.assertEqual(request.ip_version, 4)
|
self.assertEqual(req.ip_version, 4)
|
||||||
|
|
||||||
# Ensure user is not set
|
# Ensure user is not set
|
||||||
self.assertEqual(request.user, None)
|
self.assertEqual(req.user, None)
|
||||||
# And redirects to index
|
# And redirects to index
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
@ -81,17 +81,17 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
self.client.enable_ipv6()
|
self.client.enable_ipv6()
|
||||||
|
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
request = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
req = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
||||||
# session[AUTHORIZED_KEY] = False, not logged in
|
# session[AUTHORIZED_KEY] = False, not logged in
|
||||||
self.assertEqual(request.session.get(AUTHORIZED_KEY), False)
|
self.assertEqual(req.session.get(AUTHORIZED_KEY), False)
|
||||||
|
|
||||||
# Ensure ip, and ip_proxy are set and both are the same,
|
# Ensure ip, and ip_proxy are set and both are the same,
|
||||||
self.assertEqual(request.ip, '::1')
|
self.assertEqual(req.ip, '::1')
|
||||||
self.assertEqual(request.ip_proxy, '::1')
|
self.assertEqual(req.ip_proxy, '::1')
|
||||||
self.assertEqual(request.ip_version, 6)
|
self.assertEqual(req.ip_version, 6)
|
||||||
|
|
||||||
# Ensure user is not set
|
# Ensure user is not set
|
||||||
self.assertEqual(request.user, None)
|
self.assertEqual(req.user, None)
|
||||||
# And redirects to index
|
# And redirects to index
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
@ -105,17 +105,17 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
user = self.login(as_admin=False)
|
user = self.login(as_admin=False)
|
||||||
|
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
request = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
req = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
||||||
# session[AUTHORIZED_KEY] = True, logged in
|
# session[AUTHORIZED_KEY] = True, logged in
|
||||||
self.assertEqual(request.session.get(AUTHORIZED_KEY), True)
|
self.assertEqual(req.session.get(AUTHORIZED_KEY), True)
|
||||||
|
|
||||||
# Ensure ip, and ip_proxy are set and both are the same,
|
# Ensure ip, and ip_proxy are set and both are the same,
|
||||||
self.assertEqual(request.ip, '127.0.0.1')
|
self.assertEqual(req.ip, '127.0.0.1')
|
||||||
self.assertEqual(request.ip_proxy, '127.0.0.1')
|
self.assertEqual(req.ip_proxy, '127.0.0.1')
|
||||||
self.assertEqual(request.ip_version, 4)
|
self.assertEqual(req.ip_version, 4)
|
||||||
|
|
||||||
# Ensure user is correct
|
# Ensure user is correct
|
||||||
self.assertEqual(request.user.uuid, user.uuid)
|
self.assertEqual(req.user.uuid, user.uuid)
|
||||||
# And redirects to index
|
# And redirects to index
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
@ -129,44 +129,45 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
user = self.login(as_admin=False)
|
user = self.login(as_admin=False)
|
||||||
|
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
request = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
req = typing.cast('ExtendedHttpRequestWithUser', response.wsgi_request)
|
||||||
# session[AUTHORIZED_KEY] = True, logged in
|
# session[AUTHORIZED_KEY] = True, logged in
|
||||||
self.assertEqual(request.session.get(AUTHORIZED_KEY), True)
|
self.assertEqual(req.session.get(AUTHORIZED_KEY), True)
|
||||||
|
|
||||||
# Ensure ip, and ip_proxy are set and both are the same,
|
# Ensure ip, and ip_proxy are set and both are the same,
|
||||||
self.assertEqual(request.ip, '::1')
|
self.assertEqual(req.ip, '::1')
|
||||||
self.assertEqual(request.ip_proxy, '::1')
|
self.assertEqual(req.ip_proxy, '::1')
|
||||||
self.assertEqual(request.ip_version, 6)
|
self.assertEqual(req.ip_version, 6)
|
||||||
|
|
||||||
# Ensure user is correct
|
# Ensure user is correct
|
||||||
self.assertEqual(request.user.uuid, user.uuid)
|
self.assertEqual(req.user.uuid, user.uuid)
|
||||||
# And redirects to index
|
# And redirects to index
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
|
|
||||||
def test_no_middleware(self) -> None:
|
def test_no_middleware(self) -> None:
|
||||||
# Ensure GlobalRequestMiddleware is not present
|
# Ensure GlobalRequestMiddleware is not present
|
||||||
GlobalRequestMiddlewareTest.remove_middleware('uds.middleware.request.GlobalRequestMiddleware')
|
GlobalRequestMiddlewareTest.remove_middleware(
|
||||||
|
'uds.middleware.request.GlobalRequestMiddleware'
|
||||||
|
)
|
||||||
self.client.enable_ipv4()
|
self.client.enable_ipv4()
|
||||||
|
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
request = response.wsgi_request
|
req = response.wsgi_request
|
||||||
# session[AUTHORIZED_KEY] is not present
|
# session[AUTHORIZED_KEY] is not present
|
||||||
self.assertEqual(AUTHORIZED_KEY in request.session, False)
|
self.assertEqual(AUTHORIZED_KEY in req.session, False)
|
||||||
|
|
||||||
# ip is not present, nor ip_proxy or ip_version
|
# ip is not present, nor ip_proxy or ip_version
|
||||||
self.assertEqual(hasattr(request, 'ip'), False)
|
self.assertEqual(hasattr(req, 'ip'), False)
|
||||||
self.assertEqual(hasattr(request, 'ip_proxy'), False)
|
self.assertEqual(hasattr(req, 'ip_proxy'), False)
|
||||||
self.assertEqual(hasattr(request, 'ip_version'), False)
|
self.assertEqual(hasattr(req, 'ip_version'), False)
|
||||||
|
|
||||||
# Also, user is not present
|
# Also, user is not present
|
||||||
self.assertEqual(hasattr(request, 'user'), False)
|
self.assertEqual(hasattr(req, 'user'), False)
|
||||||
|
|
||||||
# And redirects to index
|
# And redirects to index
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
|
|
||||||
|
|
||||||
def test_detect_ips_no_proxy(self) -> None:
|
def test_detect_ips_no_proxy(self) -> None:
|
||||||
req = mock.Mock()
|
req = mock.Mock()
|
||||||
# Use an ipv4 and an ipv6 address
|
# Use an ipv4 and an ipv6 address
|
||||||
@ -174,7 +175,7 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
req.META = {
|
req.META = {
|
||||||
'REMOTE_ADDR': ip,
|
'REMOTE_ADDR': ip,
|
||||||
}
|
}
|
||||||
request._fill_ips(req)
|
request._fill_ips(req) # pylint: disable=protected-access
|
||||||
self.assertEqual(req.ip, ip)
|
self.assertEqual(req.ip, ip)
|
||||||
self.assertEqual(req.ip_proxy, ip)
|
self.assertEqual(req.ip_proxy, ip)
|
||||||
self.assertEqual(req.ip_version, 4 if '.' in ip else 6)
|
self.assertEqual(req.ip_version, 4 if '.' in ip else 6)
|
||||||
@ -193,22 +194,34 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
'HTTP_X_FORWARDED_FOR': client_ip,
|
'HTTP_X_FORWARDED_FOR': client_ip,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
req.META = {
|
req.META = {'HTTP_X_FORWARDED_FOR': f'{client_ip},{proxy}'}
|
||||||
'HTTP_X_FORWARDED_FOR': "{},{}".format(client_ip, proxy),
|
|
||||||
}
|
|
||||||
|
|
||||||
request._fill_ips(req)
|
request._fill_ips(req) # pylint: disable=protected-access
|
||||||
self.assertEqual(req.ip, client_ip, "Failed for {}".format(req.META))
|
self.assertEqual(
|
||||||
self.assertEqual(req.ip_proxy, client_ip, "Failed for {}".format(req.META))
|
req.ip, client_ip, "Failed for {}".format(req.META)
|
||||||
self.assertEqual(req.ip_version, 4 if '.' in client_ip else 6, "Failed for {}".format(req.META))
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
req.ip_proxy, client_ip, "Failed for {}".format(req.META)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
req.ip_version,
|
||||||
|
4 if '.' in client_ip else 6,
|
||||||
|
"Failed for {}".format(req.META),
|
||||||
|
)
|
||||||
|
|
||||||
def test_detect_ips_proxy_chained(self) -> None:
|
def test_detect_ips_proxy_chained(self) -> None:
|
||||||
config.GlobalConfig.BEHIND_PROXY.set(True)
|
config.GlobalConfig.BEHIND_PROXY.set(True)
|
||||||
req = mock.Mock()
|
req = mock.Mock()
|
||||||
# Use an ipv4 and an ipv6 address
|
# Use an ipv4 and an ipv6 address
|
||||||
for client_ip in ['192.168.128.128', '2001:db8:85a3:8d3:1319:8a2e:370:7348']:
|
for client_ip in ['192.168.128.128', '2001:db8:85a3:8d3:1319:8a2e:370:7348']:
|
||||||
for first_proxy in ['192.168.200.200', '2001:db8:85a3:8d3:1319:8a2e:370:7349']:
|
for first_proxy in [
|
||||||
for second_proxy in ['192.168.201.201', '2001:db8:85a3:8d3:1319:8a2e:370:7350']:
|
'192.168.200.200',
|
||||||
|
'2001:db8:85a3:8d3:1319:8a2e:370:7349',
|
||||||
|
]:
|
||||||
|
for second_proxy in [
|
||||||
|
'192.168.201.201',
|
||||||
|
'2001:db8:85a3:8d3:1319:8a2e:370:7350',
|
||||||
|
]:
|
||||||
for with_nginx in [True, False]:
|
for with_nginx in [True, False]:
|
||||||
x_forwarded_for = '{}, {}'.format(client_ip, first_proxy)
|
x_forwarded_for = '{}, {}'.format(client_ip, first_proxy)
|
||||||
if with_nginx is False:
|
if with_nginx is False:
|
||||||
@ -218,11 +231,12 @@ class GlobalRequestMiddlewareTest(test.WEBTestCase):
|
|||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
req.META = {
|
req.META = {
|
||||||
'HTTP_X_FORWARDED_FOR': "{}, {}".format(x_forwarded_for, second_proxy),
|
'HTTP_X_FORWARDED_FOR': "{}, {}".format(
|
||||||
|
x_forwarded_for, second_proxy
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
request._fill_ips(req)
|
request._fill_ips(req)
|
||||||
self.assertEqual(req.ip, first_proxy)
|
self.assertEqual(req.ip, first_proxy)
|
||||||
self.assertEqual(req.ip_proxy, client_ip)
|
self.assertEqual(req.ip_proxy, client_ip)
|
||||||
self.assertEqual(req.ip_version, 4 if '.' in first_proxy else 6)
|
self.assertEqual(req.ip_version, 4 if '.' in first_proxy else 6)
|
||||||
|
|
||||||
|
@ -28,12 +28,10 @@
|
|||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import typing
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from uds.core.util import config
|
|
||||||
from uds.core.managers.crypto import CryptoManager
|
from uds.core.managers.crypto import CryptoManager
|
||||||
|
|
||||||
from ..utils import test
|
from ..utils import test
|
||||||
@ -48,10 +46,11 @@ class RedirectMiddlewareTest(test.UDSTransactionTestCase):
|
|||||||
"""
|
"""
|
||||||
def test_redirect(self):
|
def test_redirect(self):
|
||||||
RedirectMiddlewareTest.add_middleware('uds.middleware.redirect.RedirectMiddleware')
|
RedirectMiddlewareTest.add_middleware('uds.middleware.redirect.RedirectMiddleware')
|
||||||
|
page = 'https://testserver' + reverse('page.index')
|
||||||
response = self.client.get('/', secure=False)
|
response = self.client.get('/', secure=False)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 301)
|
||||||
self.assertEqual(response.url, 'https://testserver/')
|
self.assertEqual(response.url, page)
|
||||||
# Try secure, will redirect to index
|
# Try secure, will redirect to index, not absulute url
|
||||||
response = self.client.get('/', secure=True)
|
response = self.client.get('/', secure=True)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, reverse('page.index'))
|
self.assertEqual(response.url, reverse('page.index'))
|
||||||
@ -60,7 +59,7 @@ class RedirectMiddlewareTest(test.UDSTransactionTestCase):
|
|||||||
for _ in range(32):
|
for _ in range(32):
|
||||||
url = f'/{CryptoManager().randomString(32)}'
|
url = f'/{CryptoManager().randomString(32)}'
|
||||||
response = self.client.get(url, secure=False)
|
response = self.client.get(url, secure=False)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 301)
|
||||||
self.assertEqual(response.url, f'https://testserver{url}')
|
self.assertEqual(response.url, page)
|
||||||
response = self.client.get(url, secure=True)
|
response = self.client.get(url, secure=True)
|
||||||
self.assertEqual(response.status_code, 404) # Not found
|
self.assertEqual(response.status_code, 404) # Not found
|
||||||
|
@ -53,14 +53,9 @@ class UDSHttpResponse(HttpResponse):
|
|||||||
super().__init__(content, *args, **kwargs)
|
super().__init__(content, *args, **kwargs)
|
||||||
self.content = content
|
self.content = content
|
||||||
|
|
||||||
def json(self) -> typing.Any:
|
|
||||||
return super().json() # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
class UDSClientMixin:
|
class UDSClientMixin:
|
||||||
headers: typing.Dict[str, str] = {
|
uds_headers: typing.Dict[str, str]
|
||||||
'HTTP_USER_AGENT': 'Testing user agent',
|
|
||||||
}
|
|
||||||
ip_version: int = 4
|
ip_version: int = 4
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
@ -73,18 +68,21 @@ class UDSClientMixin:
|
|||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'uds.middleware.request.GlobalRequestMiddleware',
|
'uds.middleware.request.GlobalRequestMiddleware',
|
||||||
]
|
]
|
||||||
|
self.uds_headers = {
|
||||||
|
'HTTP_USER_AGENT': 'Testing user agent',
|
||||||
|
}
|
||||||
|
|
||||||
# Update settings security options
|
# Update settings security options
|
||||||
settings.RSA_KEY = '-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcANi/08cnpn04\njKW/o2G1k4SIa6dJks8DmT4MQHOWqYC46YSIIPzqGoBPcvkbDSPSFBnByo3HhMY+\nk4JHc9SUwEmHSJWDCHjt7XSXX/ryqH0QQIJtSjk9Bc+GkOU24mnbITiw7ORjp7VN\nvgFdFhjVsZM/NjX/6Y9DoCPC1mGj0O9Dd4MfCsNwUxRhhR6LdrEnpRUSVW0Ksxbz\ncTfpQjdFr86+1BeUbzqN2HDcEGhvioj+0lGPXcOZoRNYU16H7kjLNP+o+rC7f/q/\nfoOYLzDSkmzePbcG+g0Hv7K7fuLus05ZWjupOmJA9hytB1BIF4p5f4ewl05Fx2Zj\nG2LneO2fAgMBAAECggEBANDimOnh2TkDceeMWx+OsAooC3E/zbEkjBudl3UoiNcn\nD0oCpkxeDeT0zpkgz/ZoTnd7kE0Y1e73WQc3JT5UcyXdQLMLLrIgDDnT+Jx1jB5z\n7XLN3UiJbblL2BOrZYbsCJf/fgU2l08rgBBVdJP+lAvps6YUAcd+6gDKfsnSpRhU\nWBHLZde7l6vUJ2OK9ZmHaghF5E8Xx918OSUKFJfGTYL5JLTb/scdl8vQse1quWC1\nk48PPXK10vOFvYWonQpRb2cOK/PPjPXPNWzcQyQY9D1iOeFvRyLqOXYE/ZY+qDe2\nHdPGrkl67yz01nzepkWWg/ZNbMXeZZyOnZm0aXtOxtkCgYEA/Qz3mescgwrt67yh\nFrbXjUqiVf2IpbNt88CUcbY0r1EdTA9OMtOtPYNvfpyRIRfDaZJ1zAdh3CZ2/hTm\ng+VUtseKnUDCi0xIBKX3V2O8sryWt2KStTnTo6JP0T47yXvmaRu5cutgoaD9SK+r\nN5vg1D2gNLmsT8uJh1Bl/yWGC4sCgYEA3pFGgAmiywsvmsddkI+LujoQVTiqkfFg\nMHHsJFOZlhYO83g49Q11pcQ70ukT6e89Ggwy///+z19p8jJ+wGqQWQLsM6eO1utg\nnJ8wMTwk8tOEm9MnWnnWhtG9KWcgkmwOVQiesJdWa1xOqsBKGchUkugmFycKNsiG\nHUbogbJ0OL0CgYBVLIcuxKdNKGGaxlwGVDbLdQKdJQBYncN1ly2f9K9ZD1loH4K3\nsu4N1W6y1Co5VFFO+KAzs4xp2HyW2xwX6xoPh6yNb53L2zombmKJhKWgF8A3K7Or\n0jH9UwXArUzcbZrJaC6MktNss85tJ8vepNYROkjxVkm8dgrtg89BCTVMLwKBgQCW\nSSh+uoL3cdUyQV63h4ZFOIHg2cOrin52F+bpXJ3/z2NHGa30IqOHTGtM7l+o/geX\nOBeT72tC4d2rUlduXEaeJDAUbRcxnnx9JayoAkG8ygDoK3uOR2kJXkTJ2T4QQPCo\nkIp/GaGcGxdviyo+IJyjGijmR1FJTrvotwG22iZKTQKBgQCIh50Dz0/rqZB4Om5g\nLLdZn1C8/lOR8hdK9WUyPHZfJKpQaDOlNdiy9x6xD6+uIQlbNsJhlDbOudHDurfI\nghGbJ1sy1FUloP+V3JAFS88zIwrddcGEso8YMFMCE1fH2/q35XGwZEnUq7ttDaxx\nHmTQ2w37WASIUgCl2GhM25np0Q==\n-----END PRIVATE KEY-----\n'
|
settings.RSA_KEY = '-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcANi/08cnpn04\njKW/o2G1k4SIa6dJks8DmT4MQHOWqYC46YSIIPzqGoBPcvkbDSPSFBnByo3HhMY+\nk4JHc9SUwEmHSJWDCHjt7XSXX/ryqH0QQIJtSjk9Bc+GkOU24mnbITiw7ORjp7VN\nvgFdFhjVsZM/NjX/6Y9DoCPC1mGj0O9Dd4MfCsNwUxRhhR6LdrEnpRUSVW0Ksxbz\ncTfpQjdFr86+1BeUbzqN2HDcEGhvioj+0lGPXcOZoRNYU16H7kjLNP+o+rC7f/q/\nfoOYLzDSkmzePbcG+g0Hv7K7fuLus05ZWjupOmJA9hytB1BIF4p5f4ewl05Fx2Zj\nG2LneO2fAgMBAAECggEBANDimOnh2TkDceeMWx+OsAooC3E/zbEkjBudl3UoiNcn\nD0oCpkxeDeT0zpkgz/ZoTnd7kE0Y1e73WQc3JT5UcyXdQLMLLrIgDDnT+Jx1jB5z\n7XLN3UiJbblL2BOrZYbsCJf/fgU2l08rgBBVdJP+lAvps6YUAcd+6gDKfsnSpRhU\nWBHLZde7l6vUJ2OK9ZmHaghF5E8Xx918OSUKFJfGTYL5JLTb/scdl8vQse1quWC1\nk48PPXK10vOFvYWonQpRb2cOK/PPjPXPNWzcQyQY9D1iOeFvRyLqOXYE/ZY+qDe2\nHdPGrkl67yz01nzepkWWg/ZNbMXeZZyOnZm0aXtOxtkCgYEA/Qz3mescgwrt67yh\nFrbXjUqiVf2IpbNt88CUcbY0r1EdTA9OMtOtPYNvfpyRIRfDaZJ1zAdh3CZ2/hTm\ng+VUtseKnUDCi0xIBKX3V2O8sryWt2KStTnTo6JP0T47yXvmaRu5cutgoaD9SK+r\nN5vg1D2gNLmsT8uJh1Bl/yWGC4sCgYEA3pFGgAmiywsvmsddkI+LujoQVTiqkfFg\nMHHsJFOZlhYO83g49Q11pcQ70ukT6e89Ggwy///+z19p8jJ+wGqQWQLsM6eO1utg\nnJ8wMTwk8tOEm9MnWnnWhtG9KWcgkmwOVQiesJdWa1xOqsBKGchUkugmFycKNsiG\nHUbogbJ0OL0CgYBVLIcuxKdNKGGaxlwGVDbLdQKdJQBYncN1ly2f9K9ZD1loH4K3\nsu4N1W6y1Co5VFFO+KAzs4xp2HyW2xwX6xoPh6yNb53L2zombmKJhKWgF8A3K7Or\n0jH9UwXArUzcbZrJaC6MktNss85tJ8vepNYROkjxVkm8dgrtg89BCTVMLwKBgQCW\nSSh+uoL3cdUyQV63h4ZFOIHg2cOrin52F+bpXJ3/z2NHGa30IqOHTGtM7l+o/geX\nOBeT72tC4d2rUlduXEaeJDAUbRcxnnx9JayoAkG8ygDoK3uOR2kJXkTJ2T4QQPCo\nkIp/GaGcGxdviyo+IJyjGijmR1FJTrvotwG22iZKTQKBgQCIh50Dz0/rqZB4Om5g\nLLdZn1C8/lOR8hdK9WUyPHZfJKpQaDOlNdiy9x6xD6+uIQlbNsJhlDbOudHDurfI\nghGbJ1sy1FUloP+V3JAFS88zIwrddcGEso8YMFMCE1fH2/q35XGwZEnUq7ttDaxx\nHmTQ2w37WASIUgCl2GhM25np0Q==\n-----END PRIVATE KEY-----\n'
|
||||||
settings.CERTIFICATE = '-----BEGIN CERTIFICATE-----\nMIICzTCCAjYCCQCOUQEWpuEa3jANBgkqhkiG9w0BAQUFADCBqjELMAkGA1UEBhMC\nRVMxDzANBgNVBAgMBk1hZHJpZDEUMBIGA1UEBwwLQWxjb3Jjw4PCs24xHTAbBgNV\nBAoMFFZpcnR1YWwgQ2FibGUgUy5MLlUuMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEY\nMBYGA1UEAwwPQWRvbGZvIEfDg8KzbWV6MSUwIwYJKoZIhvcNAQkBFhZhZ29tZXpA\ndmlydHVhbGNhYmxlLmVzMB4XDTEyMDYyNTA0MjM0MloXDTEzMDYyNTA0MjM0Mlow\ngaoxCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxFDASBgNVBAcMC0FsY29y\nY8ODwrNuMR0wGwYDVQQKDBRWaXJ0dWFsIENhYmxlIFMuTC5VLjEUMBIGA1UECwwL\nRGV2ZWxvcG1lbnQxGDAWBgNVBAMMD0Fkb2xmbyBHw4PCs21lejElMCMGCSqGSIb3\nDQEJARYWYWdvbWV6QHZpcnR1YWxjYWJsZS5lczCBnzANBgkqhkiG9w0BAQEFAAOB\njQAwgYkCgYEA35iGyHS/GVdWk3n9kQ+wsCLR++jd9Vez/s407/natm8YDteKksA0\nMwIvDAX722blm8PUya2NOlnum8KdyUPDOq825XERDlsIA+sTd6lb1c7w44qZ/pb+\n68mhXoRx2VJsu//+zhBkaQ1/KcugeHa4WLRIH35YLxdQDxrXS1eQWccCAwEAATAN\nBgkqhkiG9w0BAQUFAAOBgQAk+fJPpY+XvUsxR2A4SaQ8TGnE2x4PtpwCrCVzKEU9\nW2ugdXvysxkHbib3+JdA6s+lJjHs5HiMZPo/ak8adEKke+d10EU5YcUaJRRUpStY\nqQHziaqOl5Hgi75Kjskq6+tCU0Iui+s9pBg0V6y1AQsCmH2xFs7t1oEOGRFVarfF\n4Q==\n-----END CERTIFICATE-----'
|
settings.CERTIFICATE = '-----BEGIN CERTIFICATE-----\nMIICzTCCAjYCCQCOUQEWpuEa3jANBgkqhkiG9w0BAQUFADCBqjELMAkGA1UEBhMC\nRVMxDzANBgNVBAgMBk1hZHJpZDEUMBIGA1UEBwwLQWxjb3Jjw4PCs24xHTAbBgNV\nBAoMFFZpcnR1YWwgQ2FibGUgUy5MLlUuMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEY\nMBYGA1UEAwwPQWRvbGZvIEfDg8KzbWV6MSUwIwYJKoZIhvcNAQkBFhZhZ29tZXpA\ndmlydHVhbGNhYmxlLmVzMB4XDTEyMDYyNTA0MjM0MloXDTEzMDYyNTA0MjM0Mlow\ngaoxCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxFDASBgNVBAcMC0FsY29y\nY8ODwrNuMR0wGwYDVQQKDBRWaXJ0dWFsIENhYmxlIFMuTC5VLjEUMBIGA1UECwwL\nRGV2ZWxvcG1lbnQxGDAWBgNVBAMMD0Fkb2xmbyBHw4PCs21lejElMCMGCSqGSIb3\nDQEJARYWYWdvbWV6QHZpcnR1YWxjYWJsZS5lczCBnzANBgkqhkiG9w0BAQEFAAOB\njQAwgYkCgYEA35iGyHS/GVdWk3n9kQ+wsCLR++jd9Vez/s407/natm8YDteKksA0\nMwIvDAX722blm8PUya2NOlnum8KdyUPDOq825XERDlsIA+sTd6lb1c7w44qZ/pb+\n68mhXoRx2VJsu//+zhBkaQ1/KcugeHa4WLRIH35YLxdQDxrXS1eQWccCAwEAATAN\nBgkqhkiG9w0BAQUFAAOBgQAk+fJPpY+XvUsxR2A4SaQ8TGnE2x4PtpwCrCVzKEU9\nW2ugdXvysxkHbib3+JdA6s+lJjHs5HiMZPo/ak8adEKke+d10EU5YcUaJRRUpStY\nqQHziaqOl5Hgi75Kjskq6+tCU0Iui+s9pBg0V6y1AQsCmH2xFs7t1oEOGRFVarfF\n4Q==\n-----END CERTIFICATE-----'
|
||||||
|
|
||||||
def add_header(self, name: str, value: str):
|
def add_header(self, name: str, value: str):
|
||||||
self.headers[name] = value
|
self.uds_headers[name] = value
|
||||||
|
|
||||||
def set_user_agent(self, user_agent: typing.Optional[str] = None):
|
def set_user_agent(self, user_agent: typing.Optional[str] = None):
|
||||||
user_agent = user_agent or ''
|
user_agent = user_agent or ''
|
||||||
# Add 'HTTP_USER_AGENT' header
|
# Add 'HTTP_USER_AGENT' header
|
||||||
self.headers['HTTP_USER_AGENT'] = user_agent
|
self.uds_headers['HTTP_USER_AGENT'] = user_agent
|
||||||
|
|
||||||
def enable_ipv4(self):
|
def enable_ipv4(self):
|
||||||
self.ip_version = 4
|
self.ip_version = 4
|
||||||
@ -121,7 +119,7 @@ class UDSClient(UDSClientMixin, Client):
|
|||||||
# Copy request dict
|
# Copy request dict
|
||||||
request = request.copy()
|
request = request.copy()
|
||||||
# Add headers
|
# Add headers
|
||||||
request.update(self.headers)
|
request.update(self.uds_headers)
|
||||||
return super().request(**request)
|
return super().request(**request)
|
||||||
|
|
||||||
def get(self, *args, **kwargs) -> 'UDSHttpResponse':
|
def get(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||||
@ -176,9 +174,10 @@ class UDSAsyncClient(UDSClientMixin, AsyncClient):
|
|||||||
# Copy request dict
|
# Copy request dict
|
||||||
request = request.copy()
|
request = request.copy()
|
||||||
# Add headers
|
# Add headers
|
||||||
request.update(self.headers)
|
request.update(self.uds_headers)
|
||||||
return await super().request(**request)
|
return await super().request(**request)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-overridden-method
|
||||||
async def get(self, *args, **kwargs) -> 'UDSHttpResponse':
|
async def get(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||||
self.append_remote_addr(kwargs)
|
self.append_remote_addr(kwargs)
|
||||||
return typing.cast('UDSHttpResponse', await super().get(*args, **kwargs))
|
return typing.cast('UDSHttpResponse', await super().get(*args, **kwargs))
|
||||||
@ -187,6 +186,7 @@ class UDSAsyncClient(UDSClientMixin, AsyncClient):
|
|||||||
# compose url
|
# compose url
|
||||||
return await self.get(self.compose_rest_url(method), *args, **kwargs)
|
return await self.get(self.compose_rest_url(method), *args, **kwargs)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-overridden-method
|
||||||
async def post(self, *args, **kwargs) -> 'UDSHttpResponse':
|
async def post(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||||
self.append_remote_addr(kwargs)
|
self.append_remote_addr(kwargs)
|
||||||
return typing.cast('UDSHttpResponse', await super().post(*args, **kwargs))
|
return typing.cast('UDSHttpResponse', await super().post(*args, **kwargs))
|
||||||
@ -195,6 +195,7 @@ class UDSAsyncClient(UDSClientMixin, AsyncClient):
|
|||||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||||
return await self.post(self.compose_rest_url(method), *args, **kwargs)
|
return await self.post(self.compose_rest_url(method), *args, **kwargs)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-overridden-method
|
||||||
async def put(self, *args, **kwargs) -> 'UDSHttpResponse':
|
async def put(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||||
return typing.cast('UDSHttpResponse', await super().put(*args, **kwargs))
|
return typing.cast('UDSHttpResponse', await super().put(*args, **kwargs))
|
||||||
@ -203,6 +204,7 @@ class UDSAsyncClient(UDSClientMixin, AsyncClient):
|
|||||||
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
kwargs['content_type'] = kwargs.get('content_type', 'application/json')
|
||||||
return await self.put(self.compose_rest_url(method), *args, **kwargs)
|
return await self.put(self.compose_rest_url(method), *args, **kwargs)
|
||||||
|
|
||||||
|
# pylint: disable=invalid-overridden-method
|
||||||
async def delete(self, *args, **kwargs) -> 'UDSHttpResponse':
|
async def delete(self, *args, **kwargs) -> 'UDSHttpResponse':
|
||||||
self.append_remote_addr(kwargs)
|
self.append_remote_addr(kwargs)
|
||||||
return typing.cast('UDSHttpResponse', await super().delete(*args, **kwargs))
|
return typing.cast('UDSHttpResponse', await super().delete(*args, **kwargs))
|
||||||
@ -247,6 +249,7 @@ class UDSTransactionTestCase(UDSTestCaseMixin, TransactionTestCase):
|
|||||||
setupClass(cls)
|
setupClass(cls)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
def setupClass(
|
def setupClass(
|
||||||
cls: typing.Union[typing.Type[UDSTestCase], typing.Type[UDSTransactionTestCase]]
|
cls: typing.Union[typing.Type[UDSTestCase], typing.Type[UDSTransactionTestCase]]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -33,6 +33,7 @@ import typing
|
|||||||
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from uds import models
|
||||||
from uds.core.util.config import GlobalConfig
|
from uds.core.util.config import GlobalConfig
|
||||||
|
|
||||||
from ...utils.web import test
|
from ...utils.web import test
|
||||||
@ -41,8 +42,6 @@ from ...fixtures import authenticators as fixtures_authenticators
|
|||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from uds import models
|
|
||||||
|
|
||||||
|
|
||||||
class WebLoginLogoutTest(test.WEBTestCase):
|
class WebLoginLogoutTest(test.WEBTestCase):
|
||||||
"""
|
"""
|
||||||
|
@ -65,7 +65,7 @@ def loadModulesUrls() -> typing.List[typing.Any]:
|
|||||||
modName = 'uds.dispatchers'
|
modName = 'uds.dispatchers'
|
||||||
pkgpath = os.path.dirname(typing.cast(str, sys.modules[modName].__file__))
|
pkgpath = os.path.dirname(typing.cast(str, sys.modules[modName].__file__))
|
||||||
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
||||||
fullModName = '{}.{}.urls'.format(modName, name)
|
fullModName = f'{modName}.{name}.urls'
|
||||||
try:
|
try:
|
||||||
mod = importlib.import_module(fullModName)
|
mod = importlib.import_module(fullModName)
|
||||||
urlpatterns: typing.List[typing.Any] = getattr(mod, 'urlpatterns')
|
urlpatterns: typing.List[typing.Any] = getattr(mod, 'urlpatterns')
|
||||||
@ -98,7 +98,7 @@ def importModules(modName: str, *, packageName: typing.Optional[str] = None) ->
|
|||||||
pkgpath = os.path.dirname(typing.cast(str, sys.modules[modName].__file__))
|
pkgpath = os.path.dirname(typing.cast(str, sys.modules[modName].__file__))
|
||||||
if packageName: # Append package name to path and module name
|
if packageName: # Append package name to path and module name
|
||||||
pkgpath = os.path.join(pkgpath, packageName)
|
pkgpath = os.path.join(pkgpath, packageName)
|
||||||
modName = '{}.{}'.format(modName, packageName)
|
modName = f'{modName}.{packageName}'
|
||||||
|
|
||||||
logger.info('* Importing modules from %s', pkgpath)
|
logger.info('* Importing modules from %s', pkgpath)
|
||||||
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
for _, name, _ in pkgutil.iter_modules([pkgpath]):
|
||||||
|
@ -35,12 +35,13 @@ import socket
|
|||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import enum
|
|
||||||
|
|
||||||
class IpType(typing.NamedTuple):
|
class IpType(typing.NamedTuple):
|
||||||
ip: int
|
ip: int
|
||||||
version: typing.Literal[4, 6, 0] # 0 is only used for invalid detected ip
|
version: typing.Literal[4, 6, 0] # 0 is only used for invalid detected ip
|
||||||
|
|
||||||
|
|
||||||
class NetworkType(typing.NamedTuple):
|
class NetworkType(typing.NamedTuple):
|
||||||
start: int
|
start: int
|
||||||
end: int
|
end: int
|
||||||
@ -73,10 +74,11 @@ def ipToLong(ip: str) -> IpType:
|
|||||||
try:
|
try:
|
||||||
if ':' in ip and '.' not in ip:
|
if ':' in ip and '.' not in ip:
|
||||||
return IpType(int(ipaddress.IPv6Address(ip)), 6)
|
return IpType(int(ipaddress.IPv6Address(ip)), 6)
|
||||||
else: # ipv4
|
if ':' in ip and '.' in ip:
|
||||||
if ':' in ip and '.' in ip:
|
ip = ip.split(':')[
|
||||||
ip = ip.split(':')[-1] # Last part of ipv6 address
|
-1
|
||||||
return IpType(int(ipaddress.IPv4Address(ip)), 4)
|
] # Last part of ipv6 address is ipv4 address (has dots and colons, so we can't use ipaddress)
|
||||||
|
return IpType(int(ipaddress.IPv4Address(ip)), 4)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Ivalid value: %s (%s)', ip, e)
|
logger.error('Ivalid value: %s (%s)', ip, e)
|
||||||
return IpType(0, 0) # Invalid values will map to "0.0.0.0" --> 0
|
return IpType(0, 0) # Invalid values will map to "0.0.0.0" --> 0
|
||||||
@ -88,11 +90,10 @@ def longToIp(n: int, version: typing.Literal[0, 4, 6] = 0) -> str:
|
|||||||
"""
|
"""
|
||||||
if n > 2**32 or version == 6:
|
if n > 2**32 or version == 6:
|
||||||
return str(ipaddress.IPv6Address(n).compressed)
|
return str(ipaddress.IPv6Address(n).compressed)
|
||||||
else:
|
return str(ipaddress.IPv4Address(n))
|
||||||
return str(ipaddress.IPv4Address(n))
|
|
||||||
|
|
||||||
|
|
||||||
def networkFromStringIPv4(strNets: str, version: typing.Literal[0, 4, 6] = 0) -> NetworkType:
|
def networkFromStringIPv4(strNets: str) -> NetworkType:
|
||||||
'''
|
'''
|
||||||
Parses the network from strings in this forms:
|
Parses the network from strings in this forms:
|
||||||
- A.* (or A.*.* or A.*.*.*)
|
- A.* (or A.*.* or A.*.*.*)
|
||||||
@ -185,17 +186,17 @@ def networkFromStringIPv4(strNets: str, version: typing.Literal[0, 4, 6] = 0) ->
|
|||||||
raise Exception()
|
raise Exception()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Invalid network found: %s %s', strNets, e)
|
logger.error('Invalid network found: %s %s', strNets, e)
|
||||||
raise ValueError(inputString)
|
raise ValueError(inputString) from e
|
||||||
|
|
||||||
|
|
||||||
def networkFromStringIPv6(strNets: str, version: typing.Literal[0, 4, 6] = 0) -> NetworkType:
|
def networkFromStringIPv6(strNets: str) -> NetworkType:
|
||||||
'''
|
'''
|
||||||
returns a named tuple with networks start and network end
|
returns a named tuple with networks start and network end
|
||||||
'''
|
'''
|
||||||
logger.debug('Getting network from %s', strNets)
|
logger.debug('Getting network from %s', strNets)
|
||||||
|
|
||||||
# if '*' or '::*', return the whole IPv6 range
|
# if '*' or '::*', return the whole IPv6 range
|
||||||
if strNets == '*' or strNets == '::*':
|
if strNets in ('*', '::*'):
|
||||||
return NetworkType(0, 2**128 - 1, 6)
|
return NetworkType(0, 2**128 - 1, 6)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -204,7 +205,7 @@ def networkFromStringIPv6(strNets: str, version: typing.Literal[0, 4, 6] = 0) ->
|
|||||||
return NetworkType(int(net.network_address), int(net.broadcast_address), 6)
|
return NetworkType(int(net.network_address), int(net.broadcast_address), 6)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Invalid network found: %s %s', strNets, e)
|
logger.error('Invalid network found: %s %s', strNets, e)
|
||||||
raise ValueError(strNets)
|
raise ValueError(strNets) from e
|
||||||
|
|
||||||
|
|
||||||
def networkFromString(
|
def networkFromString(
|
||||||
@ -212,12 +213,12 @@ def networkFromString(
|
|||||||
version: typing.Literal[0, 4, 6] = 0,
|
version: typing.Literal[0, 4, 6] = 0,
|
||||||
) -> NetworkType:
|
) -> NetworkType:
|
||||||
if not ':' in strNets and version != 6:
|
if not ':' in strNets and version != 6:
|
||||||
return networkFromStringIPv4(strNets, version)
|
return networkFromStringIPv4(strNets)
|
||||||
else: # ':' in strNets or version == 6:
|
# ':' in strNets or version == 6:
|
||||||
# If is in fact an IPv4 address, return None network, this will not be used
|
# If is in fact an IPv4 address, return None network, this will not be used
|
||||||
if '.' in strNets:
|
if '.' in strNets:
|
||||||
return NetworkType(0, 0, 0)
|
return NetworkType(0, 0, 0)
|
||||||
return networkFromStringIPv6(strNets, version)
|
return networkFromStringIPv6(strNets)
|
||||||
|
|
||||||
|
|
||||||
def networksFromString(
|
def networksFromString(
|
||||||
@ -260,7 +261,7 @@ def isValidIp(value: str, version: typing.Literal[0, 4, 6] = 0) -> bool:
|
|||||||
# Using ipaddress module
|
# Using ipaddress module
|
||||||
try:
|
try:
|
||||||
addr = ipaddress.ip_address(value)
|
addr = ipaddress.ip_address(value)
|
||||||
return version == 0 or addr.version == version
|
return version in (0, addr.version) # Must be the same version or 0
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ if typing.TYPE_CHECKING:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(enum.Enum):
|
class ObjectType(enum.Enum):
|
||||||
PROVIDER = (1, models.Provider)
|
PROVIDER = (1, models.Provider)
|
||||||
SERVICE = (2, models.Service)
|
SERVICE = (2, models.Service)
|
||||||
@ -67,7 +68,6 @@ class ObjectType(enum.Enum):
|
|||||||
LOG = (22, models.Log)
|
LOG = (22, models.Log)
|
||||||
NOTIFICATION = (23, models.Notification)
|
NOTIFICATION = (23, models.Notification)
|
||||||
TICKET_STORE = (24, models.TicketStore)
|
TICKET_STORE = (24, models.TicketStore)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model(self) -> typing.Type['Model']:
|
def model(self) -> typing.Type['Model']:
|
||||||
@ -75,8 +75,7 @@ class ObjectType(enum.Enum):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> int:
|
def type(self) -> int:
|
||||||
"""Returns the integer value of this object type
|
"""Returns the integer value of this object type"""
|
||||||
"""
|
|
||||||
return self.value[0]
|
return self.value[0]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -84,7 +83,7 @@ class ObjectType(enum.Enum):
|
|||||||
for objType in ObjectType:
|
for objType in ObjectType:
|
||||||
if objType.model == type(model):
|
if objType.model == type(model):
|
||||||
return objType
|
return objType
|
||||||
raise ValueError('Invalid model type: {}'.format(model))
|
raise ValueError(f'Invalid model type: {model}')
|
||||||
|
|
||||||
def __eq__(self, __o: object) -> bool:
|
def __eq__(self, __o: object) -> bool:
|
||||||
"""Compares with another ObjType, and includes int comparison
|
"""Compares with another ObjType, and includes int comparison
|
||||||
|
@ -33,12 +33,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
# from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from uds import models
|
from uds import models
|
||||||
from uds.models.permissions import PermissionType
|
from uds.models.permissions import PermissionType
|
||||||
|
|
||||||
from uds.core.util import objtype
|
from uds.core.util import objtype
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
|
||||||
|
|
||||||
# Not imported at runtime, just for type checking
|
# Not imported at runtime, just for type checking
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
|
@ -24,12 +24,13 @@ KEY_SIZE = 4096
|
|||||||
SECRET_SIZE = 32
|
SECRET_SIZE = 32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Ensure that we do not get warnings about self signed certificates and so
|
# Ensure that we do not get warnings about self signed certificates and so
|
||||||
import requests.packages.urllib3 # type: ignore
|
import requests.packages.urllib3 # type: ignore
|
||||||
requests.packages.urllib3.disable_warnings() # @UndefinedVariable
|
|
||||||
except:
|
requests.packages.urllib3.disable_warnings() # type: ignore # pylint: disable=no-member
|
||||||
|
except Exception: # nosec: simple check for disabling warnings,
|
||||||
|
# Igonre if we cannot disable warnings
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +89,9 @@ def createClientSslContext(verify: bool = True) -> ssl.SSLContext:
|
|||||||
Returns:
|
Returns:
|
||||||
A SSLContext object.
|
A SSLContext object.
|
||||||
"""
|
"""
|
||||||
sslContext = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=certifi.where())
|
sslContext = ssl.create_default_context(
|
||||||
|
purpose=ssl.Purpose.SERVER_AUTH, cafile=certifi.where()
|
||||||
|
)
|
||||||
if not verify:
|
if not verify:
|
||||||
sslContext.check_hostname = False
|
sslContext.check_hostname = False
|
||||||
sslContext.verify_mode = ssl.VerifyMode.CERT_NONE
|
sslContext.verify_mode = ssl.VerifyMode.CERT_NONE
|
||||||
@ -98,7 +101,9 @@ def createClientSslContext(verify: bool = True) -> ssl.SSLContext:
|
|||||||
# sslContext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
# sslContext.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3
|
||||||
if hasattr(settings, 'SECURE_MIN_TLS_VERSION') and settings.SECURE_MIN_TLS_VERSION:
|
if hasattr(settings, 'SECURE_MIN_TLS_VERSION') and settings.SECURE_MIN_TLS_VERSION:
|
||||||
# format is "1.0, 1.1, 1.2 or 1.3", convert to ssl.TLSVersion.TLSv1_0, ssl.TLSVersion.TLSv1_1, ssl.TLSVersion.TLSv1_2 or ssl.TLSVersion.TLSv1_3
|
# format is "1.0, 1.1, 1.2 or 1.3", convert to ssl.TLSVersion.TLSv1_0, ssl.TLSVersion.TLSv1_1, ssl.TLSVersion.TLSv1_2 or ssl.TLSVersion.TLSv1_3
|
||||||
sslContext.minimum_version = getattr(ssl.TLSVersion, 'TLSv' + settings.SECURE_MIN_TLS_VERSION.replace('.', '_'))
|
sslContext.minimum_version = getattr(
|
||||||
|
ssl.TLSVersion, 'TLSv' + settings.SECURE_MIN_TLS_VERSION.replace('.', '_')
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
sslContext.minimum_version = ssl.TLSVersion.TLSv1_2
|
sslContext.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
|
||||||
@ -140,7 +145,10 @@ def checkCertificateMatchPrivateKey(*, cert: str, key: str) -> bool:
|
|||||||
# Even if the key or certificate is not valid, we only want a True if they match, False otherwise
|
# Even if the key or certificate is not valid, we only want a True if they match, False otherwise
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def secureRequestsSession(*, verify: typing.Union[str, bool] = True) -> 'requests.Session':
|
|
||||||
|
def secureRequestsSession(
|
||||||
|
*, verify: typing.Union[str, bool] = True
|
||||||
|
) -> 'requests.Session':
|
||||||
'''
|
'''
|
||||||
Generates a requests.Session object with a custom adapter that uses a custom SSLContext.
|
Generates a requests.Session object with a custom adapter that uses a custom SSLContext.
|
||||||
This is intended to be used for requests that need to be secure, but not necessarily verified.
|
This is intended to be used for requests that need to be secure, but not necessarily verified.
|
||||||
@ -152,41 +160,34 @@ def secureRequestsSession(*, verify: typing.Union[str, bool] = True) -> 'request
|
|||||||
Returns:
|
Returns:
|
||||||
A requests.Session object.
|
A requests.Session object.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
# Copy verify value
|
||||||
|
lverify = verify
|
||||||
|
|
||||||
class UDSHTTPAdapter(requests.adapters.HTTPAdapter):
|
class UDSHTTPAdapter(requests.adapters.HTTPAdapter):
|
||||||
def init_poolmanager(self, *args, **kwargs) -> None:
|
def init_poolmanager(self, *args, **kwargs) -> None:
|
||||||
kwargs["ssl_context"] = createClientSslContext(verify=verify is True)
|
kwargs["ssl_context"] = createClientSslContext(verify=verify is True)
|
||||||
|
|
||||||
|
# See urllib3.poolmanager.SSL_KEYWORDS for all available keys.
|
||||||
# See urllib3.poolmanager.SSL_KEYWORDS for all available keys.
|
|
||||||
return super().init_poolmanager(*args, **kwargs)
|
return super().init_poolmanager(*args, **kwargs)
|
||||||
|
|
||||||
def cert_verify(self, conn, url, lverify, cert) -> None:
|
def cert_verify(self, conn, url, verify, cert) -> None:
|
||||||
"""Verify a SSL certificate. This method should not be called from user
|
"""Verify a SSL certificate. This method should not be called from user
|
||||||
code, and is only exposed for use when subclassing the
|
code, and is only exposed for use when subclassing the HTTPAdapter class
|
||||||
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
|
||||||
|
|
||||||
:param conn: The urllib3 connection object associated with the cert.
|
|
||||||
:param url: The requested URL.
|
|
||||||
:param verify: Either a boolean, in which case it controls whether we verify
|
|
||||||
the server's TLS certificate, or a string, in which case it must be a path
|
|
||||||
to a CA bundle to use
|
|
||||||
:param cert: The SSL certificate to verify.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If lverify is an string, use it even if verify is False
|
# If lverify is an string, use it even if verify is False
|
||||||
# if not, use verify value
|
# if not, use verify value
|
||||||
if not isinstance(lverify, str):
|
if not isinstance(verify, str):
|
||||||
lverify = verify
|
verify = lverify
|
||||||
|
|
||||||
# logger.info('Connection info: %s', conn)
|
# logger.info('Connection info: %s', conn)
|
||||||
# for k, v in conn.__dict__.items():
|
# for k, v in conn.__dict__.items():
|
||||||
# logger.info('Connection info: %s = %s', k, v)
|
# logger.info('Connection info: %s = %s', k, v)
|
||||||
|
|
||||||
|
super().cert_verify(conn, url, verify, cert)
|
||||||
super().cert_verify(conn, url, lverify, cert)
|
|
||||||
|
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
session.mount("https://", UDSHTTPAdapter())
|
session.mount("https://", UDSHTTPAdapter())
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ def serialize(obj: typing.Any) -> bytes:
|
|||||||
"""
|
"""
|
||||||
# generate pickle dump and encrypt it to keep it safe
|
# generate pickle dump and encrypt it to keep it safe
|
||||||
# Compress data using lzma first
|
# Compress data using lzma first
|
||||||
|
|
||||||
|
|
||||||
data = CryptoManager().fastCrypt(
|
data = CryptoManager().fastCrypt(
|
||||||
lzma.compress(pickle.dumps(obj))
|
lzma.compress(pickle.dumps(obj))
|
||||||
@ -63,11 +62,12 @@ def deserialize(data: typing.Optional[bytes]) -> typing.Any:
|
|||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if data[0:2] in DESERIALIZERS:
|
if data[0:2] in DESERIALIZERS:
|
||||||
return pickle.loads(lzma.decompress(DESERIALIZERS[data[0:2]](data[2:]))) # nosec: Secured by encryption
|
return pickle.loads(
|
||||||
else:
|
lzma.decompress(DESERIALIZERS[data[0:2]](data[2:]))
|
||||||
# Old version, try to unpickle it
|
) # nosec: Secured by encryption
|
||||||
try:
|
# Old version, try to unpickle it
|
||||||
return pickle.loads(data) # nosec: Backward compatibility
|
try:
|
||||||
except Exception:
|
return pickle.loads(data) # nosec: Backward compatibility
|
||||||
return None
|
except Exception:
|
||||||
|
return None
|
||||||
|
@ -34,7 +34,6 @@ import typing
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
from django.contrib.sessions.serializers import JSONSerializer
|
|
||||||
class SessionSerializer:
|
class SessionSerializer:
|
||||||
"""
|
"""
|
||||||
Serializer for django sessions.
|
Serializer for django sessions.
|
||||||
@ -43,7 +42,8 @@ class SessionSerializer:
|
|||||||
"""
|
"""
|
||||||
Serialize data for storage in a session.
|
Serialize data for storage in a session.
|
||||||
"""
|
"""
|
||||||
return json.dumps(data).encode()
|
return json.dumps(data, separators=(',', ':')).encode()
|
||||||
|
|
||||||
|
|
||||||
def loads(self, data: bytes) -> typing.Dict[str, typing.Any]:
|
def loads(self, data: bytes) -> typing.Dict[str, typing.Any]:
|
||||||
"""
|
"""
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
|
||||||
class Singleton(type):
|
class Singleton(type):
|
||||||
'''
|
'''
|
||||||
Metaclass for singleton pattern
|
Metaclass for singleton pattern
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
class MyClass(metaclass=Singleton):
|
class MyClass(metaclass=Singleton):
|
||||||
...
|
...
|
||||||
'''
|
'''
|
||||||
|
|
||||||
_instance: typing.Optional[typing.Any]
|
_instance: typing.Optional[typing.Any]
|
||||||
|
|
||||||
# We use __init__ so we customise the created class from this metaclass
|
# We use __init__ so we customise the created class from this metaclass
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(cls, *args, **kwargs) -> None:
|
||||||
self._instance = None
|
cls._instance = None
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs) -> typing.Any:
|
def __call__(cls, *args, **kwargs) -> typing.Any:
|
||||||
if self._instance is None:
|
if cls._instance is None:
|
||||||
self._instance = super().__call__(*args, **kwargs)
|
cls._instance = super().__call__(*args, **kwargs)
|
||||||
return self._instance
|
return cls._instance
|
||||||
|
@ -42,11 +42,7 @@ class StateQueue:
|
|||||||
self._current = None
|
self._current = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
res = '<StateQueue Current: %s, Queue: (%s)>' % (
|
return f'<StateQueue Current: {self._current}, Queue: ({",".join(state for state in self._queue)})>'
|
||||||
self._current,
|
|
||||||
','.join(state for state in self._queue),
|
|
||||||
)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def clearQueue(self) -> None:
|
def clearQueue(self) -> None:
|
||||||
self._queue.clear()
|
self._queue.clear()
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import pickle # nosec: This is e controled pickle use
|
import pickle # nosec: This is e controled pickle use
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
import codecs
|
import codecs
|
||||||
@ -66,7 +66,9 @@ def _decodeValue(
|
|||||||
) -> typing.Tuple[str, typing.Any]:
|
) -> typing.Tuple[str, typing.Any]:
|
||||||
if value:
|
if value:
|
||||||
try:
|
try:
|
||||||
v = pickle.loads(base64.b64decode(value.encode())) # nosec: This is e controled pickle loading
|
v = pickle.loads(
|
||||||
|
base64.b64decode(value.encode())
|
||||||
|
) # nosec: This is e controled pickle loading
|
||||||
if isinstance(v, tuple) and v[0] == MARK:
|
if isinstance(v, tuple) and v[0] == MARK:
|
||||||
return typing.cast(typing.Tuple[str, typing.Any], v[1:])
|
return typing.cast(typing.Tuple[str, typing.Any], v[1:])
|
||||||
# Fix value so it contains also the "key" (in this case, the original key is lost, we have only the hash value...)
|
# Fix value so it contains also the "key" (in this case, the original key is lost, we have only the hash value...)
|
||||||
@ -75,7 +77,7 @@ def _decodeValue(
|
|||||||
try:
|
try:
|
||||||
return ('#' + dbk, base64.b64decode(value.encode()).decode())
|
return ('#' + dbk, base64.b64decode(value.encode()).decode())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warn('Unknown decodeable value: %s (%s)', value, e)
|
logger.warning('Unknown decodeable value: %s (%s)', value, e)
|
||||||
return ('', None)
|
return ('', None)
|
||||||
|
|
||||||
|
|
||||||
@ -108,9 +110,8 @@ class StorageAsDict(MutableMapping):
|
|||||||
@property
|
@property
|
||||||
def _db(self) -> typing.Union[models.QuerySet, models.Manager]:
|
def _db(self) -> typing.Union[models.QuerySet, models.Manager]:
|
||||||
if self._atomic:
|
if self._atomic:
|
||||||
return DBStorage.objects.select_for_update() # type: ignore
|
return DBStorage.objects.select_for_update()
|
||||||
else:
|
return DBStorage.objects
|
||||||
return DBStorage.objects # type: ignore
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _filtered(self) -> 'models.QuerySet[DBStorage]':
|
def _filtered(self) -> 'models.QuerySet[DBStorage]':
|
||||||
@ -127,7 +128,7 @@ class StorageAsDict(MutableMapping):
|
|||||||
|
|
||||||
def __getitem__(self, key: str) -> typing.Any:
|
def __getitem__(self, key: str) -> typing.Any:
|
||||||
if not isinstance(key, str):
|
if not isinstance(key, str):
|
||||||
raise TypeError('Key must be str, {} found'.format(type(key)))
|
raise TypeError(f'Key must be str, {type(key)} found')
|
||||||
|
|
||||||
dbk = self._key(key)
|
dbk = self._key(key)
|
||||||
logger.debug('Getitem: %s', dbk)
|
logger.debug('Getitem: %s', dbk)
|
||||||
@ -139,12 +140,13 @@ class StorageAsDict(MutableMapping):
|
|||||||
|
|
||||||
def __setitem__(self, key: str, value: typing.Any) -> None:
|
def __setitem__(self, key: str, value: typing.Any) -> None:
|
||||||
if not isinstance(key, str):
|
if not isinstance(key, str):
|
||||||
raise TypeError('Key must be str type, {} found'.format(type(key)))
|
raise TypeError(f'Key must be str type, {type(key)} found')
|
||||||
|
|
||||||
dbk = self._key(key)
|
dbk = self._key(key)
|
||||||
logger.debug('Setitem: %s = %s', dbk, value)
|
logger.debug('Setitem: %s = %s', dbk, value)
|
||||||
data = _encodeValue(key, value, self._compat)
|
data = _encodeValue(key, value, self._compat)
|
||||||
c, created = DBStorage.objects.update_or_create(
|
# ignores return value, we don't care if it was created or updated
|
||||||
|
DBStorage.objects.update_or_create(
|
||||||
key=dbk, defaults={'data': data, 'attr1': self._group, 'owner': self._owner}
|
key=dbk, defaults={'data': data, 'attr1': self._group, 'owner': self._owner}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -179,7 +181,7 @@ class StorageAsDict(MutableMapping):
|
|||||||
return self[key] or default
|
return self[key] or default
|
||||||
|
|
||||||
def delete(self, key: str) -> None:
|
def delete(self, key: str) -> None:
|
||||||
self.__delitem__(key)
|
self.__delitem__(key) # pylint: disable=unnecessary-dunder-call
|
||||||
|
|
||||||
# Custom utility methods
|
# Custom utility methods
|
||||||
@property
|
@property
|
||||||
@ -312,7 +314,9 @@ class Storage:
|
|||||||
def getPickle(self, skey: typing.Union[str, bytes]) -> typing.Any:
|
def getPickle(self, skey: typing.Union[str, bytes]) -> typing.Any:
|
||||||
v = self.readData(skey, True)
|
v = self.readData(skey, True)
|
||||||
if v:
|
if v:
|
||||||
return pickle.loads(typing.cast(bytes, v)) # nosec: This is e controled pickle loading
|
return pickle.loads(
|
||||||
|
typing.cast(bytes, v)
|
||||||
|
) # nosec: This is e controled pickle loading
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def getPickleByAttr1(self, attr1: str, forUpdate: bool = False):
|
def getPickleByAttr1(self, attr1: str, forUpdate: bool = False):
|
||||||
@ -335,7 +339,9 @@ class Storage:
|
|||||||
try:
|
try:
|
||||||
# Process several keys at once
|
# Process several keys at once
|
||||||
DBStorage.objects.filter(key__in=[self.getKey(k) for k in keys]).delete()
|
DBStorage.objects.filter(key__in=[self.getKey(k) for k in keys]).delete()
|
||||||
except Exception: # nosec: Not interested in processing exceptions, just ignores it
|
except (
|
||||||
|
Exception
|
||||||
|
): # nosec: Not interested in processing exceptions, just ignores it
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
|
@ -32,7 +32,6 @@ Author: Adolfo Gómez, dkmaster at dkmon dot com
|
|||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import datetime
|
import datetime
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import typing
|
import typing
|
||||||
@ -41,8 +40,6 @@ from django.utils import formats
|
|||||||
from django.utils.translation import gettext
|
from django.utils.translation import gettext
|
||||||
import django.template.defaultfilters as filters
|
import django.template.defaultfilters as filters
|
||||||
|
|
||||||
from uds.core import exceptions
|
|
||||||
|
|
||||||
|
|
||||||
class CaseInsensitiveDict(dict):
|
class CaseInsensitiveDict(dict):
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -50,46 +47,46 @@ class CaseInsensitiveDict(dict):
|
|||||||
return key.lower() if isinstance(key, str) else key
|
return key.lower() if isinstance(key, str) else key
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CaseInsensitiveDict, self).__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self._convert_keys()
|
self._convert_keys()
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
return super(CaseInsensitiveDict, self).__getitem__(self.__class__._k(key))
|
return super().__getitem__(self.__class__._k(key))
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
super(CaseInsensitiveDict, self).__setitem__(self.__class__._k(key), value)
|
super().__setitem__(self.__class__._k(key), value)
|
||||||
|
|
||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
return super(CaseInsensitiveDict, self).__delitem__(self.__class__._k(key))
|
return super().__delitem__(self.__class__._k(key))
|
||||||
|
|
||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return super(CaseInsensitiveDict, self).__contains__(self.__class__._k(key))
|
return super().__contains__(self.__class__._k(key))
|
||||||
|
|
||||||
def pop(self, key, *args, **kwargs):
|
def pop(self, key, *args, **kwargs):
|
||||||
return super(CaseInsensitiveDict, self).pop(
|
return super().pop(
|
||||||
self.__class__._k(key), *args, **kwargs
|
self.__class__._k(key), *args, **kwargs # pylint: disable=protected-access
|
||||||
)
|
)
|
||||||
|
|
||||||
def get(self, key, *args, **kwargs):
|
def get(self, key, *args, **kwargs):
|
||||||
return super(CaseInsensitiveDict, self).get(
|
return super().get(
|
||||||
self.__class__._k(key), *args, **kwargs
|
self.__class__._k(key), *args, **kwargs # pylint: disable=protected-access
|
||||||
)
|
)
|
||||||
|
|
||||||
def setdefault(self, key, *args, **kwargs):
|
def setdefault(self, key, *args, **kwargs):
|
||||||
return super(CaseInsensitiveDict, self).setdefault(
|
return super().setdefault(
|
||||||
self.__class__._k(key), *args, **kwargs
|
self.__class__._k(key), *args, **kwargs # pylint: disable=protected-access
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, E=None, **F):
|
def update(self, E=None, **F):
|
||||||
if E is None:
|
if E is None:
|
||||||
E = {}
|
E = {}
|
||||||
super(CaseInsensitiveDict, self).update(self.__class__(E))
|
super().update(self.__class__(E))
|
||||||
super(CaseInsensitiveDict, self).update(self.__class__(**F))
|
super().update(self.__class__(**F))
|
||||||
|
|
||||||
def _convert_keys(self):
|
def _convert_keys(self):
|
||||||
for k in list(self.keys()):
|
for k in list(self.keys()):
|
||||||
v = super(CaseInsensitiveDict, self).pop(k)
|
v = super().pop(k)
|
||||||
self.__setitem__(k, v)
|
self.__setitem__(k, v) # pylint: disable=unnecessary-dunder-call
|
||||||
|
|
||||||
|
|
||||||
def as_list(value: typing.Any) -> typing.List[typing.Any]:
|
def as_list(value: typing.Any) -> typing.List[typing.Any]:
|
||||||
@ -104,7 +101,7 @@ def as_list(value: typing.Any) -> typing.List[typing.Any]:
|
|||||||
if isinstance(value, (bytes, str, int, float)):
|
if isinstance(value, (bytes, str, int, float)):
|
||||||
return [value]
|
return [value]
|
||||||
try:
|
try:
|
||||||
return [v for v in value]
|
return list(value)
|
||||||
except Exception:
|
except Exception:
|
||||||
return [value]
|
return [value]
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class UniqueGIDGenerator(UniqueIDGenerator):
|
|||||||
def __toName(self, seq: int) -> str:
|
def __toName(self, seq: int) -> str:
|
||||||
if seq == -1:
|
if seq == -1:
|
||||||
raise KeyError('No more GIDS available.')
|
raise KeyError('No more GIDS available.')
|
||||||
return '{:s}{:08d}'.format(self._baseName, seq)
|
return f'{self._baseName}{seq:08d}'
|
||||||
# return "%s%0*d" % (self._baseName, 8, seq)
|
# return "%s%0*d" % (self._baseName, 8, seq)
|
||||||
|
|
||||||
def get(self, rangeStart: int = 0, rangeEnd: int = MAX_SEQ) -> str: # type: ignore
|
def get(self, rangeStart: int = 0, rangeEnd: int = MAX_SEQ) -> str: # type: ignore
|
||||||
|
@ -148,9 +148,9 @@ class UniqueIDGenerator:
|
|||||||
|
|
||||||
def transfer(self, seq: int, toUidGen: 'UniqueIDGenerator') -> bool:
|
def transfer(self, seq: int, toUidGen: 'UniqueIDGenerator') -> bool:
|
||||||
self.__filter(0, forUpdate=True).filter(owner=self._owner, seq=seq).update(
|
self.__filter(0, forUpdate=True).filter(owner=self._owner, seq=seq).update(
|
||||||
owner=toUidGen._owner,
|
owner=toUidGen._owner, # pylint: disable=protected-access
|
||||||
basename=toUidGen._baseName,
|
basename=toUidGen._baseName, # pylint: disable=protected-access
|
||||||
stamp=getSqlDatetimeAsUnix(), # pylint: disable=protected-access
|
stamp=getSqlDatetimeAsUnix(),
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -50,17 +50,17 @@ class UniqueMacGenerator(UniqueIDGenerator):
|
|||||||
def __toMac(self, seq: int) -> str:
|
def __toMac(self, seq: int) -> str:
|
||||||
if seq == -1: # No mor macs available
|
if seq == -1: # No mor macs available
|
||||||
return '00:00:00:00:00:00'
|
return '00:00:00:00:00:00'
|
||||||
return re.sub(r"(..)", r"\1:", "%0*X" % (12, seq))[:-1]
|
return re.sub(r"(..)", r"\1:", f'{seq:012X}')[:-1]
|
||||||
|
|
||||||
# noinspection PyMethodOverriding
|
# noinspection PyMethodOverriding
|
||||||
def get(self, macRange: str) -> str: # type: ignore
|
def get(self, macRange: str) -> str: # type: ignore # pylint: disable=arguments-differ
|
||||||
firstMac, lastMac = macRange.split('-')
|
firstMac, lastMac = macRange.split('-')
|
||||||
return self.__toMac(super().get(self.__toInt(firstMac), self.__toInt(lastMac)))
|
return self.__toMac(super().get(self.__toInt(firstMac), self.__toInt(lastMac)))
|
||||||
|
|
||||||
def transfer(self, mac: str, toUMgen: 'UniqueMacGenerator'): # type: ignore
|
def transfer(self, mac: str, toUMgen: 'UniqueMacGenerator'): # type: ignore # pylint: disable=arguments-renamed
|
||||||
super().transfer(self.__toInt(mac), toUMgen)
|
super().transfer(self.__toInt(mac), toUMgen)
|
||||||
|
|
||||||
def free(self, mac: str) -> None:
|
def free(self, mac: str) -> None: # type: ignore # pylint: disable=arguments-renamed
|
||||||
super().free(self.__toInt(mac))
|
super().free(self.__toInt(mac))
|
||||||
|
|
||||||
# Release is inherited, no mod needed
|
# Release is inherited, no mod needed
|
||||||
|
@ -47,15 +47,15 @@ class UniqueNameGenerator(UniqueIDGenerator):
|
|||||||
def __toName(self, seq: int, length: int) -> str:
|
def __toName(self, seq: int, length: int) -> str:
|
||||||
if seq == -1:
|
if seq == -1:
|
||||||
raise KeyError('No more names available. Please, increase service digits.')
|
raise KeyError('No more names available. Please, increase service digits.')
|
||||||
return "%s%0*d" % (self._baseName, length, seq)
|
return f'{self._baseName}{seq:0{length}d}'
|
||||||
|
|
||||||
def get(self, baseName: str, length: int = 5) -> str: # type: ignore # pylint: disable=arguments-differ
|
def get(self, baseName: str, length: int = 5) -> str: # type: ignore # pylint: disable=arguments-differ,arguments-renamed
|
||||||
self.setBaseName(baseName)
|
self.setBaseName(baseName)
|
||||||
minVal = 0
|
minVal = 0
|
||||||
maxVal = 10 ** length - 1
|
maxVal = 10**length - 1
|
||||||
return self.__toName(super().get(minVal, maxVal), length)
|
return self.__toName(super().get(minVal, maxVal), length)
|
||||||
|
|
||||||
def transfer(self, baseName: str, name: str, toUNGen: 'UniqueNameGenerator') -> None: # type: ignore
|
def transfer(self, baseName: str, name: str, toUNGen: 'UniqueNameGenerator') -> None: # type: ignore # pylint: disable=arguments-differ
|
||||||
self.setBaseName(baseName)
|
self.setBaseName(baseName)
|
||||||
super().transfer(int(name[len(self._baseName) :]), toUNGen)
|
super().transfer(int(name[len(self._baseName) :]), toUNGen)
|
||||||
|
|
||||||
|
@ -65,16 +65,14 @@ def validateNumeric(
|
|||||||
numeric = int(value)
|
numeric = int(value)
|
||||||
if minValue is not None and numeric < minValue:
|
if minValue is not None and numeric < minValue:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_(
|
_('{0} must be greater than or equal to {1}').format(
|
||||||
'{0} must be greater than or equal to {1}'.format(
|
fieldName, minValue
|
||||||
fieldName, minValue
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if maxValue is not None and numeric > maxValue:
|
if maxValue is not None and numeric > maxValue:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{0} must be lower than or equal to {1}'.format(fieldName, maxValue))
|
_('{0} must be lower than or equal to {1}').format(fieldName, maxValue)
|
||||||
)
|
)
|
||||||
|
|
||||||
value = str(numeric)
|
value = str(numeric)
|
||||||
@ -82,7 +80,7 @@ def validateNumeric(
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{0} contains invalid characters').format(fieldName)
|
_('{0} contains invalid characters').format(fieldName)
|
||||||
)
|
) from None
|
||||||
|
|
||||||
return int(value)
|
return int(value)
|
||||||
|
|
||||||
@ -139,7 +137,7 @@ def validateIpv4(ipv4: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{} is not a valid IPv4 address').format(ipv4)
|
_('{} is not a valid IPv4 address').format(ipv4)
|
||||||
)
|
) from None
|
||||||
return ipv4
|
return ipv4
|
||||||
|
|
||||||
|
|
||||||
@ -155,7 +153,7 @@ def validateIpv6(ipv6: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{} is not a valid IPv6 address').format(ipv6)
|
_('{} is not a valid IPv6 address').format(ipv6)
|
||||||
)
|
) from None
|
||||||
return ipv6
|
return ipv6
|
||||||
|
|
||||||
|
|
||||||
@ -171,7 +169,7 @@ def validateIpv4OrIpv6(ipv4OrIpv6: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{} is not a valid IPv4 or IPv6 address').format(ipv4OrIpv6)
|
_('{} is not a valid IPv4 or IPv6 address').format(ipv4OrIpv6)
|
||||||
)
|
) from None
|
||||||
return ipv4OrIpv6
|
return ipv4OrIpv6
|
||||||
|
|
||||||
|
|
||||||
@ -216,9 +214,7 @@ def validatePath(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not valid_for_windows.match(path) and not valid_for_unix.match(path):
|
if not valid_for_windows.match(path) and not valid_for_unix.match(path):
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(_('{} is not a valid path').format(path))
|
||||||
_('{} is not a valid path').format(path)
|
|
||||||
)
|
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
@ -246,7 +242,7 @@ def validateHostPortPair(hostPortPair: str) -> typing.Tuple[str, int]:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{} is not a valid host:port pair').format(hostPortPair)
|
_('{} is not a valid host:port pair').format(hostPortPair)
|
||||||
)
|
) from None
|
||||||
|
|
||||||
|
|
||||||
def validateTimeout(timeOutStr: str) -> int:
|
def validateTimeout(timeOutStr: str) -> int:
|
||||||
@ -273,9 +269,7 @@ def validateMac(mac: str) -> str:
|
|||||||
) # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
|
) # In fact, it could be XX-XX-XX-XX-XX-XX, but we use - as range separator
|
||||||
|
|
||||||
if macRE.match(mac) is None:
|
if macRE.match(mac) is None:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(_('{} is not a valid MAC address').format(mac))
|
||||||
_('{} is not a valid MAC address').format(mac)
|
|
||||||
)
|
|
||||||
|
|
||||||
return mac
|
return mac
|
||||||
|
|
||||||
@ -293,7 +287,7 @@ def validateMacRange(macRange: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(
|
||||||
_('{} is not a valid MAC range').format(macRange)
|
_('{} is not a valid MAC range').format(macRange)
|
||||||
)
|
) from None
|
||||||
|
|
||||||
return macRange
|
return macRange
|
||||||
|
|
||||||
@ -343,8 +337,6 @@ def validateBasename(baseName: str, length: int = -1) -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if baseName.isdigit():
|
if baseName.isdigit():
|
||||||
raise exceptions.ValidationError(
|
raise exceptions.ValidationError(_('The machine name can\'t be only numbers'))
|
||||||
_('The machine name can\'t be only numbers')
|
|
||||||
)
|
|
||||||
|
|
||||||
return baseName
|
return baseName
|
||||||
|
@ -35,8 +35,6 @@ import typing
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponsePermanentRedirect
|
from django.http import HttpResponsePermanentRedirect
|
||||||
|
|
||||||
from uds.core.util.config import GlobalConfig
|
|
||||||
|
|
||||||
from . import builder
|
from . import builder
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -46,9 +44,9 @@ if typing.TYPE_CHECKING:
|
|||||||
|
|
||||||
def _check_redirectable(request: 'HttpRequest') -> typing.Optional['HttpResponse']:
|
def _check_redirectable(request: 'HttpRequest') -> typing.Optional['HttpResponse']:
|
||||||
if request.is_secure():
|
if request.is_secure():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return HttpResponsePermanentRedirect(reverse('page.login'))
|
return HttpResponsePermanentRedirect(request.build_absolute_uri(reverse('page.index')).replace('http://', 'https://', 1))
|
||||||
|
|
||||||
|
|
||||||
# Compatibility with old middleware, so we can use it in settings.py as it was
|
# Compatibility with old middleware, so we can use it in settings.py as it was
|
||||||
|
Loading…
Reference in New Issue
Block a user