diff --git a/examples/Accounts API/create_child_account.py b/examples/Accounts API/create_child_account.py new file mode 100644 index 0000000..38e6966 --- /dev/null +++ b/examples/Accounts API/create_child_account.py @@ -0,0 +1,63 @@ +""" +Example of Duo Accounts API child account creation +""" + +import duo_client +import os +import sys +import getpass + +from pprint import pprint + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Accounts API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Accounts API integration key ("DI..."): ') + skey = _get_next_arg('Duo Accounts API integration secret key: ', secure=True) + host = _get_next_arg('Duo Accounts API hostname ("api-....duosecurity.com"): ') + account_name = _get_next_arg('Name for new child account: ') + + return {"IKEY": ikey, "SKEY": skey, "APIHOST": host, "ACCOUNT_NAME": account_name} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + account_client = duo_client.Accounts( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + print(f"Creating child account with name [{inputs['ACCOUNT_NAME']}]") + child_account = account_client.create_account(inputs['ACCOUNT_NAME']) + + if 'account_id' in child_account: + print(f"Child account for [{inputs['ACCOUNT_NAME']}] created successfully.") + else: + print(f"An unexpected error occurred while creating child account for {inputs['ACCOUNT_NAME']}") + print(child_account) + + +if __name__ == '__main__': + main() diff --git a/examples/Accounts API/delete_child_account.py b/examples/Accounts API/delete_child_account.py new file mode 100644 index 0000000..07ea663 --- /dev/null +++ b/examples/Accounts API/delete_child_account.py @@ -0,0 +1,70 @@ +""" +Example of Duo Accounts API child account deletiom +""" + +import duo_client +import os +import sys +import getpass + +from pprint import pprint + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Accounts API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Accounts API integration key ("DI..."): ') + skey = _get_next_arg('Duo Accounts API integration secret key: ', secure=True) + host = _get_next_arg('Duo Accounts API hostname ("api-....duosecurity.com"): ') + account_id = _get_next_arg('ID of child account to delete: ') + + return {"IKEY": ikey, "SKEY": skey, "APIHOST": host, "ACCOUNT_ID": account_id} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + account_client = duo_client.Accounts( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + account_name = None + child_account_list = account_client.get_child_accounts() + for account in child_account_list: + if account['account_id'] == inputs['ACCOUNT_ID']: + account_name = account['name'] + if account_name is None: + print(f"Unable to find account with ID [{inputs['ACCOUNT_ID']}]") + sys.exit() + + print(f"Deleting child account with name [{account_name}]") + deleted_account = account_client.delete_account(inputs['ACCOUNT_ID']) + if deleted_account == '': + print(f"Account {inputs['ACCOUNT_ID']} was deleted successfully.") + else: + print(f"An unexpected error occurred while deleting account [{account_name}: {deleted_account}]") + + +if __name__ == '__main__': + main() diff --git a/examples/Accounts API/retrieve_account_list.py b/examples/Accounts API/retrieve_account_list.py new file mode 100644 index 0000000..b8f1474 --- /dev/null +++ b/examples/Accounts API/retrieve_account_list.py @@ -0,0 +1,64 @@ +""" +Example of Duo account API uaer accountentication with synchronous request/response +""" + +import duo_client +import os +import sys +import getpass + +from pprint import pprint + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Accounts API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Accounts API integration key ("DI..."): ') + skey = _get_next_arg('Duo Accounts API integration secret key: ', secure=True) + host = _get_next_arg('Duo Accounts API hostname ("api-....duosecurity.com"): ') + + return {"IKEY": ikey, "SKEY": skey, "APIHOST": host} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + account_client = duo_client.Accounts( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + child_accounts = account_client.get_child_accounts() + + if isinstance(child_accounts, list): + # Expected list of child accounts returned + for child_account in child_accounts: + print(child_account) + + if isinstance(child_accounts, dict): + # Non-successful response returned + print(child_accounts) + + +if __name__ == '__main__': + main() diff --git a/examples/create_integration_sso_generic.py b/examples/Admin API/create_integration_sso_generic.py similarity index 100% rename from examples/create_integration_sso_generic.py rename to examples/Admin API/create_integration_sso_generic.py diff --git a/examples/create_user_and_phone.py b/examples/Admin API/create_user_and_phone.py similarity index 100% rename from examples/create_user_and_phone.py rename to examples/Admin API/create_user_and_phone.py diff --git a/examples/get_billing_and_telephony_credits.py b/examples/Admin API/get_billing_and_telephony_credits.py similarity index 100% rename from examples/get_billing_and_telephony_credits.py rename to examples/Admin API/get_billing_and_telephony_credits.py diff --git a/examples/log_examples.py b/examples/Admin API/log_examples.py similarity index 100% rename from examples/log_examples.py rename to examples/Admin API/log_examples.py diff --git a/examples/policies.py b/examples/Admin API/policies.py similarity index 100% rename from examples/policies.py rename to examples/Admin API/policies.py diff --git a/examples/report_auths_by_country.py b/examples/Admin API/report_auths_by_country.py similarity index 100% rename from examples/report_auths_by_country.py rename to examples/Admin API/report_auths_by_country.py diff --git a/examples/Admin API/report_user_by_email.py b/examples/Admin API/report_user_by_email.py new file mode 100755 index 0000000..6c971a3 --- /dev/null +++ b/examples/Admin API/report_user_by_email.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +""" Script to illustrate how to retrieve a user from the Duo Admin API using the associated email address""" + +from __future__ import absolute_import, print_function +import sys +import getpass + +import duo_client +from six.moves import input + +argv_iter = iter(sys.argv[1:]) + + +def get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def main(): + """ Primary script execution code """ + # Configuration and information about objects to create. + admin_api = duo_client.Admin( + ikey=get_next_arg('Admin API integration key ("DI..."): '), + skey=get_next_arg('integration secret key: ', secure=True), + host=get_next_arg('API hostname ("api-....duosecurity.com"): '), + ) + + # Retrieve user info from API: + email_address = get_next_arg('E-mail address of user to retrieve: ') + user = admin_api.get_user_by_email(email_address) + + if user: + print(user) + else: + print(f"User with email [{email_address}] could not be found.") + + +if __name__ == '__main__': + main() diff --git a/examples/report_users_and_phones.py b/examples/Admin API/report_users_and_phones.py similarity index 100% rename from examples/report_users_and_phones.py rename to examples/Admin API/report_users_and_phones.py diff --git a/examples/trust_monitor_events.py b/examples/Admin API/trust_monitor_events.py similarity index 100% rename from examples/trust_monitor_events.py rename to examples/Admin API/trust_monitor_events.py diff --git a/examples/Auth API/async_basic_user_mfa.py b/examples/Auth API/async_basic_user_mfa.py new file mode 100644 index 0000000..10b7106 --- /dev/null +++ b/examples/Auth API/async_basic_user_mfa.py @@ -0,0 +1,96 @@ +""" +Example of Duo Auth API user authentication using asynchronous resquest/response methods +""" + +import duo_client +import os +import sys +import getpass + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Auth API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Auth API integration key ("DI..."): ') + skey = _get_next_arg('Duo Auth API integration secret key: ', secure=True) + host = _get_next_arg('Duo Auth API hostname ("api-....duosecurity.com"): ') + username = _get_next_arg('Duo Username: ') + + return {"USERNAME": username, "IKEY": ikey, "SKEY": skey, "APIHOST": host} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + auth_client = duo_client.Auth( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + # Verify that the Duo service is available + duo_ping = auth_client.ping() + if 'time' in duo_ping: + print("\nDuo service check completed successfully.") + else: + print(f"Error: {duo_ping}") + + # Verify that IKEY and SKEY information provided are valid + duo_check= auth_client.check() + if 'time' in duo_check: + print("IKEY and SKEY provided have been verified.") + else: + print(f"Error: {duo_check}") + + # Execute pre-authentication for given user + print(f"\nExecuting pre-authentication for {inputs['USERNAME']}...") + pre_auth = auth_client.preauth(username=inputs['USERNAME']) + + if pre_auth['result'] == "auth": + try: + print(f"Executing authentication action for {inputs['USERNAME']}...") + auth = auth_client.auth(factor="push", username=inputs['USERNAME'], device="auto", async_txn=True) + if 'txid' in auth: + waiting = True + # Collect the authentication result + print("Getting authentication result...") + # Repeat long polling for async authentication status until no longer in a 'waiting' state + while waiting is True: + # Poll Duo Auth API for the status of the async authentication based upon transaction ID + auth_status = auth_client.auth_status(auth['txid']) + print(f"Auth status: {auth_status}") + if auth_status['waiting'] is not True: + # Waiting for response too async authentication is no longer 'True', so break the loop + waiting = False + # Parse response for the 'status' dictionary key to determine whether to allow or deny + print(auth_status) + else: + # Some kind of unexpected error occurred + print(f"Error: an unknown error occurred attempting authentication for [{inputs['USERNAME']}]") + except Exception as e_str: + print(e_str) + else: + print(pre_auth['status_msg']) + + +if __name__ == '__main__': + main() diff --git a/examples/Auth API/basic_user_mfa.py b/examples/Auth API/basic_user_mfa.py new file mode 100644 index 0000000..31604cc --- /dev/null +++ b/examples/Auth API/basic_user_mfa.py @@ -0,0 +1,99 @@ +""" +Example of Duo Auth API uaer authentication with synchronous request/response +""" + +import duo_client +import os +import sys +import getpass + +from pprint import pprint + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Auth API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Auth API integration key ("DI..."): ') + skey = _get_next_arg('Duo Auth API integration secret key: ', secure=True) + host = _get_next_arg('Duo Auth API hostname ("api-....duosecurity.com"): ') + username = _get_next_arg('Duo Username: ') + + return {"USERNAME": username, "IKEY": ikey, "SKEY": skey, "APIHOST": host} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + auth_client = duo_client.Auth( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + # Verify that the Duo service is available + duo_ping = auth_client.ping() + if 'time' in duo_ping: + print("\nDuo service check completed successfully.") + else: + print(f"Error: {duo_ping}") + + # Verify that IKEY and SKEY information provided are valid + duo_check= auth_client.check() + if 'time' in duo_check: + print("IKEY and SKEY provided have been verified.") + else: + print(f"Error: {duo_check}") + + # Execute pre-authentication for given user + print(f"\nExecuting pre-authentication for {inputs['USERNAME']}...") + pre_auth = auth_client.preauth(username=inputs['USERNAME']) + + if pre_auth['result'] == "auth": + try: + # User exists and has an MFA device enrolled + print(f"Executing authentication action for {inputs['USERNAME']}...") + # "auto" is selected for the factor in this example, however the pre_auth['devices'] dictionary + # element contains a list of factors available for the provided user, if an alternate method is desired + auth = auth_client.auth(factor="auto", username=inputs['USERNAME'], device="auto") + print(f"\n{auth['status_msg']}") + except Exception as e_str: + print(e_str) + elif pre_auth['result'] == "allow": + # User is in bypass mode + print(pre_auth['status_msg']) + elif pre_auth['result'] == "enroll": + # User is unknown and not enrolled in Duo with a 'New User' policy setting of 'Require Enrollment' + # Setting a 'New User' policy to 'Require Enrollment' should only be done for Group level policies where + # the intent is to capture "partially enrolled" users. "Parially enrolled" users are those that Duo has a + # defined username but does not have an MFA device enrolled. + print("Please enroll in Duo using the following URL.") + print(pre_auth['enroll_portal_url']) + elif pre_auth['result'] == "deny": + # User is denied by policy setting + print(pre_auth['status_msg']) + else: + print("Error: an unexpected error occurred") + print(pre_auth) + + +if __name__ == '__main__': + main() diff --git a/examples/Auth API/basic_user_mfa_token.py b/examples/Auth API/basic_user_mfa_token.py new file mode 100644 index 0000000..8632085 --- /dev/null +++ b/examples/Auth API/basic_user_mfa_token.py @@ -0,0 +1,93 @@ +""" +Example of Duo Auth API uaer authentication with synchronous request/response using an assigned token +as the MFA factor +""" + +import duo_client +import os +import sys +import getpass + +from pprint import pprint + + +argv_iter = iter(sys.argv[1:]) + + +def _get_next_arg(prompt, secure=False): + """Read information from STDIN, using getpass when sensitive information should not be echoed to tty""" + try: + return next(argv_iter) + except StopIteration: + if secure is True: + return getpass.getpass(prompt) + else: + return input(prompt) + + +def prompt_for_credentials() -> dict: + """Collect required API credentials from command line prompts + + :return: dictionary containing Duo Auth API ikey, skey and hostname strings + """ + + ikey = _get_next_arg('Duo Auth API integration key ("DI..."): ') + skey = _get_next_arg('Duo Auth API integration secret key: ', secure=True) + host = _get_next_arg('Duo Auth API hostname ("api-....duosecurity.com"): ') + username = _get_next_arg('Duo Username: ') + + return {"USERNAME": username, "IKEY": ikey, "SKEY": skey, "APIHOST": host} + + +def main(): + """Main program entry point""" + + inputs = prompt_for_credentials() + + auth_client = duo_client.Auth( + ikey=inputs['IKEY'], + skey=inputs['SKEY'], + host=inputs['APIHOST'] + ) + + # Verify that the Duo service is available + duo_ping = auth_client.ping() + if 'time' in duo_ping: + print("\nDuo service check completed successfully.") + else: + print(f"Error: {duo_ping}") + + # Verify that IKEY and SKEY information provided are valid + duo_check= auth_client.check() + if 'time' in duo_check: + print("IKEY and SKEY provided have been verified.") + else: + print(f"Error: {duo_check}") + + # Execute pre-authentication for given user + print(f"\nExecuting pre-authentication for {inputs['USERNAME']}...") + pre_auth = auth_client.preauth(username=inputs['USERNAME']) + + print("\n" + "=" * 30) + pprint(f"Pre-Auth result: {pre_auth}") + print("=" * 30 + "\n") + + for device in pre_auth['devices']: + pprint(device) + print() + + if pre_auth['result'] == "auth": + try: + print(f"Executing authentication action for {inputs['USERNAME']}...") + # Prompt for the hardware token passcode + passcode = _get_next_arg('Duo token passcode: ') + auth = auth_client.auth(factor="passcode", username=inputs['USERNAME'], passcode=passcode) + print(f"\n{auth['status_msg']}") + except Exception as e_str: + print(e_str) + else: + print(pre_auth) + + +if __name__ == '__main__': + main()