def resolveOutputFilePath(self): filePath = self.args.get("file", None) if not isEmpty(filePath): filePath = self.resolvePathVariables(filePath) if os.path.isabs(filePath): print("report exported: {}".format(filePath)) return filePath dirPath = self.args.get("dir", None) if not isEmpty(dirPath): dirPath = self.resolvePathVariables(dirPath) else: dirPath = gettempdir() if isEmpty(filePath): today = datetime.datetime.utcnow() filePath = "%s_%s.csv" % (self.file_name_prefix, today.strftime("%Y%m%d_%H%M")) if not os.path.isabs(dirPath): dirPath = os.path.abspath(dirPath) path = os.path.join(dirPath, filePath) print("Report exported to: {}".format(path)) return path
def run(self, opt_args=None): pyVersion = sys.version_info if pyVersion.major != 3: die("Major Python version must be 3.x: %s" % str(pyVersion)) if pyVersion.minor < 0: die("Minor Python version %s should be at least 3.0+" % str(pyVersion)) signal.signal(signal.SIGINT, self.signal_handler) if os.name == 'nt': sys.stderr.write("Use Ctrl+Break to stop the script\n") else: sys.stderr.write("Use Ctrl+C to stop the script\n") if opt_args is None: args = arguments_parser.ArgumentParser( self.report_type).parse_arguments() else: args = opt_args arguments_parser.process_args(args, self.report_type) logFactory = LogFactory(args) logger = logFactory.getLogger("main") token_fetcher = access_token_fetcher.AccessTokenFetcher(logger, args) token = token_fetcher.get_access_token() if isEmpty(token): die("Could not get access token for the username") try: if self.report_type == ReportType.RESPONSE: self.response_report(args, logger, token) else: self.earnings_report(args, logger, token) finally: token_fetcher.logout(token)
def resolveStandardUserToken(self, username, password, clientId): params = self.resolveStandardUserTokenRequestParameters(clientId) redirectUri = urllib.parse.unquote(params['redirectUri']) params = self.resolveKeycloakSessionAuthenticationCode(username, password, params) code = params.get("code", None) if isEmpty(code): die("No Keycloak access token session code") requestHeaders = { "Accepts": "application/json", "Content-Type": "application/x-www-form-urlencoded" } formData = "&".join( ( "grant_type=authorization_code", "code=%s" % code, "client_id=%s" % clientId, "redirect_uri=%s"% redirectUri ) ) url = self.resolveKeycloakDirectTokenAccessUrl() rsp = self.executeHttpRequest(url, "POST", requestHeaders, formData, asJson=True) return rsp["body"]
def resolveKeycloakSessionAuthenticationCode(self, username, password, params): url = params['loginUrl'] if isEmpty(url): die("Failed to extract Keycloak login URL value") cookies = {} sessionId = params.get('AUTH_SESSION_ID', None) if isEmpty(sessionId): die("No Keycloak session ID cookie present") cookies["AUTH_SESSION_ID"] = sessionId kcToken = params.get('KC_RESTART', None) if isEmpty(kcToken): die("No Keycloak restart cookie present") cookies["KC_RESTART"] = kcToken requestHeaders = { "Content-Type": "application/x-www-form-urlencoded", "Referer": params["referer"] } formData = "&".join(("username=%s" % username, "password=%s" % password)) rsp = self.executeHttpRequest(url, "POST", requestHeaders, formData, cookieJar=cookies, asJson=False) redirectHistory = rsp.get("history", None) if isEmpty(redirectHistory): if "INVALID_USER_PASS" in rsp.get("body", None): die("Invalid Username or Password") else: die("No Keycloak redirection available") rdData = redirectHistory[0] rdHeaders = rdData.headers locHeader = rdHeaders.get("Location", None) locItems = urllib.parse.urlparse(locHeader) # Response is https://..../#state=... queryParams = urllib.parse.parse_qs(locItems.fragment) code = queryParams.get("code", None) if isinstance(code, list): code = code[0] return { "url": rdData.url, "cookies": rsp.get("cookies", None), "code": code }
def parse_arguments(self): args = self.parser.parse_args() args = vars(args) for arg in args: if isEmpty(arg): args.pop(arg, None) process_args(args, self.report_type) return args
def resolvePathVariables(self, path): """ Expands ~/xxx and ${XXX} variables """ if isEmpty(path): return path path = os.path.expanduser(path) path = os.path.expandvars(path) return path
def resolve_user_token(self, username, password): self.logger.debug("Retrieving Keycloak access token") if isinstance(username, (int, float)): username = str(username) elif isEmpty(username): die("No Keycloak access username provided") if isinstance(password, (int, float)): password = str(password) elif isEmpty(password): die("No Keycloak access password provided") clientId = self.args.get("clientId", None) if isEmpty(clientId): die("No Keycloak client identifier provided") clientIdFormat = self.args.get("clientIdFormat", "ups-installation-%s") effectiveClientId = clientIdFormat % clientId mode = self.args.get("mode", "STANDARD") mode = mode.upper() if mode == "STANDARD": rsp = self.resolveStandardUserToken( username, password, effectiveClientId) elif mode == "DIRECT": rsp = self.resolveDirectGrantUserToken( username, password, effectiveClientId) else: die("Unknown Keycloak access token retrieval mode: %s" % mode) accessToken = rsp.get("access_token", None) self.refresh_token = rsp.get("refresh_token", None) if isEmpty(accessToken): self.logger.error("No access token in Keycloak response: %s" % str(rsp)) die("No access token returned from Keycloak") self.logger.debug("Retrieved Keycloak access token") return accessToken
def manage_earnings_report_start_and_end_dates(args): req_start_date = args.get("start_date") day_of_week = args.get("first_day_of_week", DEFAULT_FIRST_DAY_OF_WEEK) today = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) if not isEmpty(req_start_date): args["start_date"] = shift_date_to_first_day_of_week( parse_date(req_start_date), day_of_week) else: start_date = today - datetime.timedelta(weeks=5 * 12 * 4) args["start_date"] = shift_date_to_first_day_of_week( start_date, day_of_week) req_end_date = args.get("end_date") if not isEmpty(req_end_date): args["end_date"] = shift_date_to_end_day_of_week( parse_date(req_end_date), day_of_week) else: args["end_date"] = shift_date_to_end_day_of_week(today, day_of_week) attribute_start_month = args.get("attribute_start_month") if not isEmpty(attribute_start_month): args["attribute_start_month"] = shift_date_to_first_day_of_month( parse_date(attribute_start_month, "%b-%Y", "MMM-yyyy")) else: start_date = today - datetime.timedelta(weeks=5 * 12 * 4) args["attribute_start_month"] = shift_date_to_first_day_of_month( start_date) attribute_end_month = args.get("attribute_end_month") if not isEmpty(attribute_end_month): args["attribute_end_month"] = shift_date_to_end_day_of_month( parse_date(attribute_end_month, "%b-%Y", "MMM-yyyy")) else: args["attribute_end_month"] = shift_date_to_end_day_of_month(today)
def resolveKeycloakRealmAccessUrl(self): if self.args is None: die("No Keycloak access arguments provided") protocol = self.args.get("protocol", "https") host = self.args.get("host", None) if isEmpty(host): die("No Keycloak host specified") port = self.args.get("port", -1) realm = self.args.get("realm", "unifiedpush-installations") if port > 0: return "%s://%s:%d/auth/realms/%s" % (protocol, host, port, realm) else: return "%s://%s/auth/realms/%s" % (protocol, host, realm)
def downloadReport(self, url, verb, headersMap, reqData): if headersMap is None: headersMap = {} dataBytes = None if not isEmpty(reqData): dataBytes = bytes(reqData, 'utf-8') headersMap["Content-Length"] = str(len(dataBytes)) try: # InsecureRequestWarning: Unverified HTTPS request is being made. requests.packages.urllib3.disable_warnings() self.logger.debug("url: %s, header:%s, data: %s" % (url, headersMap, dataBytes)) connTimeout = self.args.get("connectTimeout", 15) rspTimeout = self.args.get("responseTimeout", 3000) rsp = requests.request(verb, url, headers=headersMap, data=dataBytes, verify=False, timeout=(connTimeout, rspTimeout)) statusCode = rsp.status_code if (statusCode < 200) or (statusCode >= 400): raise urllib.error.HTTPError(url, statusCode, "Failed: %d" % statusCode, rsp.headers, None) filePath = self.resolveOutputFilePath() text_file = open(filePath, "wb") for chunk in rsp.iter_content(chunk_size=1024): text_file.write(chunk) text_file.close() except urllib.error.HTTPError as err: self.logger.error("Failed (%d %s) to invoke %s %s" % (err.code, err.msg, verb, url)) raise err except urllib.error.URLError as err: self.logger.error("Some unknown error for %s %s: %s" % (verb, url, err.reason)) raise err
def executeHttpRequest(self, url, verb, headersMap, reqData, cookieJar=None, asJson=True): if headersMap is None: headersMap = {} dataBytes = None if not isEmpty(reqData): dataBytes = bytes(reqData, 'utf-8') headersMap["Content-Length"] = str(len(dataBytes)) try: # InsecureRequestWarning: Unverified HTTPS request is being made. requests.packages.urllib3.disable_warnings() self.logger.debug("%s %s" % (verb, url)) connTimeout = self.args.get("connectTimeout", 15) rspTimeout = self.args.get("responseTimeout", 30) rsp = requests.request(verb, url, headers=headersMap, data=dataBytes, cookies=cookieJar, verify=False, timeout=(connTimeout, rspTimeout)) statusCode = rsp.status_code if asJson and (statusCode >= 200) and (statusCode < 300): rspContent = rsp.json() else: rspContent = rsp.text # NOTE: we extract the response context regardless of the status code # so we can place a debug breakpoint here and see it if (statusCode < 200) or (statusCode >= 400): raise urllib.error.HTTPError(url, statusCode, "Reason: %s. URL: %s" % (rsp.reason, url), rsp.headers, None) result = { "statusCode": statusCode, "headers": rsp.headers, "cookies": rsp.cookies, "history": rsp.history, "body": rspContent } return result except urllib.error.HTTPError as err: self.logger.error("Failed (%d %s) to invoke %s %s" % (err.code, err.msg, verb, url)) raise err except urllib.error.URLError as err: self.logger.error("Some unknown error for %s %s: %s" % (verb, url, err.reason)) raise err
def manage_response_report_start_and_end_dates(args): req_start_date = args.get("start_date") req_end_date = args.get("end_date") day_of_week = args.get("first_day_of_week", DEFAULT_FIRST_DAY_OF_WEEK) weeks_count = args.get("weeks_count", DEFAULT_WEEKS_COUNT) if isEmpty(req_end_date) and isEmpty(req_start_date): today = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) end_date = shift_date_to_end_day_of_week(today, day_of_week) args["end_date"] = end_date args["start_date"] = shift_date_to_first_day_of_week( end_date - datetime.timedelta(weeks=weeks_count), day_of_week) return if not isEmpty(req_end_date) and not isEmpty(req_start_date): args["start_date"] = shift_date_to_first_day_of_week( parse_date(req_start_date), day_of_week) args["end_date"] = shift_date_to_end_day_of_week( parse_date(req_end_date), day_of_week) return if not isEmpty(req_start_date): start_date = shift_date_to_first_day_of_week( parse_date(req_start_date), day_of_week) args["start_date"] = start_date args["end_date"] = shift_date_to_end_day_of_week( start_date + datetime.timedelta(weeks=weeks_count), day_of_week) return if not isEmpty(req_end_date): end_date = shift_date_to_end_day_of_week(parse_date(req_end_date), day_of_week) args["end_date"] = end_date args["start_date"] = shift_date_to_first_day_of_week( end_date - datetime.timedelta(weeks=weeks_count), day_of_week)
def get_access_token(self) -> str: password = self.args.get("password") if isEmpty(password): password = getpass('Password: ') return self.resolve_user_token(self.args["username"], password)