diff --git a/server/src/uds/REST/__init__.py b/server/src/uds/REST/__init__.py index 5a888a87f..743c7f736 100644 --- a/server/src/uds/REST/__init__.py +++ b/server/src/uds/REST/__init__.py @@ -37,29 +37,36 @@ from django.views.decorators.csrf import csrf_exempt from django.utils.decorators import method_decorator from django.utils.translation import ugettext as _, activate from django.conf import settings -from handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError, ResponseError +from uds.REST.handlers import Handler, HandlerError, AccessDenied, NotFound, RequestError, ResponseError import time import logging logger = logging.getLogger(__name__) -__all__ = [str(v) for v in ['Handler', 'Dispatcher']] +__all__ = [str(v) for v in ['Handler', 'Dispatcher']] AUTH_TOKEN_HEADER = 'X-Auth-Token' class Dispatcher(View): + ''' + This class is responsible of dispatching REST requests + ''' + # This attribute will contain all paths-->handler relations, added at Initialized method services = {'': None} # Will include a default /rest handler, but rigth now this will be fine @method_decorator(csrf_exempt) def dispatch(self, request, **kwargs): + ''' + Processes the REST request and routes it wherever it needs to be routed + ''' logger.debug('Language in dispatcher: {0}'.format(request.LANGUAGE_CODE)) - import processors + from uds.REST import processors - # Remove session, so response middelwares do nothing with this + # Remove session, so response middleware do nothing with this del request.session - # Now we extract method and posible variables from path + # Now we extract method and possible variables from path path = kwargs['arguments'].split('/') del kwargs['arguments'] @@ -67,22 +74,23 @@ class Dispatcher(View): service = Dispatcher.services full_path = [] # Last element will be - do_break = False cls = None - while len(path) > 0 and not do_break: - # .json, .xml, ... will break path recursion - do_break = path[0].find('.') != -1 + while len(path) > 0: clean_path = path[0].split('.')[0] - if service.has_key(clean_path): + if clean_path in service: service = service[clean_path] full_path.append(path[0]) path = path[1:] else: break + # .json, .xml, ... will break path recursion + if path[0].find('.') != -1: + break full_path = '/'.join(full_path) logger.debug(full_path) + # Here, service points to the path cls = service[''] if cls is None: return http.HttpResponseNotFound('method not found') @@ -91,8 +99,7 @@ class Dispatcher(View): try: p = full_path.split('.') processor = processors.available_processors_ext_dict[p[1]](request) - except: - # TODO: Extract processor from accept and/or content type? + except Exception: processor = processors.available_processors_mime_dict.get(request.META.get('CONTENT_TYPE', 'json'), processors.default_processor)(request) # Obtain method to be invoked @@ -115,7 +122,7 @@ class Dispatcher(View): return http.HttpResponseNotAllowed(allowedMethods) except AccessDenied: return http.HttpResponseForbidden('access denied') - except: + except Exception: logger.exception('error accessing attribute') logger.debug('Getting attribute {0} for {1}'.format(http_method, full_path)) return http.HttpResponseServerError('Unexcepected error') @@ -130,8 +137,8 @@ class Dispatcher(View): start = time.time() response = processor.getResponse(response) logger.debug('Execution time for encoding: {0}'.format(time.time() - start)) - for k, v in handler.headers().iteritems(): - response[k] = v + for k, val in handler.headers().iteritems(): + response[k] = val return response except RequestError as e: return http.HttpResponseServerError(unicode(e)) @@ -149,6 +156,9 @@ class Dispatcher(View): @staticmethod def registerSubclasses(classes): + ''' + Try to register Handler subclasses that have not been inherited + ''' for cls in classes: if len(cls.__subclasses__()) == 0: # Only classes that has not been inherited will be registered as Handlers logger.debug('Found class {0}'.format(cls)) diff --git a/server/src/uds/REST/handlers.py b/server/src/uds/REST/handlers.py index e7e165a94..4aed28832 100644 --- a/server/src/uds/REST/handlers.py +++ b/server/src/uds/REST/handlers.py @@ -33,7 +33,6 @@ from __future__ import unicode_literals from django.contrib.sessions.backends.db import SessionStore -from django.conf import settings from uds.core.util.Config import GlobalConfig @@ -45,26 +44,44 @@ AUTH_TOKEN_HEADER = 'HTTP_X_AUTH_TOKEN' class HandlerError(Exception): + ''' + Generic error for a REST handler + ''' pass class NotFound(HandlerError): + ''' + Item not found error + ''' pass class AccessDenied(HandlerError): + ''' + Access denied error + ''' pass class RequestError(HandlerError): + ''' + Request is invalid error + ''' pass class ResponseError(HandlerError): + ''' + Generic response error + ''' pass class Handler(object): + ''' + REST requests handler base class + ''' raw = False # If true, Handler will return directly an HttpResponse Object name = None # If name is not used, name will be the class name in lower case path = None # Path for this method, so we can do /auth/login, /auth/logout, /auth/auths in a simple way @@ -95,7 +112,7 @@ class Handler(object): self._session = SessionStore(session_key=self._authToken) if 'REST' not in self._session: raise Exception() # No valid session, so auth_token is also invalid - except: + except Exception: # Couldn't authenticate self._authToken = None self._session = None @@ -109,26 +126,44 @@ class Handler(object): raise AccessDenied() def headers(self): + ''' + Returns the headers of the REST request (all) + ''' return self._headers - def header(self, header_): - return self._headers.get(header_) + def header(self, headerName): + ''' + Get's an specific header name from REST request + ''' + return self._headers.get(headerName) def addHeader(self, header, value): + ''' + Inserts a new header inside the headers list + ''' self._headers[header] = value def removeHeader(self, header): + ''' + Removes an specific header from the headers list + ''' try: del self._headers[header] - except: - pass + except Exception: + pass # If not found, just ignore it # Auth related def getAuthToken(self): + ''' + Returns the authentication token for this REST request + ''' return self._authToken @staticmethod def storeSessionAuthdata(session, id_auth, username, locale, is_admin, staff_member): + ''' + Stores the authentication data inside current session + ''' if is_admin: staff_member = True # Make admins also staff members :-) @@ -141,6 +176,10 @@ class Handler(object): } def genAuthToken(self, id_auth, username, locale, is_admin, staf_member): + ''' + Generates the authentication token from a session, that is basically + the session key itself + ''' session = SessionStore() session.set_expiry(GlobalConfig.ADMIN_IDLE_TIME.getInt()) Handler.storeSessionAuthdata(session, id_auth, username, locale, is_admin, staf_member) @@ -150,6 +189,9 @@ class Handler(object): return self._authToken def cleanAuthToken(self): + ''' + Cleans up the authentication token + ''' self._authToken = None if self._session: self._session.delete() @@ -157,21 +199,33 @@ class Handler(object): # Session related (from auth token) def getValue(self, key): + ''' + Get REST session related value for a key + ''' try: return self._session['REST'].get(key) - except: - return None + except Exception: + return None # _session['REST'] does not exists? def setValue(self, key, value): + ''' + Set a session key value + ''' try: self._session['REST'][key] = value self._session.accessed = True self._session.save() - except: - pass + except Exception: + logger.exception('Got an exception setting session value {} to {}'.format(key, value)) def is_admin(self): + ''' + True if user of this REST request is administrator + ''' return self.getValue('is_admin') and True or False def is_staff_member(self): + ''' + True if user of this REST request is member of staff + ''' return self.getValue('staff_member') and True or False diff --git a/server/src/uds/REST/methods/user_services.py b/server/src/uds/REST/methods/user_services.py index 38fb7e181..5733c4277 100644 --- a/server/src/uds/REST/methods/user_services.py +++ b/server/src/uds/REST/methods/user_services.py @@ -199,7 +199,7 @@ class Transports(DetailHandler): return [{ 'id': i.id, 'name': i.name, - 'type': self.type_as_dict(i.getType()), + 'type': self.typeAsDict(i.getType()), 'comments': i.comments, 'priority': i.priority, } for i in parent.transports.all()] diff --git a/server/src/uds/REST/model.py b/server/src/uds/REST/model.py index 308bacd16..d2ab09b1e 100644 --- a/server/src/uds/REST/model.py +++ b/server/src/uds/REST/model.py @@ -37,14 +37,14 @@ from django.utils.translation import ugettext as _ from django.db import IntegrityError from uds.core.ui.UserInterface import gui as uiGui -from uds.REST.handlers import Handler +from uds.REST.handlers import Handler, HandlerError from uds.core.util import log import logging logger = logging.getLogger(__name__) -__updated__ = '2014-05-19' +__updated__ = '2014-09-15' # a few constants @@ -56,7 +56,10 @@ LOG = 'log' # Exception to "rethrow" on save error -class SaveException(Exception): +class SaveException(HandlerError): + ''' + Exception thrown if couldn't save + ''' pass @@ -64,6 +67,9 @@ class SaveException(Exception): class BaseModelHandler(Handler): def addField(self, gui, field): + ''' + Add a field to a "gui" description + ''' gui.append({ 'name': field.get('name', ''), 'value': '', @@ -84,6 +90,9 @@ class BaseModelHandler(Handler): return gui def addDefaultFields(self, gui, flds): + ''' + Adds default fields (based in a list) to a "gui" description + ''' if 'name' in flds: self.addField(gui, { 'name': 'name', @@ -94,40 +103,47 @@ class BaseModelHandler(Handler): }) if 'comments' in flds: self.addField(gui, { - 'name': 'comments', - 'label': _('Comments'), - 'tooltip': _('Comments for this element'), - 'length': 256, - 'order': 0 - 99, + 'name': 'comments', + 'label': _('Comments'), + 'tooltip': _('Comments for this element'), + 'length': 256, + 'order': 0 - 99, }) if 'priority' in flds: self.addField(gui, { - 'name': 'priority', - 'type': 'numeric', - 'label': _('Priority'), - 'tooltip': _('Selects the priority of this element (lower number means higher priority)'), - 'required': True, - 'value': 1, - 'length': 4, - 'order': 0 - 98, + 'name': 'priority', + 'type': 'numeric', + 'label': _('Priority'), + 'tooltip': _('Selects the priority of this element (lower number means higher priority)'), + 'required': True, + 'value': 1, + 'length': 4, + 'order': 0 - 98, }) if 'small_name' in flds: self.addField(gui, { - 'name': 'small_name', - 'type': 'text', - 'label': _('Short name'), - 'tooltip': _('Short name of this element'), - 'required': True, - 'length': 128, - 'order': 0 - 97, + 'name': 'small_name', + 'type': 'text', + 'label': _('Short name'), + 'tooltip': _('Short name of this element'), + 'required': True, + 'length': 128, + 'order': 0 - 97, }) return gui def typeInfo(self, type_): + ''' + Returns info about the type + In fact, right now, it returns an empty dict, that will be extended by typeAsDict + ''' return {} - def type_as_dict(self, type_): + def typeAsDict(self, type_): + ''' + Returns a dictionary describing the type (the name, the icon, description, etc...) + ''' res = self.typeInfo(type_) res.update({ 'name': _(type_.name()), @@ -138,6 +154,9 @@ class BaseModelHandler(Handler): return res def processTableFields(self, title, fields, row_style): + ''' + Returns a dict containing the table fields description + ''' return { 'title': unicode(title), 'fields': fields, @@ -369,10 +388,11 @@ class ModelHandler(BaseModelHandler): needs_staff = True # Which model does this manage model = None + + # This is an array of tuples of two items, where first is method and second inticates if method needs parent id + # For example ('services', True) -- > .../id_parent/services + # ('services', False) --> ..../services custom_methods = [] # If this model respond to "custom" methods, we will declare them here - # This is an array of tuples of two items, where first is method and second inticates if method needs parent id - # For example ('services', True) -- > .../id_parent/services - # ('services', False) --> ..../services # If this model has details, which ones detail = None # Dictionary containing detail routing # Put needed fields @@ -394,7 +414,7 @@ class ModelHandler(BaseModelHandler): def getTypes(self, *args, **kwargs): for type_ in self.enum_types(): - yield self.type_as_dict(type_) + yield self.typeAsDict(type_) def getType(self, type_): found = None @@ -480,14 +500,14 @@ class ModelHandler(BaseModelHandler): try: operation = getattr(self, self._args[1]) item = self.model.objects.get(pk=self._args[0]) - except: + except Exception: self.invalidMethodException() return operation(item) elif self._args[0] == cm[0]: try: operation = getattr(self, self._args[0]) - except: + except Exception: self.invalidMethodException() return operation() @@ -538,6 +558,9 @@ class ModelHandler(BaseModelHandler): self.invalidRequestException() def post(self): + ''' + Processes a POST request + ''' # right now logger.debug('method POST for {0}, {1}'.format(self.__class__.__name__, self._args)) if len(self._args) == 2: diff --git a/server/src/uds/admin/urls.py b/server/src/uds/admin/urls.py index 763b844e5..0c2799020 100644 --- a/server/src/uds/admin/urls.py +++ b/server/src/uds/admin/urls.py @@ -32,9 +32,10 @@ from __future__ import unicode_literals from django.conf.urls import patterns -__updated__ = '2014-02-19' +__updated__ = '2014-09-15' -urlpatterns = patterns('uds.admin.views', +urlpatterns = patterns( + 'uds.admin.views', (r'^$', 'index'), (r'^tmpl/(?P