diff --git a/src/risotto/services/config/config.py b/src/risotto/services/config/config.py index 11d381a..cea5a67 100644 --- a/src/risotto/services/config/config.py +++ b/src/risotto/services/config/config.py @@ -13,19 +13,18 @@ from ...register import register from ...config import ROOT_CACHE_DIR, DATABASE_DIR, DEBUG, ROUGAIL_DTD_PATH from ...context import Context from ...utils import _ -from ...error import CallError, NotAllowedError, RegistrationError +from ...error import CallError, RegistrationError from ...logger import log -if not isdir(ROOT_CACHE_DIR): - raise RegistrationError(_(f'unable to find the cache dir "{ROOT_CACHE_DIR}"')) - - class Risotto(Controller): servermodel = {} server = {} def __init__(self) -> None: + for dirname in [ROOT_CACHE_DIR, DATABASE_DIR, ROUGAIL_DTD_PATH]: + if not isdir(dirname): + raise RegistrationError(_(f'unable to find the cache dir "{dirname}"')) self.save_storage = Storage(engine='sqlite3', dir_database=DATABASE_DIR) super().__init__() diff --git a/src/risotto/services/session/session.py b/src/risotto/services/session/session.py index a61073e..8383d95 100644 --- a/src/risotto/services/session/session.py +++ b/src/risotto/services/session/session.py @@ -21,19 +21,47 @@ class Risotto(Controller): def __init__(self): self.modify_storage = Storage(engine='dictionary') - def valid_user(self, - session_id: str, - risotto_context: Context, - type: str) -> None: - """ check if current user is the session owner + def get_storage(self, + type: str): + if type == 'server': + return storage_server + return storage_servermodel + + def get_session(self, + session_id: str, + type: str) -> Dict: + """ Get session information from storage """ if type == 'server': storage = storage_server else: storage = storage_servermodel - username = risotto_context.username - if username != storage.get_session(session_id)['username']: - raise NotAllowedError() + return storage.get_session(session_id) + + def get_session_informations(self, + risotto_context: Context, + session_id: str, + type: str) -> Dict: + """ format session with a session ID name + """ + session = self.get_session(session_id, + type, + risotto_context.username) + return self.format_session(session_id, + session) + + def format_session(self, + session_name: str, + session: Dict) -> Dict: + """ format session + """ + return {'session_id': session_name, + 'id': session['id'], + 'username': session['username'], + 'timestamp': session['timestamp'], + 'namespace': session['namespace'], + 'mode': session['mode'], + 'debug': session['debug']} @register(['v1.session.server.start', 'v1.session.servermodel.start'], None) async def start_session(self, @@ -46,44 +74,47 @@ class Risotto(Controller): if type == 'server': if id not in config_module.server: raise Exception(_(f'cannot find {type} with id {id}')) - server = config_module.server[id] - config = server['server'] - storage = storage_server + config = config_module.server[id]['server'] else: if id not in config_module.servermodel: raise Exception(_(f'cannot find {type} with id {id}')) config = config_module.servermodel[id] - storage = storage_servermodel - # check if a session already exists, in this case returns it - session_list = self.list_sessions(type) - for sess in session_list: - if sess['id'] == id and sess['username'] == risotto_context.username: - session_id = sess['session_id'] - session = self.get_session(session_id, type) - return self.format_session(session_id, session) + storage = self.get_storage(type) + + # check if a session already exists + sessions = storage.get_sessions() + for session in sessions.values(): + if sess['id'] == id: + if sess['username'] == risotto_context.username: + # same user so returns it + return self.format_session(session['session_id'], session) + else: + raise CallError(_(f'{username} already edits this configuration')) # create a new session while True: session_id = 'z' + hexlify(urandom(23)).decode() - if not storage.has_session(session_id): + if not session_id in sessions: break - else: - print('session {} already exists'.format(session_id)) - username = risotto_context.username storage.add_session(session_id, config, id, - username, + risotto_context.username, self.modify_storage) + + # return session's information return self.get_session_informations(session_id, type) @register(['v1.session.server.list', 'v1.session.servermodel.list'], None) async def list_session_server(self, - risotto_context: Context): + risotto_context: Context) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - return self.list_sessions(type) + storage = self.get_storage(type, + risotto_context.username) + return [self.format_session(session_id, session) or session_id, session in storage.get_sessions().items()] + @register(['v1.session.server.filter', 'v1.session.servermodel.filter'], None) async def filter_session(self, @@ -93,10 +124,10 @@ class Risotto(Controller): mode: str, debug: Optional[bool]): type = risotto_context.message.rsplit('.', 2)[-2] - if type == 'server': - storage = storage_server - else: - storage = storage_servermodel + storage = self.get_storage(type) + # to validate the session right + storage.get_session(session_id, + username) if namespace is not None: storage.set_namespace(session_id, namespace) @@ -122,11 +153,9 @@ class Risotto(Controller): value_multi: Optional[List]) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] session = self.get_session(session_id, - type) - ret = {'session_id': session_id, - 'name': name} - if index is not None: - ret['index'] = index + type, + risotto_context.username) + # if multi and not follower the value is in fact in value_multi option = session['config'].option(name).option if option.ismulti() and not option.isfollower(): value = value_multi @@ -138,12 +167,14 @@ class Risotto(Controller): update['index'] = index updates = {'updates': [update]} session['option'].updates(updates) - ret['status'] = 'ok' except Exception as err: if DEBUG: print_exc() - ret['message'] = str(err) - ret['status'] = 'error' + raise CallError(str(err)) + ret = {'session_id': session_id, + 'name': name} + if index is not None: + ret['index'] = index return ret @register(['v1.session.server.validate', 'v1.session.servermodel.validate'], None) @@ -151,37 +182,36 @@ class Risotto(Controller): risotto_context: Context, session_id: str) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - session = self.get_session(session_id, type) - ret = {} + session = self.get_session(session_id, + type, + risotto_context.username) try: session['config'].forcepermissive.option(session['namespace']).value.dict() except Exception as err: - ret['status'] = 'error' - ret['message'] = str(err) - else: - if type == 'server': - mandatories = list(session['config'].forcepermissive.value.mandatory()) - if mandatories: - ret['status'] = 'incomplete' - ret['mandatories'] = mandatories + raise CallError(str(err)) + if type == 'server': + mandatories = list(session['config'].forcepermissive.value.mandatory()) + if mandatories: + if len(mandatories) == 1: + mandatories = mandatories[0] + msg = _('the parameter "--{mandatories}" is mandatory') else: - ret['status'] = 'ok' - else: - ret['status'] = 'ok' - return ret + mandatories = '", "--'.join(mandatories) + msg = _('parameters "{mandatories}" are mandatories') + raise CallError(msg) + return self.format_session(session_id, + session) @register(['v1.session.server.get', 'v1.session.servermodel.get'], None) - async def get_session_(self, - risotto_context: Context, - session_id: str) -> Dict: + async def get_session_server(self, + risotto_context: Context, + session_id: str) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - info = self.get_session_informations(session_id, - type) - info['content'] = session_id session = self.get_session(session_id, - type) + type, + risotto_context.username) + info = self.format_session(session_id, session) info['content'] = dumps(session['option'].value.dict(fullpath=True)) - return info @register(['v1.session.server.stop', 'v1.session.servermodel.stop'], None) @@ -190,18 +220,14 @@ class Risotto(Controller): session_id: str, save: bool) -> Dict: type = risotto_context.message.rsplit('.', 2)[-2] - self.valid_user(session_id, - risotto_context, - type) - session = self.get_session(session_id, - type) + storage = self.get_storage(type) + session = storage.get_session(session_id, + risotto_context.username) id_ = session['id'] config_module = dispatcher.get_service('config') if type == 'server': - storage = storage_server config = config_module.server[id_]['server'] else: - storage = storage_servermodel config = config_module.servermodel[id_] if save: modif_config = session['config'] @@ -215,66 +241,15 @@ class Risotto(Controller): request, risotto_context: Context, session_id: str) -> Dict: - self.valid_user(session_id, - risotto_context, - 'server') - session = storage_server.get_session(session_id) - return self.load_dict(session) + session = storage_server.get_session(session_id, + risotto_context.username) + return session['option'].dict(remotable='all') @register_http('v1', '/config/servermodel/{session_id}') async def get_servermodel_api(self, request, risotto_context: Context, session_id: str) -> Dict: - self.valid_user(session_id, - risotto_context, - 'servermodel') - session = storage_servermodel.get_session(session_id) - return self.load_dict(session) - - def get_session(self, - session_id: str, - type: str) -> Dict: - """ Get session information from storage - """ - if type == 'server': - return storage_server.get_session(session_id) - return storage_servermodel.get_session(session_id) - - def get_session_informations(self, - session_id: str, - type: str) -> Dict: - """ format session with a session ID name - """ - session = self.get_session(session_id, - type) - return self.format_session(session_id, - session) - - def format_session(self, - session_name: str, - session: Dict) -> Dict: - """ format session - """ - return {'session_id': session_name, - 'id': session['id'], - 'username': session['username'], - 'timestamp': session['timestamp'], - 'namespace': session['namespace'], - 'mode': session['mode'], - 'debug': session['debug']} - - def list_sessions(self, - type: str) -> List: - ret = [] - if type == 'server': - storage = storage_server - else: - storage = storage_servermodel - for session_id, session in storage.get_sessions().items(): - ret.append(self.format_session(session_id, session)) - return ret - - def load_dict(self, - session: Dict) -> Dict: + session = storage_servermodel.get_session(session_id, + risotto_context.username) return session['option'].dict(remotable='all') diff --git a/src/risotto/services/session/storage.py b/src/risotto/services/session/storage.py index 1d1ff39..a320209 100644 --- a/src/risotto/services/session/storage.py +++ b/src/risotto/services/session/storage.py @@ -1,6 +1,8 @@ import time +from typing import Dict from tiramisu import Config from rougail import modes +from ...error import CallError, NotAllowedError class StorageError(Exception): @@ -13,23 +15,15 @@ class Storage(object): def __init__(self): self.sessions = {} - def has_session(self, - id: int): - return id in self.sessions - def add_session(self, session_id: int, orig_config: Config, server_id: int, username: str, config_storage): - for session in self.sessions.values(): - if session['id'] == server_id: - raise Storage(_(f'{username} already edits this configuration')) prefix_id = f'{session_id}_' config_name = self.get_config_name(server_id) config_id = f'{prefix_id}{config_name}' - print(config_id) # copy Config and all it's parents meta = orig_config.config.deepcopy(session_id=config_id, @@ -41,11 +35,12 @@ class Storage(object): while True: try: children = list(config.config.list()) - except: - break - if children: + if not children: + # it's an empty metaconfig + break config = children[0] - else: + except: + # it's a config, so no "list" method break config.property.read_write() # set the default owner @@ -66,35 +61,12 @@ class Storage(object): self.set_namespace(session_id, 'creole') - def set_namespace(self, - session_id: int, - namespace: str): - self.sessions[session_id]['option'] = self.sessions[session_id]['config'].option(namespace) - self.sessions[session_id]['namespace'] = namespace - - def get_sessions(self): - return self.sessions; - - def del_session(self, - id: int): - del self.sessions[id] - - def get_session(self, - id: int): - if id not in self.sessions: - raise Exception(f'the session {id} not exists') - return self.sessions[id] - - def get_username(self, - id: int): - return self.get_session(id)['username'] - def set_config_mode(self, id: int, mode: str): """ Define which edition mode to select """ - config = self.get_session(id)['config'] + config = self.session[id]['config'] for mode_level in modes.values(): if modes[mode] < mode_level: config.property.add(mode_level.name) @@ -105,13 +77,35 @@ class Storage(object): def set_config_debug(self, id_, is_debug): """ Enable/Disable debug mode """ - config = self.get_session(id_)['config'] + config = self.session[id_]['config'] if is_debug: config.property.pop('hidden') else: config.property.add('hidden') self.sessions[id_]['debug'] = is_debug + def set_namespace(self, + session_id: int, + namespace: str): + self.sessions[session_id]['option'] = self.sessions[session_id]['config'].option(namespace) + self.sessions[session_id]['namespace'] = namespace + + def get_sessions(self): + return self.sessions; + + def get_session(self, + id: int, + username: str) -> Dict: + if id not in self.sessions: + raise Exception(f'the session {id} not exists') + if username != storage.get_session(session_id)['username']: + raise NotAllowedError() + return self.sessions[id] + + def del_session(self, + id: int): + del self.sessions[id] + class StorageServer(Storage): def get_config_name(self,