forked from shaba/openuds
Refactor for all middlewares (now are all on same place..)
This commit is contained in:
parent
394ceb9e66
commit
21f6df36b0
@ -175,9 +175,9 @@ MIDDLEWARE = [
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'uds.core.util.request.GlobalRequestMiddleware',
|
||||
'uds.core.util.middleware.XUACompatibleMiddleware',
|
||||
'uds.core.util.middleware.RedirectMiddleware',
|
||||
'uds.core.util.middleware.request.GlobalRequestMiddleware',
|
||||
'uds.core.util.middleware.xua.XUACompatibleMiddleware',
|
||||
'uds.core.util.middleware.redirect.RedirectMiddleware',
|
||||
]
|
||||
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
|
@ -35,5 +35,3 @@ take care of registering it as provider
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
from .authenticator import RadiusAuth
|
||||
|
||||
__updated__ = '2014-02-19'
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2020 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2013-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -25,70 +25,3 @@
|
||||
# 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.
|
||||
import logging
|
||||
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from uds.core.util.config import GlobalConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XUACompatibleMiddleware:
|
||||
"""
|
||||
Add a X-UA-Compatible header to the response
|
||||
This header tells to Internet Explorer to render page with latest
|
||||
possible version or to use chrome frame if it is installed.
|
||||
"""
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if response.get('content-type', '').startswith('text/html'):
|
||||
response['X-UA-Compatible'] = 'IE=edge'
|
||||
return response
|
||||
|
||||
|
||||
class RedirectMiddleware:
|
||||
"""
|
||||
This class is responsible of redirection, if checked, requests to HTTPS.
|
||||
|
||||
Some paths will not be redirected, to avoid problems, but they are advised to use SSL (this is for backwards compat)
|
||||
"""
|
||||
NO_REDIRECT = [
|
||||
'rest',
|
||||
'pam',
|
||||
'guacamole',
|
||||
# For new paths
|
||||
# 'uds/rest', # REST must be HTTPS if redirect is enabled
|
||||
'uds/pam',
|
||||
'uds/guacamole',
|
||||
'uds/rest/client/test'
|
||||
]
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
full_path = request.get_full_path()
|
||||
redirect = True
|
||||
for nr in RedirectMiddleware.NO_REDIRECT:
|
||||
if full_path.startswith('/' + nr):
|
||||
redirect = False
|
||||
break
|
||||
|
||||
if redirect and request.is_secure() is False and GlobalConfig.REDIRECT_TO_HTTPS.getBool():
|
||||
if request.method == 'POST':
|
||||
# url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
||||
url = reverse('page.login')
|
||||
else:
|
||||
url = request.build_absolute_uri(full_path)
|
||||
url = url.replace('http://', 'https://')
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
return self.get_response(request)
|
||||
|
||||
@staticmethod
|
||||
def registerException(path: str) -> None:
|
||||
RedirectMiddleware.NO_REDIRECT.append(path)
|
||||
|
78
server/src/uds/core/util/middleware/redirect.py
Normal file
78
server/src/uds/core/util/middleware/redirect.py
Normal file
@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2021 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. 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.
|
||||
import logging
|
||||
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from uds.core.util.config import GlobalConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RedirectMiddleware:
|
||||
"""
|
||||
This class is responsible of redirection, if checked, requests to HTTPS.
|
||||
|
||||
Some paths will not be redirected, to avoid problems, but they are advised to use SSL (this is for backwards compat)
|
||||
"""
|
||||
NO_REDIRECT = [
|
||||
'rest',
|
||||
'pam',
|
||||
'guacamole',
|
||||
# For new paths
|
||||
# 'uds/rest', # REST must be HTTPS if redirect is enabled
|
||||
'uds/pam',
|
||||
'uds/guacamole',
|
||||
'uds/rest/client/test'
|
||||
]
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
full_path = request.get_full_path()
|
||||
redirect = True
|
||||
for nr in RedirectMiddleware.NO_REDIRECT:
|
||||
if full_path.startswith('/' + nr):
|
||||
redirect = False
|
||||
break
|
||||
|
||||
if redirect and request.is_secure() is False and GlobalConfig.REDIRECT_TO_HTTPS.getBool():
|
||||
if request.method == 'POST':
|
||||
# url = request.build_absolute_uri(GlobalConfig.LOGIN_URL.get())
|
||||
url = reverse('page.login')
|
||||
else:
|
||||
url = request.build_absolute_uri(full_path)
|
||||
url = url.replace('http://', 'https://')
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
return self.get_response(request)
|
||||
|
||||
@staticmethod
|
||||
def registerException(path: str) -> None:
|
||||
RedirectMiddleware.NO_REDIRECT.append(path)
|
160
server/src/uds/core/util/middleware/request.py
Normal file
160
server/src/uds/core/util/middleware/request.py
Normal file
@ -0,0 +1,160 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2021 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.
|
||||
"""
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import threading
|
||||
import datetime
|
||||
import weakref
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils import timezone
|
||||
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
from uds.core.util.config import GlobalConfig
|
||||
from uds.core.auths.auth import EXPIRY_KEY, ROOT_ID, USER_KEY, getRootUser, webLogout
|
||||
from uds.core.util.request import setRequest, delCurrentRequest, cleanOldRequests, ExtendedHttpRequest
|
||||
from uds.models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# How often to check the requests cache for stuck objects
|
||||
CHECK_SECONDS = 3600 * 24 # Once a day is more than enough
|
||||
|
||||
|
||||
class GlobalRequestMiddleware:
|
||||
lastCheck: typing.ClassVar[datetime.datetime] = datetime.datetime.now()
|
||||
|
||||
def __init__(self, get_response: typing.Callable[[HttpRequest], HttpResponse]):
|
||||
self._get_response: typing.Callable[[HttpRequest], HttpResponse] = get_response
|
||||
|
||||
def _process_request(self, request: ExtendedHttpRequest) -> None:
|
||||
# Store request on cache
|
||||
setRequest(request=request)
|
||||
|
||||
# Add IP to request
|
||||
GlobalRequestMiddleware.fillIps(request)
|
||||
# Ensures request contains os
|
||||
request.os = OsDetector.getOsFromUA(
|
||||
request.META.get('HTTP_USER_AGENT', 'Unknown')
|
||||
)
|
||||
# Ensures that requests contains the valid user
|
||||
GlobalRequestMiddleware.getUser(request)
|
||||
|
||||
def _process_response(self, request: ExtendedHttpRequest, response: HttpResponse):
|
||||
# Remove IP from global cache (processing responses after this will make global request unavailable,
|
||||
# but can be got from request again)
|
||||
delCurrentRequest()
|
||||
# Clean old stored if needed
|
||||
GlobalRequestMiddleware.cleanStuckRequests()
|
||||
|
||||
return response
|
||||
|
||||
def __call__(self, request: ExtendedHttpRequest):
|
||||
self._process_request(request)
|
||||
|
||||
# Now, check if session is timed out...
|
||||
if request.user:
|
||||
# return HttpResponse(content='Session Expired', status=403, content_type='text/plain')
|
||||
now = timezone.now()
|
||||
expiry = request.session.get(EXPIRY_KEY, now)
|
||||
if expiry < now:
|
||||
webLogout(
|
||||
request=request
|
||||
) # Ignore the response, just processes usere session logout
|
||||
return HttpResponse(content='Session Expired', status=403)
|
||||
# Update session timeout..self.
|
||||
request.session[EXPIRY_KEY] = now + datetime.timedelta(
|
||||
seconds=GlobalConfig.SESSION_DURATION_ADMIN.getInt()
|
||||
if request.user.isStaff()
|
||||
else GlobalConfig.SESSION_DURATION_USER.getInt()
|
||||
)
|
||||
|
||||
response = self._get_response(request)
|
||||
|
||||
return self._process_response(request, response)
|
||||
|
||||
@staticmethod
|
||||
def cleanStuckRequests() -> None:
|
||||
# In case of some exception, keep clean very old request from time to time...
|
||||
if (
|
||||
GlobalRequestMiddleware.lastCheck
|
||||
> datetime.datetime.now() - datetime.timedelta(seconds=CHECK_SECONDS)
|
||||
):
|
||||
return
|
||||
cleanOldRequests()
|
||||
|
||||
@staticmethod
|
||||
def fillIps(request: ExtendedHttpRequest):
|
||||
"""
|
||||
Obtains the IP of a Django Request, even behind a proxy
|
||||
|
||||
Returns the obtained IP, that always will be a valid ip address.
|
||||
"""
|
||||
behind_proxy = GlobalConfig.BEHIND_PROXY.getBool(False)
|
||||
try:
|
||||
request.ip = request.META['REMOTE_ADDR']
|
||||
except Exception:
|
||||
logger.exception('Request ip not found!!')
|
||||
request.ip = '' # No remote addr?? ...
|
||||
|
||||
try:
|
||||
proxies = request.META['HTTP_X_FORWARDED_FOR'].split(",")
|
||||
request.ip_proxy = proxies[0]
|
||||
|
||||
if not request.ip or behind_proxy:
|
||||
# Request.IP will be None in case of nginx & gunicorn
|
||||
# Some load balancers may include "domains" on x-forwarded for,
|
||||
request.ip = request.ip_proxy.split('%')[0] # Stores the ip
|
||||
|
||||
# will raise "list out of range", leaving ip_proxy = proxy in case of no other proxy apart of nginx
|
||||
request.ip_proxy = proxies[1].strip()
|
||||
except Exception:
|
||||
request.ip_proxy = request.ip
|
||||
|
||||
@staticmethod
|
||||
def getUser(request: ExtendedHttpRequest) -> None:
|
||||
"""
|
||||
Ensures request user is the correct user
|
||||
"""
|
||||
user_id = request.session.get(USER_KEY)
|
||||
user: typing.Optional[User] = None
|
||||
if user_id:
|
||||
try:
|
||||
if user_id == ROOT_ID:
|
||||
user = getRootUser()
|
||||
else:
|
||||
user = User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
|
||||
logger.debug('User at Middleware: %s %s', user_id, user)
|
||||
|
||||
request.user = user
|
54
server/src/uds/core/util/middleware/xua.py
Normal file
54
server/src/uds/core/util/middleware/xua.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2013-2021 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. 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.
|
||||
"""
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from uds.core.util.config import GlobalConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XUACompatibleMiddleware:
|
||||
"""
|
||||
Add a X-UA-Compatible header to the response
|
||||
This header tells to Internet Explorer to render page with latest
|
||||
possible version or to use chrome frame if it is installed.
|
||||
"""
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if response.get('content-type', '').startswith('text/html'):
|
||||
response['X-UA-Compatible'] = 'IE=edge'
|
||||
return response
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2012-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2012-2021 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -11,7 +11,7 @@
|
||||
# * 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. nor the names of its contributors
|
||||
# * 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.
|
||||
#
|
||||
@ -35,17 +35,14 @@ import weakref
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils import timezone
|
||||
from django.http import HttpRequest
|
||||
|
||||
from uds.core.util import os_detector as OsDetector
|
||||
from uds.core.util.tools import DictAsObj
|
||||
from uds.core.util.config import GlobalConfig
|
||||
from uds.core.auths.auth import EXPIRY_KEY, ROOT_ID, USER_KEY, getRootUser, webLogout
|
||||
from uds.models import User
|
||||
|
||||
# How often to check the requests cache for stuck objects
|
||||
CHECK_SECONDS = 3600 * 24 # Once a day is more than enough
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_requests: typing.Dict[int, typing.Tuple[weakref.ref, datetime.datetime]] = {}
|
||||
|
||||
|
||||
class ExtendedHttpRequest(HttpRequest):
|
||||
@ -59,11 +56,6 @@ class ExtendedHttpRequestWithUser(ExtendedHttpRequest):
|
||||
user: User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_requests: typing.Dict[int, typing.Tuple[weakref.ref, datetime.datetime]] = {}
|
||||
|
||||
|
||||
def getIdent() -> int:
|
||||
ident = threading.current_thread().ident
|
||||
return ident if ident else -1
|
||||
@ -80,144 +72,36 @@ def getRequest() -> ExtendedHttpRequest:
|
||||
|
||||
return ExtendedHttpRequest()
|
||||
|
||||
def delCurrentRequest() -> None:
|
||||
ident = getIdent()
|
||||
logger.debug('Deleting %s', ident)
|
||||
try:
|
||||
if ident in _requests:
|
||||
del _requests[ident] # Remove stored request
|
||||
else:
|
||||
logger.info('Request id %s not stored in cache', ident)
|
||||
except Exception:
|
||||
logger.exception('Deleting stored request')
|
||||
|
||||
class UDSSessionMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
|
||||
class GlobalRequestMiddleware:
|
||||
lastCheck: typing.ClassVar[datetime.datetime] = datetime.datetime.now()
|
||||
|
||||
def __init__(self, get_response: typing.Callable[[HttpRequest], HttpResponse]):
|
||||
self._get_response: typing.Callable[[HttpRequest], HttpResponse] = get_response
|
||||
|
||||
def _process_request(self, request: ExtendedHttpRequest) -> None:
|
||||
# Store request on cache
|
||||
|
||||
_requests[getIdent()] = (
|
||||
weakref.ref(typing.cast(ExtendedHttpRequest, request)),
|
||||
datetime.datetime.now(),
|
||||
)
|
||||
|
||||
# Add IP to request
|
||||
GlobalRequestMiddleware.fillIps(request)
|
||||
# Ensures request contains os
|
||||
request.os = OsDetector.getOsFromUA(
|
||||
request.META.get('HTTP_USER_AGENT', 'Unknown')
|
||||
)
|
||||
# Ensures that requests contains the valid user
|
||||
GlobalRequestMiddleware.getUser(request)
|
||||
|
||||
def _process_response(self, request: ExtendedHttpRequest, response: HttpResponse):
|
||||
# Remove IP from global cache (processing responses after this will make global request unavailable,
|
||||
# but can be got from request again)
|
||||
ident = getIdent()
|
||||
logger.debug('Deleting %s', ident)
|
||||
def cleanOldRequests() -> None:
|
||||
logger.debug('Cleaning stuck requests from %s', _requests)
|
||||
# No request lives 60 seconds, so 60 seconds is fine
|
||||
cleanFrom: datetime.datetime = datetime.datetime.now() - datetime.timedelta(
|
||||
seconds=60
|
||||
)
|
||||
toDelete: typing.List[int] = []
|
||||
for ident, request in _requests.items():
|
||||
if request[1] < cleanFrom:
|
||||
toDelete.append(ident)
|
||||
for ident in toDelete:
|
||||
try:
|
||||
if ident in _requests:
|
||||
del _requests[ident] # Remove stored request
|
||||
else:
|
||||
logger.info('Request id %s not stored in cache', ident)
|
||||
del _requests[ident]
|
||||
except Exception:
|
||||
logger.exception('Deleting stored request')
|
||||
pass # Ignore it silently
|
||||
|
||||
|
||||
# Clean old stored if needed
|
||||
GlobalRequestMiddleware.cleanStuckRequests()
|
||||
|
||||
return response
|
||||
|
||||
def __call__(self, request: ExtendedHttpRequest):
|
||||
self._process_request(request)
|
||||
|
||||
# Now, check if session is timed out...
|
||||
if request.user:
|
||||
# return HttpResponse(content='Session Expired', status=403, content_type='text/plain')
|
||||
now = timezone.now()
|
||||
expiry = request.session.get(EXPIRY_KEY, now)
|
||||
if expiry < now:
|
||||
webLogout(
|
||||
request=request
|
||||
) # Ignore the response, just processes usere session logout
|
||||
return HttpResponse(content='Session Expired', status=403)
|
||||
# Update session timeout..self.
|
||||
request.session[EXPIRY_KEY] = now + datetime.timedelta(
|
||||
seconds=GlobalConfig.SESSION_DURATION_ADMIN.getInt()
|
||||
if request.user.isStaff()
|
||||
else GlobalConfig.SESSION_DURATION_USER.getInt()
|
||||
)
|
||||
|
||||
response = self._get_response(request)
|
||||
|
||||
return self._process_response(request, response)
|
||||
|
||||
@staticmethod
|
||||
def cleanStuckRequests() -> None:
|
||||
# In case of some exception, keep clean very old request from time to time...
|
||||
if (
|
||||
GlobalRequestMiddleware.lastCheck
|
||||
> datetime.datetime.now() - datetime.timedelta(seconds=CHECK_SECONDS)
|
||||
):
|
||||
return
|
||||
logger.debug('Cleaning stuck requests from %s', _requests)
|
||||
# No request lives 60 seconds, so 60 seconds is fine
|
||||
cleanFrom: datetime.datetime = datetime.datetime.now() - datetime.timedelta(
|
||||
seconds=60
|
||||
)
|
||||
toDelete: typing.List[int] = []
|
||||
for ident, request in _requests.items():
|
||||
if request[1] < cleanFrom:
|
||||
toDelete.append(ident)
|
||||
for ident in toDelete:
|
||||
try:
|
||||
del _requests[ident]
|
||||
except Exception:
|
||||
pass # Ignore it silently
|
||||
|
||||
@staticmethod
|
||||
def fillIps(request: ExtendedHttpRequest):
|
||||
"""
|
||||
Obtains the IP of a Django Request, even behind a proxy
|
||||
|
||||
Returns the obtained IP, that always will be a valid ip address.
|
||||
"""
|
||||
behind_proxy = GlobalConfig.BEHIND_PROXY.getBool(False)
|
||||
try:
|
||||
request.ip = request.META['REMOTE_ADDR']
|
||||
except Exception:
|
||||
logger.exception('Request ip not found!!')
|
||||
request.ip = '' # No remote addr?? ...
|
||||
|
||||
try:
|
||||
proxies = request.META['HTTP_X_FORWARDED_FOR'].split(",")
|
||||
request.ip_proxy = proxies[0]
|
||||
|
||||
if not request.ip or behind_proxy:
|
||||
# Request.IP will be None in case of nginx & gunicorn
|
||||
# Some load balancers may include "domains" on x-forwarded for,
|
||||
request.ip = request.ip_proxy.split('%')[0] # Stores the ip
|
||||
|
||||
# will raise "list out of range", leaving ip_proxy = proxy in case of no other proxy apart of nginx
|
||||
request.ip_proxy = proxies[1].strip()
|
||||
except Exception:
|
||||
request.ip_proxy = request.ip
|
||||
|
||||
@staticmethod
|
||||
def getUser(request: ExtendedHttpRequest) -> None:
|
||||
"""
|
||||
Ensures request user is the correct user
|
||||
"""
|
||||
user_id = request.session.get(USER_KEY)
|
||||
user: typing.Optional[User] = None
|
||||
if user_id:
|
||||
try:
|
||||
if user_id == ROOT_ID:
|
||||
user = getRootUser()
|
||||
else:
|
||||
user = User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
|
||||
logger.debug('User at Middleware: %s %s', user_id, user)
|
||||
|
||||
request.user = user
|
||||
def setRequest(request: ExtendedHttpRequest):
|
||||
_requests[getIdent()] = (
|
||||
weakref.ref(typing.cast(ExtendedHttpRequest, request)),
|
||||
datetime.datetime.now(),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user