1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2025-11-02 04:20:55 +01:00

Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel

This commit is contained in:
prieto 2016-08-25 08:51:29 +02:00
commit f0f1ed9a83
7 changed files with 146 additions and 78 deletions

View file

@ -121,31 +121,20 @@ a session is allready started !!!")
#@todo Maybe we can delete this metaclass.... #@todo Maybe we can delete this metaclass....
class ClientMetaclass(type): class ClientMetaclass(type):
SESSION_ID_NAME = '__SESSION_ID__'
def __init__(self, name, bases, attrs): def __init__(self, name, bases, attrs):
self.__session = dict()
return super(ClientMetaclass, self).__init__(name, bases, attrs) return super(ClientMetaclass, self).__init__(name, bases, attrs)
def __getitem__(self, key): def __getitem__(self, key):
if key not in self.__session: return self.session()[key]
raise KeyError("This client instance does not have a '%s' data" % key)
return self.__session[key] def __delitem__(self, key):
del(self.session()[key])
def __setitem__(self, key, value): def __setitem__(self, key, value):
if SESSION_ID_NAME not in self.__session: self.session()[key] = value
self.__session[SESSION_ID_NAME] = SessionHandler.start_session()
self.__session[key] = value
def __token(self):
return None if SESSION_ID_NAME not in self.__sessions else self.__session[SESSION_ID_NAME]
##@brief Return a copy of sessions infos
def session_dump(self):
#first set all sessions values
SessionHandler.save_session(self.__session)
return copy.copy(self.__session)
def __str__(self):
return str(self._instance)
##@brief Abstract singleton class designed to handle client informations ##@brief Abstract singleton class designed to handle client informations
# #
@ -196,20 +185,6 @@ class Client(object, metaclass = ClientMetaclass):
self.__session = LodelSession(session_token) self.__session = LodelSession(session_token)
logger.debug("New client : %s" % self) logger.debug("New client : %s" % self)
##@brief Attempt to restore a session given a session token
#@param token mixed : a session token
#@return Session datas (a dict)
#@throw ClientAuthenticationFailure if token is not valid or not
#existing
def _restore_session(self, token):
return self.__session.restore(token)
##@brief Return the current session token or None
#@return A session token or None
@classmethod
def session_token(cls):
return self.__session.retrieve_token()
##@brief Try to authenticate a user with a login and a password ##@brief Try to authenticate a user with a login and a password
#@param login str : provided login #@param login str : provided login
#@param password str : provided password (hash) #@param password str : provided password (hash)
@ -245,6 +220,28 @@ class Client(object, metaclass = ClientMetaclass):
break break
if self.is_anon(): if self.is_anon():
self.fail() #Security logging self.fail() #Security logging
##@brief Attempt to restore a session given a session token
#@param token mixed : a session token
#@return Session datas (a dict)
#@throw ClientAuthenticationFailure if token is not valid or not
#existing
@classmethod
def restore_session(self, token):
cls._assert_instance()
return self.__session.restore(token)
##@brief Return the current session token or None
#@return A session token or None
@classmethod
def session_token(cls):
cls._assert_instance()
return cls._instance.__session.retrieve_token()
@classmethod
def session(cls):
cls._assert_instance()
return cls._instance.__session
@classmethod @classmethod
def destroy(cls): def destroy(cls):

View file

@ -1,9 +1,10 @@
from lodel import logger from lodel import logger
from lodel.plugin.hooks import LodelHook
##@brief Handles common errors with a Client ##@brief Handles common errors with a Client
class ClientError(Exception): class ClientError(Exception):
##@brief The logger function to use to log the error message ##@brief The logger function to use to log the error message
_loglvl = logger.warning _loglvl = 'warning'
##@brief Error str ##@brief Error str
_err_str = "Error" _err_str = "Error"
##@brief the hook name to trigger with error ##@brief the hook name to trigger with error
@ -17,8 +18,9 @@ class ClientError(Exception):
#"<client infos> : <_err_str>[ : <msg>]" #"<client infos> : <_err_str>[ : <msg>]"
def __init__(self, client, msg = ""): def __init__(self, client, msg = ""):
msg = self.build_message(client, msg) msg = self.build_message(client, msg)
if cls._loglvl is not None: if self._loglvl is not None:
cls._loglvl(msg) logfun = getattr(logger, self._loglvl)
logfun(msg)
super().__init__(msg) super().__init__(msg)
if self._action is not None: if self._action is not None:
LodelHook.call_hook(self._action, self, self._payload) LodelHook.call_hook(self._action, self, self._payload)
@ -32,21 +34,21 @@ class ClientError(Exception):
##@brief Handles authentication failure errors ##@brief Handles authentication failure errors
class ClientAuthenticationFailure(ClientError): class ClientAuthenticationFailure(ClientError):
_loglvl = logger.security _loglvl = 'security'
_err_str = 'Authentication failure' _err_str = 'Authentication failure'
_action = 'lodel2_ui_authentication_failure' _action = 'lodel2_ui_authentication_failure'
##@brief Handles permission denied errors ##@brief Handles permission denied errors
class ClientPermissionDenied(ClientError): class ClientPermissionDenied(ClientError):
_loglvl = logger.security _loglvl = 'security'
_err_str = 'Permission denied' _err_str = 'Permission denied'
_action = 'lodel2_ui_permission_denied' _action = 'lodel2_ui_permission_denied'
##@brief Handles common errors on authentication ##@brief Handles common errors on authentication
class ClientAuthenticationError(ClientError): class ClientAuthenticationError(ClientError):
_loglvl = logger.error _loglvl = 'error'
_err_str = 'Authentication error' _err_str = 'Authentication error'
_action = 'lodel2_ui_error' _action = 'lodel2_ui_error'

