From 81a81312e3adff994d8e3ff1867cfd712e46186f Mon Sep 17 00:00:00 2001 From: Fabio Manganiello Date: Thu, 5 Jul 2018 09:15:53 +0200 Subject: [PATCH] Basic support for token authentication on request level --- platypush/__init__.py | 6 +++++- platypush/backend/http/__init__.py | 6 +++++- platypush/config/__init__.py | 21 +++++---------------- platypush/message/request/__init__.py | 15 +++++++++++++-- platypush/plugins/__init__.py | 6 ++++++ platypush/utils/__init__.py | 5 +++++ 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/platypush/__init__.py b/platypush/__init__.py index 86223b308..79fa2ee54 100644 --- a/platypush/__init__.py +++ b/platypush/__init__.py @@ -78,7 +78,11 @@ class Daemon(object): msg -- platypush.message.Message instance """ if isinstance(msg, Request): - msg.execute(n_tries=self.n_tries) + try: + msg.execute(n_tries=self.n_tries) + except PermissionError: + logger.info('Dropped unauthorized request: {}'.format(msg)) + self.processed_requests += 1 if self.requests_to_process \ and self.processed_requests >= self.requests_to_process: diff --git a/platypush/backend/http/__init__.py b/platypush/backend/http/__init__.py index 14e869711..1eaaa2707 100644 --- a/platypush/backend/http/__init__.py +++ b/platypush/backend/http/__init__.py @@ -180,7 +180,11 @@ class HttpBackend(Backend): self.logger.info('Received message on the HTTP backend: {}'.format(msg)) if isinstance(msg, Request): - response = msg.execute(async=False) + try: + response = msg.execute(async=False) + except PermissionError: + abort(401) + self.logger.info('Processing response on the HTTP backend: {}'.format(msg)) return str(response) elif isinstance(msg, Event): diff --git a/platypush/config/__init__.py b/platypush/config/__init__.py index 2e18687e0..cb559c008 100644 --- a/platypush/config/__init__.py +++ b/platypush/config/__init__.py @@ -6,6 +6,7 @@ import sys import time import yaml +from platypush.utils import get_hash """ Config singleton instance """ _default_config_instance = None @@ -18,8 +19,6 @@ class Config(object): Config.init() - Initialize config from a custom path Config.init(config_file_path) - - Set a value - Config.set('foo', 'bar') - Get a value Config.get('foo') """ @@ -60,6 +59,9 @@ class Config(object): self._cfgfile = cfgfile self._config = self._read_config_file(self._cfgfile) + if 'token' in self._config: + self._config['token_hash'] = get_hash(self._config['token']) + if 'workdir' not in self._config: self._config['workdir'] = self._workdir_location os.makedirs(self._config['workdir'], exist_ok=True) @@ -242,20 +244,7 @@ class Config(object): """ global _default_config_instance if _default_config_instance is None: _default_config_instance = Config() - return _default_config_instance._config[key] - - @staticmethod - def set(key, value): - """ - Sets a config value - Params: - key -- Config key to set - value -- Value for key - """ - global _default_config_instance - if _default_config_instance is None: _default_config_instance = Config() - _default_config_instance._config[key] = key - + return _default_config_instance._config.get(key) # vim:sw=4:ts=4:et: diff --git a/platypush/message/request/__init__.py b/platypush/message/request/__init__.py index d02c18027..345f5e575 100644 --- a/platypush/message/request/__init__.py +++ b/platypush/message/request/__init__.py @@ -8,10 +8,11 @@ import traceback from threading import Thread +from platypush.config import Config from platypush.context import get_plugin from platypush.message import Message from platypush.message.response import Response -from platypush.utils import get_module_and_method_from_action +from platypush.utils import get_hash, get_module_and_method_from_action logger = logging.getLogger(__name__) @@ -19,7 +20,8 @@ logger = logging.getLogger(__name__) class Request(Message): """ Request message class """ - def __init__(self, target, action, origin=None, id=None, backend=None, args=None): + def __init__(self, target, action, origin=None, id=None, backend=None, + args=None, token=None): """ Params: target -- Target node [String] @@ -28,6 +30,7 @@ class Request(Message): id -- Message ID, or None to get it auto-generated backend -- Backend connected to the request, where the response will be delivered args -- Additional arguments for the action [Dict] + token -- Authorization token, if required on the server [Str] """ self.id = id if id else self._generate_id() @@ -36,6 +39,7 @@ class Request(Message): self.origin = origin self.args = args if args else {} self.backend = backend + self.token = token @classmethod def build(cls, msg): @@ -48,6 +52,7 @@ class Request(Message): args['id'] = msg['id'] if 'id' in msg else cls._generate_id() if 'origin' in msg: args['origin'] = msg['origin'] + if 'token' in msg: args['token'] = msg['token'] return cls(**args) @staticmethod @@ -201,6 +206,12 @@ class Request(Message): self._send_response(response) return response + token_hash = Config.get('token_hash') + + if token_hash: + if self.token is None or get_hash(self.token) != token_hash: + raise PermissionError() + if async: Thread(target=_thread_func, args=(n_tries,)).start() else: diff --git a/platypush/plugins/__init__.py b/platypush/plugins/__init__.py index e5df9d73a..70f77f36b 100644 --- a/platypush/plugins/__init__.py +++ b/platypush/plugins/__init__.py @@ -5,6 +5,12 @@ from platypush.config import Config from platypush.message.response import Response +def action(f): + def _execute_action(*args, **kwargs): + return f(*args, **kwargs) + return _execute_action + + class Plugin(object): """ Base plugin class """ diff --git a/platypush/utils/__init__.py b/platypush/utils/__init__.py index f17acac21..62317e62e 100644 --- a/platypush/utils/__init__.py +++ b/platypush/utils/__init__.py @@ -1,4 +1,5 @@ import errno +import hashlib import importlib import logging import os @@ -65,5 +66,9 @@ def clear_timeout(): signal.alarm(0) +def get_hash(s): + return hashlib.sha256(s.encode('utf-8')).hexdigest() + + # vim:sw=4:ts=4:et: