def stop(self, # success exit (bool). success=True, # optional order 1 success message (overwrites success to response.success) (ResponseObject, OutputObject, dict). response={}, # optional order 2 success message (overwrites success to True) (str). message=None, # optional order 3 message (str). error=None, # json format (bool). json=False, ): if response != {}: if response["success"] in [True, "True", "true", "TRUE"]: success = True message = response["message"] else: success = False error = response["error"] if message != None: success = True _response_.log(message=message, json=json) elif error != None: success = False _response_.log(error=error, json=json) if success: sys.exit(0) else: sys.exit(1)
def create(self): # check params. response = _response_.parameters.check( traceback=self.__traceback__(function="create"), parameters={ "id": self.id, "user": self.user, "start": self.start_, }) if not response.success: return response # checks. if self.service.fp.exists(): return _response_.error( f"Service [{self.service.fp.path}] already exists (call service.check() instead)." ) # save. _response_.log( f"&ORANGE&Root permission&END& required to save changes to [{self.service.fp}].", log_level=self.log_level) self.service.save(data=self.__create__(), sudo=True) self.service.fp.ownership.set("root", sudo=True) self.service.fp.permission.set(700, sudo=True) if dev0s.defaults.vars.os in ["macos"]: os.system("sudo systemctl daemon-reload") # handler. if self.service.fp.exists(): return _response_.success( f"Successfully created service {self.id}.") else: return _response_.error(f"Failed to create service {self.id}.")
def check(self, # the passed args to except (list). exceptions=[], # json mode (bool). json=False, ): exceptions += ["--log-level", "--create-alias", "-v", "--version", "-j", "--json", "--non-interactive"] lexecutable = self.executable while True: if len(lexecutable) > 0 and lexecutable[len(lexecutable)-1] == "/": lexecutable = lexecutable[:-1] elif "//" in lexecutable: lexecutable = lexecutable.replace("//","/") else: break for i in sys.argv: if i not in ["", lexecutable] and not Files.exists(i) and (len(i) < 1 or (i[0] == "-")): try: int(i) integer = True except: integer = False if not integer: if i not in exceptions and f" {i}: " not in self.modes_str and f" {i}: " not in self.options_str and f" {i} " not in self.modes_str and f" {i} " not in self.options_str: error = f"Argument [{i}] is not a valid mode nor option." if json: _response_.log(error=error, json=True) sys.exit(1) else: self.docs(stop=False) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.UnknownArgumentError(error)
def __init__( self, # the path to the directory (str) (#1) path=None, # root permission required. sudo=False, ): # docs. DOCS = { "module": "dev0s.database.Database", "initialized": False, "description": [], "chapter": "Database", } # traceback. Traceback.__init__(self, traceback="Database") # checks. if path == None: raise Exceptions.InvalidUsage(self.__traceback__() + " Define parameter [path].") # args. self.path = gfp.clean(path) self.sudo = sudo # sys args. self.__cache__ = {} # attributes. self.dir = self.directory = Directory(self.path) # checks. if not self.dir.fp.exists(sudo=sudo): if self.sudo: _response_.log( f"&ORANGE&Root permission&END& required to create database [{self.path}]." ) Files.create( str(self.path), sudo=self.sudo, directory=True, permission=700, owner=defaults.vars.user, group=defaults.vars.group, ) # copy objects. self.fp = self.file_path = self.dir.fp self.ownership = self.fp.ownership self.permission = self.fp.permission # copy functions. self.join = self.dir.join self.subpath = self.dir.subpath self.fullpath = self.dir.fullpath
def send_crash( self, # all optional. # option 1: the success message. message=None, # (1) args={}, # (2) # option 2: the error message. error=None, # option 3: the response object. response=None, # save the message/error/response. save=False, # the active log level (int) (leave None to use self.log_level). log_level=None, # the required log level for when to print to console (leave None to use _response_.log_level ; default: 0). required_log_level=_response_.log_level, ): if log_level == None: log_level = self.log_level msg = None if isinstance(message, (str, String)): self.__response__ = _response_.success(message, args, log_level=-1, save=False) msg = message elif isinstance(error, (str, String)): self.__response__ = _response_.error(error, log_level=-1, save=False) msg = error elif isinstance(response, ResponseObject): self.__response__ = response if response["success"]: msg = response["message"] else: msg = response["error"] #else: # raise Exceptions.InvalidUsage("Define one of the following parameters: [message:str, error:str, response:ResponseObject].") self.__status__ = "crashed" if msg != None and log_level >= required_log_level or save: _response_.log(response=self.__response__, save=save, log_level=log_level, required_log_level=required_log_level) return self.__response__
def log( self, # option 1: # the message (#1 param). message=None, # option 2: # the error. error=None, # option 3: # the response dict (leave message None to use). response={}, # print the response as json. json=JSON, # optionals: # the active log level (leave None to use self.log_level). log_level=None, # the required log level for when printed to console. required_log_level=0, # save to log file. save=False, # save errors always (for options 2 & 3 only). save_errors=None, # the log mode (leave None for default). mode=None, ): if log_level == None: log_level = self.log_level return _response_.log( # option 1: # the message (#1 param). message=message, # option 2: # the error. error=error, # option 3: # the response dict (leave message None to use). response=response, # print the response as json. json=json, # optionals: # the active log level. log_level=log_level, # the required log level for when printed to console (leave None to use self.log_level). required_log_level=required_log_level, # save to log file. save=save, # save errors always (for options 2 & 3 only). save_errors=save_errors, # the log mode (leave None for default). mode=mode, )
def check(self): # check params. response = _response_.parameters.check( traceback=self.__traceback__(function="check"), parameters={ "id": self.id, "user": self.user, "start": self.start_, }) if not response.success: return response # checks. if not self.service.fp.exists(): return self.create() # save. self.service.load(sudo=True) data = self.__create__() if self.service.data != data: _response_.log( f"&ORANGE&Root permission&END& required to save changes to [{self.service.fp}].", log_level=self.log_level) self.service.save(data=data, sudo=True) os.system("sudo systemctl daemon-reload") # always set permissions. self.service.fp.ownership.set("root", sudo=True) self.service.fp.permission.set(700, sudo=True) # handler. if self.service.fp.exists(): return _response_.success( f"Successfully checked service {self.id}.") else: return _response_.error(f"Failed to check service {self.id}.")
def get(self, # the argument id (str) (example: --path). argument, # whether the argument is required or not (bool). required=True, # the plus index (int). index=1, # the argument id count index (int). count=1, # the value format (str, list) (example: formats=[str]. format="*", # pack multiple into tuple (bool). pack=True, # default value when empty (bool, int, float, str, dict, list). default=None, # docs chapter (str). chapter=None, # docs mode (str). mode=None, # json mode (bool). json=False, # overwrite default notes (dict). notes=None, ): # check & clean format. def clean_string(string="", remove_first_space=False, remove_last_space=False): while True: if string in [str, bool, int, float, dict, list, object, tuple]: break elif remove_first_space and len(string) > 0 and string[0] == " ": string = string[1:] elif remove_last_space and len(string) > 0 and string[len(string)-1] == " ": string = string[:-1] else: break return string def clean_array(array=["x", "y"] or "x,y", remove_first_space=False, remove_last_space=False): if isinstance(array, str): array = array.split(",") new = [] for i in array: new.append(clean_string(i, remove_first_space=remove_first_space, remove_last_space=remove_last_space)) return new def clean_dict(dictionary={} or "x:0,y:1"): if isinstance(dictionary, str): dictionary = clean_array(dict.split(","), remove_first_space=True, remove_last_space=True) if isinstance(dictionary, list): new = {} for key in dictionary: new[clean_string(key, remove_first_space=remove_first_space, remove_last_space=remove_last_space)] = clean_string(value, remove_first_space=remove_first_space, remove_last_space=remove_last_space) return new elif isinstance(dictionary, dict): new = {} for key,value in dictionary.items(): new[clean_string(key, remove_first_space=remove_first_space, remove_last_space=remove_last_space)] = clean_string(value, remove_first_space=remove_first_space, remove_last_space=remove_last_space) return new if not isinstance(format, list): if format not in [str, bool, int, float, dict, list, object, tuple]: try: format = format.split(",") except: format = [format] else: format = [format] if "*" not in format: l = [] for i in clean_array(format, remove_first_space=True, remove_last_space=True): if i not in [ str, "str", "string", bool, "bool", "boolean", int, "int", "integer", float, "float", "double", dict, "dict", "json", "dictionary", list, "list", "array", object, "object", "obj", tuple, "tuple", ]: format_error = f"""Specified an invalid format type [{i}], valid formats: {str([str, bool, int, float, tuple, dict, list, object]).replace("'",'')}.""" if json: _response_.log(error=empty_error, json=True) sys.exit(1) else: self.docs(stop=False, chapter=chapter, mode=mode, notes=notes) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.InvalidFormatError(invalid_format_error) if i in [str, "str", "string"]: i = str if i in [bool, "bool", "boolean"]: i = int if i in [int, "int", "integer"]: i = int if i in [float, "float", "double"]: i = float if i in [dict, "dict", "json", "dictionary"]: i = dict if i in [list, "list", "array"]: i = list if i in [object, "object", "obj"]: i = object if i in [tuple, "tuple"]: i = tuple l.append(i) format = l # handle string. if isinstance(argument, str): # set error msg. if index == 1: empty_error = f"Define argument [{argument}]." else: empty_error = f"Define argument [{argument}] (index: {index})." # check presence. if argument not in sys.argv: if required: if json: _response_.log(error=empty_error, json=True) sys.exit(1) else: self.docs(stop=False, chapter=chapter, mode=mode, notes=notes) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.EmptyArgumentError(empty_error) else: return default # retrieve. y, c = 0, 0 for x in sys.argv: # get value. failed = False try: if x == argument: c += 1 if c == count: value = sys.argv[y+index] # check format. if "*" not in format: x_format = Formats.get(value, serialize=False) error = False if x_format == str and int in format: try: value = int(value) except: error = True if x_format == str and bool in format: try: if value in ["true", True, "True", "TRUE"]: value = True else: value = False except: error = True if x_format == str and float in format: try: value = float(value) except: error = True if x_format == str and list in format: try: value = clean_array(value, remove_first_space=True, remove_last_space=True) except: error = True if x_format == str and dict in format: try: value = clean_dict(value, remove_first_space=True, remove_last_space=True) except: error = True x_format = Formats.get(value, serialize=False) if error or x_format not in format: format_error = f"""Provided an incorrect [{argument}] format: [{value}:{x_format}], valid format options: {str(format).replace("'",'')}.""" if json: _response_.log(error=format_error, json=True) sys.exit(1) else: self.docs(stop=False, chapter=chapter, mode=mode, notes=notes) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.ArgumentFormatError(format_error) return value except IndexError: failed = True # not present. if failed: if required: if json: _response_.log(error=empty_error, json=True) sys.exit(1) else: self.docs(stop=False, chapter=chapter, mode=mode, notes=notes) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.EmptyArgumentError(empty_error) else: return default y += 1 # should not happen. if required: if json: _response_.log(error=empty_error, json=True) sys.exit(1) else: self.docs(stop=False, chapter=chapter, mode=mode, notes=notes) sys.tracebacklimit = -1 ; sys.traceback_limit = -1 raise Exceptions.EmptyArgumentError(empty_error) else: return default else: if pack: arguments = [] for i in argument: arguments.append(self.get(i, required=required, index=index,default=default, mode=mode, chapter=chapter, notes=notes, json=json, count=count)) return arguments else: for i in argument: a = self.get(i, required=required, index=index,default=default, mode=mode, chapter=chapter, notes=notes, json=json, count=count) if a != default: return a return default
def activate( self, # the key's passphrase (optional to retrieve from webserver) (str). passphrase=None, # interactive (optional) interactive=None, ): if passphrase == None: passphrase = self.passphrase if interactive == None: interactive = self.interactive new = False if passphrase in [False, None, "", "null", "None", "none"]: # check webserver. if not self.webserver.running: if not interactive: return _response_.error( f"{self.traceback}: The webserver is not running.") else: if defaults.options.log_level >= 1: _response_.log( f"{ALIAS}: forking {self.id} webserver.") response = self.webserver.fork() if not response.success: return response # get pass. response, passphrase = self.webserver.get(group="passphrases", id="master"), None if not response.success and "There is no data cached for" not in response[ "error"]: return response elif response["success"]: passphrase = response["data"] if passphrase in [False, None, "", "null", "None", "none"]: if not interactive: return _response_.error( self.__traceback__(function="activate") + ": Define parameter [passphrase].") else: new = True passphrase = getpass.getpass( f"Enter the passphrase of the {self.id} encryption:") # activate. self.encryption.rsa.passphrase = passphrase response = self.encryption.load_keys() if not response["success"]: return _response_.error( f"Encoutered an error while activating the {self.id} encryption: {response['error']}" ) self.passphrase = passphrase self.db.aes.rsa.passphrase = passphrase response = self.db.activate() if not response["success"]: return _response_.error( f"Encoutered an error while activating the encrypted cache: {response['error']}" ) # chache. if new: response = self.webserver.set(group="passphrases", id="master", data=passphrase) if not response["success"]: return _response_.error( f"Encoutered an error while caching the passphrase (#2): {response['error']}" ) # handler. return _response_.success("Successfully activated the encryption.")
def __init__( self, # the encryption & webserver's id (str). id="dev0s-agent", # the path to the encrypted database (str, String, FilePath). database=None, # the webserver's host (str). host="127.0.0.1", # the webserver's port (int). port=56000, # the path to the private key / the raw private key (str). private_key=None, # the path to the public key / the raw public key (str). public_key=None, # the passphrase (optional to prompt) (str). passphrase=None, # the encryption key in memory only (enable when you passed the private_key & public_key in raw format and the file path) (bool). memory=False, # the interactive mode (prompt for password) (bool). interactive=True, # the object traceback (str). traceback="dev0s.encryption.Agent", ): # docs. DOCS = { "module": "dev0s.encryption.Agent", "initialized": False, "description": [], "chapter": "Encryption", } # traceback. Traceback.__init__(self, traceback=traceback) # checks. response = _response_.parameters.check( traceback=self.__traceback__(), parameters={ "id:str,String": id, "database:str,String,FilePath": database, "host:str,String": host, "port:str,String,int,Integer": port, "private_key:str,String,FilePath": private_key, "public_key:str,String,FilePath": public_key, #"passphrase:str,String":passphrase, "memory:bool,Boolean": memory, "interactive:bool,Boolean": interactive, "traceback:str,String": traceback, }) if not response.success: response.crash() # check instances. if not Files.exists(str(database)): _response_.log( f"{color.orange}Root permission{color.end} required to create database [{database}]." ) Files.create(str(database), sudo=True, permission=700, owner=defaults.vars.owner, group=defaults.vars.group, directory=True) # init. self.id = str(id) self.host = str(host) self.port = int(port) self.private_key = private_key self.public_key = public_key self.memory = bool(memory) self.passphrase = passphrase self.interactive = bool(interactive) self.db_path = str(database) # vars. self._activated = False # objects. self.webserver = _database_.WebServer( id=self.id, host=self.host, port=self.port, ) self.aes = self.encryption = aes.AsymmetricAES( public_key=self.public_key, private_key=self.private_key, passphrase=self.passphrase, memory=self.memory, ) self.db = self.database = aes.Database(path=str(self.db_path), aes=self.encryption)
def generate( self, # the passphrase (optional to prompt) (str). passphrase=None, # the verify passphrase (optional). verify_passphrase=None, # interactive (optional). interactive=None): # checks. if passphrase == None: passphrase = self.passphrase if interactive == None: interactive = self.interactive if passphrase == None: if not interactive: return _response_.error( self.__traceback__(function="generate") + ": Define parameter [passphrase].") else: passphrase = getpass.getpass( f"Enter the passphrase of the {self.id} encryption:") elif len(passphrase) < 8: return _response_.error( "The passphrase must contain at least 8 characters.") elif passphrase.lower() == passphrase: return _response_.error( "The passphrase must contain capital characters.") elif (interactive and passphrase != getpass.getpass("Enter the same passphrase:") ) or (verify_passphrase != None and passphrase != verify_passphrase): return _response_.error( "The passphrase must contain at least 8 characters.") # check webserver. if not self.webserver.running: #if not interactive: # return _response_.error(f"{self.traceback}: The webserver is not running.") #else: if defaults.options.log_level >= 1: _response_.log(f"{ALIAS}: forking {self.id} webserver.") response = self.webserver.fork() if not response.success: return response # generate. self.encryption.rsa.passphrase = passphrase response = self.encryption.generate_keys() if not response["success"]: return _response_.error( f"Encoutered an error while generating the master encryption key: {response['error']}" ) self.passphrase = passphrase if self.memory: self.encryption.rsa.private_key = response.private_key self.encryption.rsa.public_key = response.public_key response = self.encryption.load_keys() if not response["success"]: return _response_.error( f"Encoutered an error while activating the {self.id} encryption: {response['error']}" ) # cache. response = self.webserver.set(group="passphrases", id="master", data=passphrase) if not response["success"]: return _response_.error( f"Encoutered an error while caching the passphrase (#1): {response['error']}" ) # database. self.db = aes.Database(path=str(self.db_path), aes=self.encryption) response = self.db.activate() if not response["success"]: return _response_.error( f"Encoutered an error while activating the encrypted cache: {response['error']}" ) # hander. return _response_.success("Successfully generated the encryption.")