""" Copyright (c) 2016-2020 Keith Sterling http://www.keithsterling.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from programy.utils.logging.ylogger import YLogger from programy.utils.classes.loader import ClassLoader from programy.dialog.sentence import Sentence from programy.config.brain.securities import BrainSecuritiesConfiguration class SecurityManager: def __init__(self, security_configuration): assert security_configuration is not None assert isinstance(security_configuration, BrainSecuritiesConfiguration) self._configuration = security_configuration self._authentication = None self._authorisation = None self._account_linker = None @property def authentication(self): return self._authentication @property def authorisation(self): return self._authorisation @property def account_linker(self): return self._account_linker def load_security_services(self, client): self.load_authentication_service(client) self.load_authorisation_service(client) self.load_account_linking_service(client) def _load_authentication_class(self, client): if self._configuration.authentication is not None: if self._configuration.authentication.classname is not None: classobject = ClassLoader.instantiate_class(self._configuration.authentication.classname) self._authentication = classobject(self._configuration.authentication) self._authentication.initialise(client) def load_authentication_service(self, client): try: self._load_authentication_class(client) except Exception as excep: YLogger.exception(self, "Failed to load security services", excep) def _load_authorisation_class(self, client): if self._configuration.authorisation is not None: if self._configuration.authorisation.classname is not None: classobject = ClassLoader.instantiate_class(self._configuration.authorisation.classname) self._authorisation = classobject(self._configuration.authorisation) self._authorisation.initialise(client) def load_authorisation_service(self, client): try: self._load_authorisation_class(client) except Exception as excep: YLogger.exception(self, "Failed to instatiate authorisation class", excep) def _load_account_linking_class(self, client): if self._configuration.account_linker is not None: if self._configuration.account_linker.classname is not None: classobject = ClassLoader.instantiate_class(self._configuration.account_linker.classname) self._account_linker = classobject(self._configuration.account_linker) self.account_linker.initialise(client) def load_account_linking_service(self, client): try: self._load_account_linking_class(client) except Exception as excep: YLogger.exception(self, "Failed to instatiate authorisation class", excep) def failed_authentication(self, client_context): YLogger.error(client_context, "[%s] failed authentication!") # If we have an SRAI defined, then use that if self.authentication.configuration.denied_srai is not None: match_context = client_context.brain.aiml_parser.\ match_sentence(client_context, Sentence(client_context, self.authentication.configuration.denied_srai), topic_pattern="*", that_pattern="*") # If the SRAI matched then return the result if match_context is not None: return client_context.brain.resolve_matched_template(client_context, match_context) # Otherswise return the static text, which is either # User defined via config.yaml # Or use the default value BrainSecurityConfiguration.DEFAULT_ACCESS_DENIED return self.authentication.configuration.denied_text def authenticate_user(self, client_context): if self.authentication is not None: if self.authentication.authenticate(client_context) is False: return self.failed_authentication(client_context) return None