from xpresso.ai.core.logging.xpr_log import XprLogger from xpresso.ai.admin.controller.xprobject import XprObject from xpresso.ai.admin.controller.exceptions.xpr_exceptions import * logger = XprLogger() class AuthenticationContext(XprObject): def __init__(self, credentials): super().__init__(credentials) if 'uid' not in credentials or not len(credentials['uid']): raise InvalidUserIDException elif 'pwd' not in credentials or not len(credentials['pwd']): raise InvalidPasswordException
class xpruser(): config_path = XprConfigParser.DEFAULT_CONFIG_PATH CONTROLLER_SECTION = 'controller' TOKEN_EXPIRY = 'soft_expiry' LOGIN_EXPIRY = 'hard_expiry' MONGO_SECTION = 'mongodb' URL = 'mongo_url' DB = 'database' UID = 'mongo_uid' PWD = 'mongo_pwd' W = 'w' def __init__(self): self.config = XprConfigParser(self.config_path) self.db_utils = Utils(url=self.config[self.MONGO_SECTION][self.URL], db=self.config[self.MONGO_SECTION][self.DB], uid=self.config[self.MONGO_SECTION][self.UID], pwd=self.config[self.MONGO_SECTION][self.PWD], w=self.config[self.MONGO_SECTION][self.W]) self.logger = XprLogger() def registeruser(self, user_json): """ register a new user in the db checks if the user already exists and then adds to db Parameters: userjson [json]: json with node information Return: Success -> 'OK' [str] : returns 'OK' as response Failure -> [str] : returns appropriate failure response """ self.logger.debug(f"user info provided is {user_json}") info_check = userinfocheck(user_json) # user info_check checks if the user_json has sufficient info if info_check == -1: errcode = error_codes.incomplete_user_information self.logger.error("Insufficient information to create a new user") return xprresponse('failure', errcode, {}) elif info_check == 0: errcode = error_codes.incorrect_primaryRole self.logger.error("Incorrect primaryRole has been provided") return xprresponse('failure', errcode, {}) # Password should not be saved as plain text in db. # Encrypting the password before saving it to db password = sha512_crypt.encrypt(user_json['pwd']) user_json['pwd'] = password # checks if the user is already present in the db self.logger.info("Registering a new user") uid_json = {'uid': user_json['uid']} self.logger.info("Checking the db if user is already present") user = self.db_utils.find("users", uid_json) if len(user) != 0: errcode = error_codes.user_exists return xprresponse('failure', errcode, {}) user_json['loginStatus'] = False user_json['activationStatus'] = True user_json['nodes'] = [] self.logger.info("adding user to the db") add_user = self.db_utils.insert("users", user_json, False) if add_user == -1: errcode = error_codes.username_already_exists self.logger.error("username already exists in the db") return xprresponse('failure', errcode, {}) self.logger.info("user successfully added to the db") # NFS User directory changes nfs_manager = NFSUserManager(config=self.config) nfs_manager.setup_user_folder(user=user_json['uid']) return xprresponse('success', '', {}) def modifyuser(self, token, changesjson): """ modify_user updates the user info in the db checks if user is available and then updates the info as per changesjson Parameters: uid [str]: uid of the user changesjson [json] : json with user changes info Return: Success -> 'OK' [str] : returns OK if provision_node succeeds Failure -> [str] : returns appropriate failure response """ check = modify_user_check(changesjson) if check != 200: return xprresponse('failure', check, {}) uidjson = {"uid": changesjson['uid']} self.logger.info(f"Modifying user information of {uidjson}") self.logger.debug(f"Info provided to be modified is {changesjson}") # checks if the user is present in db self.logger.info("Checking if the user is present in the db") user = self.db_utils.find("users", uidjson) if len(user) == 0: errcode = error_codes.user_not_found self.logger.error(f"user {uidjson['uid']} not found in the db") return xprresponse('failure', errcode, {}) self.logger.info("updating the user information") updateuser = self.db_utils.update("users", uidjson, changesjson) return xprresponse('success', '', {}) def deactivateuser(self, uid): """ Deletes an user and his info from the db Deletes the user from database Parameters: uid [str] : uid of the user Return: returns appropriate output """ uidjson = {"uid": uid} # deletes the user from db deluser = self.db_utils.find("users", uidjson) if len(deluser) != 0: self.logger.info(f"deactivating the user {uidjson['uid']}") if 'activationStatus' in deluser[0] and \ deluser[0]['activationStatus']: self.db_utils.update("users", uidjson, {"activationStatus": False}) self.logger.info(f"user {uidjson['uid']} successfully deleted") return xprresponse('success', '', {}) else: errcode = error_codes.user_already_deactivated return xprresponse('failure', errcode, {}) else: errcode = error_codes.user_not_found self.logger.info("user not found") return xprresponse('failure', errcode, {}) def getusers(self, filterjson): """ Calls the db with input filters to fetch the list of users. After fetching, the users list is filtered before sending as output in order to send relevant information only Parameters: filterjson [json] : json with filter key & value pairs Return: Success -> [list] : returns list of users Failure -> [str] : returns db failure response """ self.logger.info("getting all the users in the db") self.logger.debug(f"filterjson is : {filterjson}") users = self.db_utils.find("users", filterjson) # get users call retrieves whole user info from db # Filtering the data that needs to be shown as output self.logger.info("filtering the users before sending output") users = filteruseroutput(users) self.logger.debug(f'Output of users sent: {users}') return xprresponse('success', '', users)
def __init__(self, attribute_name): self.logger = XprLogger() self.name = attribute_name self.metrics = dict()
class ControllerClient: CONTROLLER_SECTION = 'controller' SERVER_URL = 'server_url' CLIENT_PATH = 'client_path' JENKINS_SECTION = 'jenkins' JENKINS_HOST = 'master_host' relogin_response = { "outcome": "failure", "error_code": "106", "results": {} } API_JSON_OUTCOME = "outcome" API_JSON_RESULTS = "results" API_JSON_ERROR_CODE = "error_code" API_JSON_SUCCESS = "success" API_JSON_FAILURE = "failure" def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH): self.logger = XprLogger() self.config = XprConfigParser(config_path) self.path = os.path.join( os.path.expanduser('~'), self.config[self.CONTROLLER_SECTION][self.CLIENT_PATH]) self.token_file = '{}.current'.format(self.path) self.server_path = self.config[self.CONTROLLER_SECTION][ self.SERVER_URL] def sso_login(self): """ It performs Single Sign-On authentication for the client. It follows following steps 1. Check if token exists 2. If exists: Send to the server for validation 2.1 If token is validated then login is successful 2.2 If token is not validated, assume token does not exist and go to point 3 3. If no token exists: 3.1 Print the SSO authentication url for user to login 3.2 Send request to server every few seconds to check if user signed in successful. Wait for 60 seconds. Throw error if not logged in 3.3 When user logged in, fetch the token and save """ self.logger.info('CLIENT : Entering SSO Login Method') # Check if token exists: try: token = self.get_token() except ControllerClientResponseException: self.logger.info("No Token found") token = None # Since no token exist, ask for new login if token: url = f"{self.server_path}/sso/token_login" self.logger.debug('CLIENT : Making post request to server') data = {"token": token} try: response = self.send_http_request(url=url, header=data, http_method=HTTPMethod.POST, data=data) return response except ControllerClientResponseException as e: self.logger.info("Assuming logging request failed") self.logger.info(e.message) url = f"{self.server_path}/sso/get_authentication_url" self.logger.debug('CLIENT : Making post request to server') response = self.send_http_request(url=url, http_method=HTTPMethod.GET) return response def sso_validate(self, validation_token): """ Check whether SSO authentication is completed and successful Args: validation_token: sso validation token which is used to check if a user has logged in or not. Returns: """ # We keep requesting the sso server to test for interval_second = 2 wait_second = 60 start_time = time.time() while time.time() - start_time < wait_second: self.logger.debug('CLIENT : Making post request to server') url = f"{self.server_path}/sso/validate" data = {"validation_token": validation_token} try: response = self.send_http_request(url=url, http_method=HTTPMethod.POST, data=data) self.logger.info("Token validated") self.save_token(response["token"]) return {"message": "SSO Login Successfull"} except ControllerClientResponseException: time.sleep(interval_second) self.logger.info('CLIENT : Existing SSO Login Method') raise ControllerClientResponseException("Session over without login", error_codes.server_error) def login(self, username, password): """Sends request to Controller server and get the status on login request""" self.logger.info('CLIENT : entering login method') if not os.path.isdir(self.path): os.makedirs(self.path, 0o755) if os.path.isfile(self.token_file): os.remove(self.token_file) if not username: self.logger.error('CLIENT : Empty username passed. Exiting.') raise ControllerClientResponseException("Username can't be empty", error_codes.empty_uid) if not password: self.logger.error('CLIENT : Empty password passed. Exiting.') raise ControllerClientResponseException("Password can't be empty", error_codes.empty_uid) url = f"{self.server_path}/auth" credentials = {"uid": username, "pwd": password} self.logger.debug('CLIENT : Making post request to server') response = self.send_http_request(url=url, http_method=HTTPMethod.POST, data=credentials) self.save_token(token=response['access_token']) if 'relogin' in response and response['relogin']: self.logger.debug('CLIENT : already logged in. Saving new token.') return {"message": f"You are already logged in"} elif 'relogin' in response and not response['relogin']: self.logger.info( 'CLIENT : Login successful. Writing token to file.') return {"message": f"Welcome, {username}!"} return response def save_token(self, token): """Token is saved in the local file system for """ file = open(self.token_file, 'w+') file.write(token) file.close() self.logger.info('CLIENT : Token written to file. Exiting.') def get_token(self): """Token is saved in the local file system for """ token = None try: with open(self.token_file, "r") as f: token = f.read() except FileNotFoundError: self.logger.error("No Token Found. Need to Relogin") raise ControllerClientResponseException( "No Session found. Login again", error_codes.expired_token) return token def logout(self): self.logger.info('CLIENT : entering logout method') url = f'{self.server_path}/auth' token = self.get_token() headers = {'token': token} self.logger.debug('CLIENT : Making delete request to server') self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header=headers) os.remove(self.token_file) self.logger.info('CLIENT : Logout successful. Exiting.') return {"message": "Successfully logged out"} def get_clusters(self, argument): self.logger.info(f'CLIENT : entering get_clusters method ' f'with arguments {argument}') url = f'{self.server_path}/clusters' headers = {"token": self.get_token()} self.logger.debug('CLIENT : Making get request to server') response = self.send_http_request(url=url, http_method=HTTPMethod.GET, header=headers, data=argument) self.logger.info('CLIENT : Get request successful. Exiting.') return response def deactivate_cluster(self, argument): self.logger.info('CLIENT : Entering deactivate_cluster method') if not argument: self.logger.error('CLIENT : No input arguments provided. Exiting.') raise ControllerClientResponseException( f"Please provide some input arguments ===", error_codes.incomplete_cluster_info) url = f'{self.server_path}/clusters' headers = {"token": self.get_token()} self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header=headers, data=argument) self.logger.info('CLIENT : Deactivation successful. Exiting.') return {"message": "Cluster deactivated."} def register_cluster(self, argument): self.logger.info('CLIENT : Entering register_cluster ' 'with arguments {}'.format(argument)) if not argument: self.logger.error('CLIENT : No input arguments provided. Exiting.') raise ControllerClientResponseException( f"Please provide some input arguments ===", error_codes.incomplete_cluster_info) url = f'{self.server_path}/clusters' headers = {"token": self.get_token()} response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header=headers, data=argument) self.logger.info('CLIENT : Cluster registration successful.Exiting.') return { "message": f"Cluster successfully registered with " f"ID {response} ###" } def register_user(self, user_json): url = f"{self.server_path}/users" response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=user_json) return response def get_users(self, filter_json): url = f"{self.server_path}/users" response = self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=filter_json) return response def modify_user(self, changes_json): url = f"{self.server_path}/users" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=changes_json) return response def update_password(self, password_json): url = f"{self.server_path}/user/pwd" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=password_json) return response def deactivate_user(self, uid_json): url = f"{self.server_path}/users" response = self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header={"token": self.get_token()}, data=uid_json) return response def register_node(self, node_json): url = f"{self.server_path}/nodes" response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=node_json) print(response) return response def get_nodes(self, filter_json): url = f"{self.server_path}/nodes" response = self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=filter_json) return response def provision_node(self, changes_json): url = f"{self.server_path}/nodes" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=changes_json) return response def deactivate_node(self, node_json): url = f"{self.server_path}/nodes" response = self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header={"token": self.get_token()}, data=node_json) return response def assign_node(self, assign_json): url = f"{self.server_path}/assign_node" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=assign_json) return response def check_for_declarative_json(self, project_json): """ Checks if the provided declarative json exists and replaces that field with the contents of declarative json. Args: project_json: input file from user Returns: modified project_json """ for pipeline in project_json['pipelines']: if not os.path.isfile(pipeline['declarative_json']): self.logger.error("declarative json not found") raise FileNotFoundException('Declarative JSON not found.') with open(pipeline['declarative_json'], 'r') as f: declarative_json_data = json.load(f) pipeline['declarative_json'] = declarative_json_data return project_json def create_project(self, project_json): if 'pipelines' in project_json: project_json = self.check_for_declarative_json(project_json) url = f"{self.server_path}/projects/manage" response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=project_json) return response def get_project(self, filter_json): url = f"{self.server_path}/projects/manage" response = self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=filter_json) return response def modify_project(self, changes_json): if 'pipelines' in changes_json: changes_json = self.check_for_declarative_json(changes_json) url = f"{self.server_path}/projects/manage" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=changes_json) return response def deactivate_project(self, project_json): url = f"{self.server_path}/projects/manage" response = self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header={"token": self.get_token()}, data=project_json) print("response is ", response) return response def build_project(self, argument): self.logger.info(f'CLIENT : Entering build_project ' f'with arguments {argument}') if not argument: self.logger.error('CLIENT : No input arguments provided. Exiting.') raise ControllerClientResponseException( f"Please provide some input arguments ===", error_codes.incomplete_cluster_info) url = f'{self.server_path}/projects/build' response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=argument) self.logger.info('CLIENT : Project build successful.Exiting.') return { "message": "Project build successful!", "Build IDS": response, "Jenkins Pipeline": f"{self.config[self.JENKINS_SECTION][self.JENKINS_HOST]}" f"/blue/pipelines" } def get_build_version(self, argument): self.logger.info(f'CLIENT : entering get_build_version method ' f'with arguments {argument}') url = f'{self.server_path}/projects/build' self.logger.debug('CLIENT : Making get request to server') response = self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=argument) return response def deploy_project(self, argument): self.logger.info(f'CLIENT : Entering deploy_project ' f'with arguments {argument}') if not argument: self.logger.error('CLIENT : No input arguments provided. Exiting.') raise ControllerClientResponseException( f"Please provide some input arguments ===", error_codes.incomplete_cluster_info) url = f'{self.server_path}/projects/deploy' response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=argument) self.logger.info('CLIENT : Project deployed successfully.Exiting.') return { "message": "Project deployed successfully on the below IPs!", "Output": response } def undeploy_project(self, argument): self.logger.info(f'CLIENT : Entering undeploy_project ' f'with arguments {argument}') if not argument: self.logger.error('CLIENT : No input arguments provided. Exiting.') raise ControllerClientResponseException( f"Please provide some input arguments ===", error_codes.incomplete_cluster_info) url = f'{self.server_path}/projects/deploy' response = self.send_http_request(url=url, http_method=HTTPMethod.DELETE, header={"token": self.get_token()}, data=argument) self.logger.info('CLIENT : Project undeployed successfully.Exiting.') return {"message": "Project undeployed successfully!"} def update_xpresso(self): """ Update xpresso project to the latest commit """ # Send request to update server server_update_is_success = False url = f'{self.server_path}/update_xpresso' try: self.send_http_request(url, HTTPMethod.POST) server_update_is_success = True except ControllerClientResponseException as e: self.logger.error(e) # Update local package_manager = PackageManager() package_manager.run(package_to_install="UpdateLocalXpressoPackage", execution_type=ExecutionType.INSTALL) response = {"client": "Updated"} if server_update_is_success: response["server"] = "Updated" else: response["server"] = "Update Failed" return response def fetch_version(self): """ Fetches server version and client version, convert to a dict and returns. """ url = f'{self.server_path}/version' json_response = self.send_http_request(url, HTTPMethod.GET) server_version = "None" if "version" in json_response: server_version = json_response["version"] client_version = get_version() return { "client_version": client_version, "server_version": server_version } def send_http_request(self, url: str, http_method: HTTPMethod, data=None, header: dict = None): request = HTTPRequest(method=http_method, url=url, headers=header, data=data) handler = HTTPHandler() try: response = handler.send_request(request) json_response = response.get_data_as_json() if not json_response: raise ControllerClientResponseException( "Request Failed", error_codes.server_error) elif (json_response[self.API_JSON_OUTCOME] == self.API_JSON_SUCCESS and self.API_JSON_RESULTS in json_response): return json_response[self.API_JSON_RESULTS] elif (json_response[self.API_JSON_OUTCOME] == self.API_JSON_SUCCESS and self.API_JSON_RESULTS not in json_response): return {} elif (self.API_JSON_RESULTS in json_response and self.API_JSON_ERROR_CODE in json_response): raise ControllerClientResponseException( json_response[self.API_JSON_RESULTS], json_response[self.API_JSON_ERROR_CODE]) elif self.API_JSON_ERROR_CODE in json_response: raise ControllerClientResponseException( "Request Failed", json_response[self.API_JSON_ERROR_CODE]) raise ControllerClientResponseException("Request Failed", -1) except (HTTPRequestFailedException, HTTPInvalidRequestException) as e: self.logger.error(str(e)) raise ControllerClientResponseException("Server is not accessible", error_codes.server_error) except JSONDecodeError as e: self.logger.error(str(e)) raise ControllerClientResponseException( "Invalid response from server", error_codes.server_error) def create_repo(self, repo_json): """ creates a repo on pachyderm cluster :param repo_json: information of repo i.e. name and description :return: returns operation status """ url = f"{self.server_path}/repo" response = self.send_http_request(url=url, http_method=HTTPMethod.POST, header={"token": self.get_token()}, data=repo_json) return response # def get_repo(self): # """ # # :return: # """ # url = f"{self.server_path}/repo" # response = self.send_http_request(url=url, # http_method=HTTPMethod.GET, # header={"token": self.get_token()}, # data={}) # return response def create_branch(self, branch_json): """ creates a branch in a repo :param branch_json: information of branch i.e. repo and branch names :return: operation status """ url = f"{self.server_path}/repo" response = self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=branch_json) return response def push_dataset(self, dataset_json): """ pushes a dataset into pachyderm cluster :param dataset_json: information of dataset :return: operation status """ url = f"{self.server_path}/dataset/manage" self.send_http_request(url=url, http_method=HTTPMethod.PUT, header={"token": self.get_token()}, data=dataset_json) manager = repo_manager.PachydermRepoManager() try: commit_id = manager.push_files(dataset_json) return { "message": f"Dataset push successful. commit id: {commit_id}" } except XprExceptions as err: return err.message def pull_dataset(self, dataset_json): """ pulls a dataset from pachyderm cluster :param dataset_json: info of the dataset on pachyderm cluster :return: path of the dataset on user system """ url = f"{self.server_path}/dataset/manage" self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=dataset_json) manager = repo_manager.PachydermRepoManager() try: dataset_path = manager.manage_xprctl_dataset('pull', dataset_json) return { "message": f"Pull Successful, find the files at {dataset_path}" } except XprExceptions as err: return err.message def list_dataset(self, filter_json): """ lists datasets saved on pachyderm cluster as per filter specs :param filter_json: info to filter required dataset :return: list of all the files and their props as per filter specs """ url = f"{self.server_path}/dataset/list" self.send_http_request(url=url, http_method=HTTPMethod.GET, header={"token": self.get_token()}, data=filter_json) manager = repo_manager.PachydermRepoManager() try: dataset_list = manager.manage_xprctl_dataset('list', filter_json) return dataset_list except XprExceptions as err: return err.message
class PackageManager: """Manages the request for package installation and setup """ MANIFEST_SCRIPT_KEY = "script_path" MANIFEST_MULTI_ARG_KEY = "script_multi_arguments" MANIFEST_DEPENDENCY_KEY = "dependency_packages" MANIFEST_SSH_CONFIG = "ssh_config" def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH): """ 1. Generate metadata of existing VM 2. Reads the arguments and initiates the instance variable """ self.logger = XprLogger() self.python_version = sys.version.split('\n') self.system = platform.system() self.machine = platform.machine() self.platform = platform.platform() self.uname = platform.uname() self.version = platform.version() self.arch = platform.architecture() self.config_path = config_path self.package_dependency = PackageDependency(config_path=config_path) def list(self): """ List all supported package group name Returns: list: list of available packages """ return self.package_dependency.list_all() def run(self, package_to_install: str, execution_type: ExecutionType, parameters: dict = None): """ Perform provided execution type on the given package name Args: parameters(dict): Additional parameters package_to_install(str): name of the package to install. Must match the supported package names execution_type(ExecutionType): Type of execution """ if not self.package_dependency.check_if_supported(package_to_install): self.logger.error( "Unsupported Package Name : {}".format(package_to_install)) return False dependency_list = self.package_dependency.get_dependency( package_to_install) self.logger.info(dependency_list) response = self.execute_recursive(dependency_list, 0, execution_type, parameters=parameters) if not response: raise PackageFailedException("Package installation failed!!") self.logger.info( "{} installed successfully".format(package_to_install)) return True def execute_recursive(self, dependency_list: list, current_index: int, execution_type: ExecutionType, parameters: dict = None): """ Execute recursively. If something failed then rollback """ if current_index >= len(dependency_list): return True package_string = dependency_list[current_index] self.logger.info(package_string) try: self.execute(package_class=self.package_str_to_class( package_string, packages), execution_type=execution_type, parameters=parameters) current_index += 1 return self.execute_recursive(dependency_list, current_index, execution_type, parameters=parameters) except PackageFailedException: self.logger.error("Failed to execute package {}".format( str(package_string))) return False def execute(self, package_class, execution_type: ExecutionType, parameters: dict = None): """ Perform provided execution type on the given package name Args: parameters: Additional parameter required for installation package_class: name of the package to install. Must match the supported package names. execution_type(ExecutionType): Type of execution Returns: bool: True,if the execution is successful """ if package_class is None: self.logger.info( "{} Not Found in the hirarchy".format(package_class)) return False self.logger.info(f"Running package {package_class} with parameters" f"{parameters}") package_obj = package_class(config_path=self.config_path) if (execution_type == ExecutionType.INSTALL and package_obj.status(parameters=parameters)): return True elif execution_type == ExecutionType.INSTALL: return package_obj.install(parameters=parameters) elif execution_type == ExecutionType.UNINSTALL: return package_obj.uninstall(parameters=parameters) elif execution_type == ExecutionType.STATUS: return package_obj.status(parameters=parameters) elif execution_type == ExecutionType.START: return package_obj.start(parameters=parameters) elif execution_type == ExecutionType.STOP: return package_obj.stop(parameters=parameters) self.logger.error(str(package_obj) + " Not defined") return False def package_str_to_class(self, target_class_name: str, package_name): """ Converts class name into python class object. It looks for all classes in a package and matches the name to the class object Args: package_name(package): Find the target class name within this package name target_class_name(str): exact name of the class. It should match the class name as well Returns: Object: returns python class object, None otherwise """ for _, modname, is_pkg in \ pkgutil.iter_modules(package_name.__path__, package_name.__name__ + "."): imported_module = __import__(modname, fromlist=["dummy"]) matched_class_name = None if is_pkg: matched_class_name = self.package_str_to_class( target_class_name, imported_module) if matched_class_name: return matched_class_name for name, obj in inspect.getmembers(imported_module): if inspect.isclass(obj) and name == target_class_name: return obj return None
class Project(XprObject): """ This class represents a User """ project_config_path = "/opt/xpresso.ai/config/project_config.json" with open(project_config_path, "r", encoding="utf-8") as config_file: try: project_config = json.load(config_file) except (FileNotFoundError, json.JSONDecodeError): raise ProjectConfigException def __init__(self, project_json=None): self.logger = XprLogger() """ Constructor: """ self.logger.debug(f"Project constructor called with {project_json}") super().__init__(project_json) # List of all the fields project can contain self.complete_fields = self.project_config["project_key_list"] # These are mandatory fields that needs to be provided in user_json self.mandatory_fields = ["name", "description", "owner"] # primaryRole of a user has to one of these self.valid_values = {"primaryRole": ["Dev", "PM", "DH", "Admin", "Su"]} # fields that cannot be modified self.unmodifiable_fields = ["name"] # fields that should be displayed in the output self.display_fields = self.project_config["project_output_fields"] self.logger.debug("User constructed successfully") def check_fields(self) -> int: """ checks all the input fields in project information json checks if the provided fields type and format is same and in accordance with project format. Checks if the mandatory fields are provided. """ # checks if the type of the input fields is valid for (field, field_type) in self.project_config["project_field_types"].items(): if field in self.data: if type(self.data[field]).__name__ != field_type: raise ProjectFieldsException( f"type of '{field}' in project info is invalid" ) # checks if the mandatory fields are provided and if they are not empty for (field, field_flag) in self.project_config["project_field_flags"].items(): if field_flag: if field in self.data and len(self.data[field]): continue else: raise IncompleteProjectInfoException() return 200 @staticmethod def check_duplicate_component(component_list): """ checks if any component is specified/used more than once """ component_names = [] for component in component_list: if component["name"] not in component_names: component_names.append(component["name"]) else: component_name = component["name"] raise DuplicateComponentException( f"Component/Pipeline with name '{component_name}' " f"already exists in this project" ) def component_info_check(self, component_list): """ checks if the components are specified correctly checks if the type, flavor and name for each of the component is specified correctly in input json """ component_field_set = self.project_config["component_fields"] for component in component_list: # check if each of type, name & flavor are specified correctly for (key, val) in component.items(): # checks if only allowed keys are present in the component spec if key not in component_field_set: raise ComponentFieldException( f"'{key}' field not specified in component information" ) elif type(val).__name__ != component_field_set[key]: raise ComponentFieldException( f"Invalid field type of '{key}'" ) elif not len(val): raise ComponentFieldException(f"'{key}' field is empty") # checks if all the mandatory fields are provided in the components for field in component_field_set: if field not in component: raise ComponentFieldException( f"'{field}' is not in the allowed fields of components" ) temp_type = component["type"] temp_flavor = component["flavor"] if temp_type not in self.project_config["component_flavors"]: raise ComponentFieldException( f"Component type '{temp_type}' is not valid" ) elif temp_flavor not in\ self.project_config["component_flavors"][temp_type]: raise ComponentFieldException( f"'{temp_flavor}' flavor unavailable for {temp_type}" ) def check_owner(self, persistence_manager): if 'uid' not in self.data["owner"]: raise ProjectOwnerException("Owner uid needs to be provided") elif not len(self.data["owner"]["uid"]): raise ProjectOwnerException("Owner uid is empty") else: uid_json = {'uid': self.data["owner"]["uid"]} owner = persistence_manager.find("users", uid_json) if not len(owner): raise ProjectOwnerException( "User not found with provided owner uid" ) def check_developers(self, persistence_manager, devs=None): if not devs: developers = self.data["developers"] else: developers = devs for dev_name in developers: if type(dev_name).__name__ != 'str': raise ProjectDeveloperException( f"Developer name should be string" ) if not len(dev_name): raise ProjectDeveloperException( "Developer name should not be empty" ) else: developer = persistence_manager.find( "users", {"uid": dev_name} ) if not len(developer): raise ProjectDeveloperException( f"Developer {dev_name} not found" ) def check_pipelines(self, pipelines, persistence_manager, project_components): """ validates the pipeline format provided and checks for consistency, duplicity. Args: pipelines: input pipeline info persistence_manager: persistence manager project_components : list of names of project components Returns: """ default_pipeline_keys = self.project_config['pipeline_fields'].keys() keys_without_version = list(self.project_config['pipeline_fields'].keys()) keys_without_version.remove('deploy_version_id') declarative_pipeline_builder = DeclarativePipelineBuilder( persistence_manager) for pipeline in pipelines: if set(default_pipeline_keys) != set(pipeline.keys()) and \ set(keys_without_version) != set(pipeline.keys()): self.logger.error("default keys defined incorrectly") raise ProjectPipelinesException for component in pipeline['components']: if component not in project_components: self.logger.error('pipeline component not found in ' 'project components') raise ComponentsSpecifiedIncorrectlyException( f'Pipeline component "{component}" not ' f'found in project components.') declarative_pipeline_builder.prevalidate_declarative_json( pipeline['declarative_json']) self.check_duplicate_component(pipelines) def project_info_check(self, persistence_manager): """ checks if the information provided for project creation is valid checks all the mandatory fields and its format. checks the components, developers and owner information. """ keys = self.data.keys() # checks if the input fields provided are valid and present # support case type mistakes in next release for key in keys: if key not in self.project_config["project_key_list"]: raise ProjectFormatException( f"Invalid field '{key}' in project json" ) # checks if all fields are specified correctly self.check_fields() # checks if the components are provided with info and validates format if "components" in self.data: self.component_info_check(self.data["components"]) self.check_duplicate_component(self.data["components"]) self.check_owner(persistence_manager) if "pipelines" in self.data: project_component_names = \ [component['name'] for component in self.data['components']] self.check_pipelines(self.data['pipelines'], persistence_manager, project_component_names) # checks the developers format is valid if specified if "developers" in self.data and len(self.data["developers"]): self.check_developers(persistence_manager) def complete_project_info(self): out_json = deepcopy(self.project_config["sample_json"]) name = self.data["name"] for key, val in self.data.items(): if key != "components": out_json[key] = val if "components" in self.data: for component in self.data["components"]: component_name = component["name"] out_component = deepcopy(self.project_config["sample_component"]) for key, val in component.items(): if key in self.project_config["sample_component"]: out_component[key] = val out_component["dockerPrefix"] = ( f"dockerregistry.xpresso.ai/xprops/{name}/{component_name}--" ) out_json["components"].append(out_component) self.data = out_json if "pipelines" in self.data: for pipeline in self.data["pipelines"]: if "deploy_version_id" not in pipeline.keys(): pipeline['deploy_version_id'] = 1 def modify_info_check(self, changes_json, persistence_manager): # if not self.data["activationStatus"]: # if "activationStatus" not in changes_json: # return error_codes.activate_project_first # elif not changes_json["activationStatus"]: # return error_codes.activate_project_first # else: # changes_json["activationStatus"] = True if "activationStatus" in changes_json and \ not changes_json["activationStatus"]: raise ProjectNotFoundException("Project is currently not active.") keys = changes_json.keys() # support case type mistakes in next release for key in keys: if key not in self.project_config["project_key_list"]: raise ProjectFieldsException( f"Invalid field '{key}' provided for modify_project" ) for (field, field_type) in\ self.project_config["project_field_types"].items(): if field in changes_json: if type(changes_json[field]).__name__ != field_type: raise ProjectFieldsException( f"Invalid type of field '{field}'" ) if "owner" in changes_json and len(changes_json["owner"]): self.check_owner(changes_json["owner"]) if "developers" in changes_json and len(changes_json["developers"]): self.check_developers( persistence_manager, devs=changes_json["developers"] ) if "components" in changes_json and len(changes_json["components"]): old_components = self.data["components"] new_components = changes_json["components"] self.component_info_check(new_components) self.check_duplicate_component(old_components + new_components) if "pipelines" in changes_json and len(changes_json["pipelines"]): new_pipeline = changes_json['pipelines'] project_components = \ [component['name'] for component in self.data['components']] if "components" in changes_json.keys(): for component in changes_json['components']: project_components.append(component['name']) self.check_pipelines(new_pipeline, persistence_manager, project_components) def modify_project_info(self, changes_json): name = self.data["name"] out_json = deepcopy(changes_json) out_json["components"] = self.data["components"] if "components" in changes_json: for component in changes_json["components"]: component_name = component["name"] out_component = deepcopy(self.project_config["sample_component"]) for key, val in component.items(): if key in self.project_config["sample_component"]: out_component[key] = val out_component["dockerPrefix"] = ( f"dockerregistry.xpresso.ai/xprops/{name}/{component_name}--" ) out_json["components"].append(out_component) if 'pipelines' in changes_json: for pipeline in changes_json['pipelines']: if "deploy_version_id" not in pipeline.keys(): pipeline['deploy_version_id'] = 1 if 'pipelines' not in self.data.keys(): out_json['pipelines'] = changes_json['pipelines'] else: out_json['pipelines'] = changes_json['pipelines'] + \ self.data['pipelines'] return out_json
def __init__(self, persistence_manager): self.logger = XprLogger() self.config = XprConfigParser() self.persistence_manager = persistence_manager self.ldapmanager = LdapManager()
def __init__(self, config, persistence_manager: MongoPersistenceManager): self.config = config self.logger = XprLogger() self.persistence_manager = persistence_manager
def __init__(self, config_path): self.config = XprConfigParser(config_file_path=config_path) self.logger = XprLogger() self.metrics_list = None self.persistence_manager = None
class UserManager: CONTROLLER_SECTION = 'controller' TOKEN_EXPIRY = 'soft_expiry' LOGIN_EXPIRY = 'hard_expiry' AUTHENTICATION_TYPE = "authentication_type" def __init__(self, persistence_manager): self.logger = XprLogger() self.config = XprConfigParser() self.persistence_manager = persistence_manager self.ldapmanager = LdapManager() def register_user(self, user_json): """ register a new user in the persistence checks if the user already exists and then adds to persistence Parameters: user_json [json]: json with node information Return: Success -> 'OK' [str] : returns 'OK' as response Failure -> [str] : returns appropriate failure response """ self.logger.debug(f"Entered register_user with {user_json}") # create user object new_user = User(user_json) # run validations new_user.validate_mandatory_fields() new_user.validate_field_values() # valid inputs - exception would have been raised in case of missing / # invalid info # now, set other fields as required # Password should not be saved as plain text in database. # Encrypting the password before saving it to database self.logger.info("Registering a new user") user_pwd = user_json["pwd"] new_user.set('pwd', sha512_crypt.encrypt(user_json['pwd'])) new_user.set('loginStatus', False) new_user.set('activationStatus', True) new_user.set('nodes', []) self.logger.info("adding user to the database") self.persistence_manager.insert("users", new_user.data, False) self.logger.info("user successfully added to the persistence") try: self.ldapmanager.add(user_json["uid"], user_pwd) except Exception as e: self.logger.error("Unable to add user") print("unable to add user to ldap server : ", e) return XprResponse("failure", None, str(e)) # NFS User directory changes self.logger.info("Setting up NFS for the user") nfs_manager = NFSUserManager(config=self.config) nfs_manager.setup_user_folder(user=user_json['uid']) self.logger.info("NFS set up") return XprResponse("success", None, None) def modify_user(self, filter_json, changes_json): """ modify_user updates the user info in the persistence checks if user is available and then updates the info as per changes_json Parameters: filter_json: filter to find user changes_json: json with user changes info Return: Success -> 'OK' [str] : returns OK if provision_node succeeds Failure -> [str] : returns appropriate failure response """ self.logger.debug( f"Modifying user information of {filter_json} to {changes_json}") # checks if the user is present in database self.logger.info("Checking if the user is present in the database") users = self.persistence_manager.find("users", filter_json) if not users or len(users) == 0: self.logger.error(f"user {filter_json} not found in the database") raise UserNotFoundException() # checks if the user password is also present in changes_json temp_user = User(changes_json) temp_user.validate_field_values() temp_user.validate_modifiable_fields() self.logger.info("updating the user information") self.persistence_manager.update("users", filter_json, changes_json) return XprResponse('success', '', {}) def deactivate_user(self, uid): """ Deletes an user and his info from the persistence Deletes the user from database Parameters: uid [str] : uid of the user Return: returns appropriate output """ uid_json = {"uid": uid} # deletes the user from persistence del_users = self.persistence_manager.find("users", uid_json) if del_users and len(del_users) != 0: self.logger.info(f"deactivating the user {uid_json['uid']}") if 'activationStatus' in del_users[0] and \ del_users[0]['activationStatus']: self.persistence_manager.update("users", uid_json, {"activationStatus": False}) self.logger.info( f"user {uid_json['uid']} successfully deactivated") return XprResponse('success', '', {}) else: raise DeactivatedUserException else: raise UserNotFoundException() def get_users(self, filter_json, apply_display_filter=True): """ Calls the persistence with input filters to fetch the list of users. After fetching, the users list is filtered before sending as output in order to send relevant information only Parameters: filter_json [json] : json with filter key & value pairs Return: Success -> [list] : returns list of users Failure -> [str] : returns persistence failure response """ self.logger.info("getting all the users in the persistence") self.logger.debug(f"filter_json is : {filter_json}") users = self.persistence_manager.find("users", filter_json) # filter user fields before sending the output if apply_display_filter: new_json = [] for user_json in users: user = User(user_json) user.filter_display_fields() new_json.append(user.data) users = new_json # get users call retrieves whole user info from persistence # Filtering the data that needs to be shown as output self.logger.debug(f'Output of users sent: {users}') return users def update_password(self, password_json): """ Updates user password Checks the password and updates the password on ldap and database :param password_json: contains the uid, old password & new password :return: raises exception in case of error """ # uid is mandatory if "uid" not in password_json: self.logger.info("uid not provided for update password") raise IncompleteUserInfoException("User 'uid' not provided") uid_json = {"uid": password_json["uid"]} # fetches the user information users = self.persistence_manager.find("users", uid_json) if not len(users): self.logger.info("User not found for updating password") raise UserNotFoundException() # creates user object new_user = User(users[0]) old_password_hash = users[0]["pwd"] old_password = password_json["old_pwd"] new_password = password_json["new_pwd"] # checks if the old password provided is same as the one saved in db if not sha512_crypt.verify(old_password, old_password_hash): raise InvalidPasswordException("Current password is incorrect") # Current and new password should not be same if old_password == new_password: raise InvalidPasswordException("Current and new password is same.") # checks if the password is valid and secure enough new_user.check_password(password_json["new_pwd"]) # updates the password on ldap server self.ldapmanager.update_password(password_json["uid"], old_password, new_password) hashed_pwd = sha512_crypt.encrypt(new_password) update_json = {"pwd": hashed_pwd} self.persistence_manager.update("users", uid_json, update_json)
class AbstractPackage: """ Abstract base class for packages. It has one job to do, is execute one packages """ def __init__( self, executor: CommandExecutor = None, config_path: XprConfigParser = XprConfigParser.DEFAULT_CONFIG_PATH ): self.executor = executor self.config_path = config_path self.logger = XprLogger() @abstractmethod def install(self, **kwargs): """ Run installation scripts """ @abstractmethod def uninstall(self, **kwargs): """ Removes installed libraries """ @abstractmethod def start(self, **kwargs): """ Start the service/stop if required """ @abstractmethod def stop(self, **kwargs): """ Stop the service/stop if required """ @abstractmethod def status(self, **kwargs): """ Checks is the libraries are installed and running Returns: bool: True, if libraries are setup correctly """ def execute_command(self, command): self.logger.info(f"Running command: {command}") try: return self.executor.execute(command) except CommandExecutionFailedException: self.logger.error("Command failed {}".format(command)) raise PackageFailedException( "Base Ubuntu Package Installation Failed") def execute_command_with_output(self, command): self.logger.info(f"Running command: {command}") try: return self.executor.execute_with_output(command) except CommandExecutionFailedException: self.logger.error("Command failed {}".format(command)) raise PackageFailedException( "Base Ubuntu Package Installation Failed")
def execute(self, **kwargs): """ Mounts the NFS on the VM """ logger = XprLogger() if not linux_utils.check_root(): logger.fatal("Please run this as root") logger.info("Mounting NFS File") subnet_to_nfs_map = self.config[self.NFS_SECTION][self.SUBNET_MAP_KEY] ip_address = linux_utils.get_ip_address() matched_nfs = None for nfs, subnet in subnet_to_nfs_map.items(): logger.info("Matching {} {}".format(subnet, ip_address)) check = re.match(subnet, ip_address) print(check) if check: matched_nfs = nfs break if not matched_nfs: logger.info("Could not determine nfs value") return False mount_location = self.config[self.NFS_SECTION][self.MOUNT_LOCATION_KEY] nfs_location = self.config[self.NFS_SECTION][self.NFS_LOCATION_KEY] mount_script = "mount {}:{} {}".format(matched_nfs, nfs_location, mount_location) logger.info("Mounting {}".format(mount_script)) try: linux_utils.create_directory(mount_location, 0o755) self.executor.execute(mount_script) logger.info("Mount Succesful") logger.info("Updating fstab file") with open(self.FSTAB_FILE, "a+") as f: fstab_statement = "{}:{} {} nfs " \ "auto,nofail,noatime,nolock,intr,tcp," \ "actimeo=1800 0 0" \ .format(matched_nfs, nfs_location, mount_location) logger.info( "Updating fstab file with {}".format(fstab_statement)) f.write(fstab_statement) logger.info("Update Successful") except CommandExecutionFailedException as e: logger.error("Script Failed to run = {}\n{}".format( mount_script, str(e))) return False return True
def __init__(self, persistence_manager): self.persistence_manager = persistence_manager self.logger = XprLogger() config_path = XprConfigParser.DEFAULT_CONFIG_PATH self.config = XprConfigParser(config_path)
class PackageDependency: """ Created a directed acyclic package dependency graph using a given dependency json. """ NONE_PACKAGE = "None" DEPENDENCY_SECTION = "pkg_dependency" DEPENDENCY_CONFIG_FILE = "dependency_config_file" def __init__(self, config_path=XprConfigParser.DEFAULT_CONFIG_PATH): super().__init__() self.config = XprConfigParser(config_path)["packages_setup"] self.logger = XprLogger() dependency_config_file = self.config[self.DEPENDENCY_SECTION][ self.DEPENDENCY_CONFIG_FILE] if not os.path.exists(dependency_config_file): self.logger.error(("Unable to find the dependency js" "file at the mentioned path")) raise PackageFailedException("Invalid dependency config file") try: with open(dependency_config_file) as config_fs: dependency_config = json.load(config_fs) except EnvironmentError as err: self.logger.fatal(err) raise PackageFailedException("Invalid config file") self.graph = nx.DiGraph() edges = list() for key in dependency_config: for value in dependency_config[key]: edges.append((key, value)) self.graph.add_edges_from(edges) if not nx.is_directed_acyclic_graph(self.graph): self.logger.fatal(("Unable to handle dependencies due to cyclic " "loop")) self.graph = None raise PackageFailedException("Cyclic Dependency Found") def visualize_dependency_graph(self): """ Created a plot for the directed dependency graph """ if self.graph is None: self.logger.error("Graph value none cannot be plotted") return nx.draw(self.graph, cmap=plt.get_cmap('jet'), with_labels=True) plt.show() def check_if_supported(self, package_name: str): """ Args: package_name(str) :return: bool: Return True if supported. False, otherwise """ return bool(self.graph.has_node(package_name)) def list_all(self): """ Extracts the value of all nodes(packages) present in graph Returns: list: Array consisting of all node(packages) value """ if self.graph is None: self.logger.error("Graph value none cannot be iterated") return list() nodes = list() for node in self.graph.nodes(): if node == self.NONE_PACKAGE: continue nodes.append(node) return nodes def get_dependency(self, package_name: str) -> list: """ List of dependencies Args: package_name(str): Name of the package Returns: list: List of dependencies required for the package_name installation """ if not self.check_if_supported(package_name=package_name): self.logger.error( "{} package not present in config".format(package_name)) return list() self.logger.info(("Running Topological sorting on " "Package Dependency Graph")) try: topological_sort_list = list( reversed(list(nx.topological_sort(self.graph)))) except nx.NetworkXError as error: self.logger.error(error) raise PackageFailedException("Topological sort is defined for " "directed graphs only") except nx.NetworkXUnfeasible as error: self.logger.error(error) raise PackageFailedException( "Not a directed acyclic graph (DAG) " "and hence no topological sort exists") descendants = nx.descendants(self.graph, package_name) dependent_packages = [] for pkg in topological_sort_list: if pkg in descendants and pkg != self.NONE_PACKAGE: dependent_packages.append(pkg) if package_name != self.NONE_PACKAGE: dependent_packages.append(package_name) return dependent_packages
class AuthenticationManager: """ This class provides user authentication functionality for Xpresso Controller """ AUTHENTICATION_TYPE = "authentication_type" def __init__(self, persistence_manager): self.persistence_manager = persistence_manager self.logger = XprLogger() self.ldapmanager = LdapManager() config_path = XprConfigParser.DEFAULT_CONFIG_PATH self.config = XprConfigParser(config_path) def login(self, credentials): """ Authentication method for user login. Args: credentials: object containing uid and pwd Returns: dictionary object consisting of access token """ try: # validate data authentication_context = AuthenticationContext(credentials) uid = authentication_context.get('uid') self.logger.debug('entering login method ' 'with uid {}'.format(uid)) check = self.validate_credentials(authentication_context) if check == error_codes.already_logged_in: self.logger.debug('Relogin request from {}.'.format(uid)) relogin = True else: relogin = False self.logger.debug('Providing new token to {}.'.format(uid)) token_info = self.generate_token() self.save_token({"uid": uid}, token_info) credentials = { 'access_token': token_info.token, 'relogin': relogin } response = XprResponse('success', None, credentials) self.logger.debug('Login successful. Exiting.') return response except AuthenticationFailedException as e: self.logger.error('Authentication failed for user {}'.format(uid)) return XprResponse("failure", e.error_code, {"message": "Authentication failed"}) def validate_credentials(self, authentication_context): uid = authentication_context.get('uid') pwd = authentication_context.get('pwd') self.logger.debug('validating credentials for {}'.format(uid)) users = UserManager(self.persistence_manager).get_users({"uid": uid}, False) print('validate_credentials: {}'.format(users)) if not users: self.logger.error("User {} not found".format(uid)) raise UserNotFoundException(f"User {uid} not found") hashed_pwd = users[0]['pwd'] if not self.authenticate_password(uid, pwd, hashed_pwd): self.logger.error("Wrong password entered by {}".format(uid)) raise WrongPasswordException("Wrong password") elif not users[0]["activationStatus"]: self.logger.error("User {} has been deactivated".format(uid)) raise DeactivatedUserException("This user is deactivated. " "Please reactivate first") elif users[0]['loginStatus']: self.logger.debug("User '{}' is already logged in.".format(uid)) return error_codes.already_logged_in elif not users[0]['activationStatus']: raise DeactivatedUserException( "This user is deactivated. Please reactivate first") print('credentials validated for {}'.format(uid)) return True def authenticate_password(self, uid, pwd, hashed_pwd): """ Uses differnet authentication method to check if credentials are valid """ if self.config[self.AUTHENTICATION_TYPE] == "ldap": try: self.ldapmanager.authenticate(uid, pwd) return True except ldap.INVALID_CREDENTIALS as e: self.logger.error("Wrong password entered by {}".format(uid)) raise WrongPasswordException(str(e)) except ldap.LDAPException as e: self.logger.error("Invalid credentials {}".format(uid)) raise AuthenticationFailedException(str(e)) elif self.config[self.AUTHENTICATION_TYPE] == "mongodb": if not sha512_crypt.verify(pwd, hashed_pwd): self.logger.error("Wrong password entered by {}".format(uid)) raise WrongPasswordException("Wrong Password") return True return False def generate_token(self): self.logger.debug('Generating token') token_info = TokenInfo(None) token_info.generate() return token_info def save_token(self, search_filter, token_info): # print(search_filter) UserManager(self.persistence_manager).modify_user( search_filter, { "token": token_info.token, "loginStatus": token_info.login_status, "loginExpiry": token_info.login_expiry, "tokenExpiry": token_info.token_expiry }) def logout(self, token): """ Authentication method for user logout Args: token: access token Returns: Deletion status (True/False) """ self.logger.info('entering logout method') status = self.delete_token(token) self.logger.debug( 'exiting logout method with status {}'.format(status)) return XprResponse("success", None, {}) def delete_token(self, token): # delete token from database & change status token_info = TokenInfo(None) self.save_token({"token": token}, token_info) return True def modify_user_access(self, token, uid): users = UserManager(self.persistence_manager).get_users( {"token": token}, False) if users and len(users) > 0: if (uid == users[0]['uid']) or (users[0]['primaryRole'] == 'Admin'): return True else: return False else: return False def validate_token(self, token, access_level): users = UserManager(self.persistence_manager).get_users( {"token": token}, False) if not users: self.logger.debug('Tokens do not match. Re-login needed.') raise IncorrectTokenException else: user = User(users[0]) token_info = user.get_token_info() if token_info.has_expired(): self.logger.debug('Token expired for {}.' 'Logging out.'.format(user.get('uid'))) token_info = TokenInfo(None) self.save_token({"token": token}, token_info) raise ExpiredTokenException elif not self.check_access(user.get('primaryRole'), access_level): self.logger.debug('User access check failed! Exiting.') raise PermissionDeniedException("Permission Denied") self.logger.info('revalidating token') self.revalidate_token(token_info) self.logger.info('exiting validate_token method') return True def validate_build_deploy_token(self, token, project): users = UserManager(self.persistence_manager).get_users( {"token": token}, False) if not users or len(users) == 0: self.logger.debug('Tokens do not match. Re-login needed.') raise IncorrectTokenException else: user = User(users[0]) token_info = user.get_token_info() if token_info.has_expired(): self.logger.debug('Token expired for {}.' 'Logging out.'.format(users[0]['uid'])) token_info = TokenInfo(None) self.save_token({"token": token}, token_info) raise ExpiredTokenException if "name" not in project: raise ProjectNotFoundException("Project name is empty") search_filter = {"name": project["name"]} project_info = self.persistence_manager.find('projects', search_filter) if not project_info: self.logger.error('No such project found.') raise ProjectNotFoundException("No such Project Found") if users[0]['uid'] != project_info[0]['owner']['uid'] and \ levels[users[0]['primaryRole']] < levels['Admin']: self.logger.debug('User access check failed! Exiting.') raise PermissionDeniedException self.logger.info('revalidating token') self.revalidate_token(token_info) self.logger.info('exiting validate_token method') return project_info def revalidate_token(self, token_info): token_info.revalidate() self.save_token({"token": token_info.token}, token_info) self.logger.info('token revalidated.') def check_access(self, primary_role, access_level): self.logger.debug('Checking access') return levels[access_level] <= levels[primary_role]