mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-10 01:17:59 +03:00
request middleware improved. Fixed base model types & migrated providers to 3.x python
This commit is contained in:
parent
145ab9b865
commit
72df9b2275
@ -30,8 +30,8 @@
|
|||||||
"""
|
"""
|
||||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import typing
|
|
||||||
import logging
|
import logging
|
||||||
|
import typing
|
||||||
|
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ class Providers(ModelHandler):
|
|||||||
# Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, ....
|
# Field from where to get "class" and prefix for that class, so this will generate "row-state-A, row-state-X, ....
|
||||||
table_row_style = {'field': 'maintenance_mode', 'prefix': 'row-maintenance-'}
|
table_row_style = {'field': 'maintenance_mode', 'prefix': 'row-maintenance-'}
|
||||||
|
|
||||||
def item_as_dict(self, item) -> typing.Dict[str, typing.Any]:
|
def item_as_dict(self, item: Provider) -> typing.Dict[str, typing.Any]:
|
||||||
type_ = item.getType()
|
type_ = item.getType()
|
||||||
|
|
||||||
# Icon can have a lot of data (1-2 Kbytes), but it's not expected to have a lot of services providers, and even so, this will work fine
|
# Icon can have a lot of data (1-2 Kbytes), but it's not expected to have a lot of services providers, and even so, this will work fine
|
||||||
@ -86,7 +86,8 @@ class Providers(ModelHandler):
|
|||||||
'name': ugettext(t.name()),
|
'name': ugettext(t.name()),
|
||||||
'type': t.type(),
|
'type': t.type(),
|
||||||
'description': ugettext(t.description()),
|
'description': ugettext(t.description()),
|
||||||
'icon': t.icon().replace('\n', '')} for t in type_.getServicesTypes()]
|
'icon': typing.cast(str, t.icon()).replace('\n', '')
|
||||||
|
} for t in type_.getServicesTypes()]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': item.uuid,
|
'id': item.uuid,
|
||||||
@ -101,22 +102,22 @@ class Providers(ModelHandler):
|
|||||||
'permission': permissions.getEffectivePermission(self._user, item)
|
'permission': permissions.getEffectivePermission(self._user, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
def checkDelete(self, item):
|
def checkDelete(self, item: Provider) -> None:
|
||||||
if item.services.count() > 0:
|
if item.services.count() > 0:
|
||||||
raise RequestError('Can\'t delete providers with services already associated')
|
raise RequestError(ugettext('Can\'t delete providers with services'))
|
||||||
|
|
||||||
# Types related
|
# Types related
|
||||||
def enum_types(self):
|
def enum_types(self) -> typing.Iterable[typing.Type[services.ServiceProvider]]:
|
||||||
return services.factory().providers().values()
|
return services.factory().providers().values()
|
||||||
|
|
||||||
# Gui related
|
# Gui related
|
||||||
def getGui(self, type_):
|
def getGui(self, type_: str) -> typing.List[typing.Any]:
|
||||||
try:
|
clsType = services.factory().lookup(type_)
|
||||||
return self.addDefaultFields(services.factory().lookup(type_).guiDescription(), ['name', 'comments', 'tags'])
|
if clsType:
|
||||||
except Exception:
|
return self.addDefaultFields(clsType.guiDescription(), ['name', 'comments', 'tags'])
|
||||||
raise NotFound('type not found')
|
raise NotFound('Type not found!')
|
||||||
|
|
||||||
def allservices(self):
|
def allservices(self) -> typing.Generator[typing.Dict, None, None]:
|
||||||
"""
|
"""
|
||||||
Custom method that returns "all existing services", no mater who's his daddy :)
|
Custom method that returns "all existing services", no mater who's his daddy :)
|
||||||
"""
|
"""
|
||||||
@ -128,7 +129,7 @@ class Providers(ModelHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Passed service cause type is unknown')
|
logger.exception('Passed service cause type is unknown')
|
||||||
|
|
||||||
def service(self):
|
def service(self) -> typing.Dict:
|
||||||
"""
|
"""
|
||||||
Custom method that returns a service by its uuid, no matter who's his daddy
|
Custom method that returns a service by its uuid, no matter who's his daddy
|
||||||
"""
|
"""
|
||||||
@ -139,7 +140,7 @@ class Providers(ModelHandler):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise RequestError(ugettext('Service not found'))
|
raise RequestError(ugettext('Service not found'))
|
||||||
|
|
||||||
def maintenance(self, item):
|
def maintenance(self, item: Provider):
|
||||||
"""
|
"""
|
||||||
Custom method that swaps maintenance mode state for a provider
|
Custom method that swaps maintenance mode state for a provider
|
||||||
:param item:
|
:param item:
|
||||||
@ -149,12 +150,15 @@ class Providers(ModelHandler):
|
|||||||
item.save()
|
item.save()
|
||||||
return self.item_as_dict(item)
|
return self.item_as_dict(item)
|
||||||
|
|
||||||
def test(self, type_):
|
def test(self, type_: str):
|
||||||
from uds.core.environment import Environment
|
from uds.core.environment import Environment
|
||||||
|
|
||||||
logger.debug('Type: %s', type_)
|
logger.debug('Type: %s', type_)
|
||||||
spType = services.factory().lookup(type_)
|
spType = services.factory().lookup(type_)
|
||||||
|
|
||||||
|
if not spType:
|
||||||
|
raise NotFound('Type not found!')
|
||||||
|
|
||||||
self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True)
|
self.ensureAccess(spType, permissions.PERMISSION_MANAGEMENT, root=True)
|
||||||
|
|
||||||
logger.debug('spType: %s', spType)
|
logger.debug('spType: %s', spType)
|
||||||
@ -164,5 +168,5 @@ class Providers(ModelHandler):
|
|||||||
res = spType.test(Environment.getTempEnv(), dct)
|
res = spType.test(Environment.getTempEnv(), dct)
|
||||||
if res[0]:
|
if res[0]:
|
||||||
return 'ok'
|
return 'ok'
|
||||||
else:
|
|
||||||
return res[1]
|
return res[1]
|
||||||
|
@ -334,7 +334,7 @@ class DetailHandler(BaseModelHandler):
|
|||||||
|
|
||||||
Also accepts GET methods for "custom" methods
|
Also accepts GET methods for "custom" methods
|
||||||
"""
|
"""
|
||||||
custom_methods: typing.ClassVar[typing.Iterable[str]] = []
|
custom_methods: typing.ClassVar[typing.List[str]] = []
|
||||||
_parent: typing.Optional['ModelHandler']
|
_parent: typing.Optional['ModelHandler']
|
||||||
_path: str
|
_path: str
|
||||||
_params: typing.Any # _params is deserialized object from request
|
_params: typing.Any # _params is deserialized object from request
|
||||||
@ -602,15 +602,15 @@ class ModelHandler(BaseModelHandler):
|
|||||||
# This is an array of tuples of two items, where first is method and second inticates if method needs parent id (normal behavior is it needs it)
|
# This is an array of tuples of two items, where first is method and second inticates if method needs parent id (normal behavior is it needs it)
|
||||||
# For example ('services', True) -- > .../id_parent/services
|
# For example ('services', True) -- > .../id_parent/services
|
||||||
# ('services', False) --> ..../services
|
# ('services', False) --> ..../services
|
||||||
custom_methods: typing.ClassVar[typing.Iterable[typing.Tuple[str, bool]]] = [] # If this model respond to "custom" methods, we will declare them here
|
custom_methods: typing.ClassVar[typing.List[typing.Tuple[str, bool]]] = [] # If this model respond to "custom" methods, we will declare them here
|
||||||
# If this model has details, which ones
|
# If this model has details, which ones
|
||||||
detail: typing.ClassVar[typing.Optional[typing.Dict[str, typing.Type[DetailHandler]]]] = None # Dictionary containing detail routing
|
detail: typing.ClassVar[typing.Optional[typing.Dict[str, typing.Type[DetailHandler]]]] = None # Dictionary containing detail routing
|
||||||
# Put needed fields
|
# Put needed fields
|
||||||
save_fields: typing.ClassVar[typing.Iterable[str]] = []
|
save_fields: typing.ClassVar[typing.List[str]] = []
|
||||||
# Put removable fields before updating
|
# Put removable fields before updating
|
||||||
remove_fields: typing.ClassVar[typing.Iterable[str]] = []
|
remove_fields: typing.ClassVar[typing.List[str]] = []
|
||||||
# Table info needed fields and title
|
# Table info needed fields and title
|
||||||
table_fields: typing.ClassVar[typing.Iterable[typing.Any]] = []
|
table_fields: typing.ClassVar[typing.List[typing.Any]] = []
|
||||||
table_row_style: typing.ClassVar[typing.Dict] = {}
|
table_row_style: typing.ClassVar[typing.Dict] = {}
|
||||||
table_title: typing.ClassVar[str] = ''
|
table_title: typing.ClassVar[str] = ''
|
||||||
table_subtitle: typing.ClassVar[str] = ''
|
table_subtitle: typing.ClassVar[str] = ''
|
||||||
|
@ -30,8 +30,9 @@
|
|||||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||||
"""
|
"""
|
||||||
import threading
|
import threading
|
||||||
import logging
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import weakref
|
||||||
|
import logging
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
@ -41,11 +42,12 @@ from uds.core.util.config import GlobalConfig
|
|||||||
from uds.core.auths.auth import ROOT_ID, USER_KEY, getRootUser
|
from uds.core.auths.auth import ROOT_ID, USER_KEY, getRootUser
|
||||||
from uds.models import User
|
from uds.models import User
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
_requests: typing.Dict[int, typing.Tuple[HttpRequest, datetime.datetime]] = {}
|
_requests: typing.Dict[int, typing.Tuple[weakref.ref, datetime.datetime]] = {}
|
||||||
|
|
||||||
|
# How often to check the requests cache for stuck objects
|
||||||
|
CHECK_SECONDS = 3600 * 24 # Once a day is more than enough
|
||||||
|
|
||||||
def getIdent() -> int:
|
def getIdent() -> int:
|
||||||
ident = threading.current_thread().ident
|
ident = threading.current_thread().ident
|
||||||
@ -55,12 +57,11 @@ def getIdent() -> int:
|
|||||||
def getRequest() -> HttpRequest:
|
def getRequest() -> HttpRequest:
|
||||||
ident = getIdent()
|
ident = getIdent()
|
||||||
if ident in _requests:
|
if ident in _requests:
|
||||||
return _requests[ident][0]
|
return _requests[ident][0]() # Return obj from weakref
|
||||||
|
|
||||||
return HttpRequest()
|
return HttpRequest()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GlobalRequestMiddleware:
|
class GlobalRequestMiddleware:
|
||||||
lastCheck: typing.ClassVar[datetime.datetime] = datetime.datetime.now()
|
lastCheck: typing.ClassVar[datetime.datetime] = datetime.datetime.now()
|
||||||
|
|
||||||
@ -68,6 +69,9 @@ class GlobalRequestMiddleware:
|
|||||||
self._get_response: typing.Callable[[HttpRequest], HttpResponse] = get_response
|
self._get_response: typing.Callable[[HttpRequest], HttpResponse] = get_response
|
||||||
|
|
||||||
def _process_request(self, request: HttpRequest) -> None:
|
def _process_request(self, request: HttpRequest) -> None:
|
||||||
|
# Store request on cache
|
||||||
|
_requests[getIdent()] = (weakref.ref(request), datetime.datetime.now())
|
||||||
|
|
||||||
# Add IP to request
|
# Add IP to request
|
||||||
GlobalRequestMiddleware.fillIps(request)
|
GlobalRequestMiddleware.fillIps(request)
|
||||||
# Ensures request contains os
|
# Ensures request contains os
|
||||||
@ -75,10 +79,6 @@ class GlobalRequestMiddleware:
|
|||||||
# Ensures that requests contains the valid user
|
# Ensures that requests contains the valid user
|
||||||
GlobalRequestMiddleware.getUser(request)
|
GlobalRequestMiddleware.getUser(request)
|
||||||
|
|
||||||
# Store request on cache
|
|
||||||
_requests[getIdent()] = (request, datetime.datetime.now())
|
|
||||||
|
|
||||||
|
|
||||||
def _process_response(self, request: HttpRequest, response: HttpResponse):
|
def _process_response(self, request: HttpRequest, response: HttpResponse):
|
||||||
# Remove IP from global cache (processing responses after this will make global request unavailable,
|
# Remove IP from global cache (processing responses after this will make global request unavailable,
|
||||||
# but can be got from request again)
|
# but can be got from request again)
|
||||||
@ -91,6 +91,10 @@ class GlobalRequestMiddleware:
|
|||||||
logger.info('Request id %s not stored in cache', ident)
|
logger.info('Request id %s not stored in cache', ident)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('Deleting stored request')
|
logger.exception('Deleting stored request')
|
||||||
|
|
||||||
|
# Clean old stored if needed
|
||||||
|
GlobalRequestMiddleware.cleanStuckRequests()
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def __call__(self, request: HttpRequest):
|
def __call__(self, request: HttpRequest):
|
||||||
@ -98,17 +102,15 @@ class GlobalRequestMiddleware:
|
|||||||
|
|
||||||
response = self._get_response(request)
|
response = self._get_response(request)
|
||||||
|
|
||||||
# Clean cache
|
|
||||||
GlobalRequestMiddleware.cleanStuckRequests()
|
|
||||||
|
|
||||||
return self._process_response(request, response)
|
return self._process_response(request, response)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cleanStuckRequests() -> None:
|
def cleanStuckRequests() -> None:
|
||||||
# In case of some exception, keep clean very old request from time to time...
|
# In case of some exception, keep clean very old request from time to time...
|
||||||
if GlobalRequestMiddleware.lastCheck > datetime.datetime.now() - datetime.timedelta(seconds=60):
|
if GlobalRequestMiddleware.lastCheck > datetime.datetime.now() - datetime.timedelta(seconds=CHECK_SECONDS):
|
||||||
return
|
return
|
||||||
logger.debug('Cleaning stuck requestws from %s', _requests)
|
logger.debug('Cleaning stuck requestws from %s', _requests)
|
||||||
|
# No request lives 60 seconds, so 60 seconds is fine
|
||||||
cleanFrom: datetime.datetime = datetime.datetime.now() - datetime.timedelta(seconds=60)
|
cleanFrom: datetime.datetime = datetime.datetime.now() - datetime.timedelta(seconds=60)
|
||||||
toDelete: typing.List[int] = []
|
toDelete: typing.List[int] = []
|
||||||
for ident, request in _requests.items():
|
for ident, request in _requests.items():
|
||||||
|
Loading…
Reference in New Issue
Block a user