def create_api_token(self, token_name=None, selected_username=None): """Creates API Token :param name: Name of API Token, ``str`` :param name: Name of User to Add Token To, ``str`` """ if selected_username: if not self.can_access_script_console(): raise JenkinsException( 'You must be able to access the "/script" console.') loader = quik.FileLoader(os.path.join("data", "groovy")) script_template = loader.load_template( "create_api_token_for_user_template.groovy") if not token_name: token_name = "" result = self.execute_script( script_template.render({ "user": selected_username.replace("\\", "\\\\").replace('"', '\\"'), "token": token_name.replace("\\", "\\\\").replace('"', '\\"'), })).strip() if len(result) == 0: raise JenkinsException( 'Token Creation Failed. Perhaps you don\'t have "/script" access?' ) return result else: if not self.username or len(self.username) == 0: user_details = self.get_whoAmI() if "name" in user_details: self.username = user_details["name"] else: raise JenkinsException( "Could not get username, are you sure your credentials are valid?" ) user = self.username result = self.jenkins_open( requests.Request( "POST", self._build_url(CREATE_API_TOKEN, locals()), data={"newTokenName": token_name if token_name else ""}, )) try: return json.loads(result)["data"]["tokenValue"] except Exception: raise JenkinsException( "Server did not respond with a valid token. Something went wrong." )
def _posix_payload(self): file_name = "".join( random.choices(string.ascii_letters + string.digits, k=8)) if "." in self.args.script_path: file_name += "." + self.args.script_path.split(".")[-1] with open(self.args.script_path, "rb") as f: payload = base64.b64encode(zlib.compress(f.read(), 9)).decode("utf8") if self.args.executor: executor = self.args.executor.replace("\\", "\\\\").replace( '"', '\\"') + " " if self.args.additional_args: additional_args = " " + self.args.additional_args.replace( "\\", "\\\\").replace('"', '\\"') loader = quik.FileLoader(os.path.join("data", "python")) if self.args.ghost: cmd_template = loader.load_template("posix_ghost_job_template.py") else: cmd_template = loader.load_template("posix_normal_job_template.py") return cmd_template.render(locals())
def _generate_job_xml(self, job_type, nodes, barrier, credentials): file_name = "f" + "".join(random.choices(string.ascii_letters + string.digits, k=8)) loader = quik.FileLoader(os.path.join("data", "xml")) bindings_template = loader.load_template("credential_binding_template.xml") job_template = loader.load_template("job_template.xml") if job_type == "posix": cmd_template = quik.FileLoader(os.path.join("data", "bash")).load_template( "posix_job_dump_creds_template.sh" ) else: cmd_template = quik.FileLoader(os.path.join("data", "batch")).load_template( "windows_job_dump_creds_template.bat" ) for i in range(len(credentials)): if credentials[i]["type"] == "SSHKEY": credentials[i]["key_file_variable"] = "a{0}k".format(i) credentials[i]["username_variable"] = "a{0}u".format(i) credentials[i]["passphrase_variable"] = "a{0}p".format(i) else: # For now everything else uses only one variable credentials[i]["variable"] = "a{0}".format(i) bindings = bindings_template.render(locals()) cmds = cmd_template.render(locals()) return job_template.render( { "job_type": "BatchFile" if job_type == "windows" else "Shell", "assigned_nodes": "({})".format( xmlescape(" || ".join(['"{}"'.format(x["name"]) for x in nodes])) ), "commands": xmlescape(cmds), "credential_bindings": bindings, } )
def _generate_job_xml(self, job_type, nodes, cmd_string): job_template = quik.FileLoader(os.path.join( "data", "xml")).load_template("job_template.xml") return job_template.render({ "job_type": "BatchFile" if job_type == "windows" else "Shell", "assigned_nodes": "({})".format( xmlescape(" || ".join( ['"{}"'.format(x["name"]) for x in nodes]))), "commands": xmlescape(cmd_string), })
def __init__(self, args): super().__init__(args) loader = quik.FileLoader(os.path.join("data", "groovy")) cmd_template = loader.load_template("run_command_template.groovy") cmd = cmd_template.render( {"command": self.args.system_command.replace("\\", "\\\\").replace('"', '\\"')} ) try: cred = self.args.credentials[0] server = self._get_jenkins_server(cred) result = server.execute_script(cmd, not self.args.no_wait, node=self.args.node) if result: result = re.sub(r"[\r\n][\r\n]{2,}", "\n\n", result).strip() print(result) except jenkinslib.JenkinsException as ex: if "[403]" in str(ex).split("\n")[0]: self.logging.fatal( "%s authentication failed or not an admin with script privileges", self._get_username(cred), ) else: self.logging.fatal( "Unable to access Jenkins at: %s With User: %s For Reason:\n\t%s" % ( ( self.server_url.netloc if len(self.server_url.netloc) > 0 else self.args.server ), self._get_username(cred), str(ex).split("\n")[0], ) ) except (req_exc.SSLError, req_exc.ConnectionError): self.logging.fatal( "Unable to connect to: " + (self.server_url.netloc if len(self.server_url.netloc) > 0 else self.args.server) ) except Exception: self.logging.exception("") exit(1)
def _windows_payload(self): file_name = "".join( random.choices(string.ascii_letters + string.digits, k=8)) if "." in self.args.script_path: file_name += "." + self.args.script_path.split(".")[-1] with open(self.args.script_path, "rb") as f: data = base64.b64encode(f.read()).decode("utf8") payload = list(self.__chunk_payload(data, 240)) if self.args.executor: executor = self.args.executor + " " if self.args.additional_args: additional_args = " " + self.args.additional_args loader = quik.FileLoader(os.path.join("data", "batch")) if self.args.ghost: helper_file_name = ("".join( random.choices(string.ascii_letters + string.digits, k=8)) + ".exe") with open( os.path.join("data", "exe", "windows_ghost_job_helper.exe"), "rb") as f: data = base64.b64encode(f.read()).decode("utf8") helper_payload = list(self.__chunk_payload(data, 240)) cmd_template = loader.load_template( "windows_ghost_job_template.bat") else: cmd_template = loader.load_template( "windows_normal_job_template.bat") return cmd_template.render(locals())
def delete_api_token(self, token_identifier, selected_username=None): """Deletes API Token :param name: Name of API Token or Token UUID, ``str`` :param name: Name of User to Delete Token From, ``str`` """ if selected_username: # Permission checking and errors will be raised by list_api_tokens tokens = self.list_api_tokens(selected_username) filtered_tokens = [ x for x in tokens if x["name"] == token_identifier or x["uuid"] == token_identifier ] if len(filtered_tokens) == 0: raise JenkinsException("No matching token found.") elif len(filtered_tokens) > 1: raise JenkinsException( "Token Identifier matchs multiple tokens, pass UUID instead." ) loader = quik.FileLoader(os.path.join("data", "groovy")) script_template = loader.load_template( "delete_api_token_for_user_template.groovy") result = self.execute_script( script_template.render({ "user": selected_username.replace("\\", "\\\\").replace('"', '\\"'), "token": filtered_tokens[0]["uuid"].replace("\\", "\\\\").replace( '"', '\\"'), })).strip() if result != "Success": raise JenkinsException( 'An error occurred. Perhaps you don\'t have "/script" access?' ) else: tokens = self.list_api_tokens() # self.username will exist (if it didn't already) thanks to list_api_tokens user = self.username filtered_tokens = [ x for x in tokens if x["name"] == token_identifier or x["uuid"] == token_identifier ] if len(filtered_tokens) == 0: raise JenkinsException("No matching token found.") elif len(filtered_tokens) > 1: raise JenkinsException( "Token Identifier matchs multiple tokens, pass UUID instead." ) result = self.jenkins_open( requests.Request( "POST", self._build_url(DELETE_API_TOKEN, locals()), data={"tokenUuid": filtered_tokens[0]["uuid"]}, ))
def list_api_tokens(self, query_username=None): """List API Tokens :param name: Name of user to query tokens for, ``str`` """ tokens = [] if query_username: if not self.can_access_script_console(): raise JenkinsException( 'You must be able to access the "/script" console.') loader = quik.FileLoader(os.path.join("data", "groovy")) script_template = loader.load_template( "list_api_tokens_for_user_template.groovy") result = self.execute_script( script_template.render({ "command": query_username.replace("\\", "\\\\").replace('"', '\\"') })).strip() raw_tokens = re.split(r"\n\n", result, flags=re.M) for raw_token in raw_tokens: try: lines = raw_token.split("\n") token = { "name": ": ".join(re.split(": ", lines[0])[1:]), "creation_date": ": ".join(re.split(": ", lines[1])[1:]), "uuid": ": ".join(re.split(": ", lines[2])[1:]), } tokens.append(token) except Exception: pass else: if not self.username or len(self.username) == 0: user_details = self.get_whoAmI() if "name" in user_details: self.username = user_details["name"] else: raise JenkinsException( "Could not get username, are you sure your credentials are valid?" ) user = self.username raw_tokens = self.jenkins_open( requests.Request("GET", self._build_url(GET_API_TOKEN_LIST, locals()))) soup = BeautifulSoup(raw_tokens, "html.parser") tokens_html = soup.find_all("div", {"name": "tokenStore"}) for token_html in tokens_html: try: token = { "name": token_html.find("input", {"name": "tokenName"})["value"], "creation_date": token_html.find("span", {"class": "token-creation"})["title"], "uuid": token_html.find("input", {"name": "tokenUuid"})["value"], } tokens.append(token) except Exception: pass return tokens