diff --git a/bin/akamai-mfa b/bin/akamai-mfa index 2660b78..6d37471 100755 --- a/bin/akamai-mfa +++ b/bin/akamai-mfa @@ -39,7 +39,7 @@ import os import json -__VERSION__ = "0.0.3" +__VERSION__ = "0.0.4" LOG_FMT = '%(asctime)s [%(levelname)s] %(threadName)s %(message)s' MOST_RECENT_PADDING = 30 @@ -48,6 +48,8 @@ mfa_api_url = "https://mfa.akamai.com" mfa_api_ver = "v1" tail_pull_interval = 60 # Default is 60 page_size = 10 # default is 1000 +epilog = '''Copyright (C) Akamai Technologies, Inc\n''' \ + '''Visit http://github.com/akamai/cli-mfa for detailed documentation''' class MFAConfig(): @@ -67,13 +69,15 @@ class MFAConfig(): # 1. Scan parameters from the CLI arguments - parser = argparse.ArgumentParser(prog="akamai mfa", - description='Process command line options.') + self.parser = argparse.ArgumentParser(prog="akamai mfa", epilog=epilog, + description='Process command line options.', + formatter_class=argparse.RawTextHelpFormatter) - subparsers = parser.add_subparsers(dest='command', help='Main command') + subparsers = self.parser.add_subparsers(dest='command', help='Main command') cmdparser = subparsers.add_parser('version', help="Display CLI-MFA version") eventparser = subparsers.add_parser('event', help="Dump MFA events") - eventparser.add_argument("event_type", choices=['policy', 'auth'], default='policy', help="Event type") + eventparser.add_argument("event_type", choices=['policy', 'auth'], nargs='?', const='event_type', + default='auth', help="MFA event type, default is 'auth'") eventparser.add_argument("--start", "-s", default=None, type=int, help="Scan for events after this epoch") eventparser.add_argument("--end", "-e", default=None, type=int, help="Scan for events before this epoch") eventparser.add_argument("--tail", "-f", default=False, action="store_true", @@ -82,14 +86,15 @@ class MFAConfig(): eventparser.add_argument("--noreceipt", default=False, action="store_true", help="Discard the receipt attribute to save log space") - parser.add_argument("--edgerc", type=argparse.FileType('r'), default=os.path.expanduser("~/.edgerc"), - help='Location of the credentials file (default is %s)' % os.path.expanduser("~/.edgerc")) + self.parser.add_argument("--edgerc", type=argparse.FileType('r'), default=os.path.expanduser("~/.edgerc"), + help='Location of the credentials file (default is %s)' % os.path.expanduser("~/.edgerc")) - parser.add_argument("--section", default="default", help="Section inside .edgerc, default is [default]") - parser.add_argument("--debug", '-d', action="store_true", default=False, help="Debug mode") + self.parser.add_argument("--section", default="default", help="Section inside .edgerc, default is [default]") + self.parser.add_argument("--debug", '-d', action="store_true", default=False, help="Debug mode") + self.parser.add_argument('--user-agent-prefix', dest='ua_prefix', default='Akamai-CLI', help=argparse.SUPPRESS) try: - scanned_cli_args = parser.parse_args() + scanned_cli_args = self.parser.parse_args() cli_args = vars(scanned_cli_args) for option in cli_args: setattr(self, option, cli_args[option]) @@ -123,6 +128,9 @@ class MFAConfig(): if not hasattr(self, 'mfa_signing_key'): raise RuntimeError("Missing mfa_signing_key") + def display_help(self): + self.parser.print_help() + class AkamaiMFAAuth(requests.auth.AuthBase): """ @@ -165,55 +173,60 @@ if __name__ == "__main__": requests_log.setLevel(logging.DEBUG) requests_log.propagate = True + if config.command is None: + config.display_help() + sys.exit(1) if config.command == "version": print(__VERSION__) sys.exit(0) + elif config.command == 'event': + session = requests.Session() + session.headers.update({'User-Agent': f'{config.ua_prefix} cli-mfa/{__VERSION__}'}) + session.auth = AkamaiMFAAuth(config) + + if config.event_type == 'auth': + api_report_type = 'auths' + elif config.event_type == 'policy': + api_report_type = 'policies' + + api_url = f'{mfa_api_url}/api/{mfa_api_ver}/control/reports/{api_report_type}' + scan_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=MOST_RECENT_PADDING) + scan_start = scan_end - datetime.timedelta(minutes=5) + if config.end: + scan_end = datetime.datetime.utcfromtimestamp(config.end) + if config.start: + scan_start = datetime.datetime.utcfromtimestamp(config.start) + + + while True: + loop_start = time.time() + page = 1 + page_count = None + while page_count is None or page <= page_count: + params = { + 'after': scan_start.isoformat(), + 'before': scan_end.isoformat(), + 'page_size': page_size, + 'page': page + } + r = session.get(api_url, params=params) + page += 1 + page_count = r.json().get('result', {}).get('total_page_count') + for mfa_event in r.json().get('result', {}).get('page'): + if config.noreceipt: + mfa_event.pop('receipt') + print(json.dumps(mfa_event)) + sys.stdout.flush() + + if config.tail: + wait = tail_pull_interval - (time.time() - loop_start) + logging.debug("Wait %s sec..." % wait) + time.sleep(wait) + scan_start = scan_end # next iteration we stich, start is the previous end + scan_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=MOST_RECENT_PADDING) + + else: + break + else: + raise ValueError(f"Unsupported command: {config.command}") - session = requests.Session() - session.headers.update({'User-Agent': f'cli-mfa/{__VERSION__}'}) - session.auth = AkamaiMFAAuth(config) - - - if config.event_type == 'auth': - api_report_type = 'auths' - elif config.event_type == 'policy': - api_report_type = 'policies' - - api_url = f'{mfa_api_url}/api/{mfa_api_ver}/control/reports/{api_report_type}' - scan_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=MOST_RECENT_PADDING) - scan_start = scan_end - datetime.timedelta(minutes=5) - if config.end: - scan_end = datetime.datetime.utcfromtimestamp(config.end) - if config.start: - scan_start = datetime.datetime.utcfromtimestamp(config.start) - - - while True: - loop_start = time.time() - page = 1 - page_count = None - while page_count is None or page <= page_count: - params = { - 'after': scan_start.isoformat(), - 'before': scan_end.isoformat(), - 'page_size': page_size, - 'page': page - } - r = session.get(api_url, params=params) - page += 1 - page_count = r.json().get('result', {}).get('total_page_count') - for mfa_event in r.json().get('result', {}).get('page'): - if config.noreceipt: - mfa_event.pop('receipt') - print(json.dumps(mfa_event)) - sys.stdout.flush() - - if config.tail: - wait = tail_pull_interval - (time.time() - loop_start) - logging.debug("Wait %s sec..." % wait) - time.sleep(wait) - scan_start = scan_end # next iteration we stich, start is the previous end - scan_end = datetime.datetime.utcnow() - datetime.timedelta(seconds=MOST_RECENT_PADDING) - - else: - break diff --git a/cli.json b/cli.json index 8aa0b33..3c27cc4 100644 --- a/cli.json +++ b/cli.json @@ -5,7 +5,7 @@ "commands": [ { "name": "mfa", - "version": "0.0.3", + "version": "0.0.4", "description": "Akamai CLI for MFA" } ]