View file

@ -9,7 +9,8 @@ class DiscoverPlugin(lodel_script.LodelScript):
@classmethod @classmethod
def argparser_config(cls, parser): def argparser_config(cls, parser):
parser.add_argument('-d', '--directory', #parser.add_argument('-d', '--directory',
parser.add_argument('PLUGIN_PATH',
help="Directory to walk through looking for lodel2 plugins", help="Directory to walk through looking for lodel2 plugins",
nargs='+') nargs='+')
parser.add_argument('-l', '--list-only', default=False, parser.add_argument('-l', '--list-only', default=False,
@ -20,11 +21,11 @@ without modifying existing cache")
@classmethod @classmethod
def run(cls, args): def run(cls, args):
from lodel.plugin.plugins import Plugin from lodel.plugin.plugins import Plugin
if args.directory is None or len(args.directory) == 0: if args.PLUGIN_PATH is None or len(args.PLUGIN_PATH) == 0:
cls.help_exit("Specify a least one directory") cls.help_exit("Specify a least one directory")
no_cache = args.list_only no_cache = args.list_only
res = Plugin.discover(args.directory, no_cache) res = Plugin.discover(args.PLUGIN_PATH, no_cache)
print("Found plugins in : %s" % ', '.join(args.directory)) print("Found plugins in : %s" % ', '.join(args.PLUGIN_PATH))
for pname, pinfos in res['plugins'].items(): for pname, pinfos in res['plugins'].items():
print("\t- %s(%s) in %s" % ( print("\t- %s(%s) in %s" % (
pname, pinfos['version'], pinfos['path'])) pname, pinfos['version'], pinfos['path']))

View file

@ -142,19 +142,23 @@ def main_run():
if len(sys.argv) == 1: if len(sys.argv) == 1:
default_parser.print_help() default_parser.print_help()
exit(1) exit(1)
args = default_parser.parse_args()
if args.list_actions:
print("Available actions :")
for sname in sorted(__registered_scripts.keys()):
print("\t- %s" % __registered_scripts[sname])
exit(0)
#preparing sys.argv (deleting action) #preparing sys.argv (deleting action)
action = sys.argv[1].lower() action = sys.argv[1].lower()
del(sys.argv[1])
if action not in __registered_scripts: if action not in __registered_scripts:
#Trying to parse argument with default parser
print("PASSAGE")
args = default_parser.parse_args()
if args.list_actions:
print("Available actions :")
for sname in sorted(__registered_scripts.keys()):
print("\t- %s" % __registered_scripts[sname])
exit(0)
print("Unknow action '%s'\n" % action, file=sys.stderr) print("Unknow action '%s'\n" % action, file=sys.stderr)
default_parser.print_help() default_parser.print_help()
exit(1) exit(1)
#OK action is known, preparing argv to pass it to the action script
del(sys.argv[1])
script = __registered_scripts[action] script = __registered_scripts[action]
ret = script._run() ret = script._run()
ret = 0 if ret is None else ret ret = 0 if ret is None else ret

View file

@ -25,3 +25,10 @@ I'm a custom method on an instance of class %s" % self.__class__)
def dummy_instance_method(self): def dummy_instance_method(self):
print("Hello world !\ print("Hello world !\
I'm a custom method on class %s" % self.__class__) I'm a custom method on class %s" % self.__class__)
@LodelHook('lodel2_loader_main')
def foofun(hname, caller, payload):
from lodel import dyncode
print("Hello world ! I read dyncode from lodel.dyncode : ",
dyncode.dynclasses)

View file

@ -2,6 +2,7 @@
import os import os
import copy import copy
from lodel import logger
from lodel.settings import Settings from lodel.settings import Settings
from lodel.auth.exceptions import * from lodel.auth.exceptions import *
@ -24,6 +25,7 @@ def destroy_session(token):
def restore_session(token): def restore_session(token):
_check_token(token) _check_token(token)
logger.debug("Restoring session : %s" %__sessions[token])
return __sessions[token] return __sessions[token]
def save_session(token, datas): def save_session(token, datas):

View file

@ -2,14 +2,19 @@
import loader # Lodel2 loader import loader # Lodel2 loader
import os import os
import hashlib
import time
from werkzeug.contrib.sessions import FilesystemSessionStore from werkzeug.contrib.sessions import FilesystemSessionStore
from werkzeug.wrappers import Response from werkzeug.wrappers import Response
from werkzeug.contrib.securecookie import SecureCookie
from lodel.settings import Settings from lodel.settings import Settings
from .interface.router import get_controller from .interface.router import get_controller
from .interface.lodelrequest import LodelRequest from .interface.lodelrequest import LodelRequest
from .exceptions import * from .exceptions import *
from .client import WebUiClient from .client import WebUiClient
from lodel.auth.exceptions import *
from lodel.utils.datetime import get_utc_timestamp from lodel.utils.datetime import get_utc_timestamp
from lodel.plugin.hooks import LodelHook from lodel.plugin.hooks import LodelHook
@ -19,6 +24,48 @@ SESSION_EXPIRATION_LIMIT = Settings.webui.sessions.expiration
session_store = FilesystemSessionStore(path=SESSION_FILES_BASE_DIR, filename_template=SESSION_FILES_TEMPLATE) session_store = FilesystemSessionStore(path=SESSION_FILES_BASE_DIR, filename_template=SESSION_FILES_TEMPLATE)
COOKIE_SESSION_ID = 'toktoken'
COOKIE_SESSION_HASH = 'nekotkot'
COOKIE_SESSION_HASH_SALT = [ os.urandom(32) for _ in range(2) ] #Before and after salt (maybe useless)
COOKIE_SESSION_HASH_ALGO = hashlib.sha512
##@brief Return a salted hash of a cookie
def cookie_hash(token):
return COOKIE_SESSION_HASH_ALGO(
COOKIE_SESSION_HASH_SALT[0]+token+COOKIE_SESSION_HASH_SALT[1]).hexdigest()
##@brief Load cookie from request
#@note Can produce security warning logs
#@param request
#@return None or a session token
def load_cookie(request):
token = request.cookies.get(COOKIE_SESSION_ID)
if token is None and token != '':
return None
token = bytes(token, 'utf-8')
hashtok = request.cookies.get(COOKIE_SESSION_HASH)
if hashtok is None:
raise ClientAuthenticationFailure(
WebUiClient, 'Bad cookies : no hash provided')
if cookie_hash(token) != hashtok:
raise ClientAuthenticationFailure(
WebUiClient, 'Bad cookies : hash mismatch')
return token
##@brief Properly set cookies and hash given a token
#@param response
#@param token str : the session token
def save_cookie(response, token):
response.set_cookie(COOKIE_SESSION_ID, token)
response.set_cookie(COOKIE_SESSION_HASH, cookie_hash(token))
def empty_cookie(response):
response.set_cookie(COOKIE_SESSION_ID, '')
response.set_cookie(COOKIE_SESSION_HASH, '')
#Starting instance #Starting instance
loader.start() loader.start()
#providing access to dyncode #providing access to dyncode
@ -49,38 +96,46 @@ def is_session_file_expired(timestamp_now, sid):
# WSGI Application # WSGI Application
def application(env, start_response): def application(env, start_response):
WebUiClient(env['REMOTE_ADDR'], env['HTTP_USER_AGENT'])
current_timestamp = get_utc_timestamp()
delete_old_session_files(current_timestamp)
request = LodelRequest(env) request = LodelRequest(env)
sid = request.cookies.get('sid') session_token = None
if sid is None or sid not in session_store.list():
request.session = session_store.new()
request.session['last_accessed'] = current_timestamp
else:
request.session = session_store.get(sid)
if is_session_file_expired(current_timestamp, sid):
session_store.delete(request.session)
request.session = session_store.new()
request.session['user_context'] = None
request.session['last_accessed'] = current_timestamp
try: try:
controller = get_controller(request) #We have to create the client before restoring cookie in order to be able
response = controller(request) #to log messages with client infos
except HttpException as e: client = WebUiClient(env['REMOTE_ADDR'], env['HTTP_USER_AGENT'], None)
session_token = load_cookie(request)
if session_token is not None:
WebClient.restore_session(token)
session_token = None
#test
WebUiClient['last_request'] = time.time()
try: try:
response = e.render(request) controller = get_controller(request)
except Exception as eb: response = controller(request)
res = Response() except HttpException as e:
res.status_code = 500 try:
return res response = e.render(request)
except Exception as eb:
raise eb
if request.session.should_save: res = Response()
session_store.save(request.session) res.status_code = 500
response.set_cookie('sid', request.session.sid) return res
session_token = WebUiClient.session_token()
if session_token is not None:
save_cookie(response,session_token)
session_token = None
except (ClientError, ClientAuthenticationError):
response = HttpException(400).render(request)
empty_cookie(response)
except ClientAuthenticationFailure:
response = HttpException(401).render(request)
empty_cookie(response)
except Exception as e:
raise e
res = response(env, start_response) res = response(env, start_response)
WebUiClient.destroy() WebUiClient.destroy()
return res return res