class APIClient: def __init__(self, connection, configuration): auth = configuration.get('auth') self.client = OneLoginClient(auth.get('clientId'), auth.get('clientSecret'), connection['region']) def generate_token(self): """To generate the Token""" self.client.get_access_token() return self.response_handler() def run_search(self, query_expr, range_end=None): """get the response from onelogin endpoints :param quary_expr: dict, filter parameters :param range_end: int,length value :return: response, json object""" token = self.client.get_access_token() events = [] if token and self.client.error is None: events = self.client.get_events(query_expr, max_results=range_end) return self.response_handler(events) def response_handler(self, data=None): if data is None: data = [] response = dict() if self.client.error: response["code"] = int(self.client.error) if self.client.error is None: response.update({"code": 200, "data": data}) elif self.client.error == 500 and "local variable 'data' referenced before assignment" in self.client.error_description: response.update({"code": 200, "data": []}) else: response["message"] = self.client.error_description return response
def get_client(options): client_id = client_secret = ip = None if options.client_id is not None and options.client_secret is not None: client_id = options.client_id client_secret = options.client_secret region = options.region else: if os.path.isfile('onelogin.sdk.json'): json_data = open('onelogin.sdk.json').read() data = json.loads(json_data) if 'client_id' in data.keys() and 'client_secret' in data.keys(): client_id = data['client_id'] client_secret = data['client_secret'] region = data.get('region', 'us') ip = data.get('ip', None) if client_id is None or client_secret is None: raise Exception("OneLogin Client ID and Secret are required") client = OneLoginClient(client_id, client_secret, region) if ip: client.ip = ip client.prepare_token() if client.error == 401 or client.access_token is None: raise Exception( "Invalid client_id and client_secret. Access_token could not be retrieved" ) return client
def one_tap_login(request): received_json_data = json.loads(request.body) credential = received_json_data['credential'] decoded = jwt.decode(credential, verify=False) user_data = { "name": decoded['name'], "email": decoded['email'], "given_name": decoded['given_name'], "family_name": decoded['family_name'] } json_user_data = json.dumps(user_data) request.session['user_data'] = json_user_data # run this in command shell: # python manage.py migrate client = OneLoginClient( mysecrets.ONELOGIN_CLIENT_ID, mysecrets.ONELOGIN_CLIENT_SECRET, 'us' ) # 1. Make sure the user you want to create does not exist yet users = client.get_users({ "email": decoded["email"] }) # 2. Create the new user (explain the most interesting user parameters) if len(users) == 0: new_user_params = { "email": decoded["email"], "firstname": decoded["name"], "lastname": decoded["given_name"], "username": decoded["family_name"] } created_user = client.create_user(new_user_params) if created_user is not None: # 3. Assign the Default role to the user roles = client.get_roles({ "name": "Default" }) if len(roles) == 1: role_ids = [ roles[0].id ] client.assign_role_to_user(created_user.id, role_ids) # 4. Set the user state USER_STATE_APPROVED = 1 client.set_state_to_user(created_user.id, USER_STATE_APPROVED) return HttpResponse(json_user_data, content_type="application/json")
def onelogin_client() -> OneLoginClient: client = OneLoginClient( ArgumentParser.args.onelogin_client_id, ArgumentParser.args.onelogin_client_secret, ArgumentParser.args.onelogin_region ) return client
def testClientWithNoData(self): """ Tests the constructor method of the OneLoginClient class Build a OneLoginClient object with no data """ with self.assertRaises(Exception): client = OneLoginClient()
def onelogin_client() -> OneLoginClient: client = OneLoginClient( Config.onelogin.client_id, Config.onelogin.client_secret, Config.onelogin.region, ) return client
def __init__(self, config: Section): self.sts_client = boto3.client("sts") self.config = config self.saml = None self.all_roles = None self.role_arn = None self.credentials = None self.duration_seconds = int(config['duration_seconds']) self.user_credentials = UserCredentials(config) self.mfa = MFACredentials(config) base_uri_parts = self.config['base_uri'].split('.') self.ol_client = OneLoginClient( self.config['client_id'], self.config['client_secret'], base_uri_parts[1], )
def get_client(options): client_id = client_secret = ip = json_data = None region = 'us' client_file_name = 'onelogin.sdk.json' if options.client_id is not None and options.client_secret is not None: client_id = options.client_id client_secret = options.client_secret region = options.region ip = options.ip else: if options.config_file_path is not None and os.path.isfile( os.path.join(options.config_file_path, client_file_name)): json_data = open( os.path.join(options.config_file_path, client_file_name)).read() elif os.path.isfile(client_file_name): json_data = open(client_file_name).read() elif os.path.isfile( os.path.expanduser('~') + '/.onelogin/' + client_file_name): json_data = open( os.path.expanduser('~') + '/.onelogin/' + client_file_name).read() if json_data is not None: data = json.loads(json_data) if 'client_id' in data.keys() and 'client_secret' in data.keys(): client_id = data['client_id'] client_secret = data['client_secret'] if 'region' in data.keys() and data['region']: region = data['region'] if 'ip' in data.keys() and data['ip']: ip = data['ip'] if not client_id or not client_secret: raise Exception("OneLogin Client ID and Secret are required") client = OneLoginClient(client_id, client_secret, region) client.api_configuration["assertion"] = options.saml_api_version if ip: client.ip = ip client.prepare_token() if client.error == 401 or client.access_token is None: raise Exception( "Invalid client_id and client_secret. Access_token could not be retrieved" ) return client
def testClientDefaultErrorValues(self): """ Tests the constructor method of the OneLoginClient class Build a OneLoginClient object and check if the error attributes exist and are None """ client = OneLoginClient(client_id='test_client_id', client_secret='test_client_secret', region='eu') self.assertIsNone(client.error) self.assertIsNone(client.error_description)
def testClientWithData(self): """ Tests the constructor method of the OneLoginClient class Build a OneLoginClient object with a client_id and client_secret """ client = OneLoginClient(client_id='test_client_id', client_secret='test_client_secret') self.assertIsNot(client, None) self.assertEqual('test_client_id', client.client_id) self.assertEqual('test_client_secret', client.client_secret) self.assertEqual('us', client.url_builder.region)
def __init__(self, config, args): self.sts_client = boto3.client("sts") self.config = config self.args = args self.token = None self.account_id = None self.saml = None self.all_roles = None self.role_arn = None self.principal_arn = None self.credentials = None self.username = self.args.username self.password = None base_uri_parts = self.config['base_uri'].split('.') self.ol_client = OneLoginClient( self.config['client_id'], self.config['client_secret'], base_uri_parts[1], )
class OneLogin: def __init__(self): CLIENT_ID = os.environ["ONELOGIN_CLIENT_ID"] CLIENT_SECRET = os.environ["ONELOGIN_CLIENT_SECRET"] REGION = os.environ.get("ONELOGIN_REGION", "US").upper() self.client = OneLoginClient(CLIENT_ID, CLIENT_SECRET, REGION) def get_group_members(self, group_name=None): """ This is technically not named well, since we're getting users assigned to a role, but because of the existing framework, the matching the function name keeps it reusable :param group_name: :return: """ member_list = [] role = self.client.get_roles(query_parameters={"name": group_name}) users = self.client.get_users(query_parameters={"role_id": role[0].id}) for user in users: member_list.append({"username": user.username, "email": user.email}) return member_list
def get_client(options): client_id = client_secret = None if options.client_id is not None and options.client_secret is not None: client_id = options.client_id client_secret = options.client_secret region = options.region else: if os.path.isfile('onelogin.sdk.json'): json_data = open('onelogin.sdk.json').read() data = json.loads(json_data) if 'client_id' in data.keys() and 'client_secret' in data.keys(): client_id = data['client_id'] client_secret = data['client_secret'] region = data.get('region', 'us') if client_id is None or client_secret is None: raise Exception("OneLogin Client ID and Secret are required") return OneLoginClient(client_id, client_secret, region)
def __init__(self): CLIENT_ID = os.environ["ONELOGIN_CLIENT_ID"] CLIENT_SECRET = os.environ["ONELOGIN_CLIENT_SECRET"] REGION = os.environ.get("ONELOGIN_REGION", "US").upper() self.client = OneLoginClient(CLIENT_ID, CLIENT_SECRET, REGION)
class OneloginAWS(object): """ Handles the authentication between OneLogin SAML Assertion and the AWS identity federation """ def __init__(self, config: Section): self.sts_client = boto3.client("sts") self.config = config self.saml = None self.all_roles = None self.role_arn = None self.credentials = None self.duration_seconds = int(config['duration_seconds']) self.user_credentials = UserCredentials(config) self.mfa = MFACredentials(config) base_uri_parts = self.config['base_uri'].split('.') self.ol_client = OneLoginClient( self.config['client_id'], self.config['client_secret'], base_uri_parts[1], ) def get_saml_assertion(self): """ Retrieve users credentials and get the SAML assertion from Onelogin, based on the users choice of AWS account to log into """ self.user_credentials.load_credentials() saml_resp = self.ol_client.get_saml_assertion( self.user_credentials.username, self.user_credentials.password, self.config['aws_app_id'], self.config['subdomain']) if saml_resp is None: raise Exception("Onelogin Error: '{error}' '{desc}'".format( error=self.ol_client.error, desc=self.ol_client.error_description)) if saml_resp.mfa: if not self.mfa.ready(): self.mfa.select_device(saml_resp.mfa.devices) if not self.mfa.has_otp: self.mfa.prompt_token() saml_resp = self.ol_client.get_saml_assertion_verifying( self.config['aws_app_id'], self.mfa.device.id, saml_resp.mfa.state_token, self.mfa.otp) self.saml = saml_resp def get_arns(self): """Extract the IAM Role ARNs from the SAML Assertion""" if not self.saml: self.get_saml_assertion() # Parse the returned assertion and extract the authorized roles aws_roles = [] root = ElementTree.fromstring(base64.b64decode( self.saml.saml_response)) namespace = "{urn:oasis:names:tc:SAML:2.0:assertion}" role_name = "https://aws.amazon.com/SAML/Attributes/Role" for attr in root.iter(namespace + "Attribute"): if attr.get("Name") == role_name: for val in attr.iter(namespace + "AttributeValue"): aws_roles.append(val.text) # Note the format of the attribute value should be role_arn, # principal_arn but lots of blogs list it as principal_arn,role_arn so # let's reverse them if needed aws_roles = [role.split(",") for role in aws_roles] aws_roles = [(role, principal) for role, principal in aws_roles] self.all_roles = aws_roles def get_role(self): """ Prompt the user to choose a Role ARN if more than one is available """ if not self.all_roles: self.get_arns() if not self.all_roles: raise Exception("No roles found") # If I have more than one role, ask the user which one they want, # otherwise just proceed self.role_arn, self.principal_arn = user_role_prompt( self.all_roles, saved_choice=self.config.get("role_arn"), ) def assume_role(self): """Perform an AWS SAML role assumption""" if not self.role_arn: self.get_role() res = self.sts_client.assume_role_with_saml( RoleArn=self.role_arn, PrincipalArn=self.principal_arn, SAMLAssertion=self.saml.saml_response, DurationSeconds=self.duration_seconds) self.credentials = res def save_credentials(self): """Save the AWS Federation credentials to disk""" if not self.credentials: self.assume_role() creds = self.credentials["Credentials"] cred_file = self._initialize_credentials() cred_config = configparser.ConfigParser() cred_config.read(cred_file) # Update with new credentials name = self.credentials["AssumedRoleUser"]["Arn"] if name.startswith("arn:aws:sts::"): name = name[13:] name = name.replace(":assumed-role", "") if "profile" in self.config: name = self.config["profile"] cred_config[name] = { "aws_access_key_id": creds["AccessKeyId"], "aws_secret_access_key": creds["SecretAccessKey"], "aws_session_token": creds["SessionToken"] } with open(cred_file, "w") as cred_config_file: cred_config.write(cred_config_file) print("Credentials cached in '{}'".format(cred_file)) print("Expires at {}".format(creds["Expiration"])) print("Use aws cli with --profile " + name) # Reset state in the case of another transaction self.credentials = None def _initialize_credentials(self): cred_file = os.environ.get('AWS_SHARED_CREDENTIALS_FILE', None) if cred_file is None: cred_file = os.path.expanduser("~/.aws/credentials") cred_dir = os.path.expanduser("~/.aws/") if not os.path.exists(cred_dir): os.makedirs(cred_dir) return cred_file
def __init__(self, connection, configuration): auth = configuration.get('auth') self.client = OneLoginClient(auth.get('clientId'), auth.get('clientSecret'), connection['region'])
# Uncomment to enable low level debugging # logging.basicConfig(level=logging.DEBUG) ########################################################################## # Get the credentials from the user if not email: print "Email: ", email = raw_input() else: print "Using: %s" % email password = getpass.getpass() print "OTP Code (MFA): ", otp_code = raw_input() print '' client = OneLoginClient(onelogin_client_id, onelogin_client_secret, onelogin_region) onelogin_response = client.get_saml_assertion(email, password, app_id, onelogin_subdomain) saml = None if onelogin_response is None: print('Failed logging in (password was incorrect)') exit(1) elif onelogin_response and onelogin_response.type == "success": state_token = onelogin_response.mfa.state_token device_id = onelogin_response.mfa.devices[0].id mfa_response = client.get_saml_assertion_verifying(app_id, device_id, state_token,
def pam_sm_authenticate(pamh, _flags, _argv): ''' Authenticates a user via onelogin email/username and OTP ''' # Load config file and build access token try: config_dpath = os.path.dirname(os.path.realpath(__file__)) config_fpath = os.path.join(config_dpath, 'onepam.json') config_fd = open(config_fpath, 'r') config = config_fd.read() config_fd.close() config = json.loads(config) except Exception as error: logit('Error loading configuration: %s' % error) return pamh.PAM_AUTH_ERR # Create a client to OneLogin with the config details client = OneLoginClient(config['client_id'], config['client_secret'], config['region']) if not client.get_access_token(): logit('Error authenticating with onelogin') return pamh.PAM_AUTH_ERR # Prompt user for needed information try: # Unix user (aka, the onelogin role - usually passed via ssh, but may need to prompt) rolename = pamh.get_user(None) if rolename is None: return pamh.PAM_USER_UNKNOWN # OneLogin email/user email_or_user = pamh.conversation( pamh.Message(pamh.PAM_PROMPT_ECHO_ON, 'OneLogin email or user: '******'OneLogin password: '******'*') >= 0: logit('Invalid user "%s"' % email_or_user) return pamh.PAM_AUTH_ERR # Build uniform request object from config file uniform_timer = UniformTimer(config['request_duration_secs']) # Make all email/user/checks have a uniform duration (start) uniform_timer.start() # Query emails emails = client.get_users({'email': email_or_user}) if emails is None: logit('Error querying email "%s"' % email_or_user) return pamh.PAM_AUTH_ERR # Query users users = client.get_users({'username': email_or_user}) if users is None: logit('Error querying user "%s"' % email_or_user) return pamh.PAM_AUTH_ERR # Search emails first then users user = None for entry in emails: if entry.email == email_or_user: user = entry break if not user: for entry in users: if entry.username == email_or_user: user = entry break # Make all email/user/checks have a uniform duration (finish) uniform_timer.finish() # Check password (uncomment to add password auth) # uniform_timer.start() # token = client.create_session_login_token({'username_or_email': email_or_user, # 'password': password, # 'subdomain': config['subdomain']}) # if token is None: # logit('Invalid username or password "%s"' % email_or_user) # user = None # uniform_timer.finish() # Valid user - query otp factors uniform_timer.start() device = None if user: factors = client.get_enrolled_factors(user.id) # Error querying devices - log and set user to None if factors is None: logit('Error querying enrolled factors for user "%s"' % email_or_user) user = None # Find a factor for user (default is preferred, will use first listed otherwise) else: for factor in factors: # Only care about usable factors if not factor.active: continue # Device is the default device - set and break if factor.default: device = factor break # Device isn't default, but some device is better than no device if device is None: device = factor else: logit('Invalid email/user "%s"' % email_or_user) uniform_timer.finish() # User has a valid otp factor - activate it uniform_timer.start() state_token = None if user is not None and device is not None: # Grab state token state_token = device.state_token # Only trigger if device needs it if device.needs_trigger: activation = client.activate_factor(user.id, device.id) if not activation: logit('Error activating factor id %d for user "%s"' % (device.id, email_or_user)) else: state_token = activation.state_token # No active/default factor - log and set user to None else: logit('No valid otp factor found for user "%s"' % email_or_user) uniform_timer.finish() # Prompt for otp try: otp_token = pamh.conversation( pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, 'OTP: ')).resp if not otp_token: otp_token = None except pamh.exception as error: return error.pam_result # Verify otp uniform_timer.start() result = False if user is not None and device is not None: result = client.verify_factor(user.id, device.id, otp_token=otp_token, state_token=state_token) uniform_timer.finish() # Error verifying otp - log if result is None: logit('Error verifying factor id %d for user "%s"' % (device.id, email_or_user)) return pamh.PAM_AUTH_ERR # Invalid otp - log if not result: logit('Invalid otp auth for user "%s"' % email_or_user) return pamh.PAM_AUTH_ERR # Check if user is authorized to login to provided role if config['user_roles'].get(rolename, None) not in user.get_role_ids(): logit('User "%s" is not authorized to login as "%s"' % (email_or_user, rolename)) return pamh.PAM_AUTH_ERR # Auth'd login return pamh.PAM_SUCCESS
class OneloginAWS(object): def __init__(self, config, args): self.sts_client = boto3.client("sts") self.config = config self.args = args self.token = None self.account_id = None self.saml = None self.all_roles = None self.role_arn = None self.principal_arn = None self.credentials = None self.username = self.args.username self.password = None base_uri_parts = self.config['base_uri'].split('.') self.ol_client = OneLoginClient( self.config['client_id'], self.config['client_secret'], base_uri_parts[1], ) def get_saml_assertion(self): if not self.username: self.username = input("Onelogin Username: "******"Onelogin Password: "******"{}. {}".format(i + 1, device.type)) device_num = input("Which OTP Device? ") device = devices[int(device_num) - 1] else: device = devices[0] otp_token = input("OTP Token: ") saml_resp = self.ol_client.get_saml_assertion_verifying( self.config['aws_app_id'], device.id, saml_resp.mfa.state_token, otp_token) self.saml = saml_resp def get_arns(self): if not self.saml: self.get_saml_assertion() # Parse the returned assertion and extract the authorized roles aws_roles = [] root = ET.fromstring(base64.b64decode(self.saml.saml_response)) namespace = "{urn:oasis:names:tc:SAML:2.0:assertion}" role_name = "https://aws.amazon.com/SAML/Attributes/Role" for attr in root.iter(namespace + "Attribute"): if attr.get("Name") == role_name: for val in attr.iter(namespace + "AttributeValue"): aws_roles.append(val.text) # Note the format of the attribute value should be role_arn, # principal_arn but lots of blogs list it as principal_arn,role_arn so # let's reverse them if needed aws_roles = [role.split(",") for role in aws_roles] aws_roles = [(role, principal) for role, principal in aws_roles] self.all_roles = aws_roles def get_role(self): if not self.all_roles: self.get_arns() if not self.all_roles: raise Exception("No roles found") selected_role = None # If I have more than one role, ask the user which one they want, # otherwise just proceed if len(self.all_roles) > 1: ind = 0 for role, principal in self.all_roles: print("[{}] {}".format(ind, role)) ind += 1 while selected_role is None: choice = int(input("Role Number: ")) if choice in range(len(self.all_roles)): selected_role = choice else: print("Invalid role index, please try again") else: selected_role = 0 self.role_arn, self.principal_arn = self.all_roles[selected_role] def assume_role(self): if not self.role_arn: self.get_role() res = self.sts_client.assume_role_with_saml( RoleArn=self.role_arn, PrincipalArn=self.principal_arn, SAMLAssertion=self.saml.saml_response) self.credentials = res def save_credentials(self): if not self.credentials: self.assume_role() creds = self.credentials["Credentials"] cred_file = os.path.expanduser("~/.aws/credentials") cred_dir = os.path.expanduser("~/.aws/") if not os.path.exists(cred_dir): os.makedirs(cred_dir) cred_config = configparser.ConfigParser() cred_config.read(cred_file) # Update with new credentials name = self.credentials["AssumedRoleUser"]["Arn"] if name.startswith("arn:aws:sts::"): name = name[13:] name = name.replace(":assumed-role", "") if self.config.get("profile"): name = self.config["profile"] elif self.args.profile != "": name = self.args.profile cred_config[name] = { "aws_access_key_id": creds["AccessKeyId"], "aws_secret_access_key": creds["SecretAccessKey"], "aws_session_token": creds["SessionToken"] } with open(cred_file, "w") as cred_config_file: cred_config.write(cred_config_file) print("Credentials cached in '{}'".format(cred_file)) print("Expires at {}".format(creds["Expiration"])) print("Use aws cli with --profile " + name) # Reset state in the case of another transaction self.token = None self.credentials = None @staticmethod def generate_config(): print("Configure Onelogin and AWS\n\n") config = configparser.ConfigParser() config.add_section("default") default = config["default"] default["base_uri"] = user_choice( "Pick a Onelogin API server:", ["https://api.us.onelogin.com/", "https://api.eu.onelogin.com/"]) print("\nOnelogin API credentials. These can be found at:\n" "https://admin.us.onelogin.com/api_credentials") default["client_id"] = input("Onelogin API Client ID: ") default["client_secret"] = input("Onelogin API Client Secret: ") print("\nOnelogin AWS App ID. This can be found at:\n" "https://admin.us.onelogin.com/apps") default["aws_app_id"] = input("Onelogin App ID for AWS: ") print("\nOnelogin subdomain is 'company' for login domain of " "'comany.onelogin.com'") default["subdomain"] = input("Onelogin subdomain: ") config_fn = os.path.expanduser("~/{}".format(CONFIG_FILENAME)) with open(config_fn, "w") as config_file: config.write(config_file) print("Configuration written to '{}'".format(config_fn)) @staticmethod def load_config(): try: config_fn = os.path.expanduser("~/{}".format(CONFIG_FILENAME)) config = configparser.ConfigParser() config.read_file(open(config_fn)) return config except FileNotFoundError: return None
class OneloginAWS(object): """ Handles the authentication between OneLogin SAML Assertion and the AWS identity federation """ def __init__(self, config: Section): self.sts_client = boto3.client("sts") self.config = config self.saml = None self.all_roles = None self.role_arn = None self.credentials = None self.duration_seconds = int(config['duration_seconds']) self.user_credentials = UserCredentials(config) self.mfa = MFACredentials(config) base_uri_parts = self.config['base_uri'].split('.') self.ol_client = OneLoginClient( self.config['client_id'], self.config['client_secret'], base_uri_parts[1], ) def get_saml_assertion(self): """ Retrieve users credentials and get the SAML assertion from Onelogin, based on the users choice of AWS account to log into """ self.user_credentials.load_credentials() saml_resp = self.ol_client.get_saml_assertion( username_or_email=self.user_credentials.username, password=self.user_credentials.password, app_id=self.config['aws_app_id'], subdomain=self.config['subdomain'], ip_address=self.get_ip_address(), ) if saml_resp is None: raise Exception("Onelogin Error: '{error}' '{desc}'".format( error=self.ol_client.error, desc=self.ol_client.error_description )) if saml_resp.mfa: if not self.mfa.ready(): self.mfa.select_device(saml_resp.mfa.devices) if not self.mfa.has_otp: self.mfa.prompt_token() saml_resp = self.ol_client.get_saml_assertion_verifying( self.config['aws_app_id'], self.mfa.device.id, saml_resp.mfa.state_token, self.mfa.otp ) self.saml = saml_resp def get_ip_address(self) -> Optional[str]: """ Get the client IP address. Uses either the `ip_address` in config, or if `auto_determine_ip_address` is specified in config, the ipify service is used to dynamically lookup the IP address. """ # if ip address has been hard coded in config file, use that ip_address = self.config.get('ip_address') if ip_address is not None: return ip_address # if auto determine is enabled, use ipify to lookup the ip if self.config.auto_determine_ip_address: ip_address = ipify.get_ip() return ip_address def get_arns(self): """Extract the IAM Role ARNs from the SAML Assertion""" if not self.saml: self.get_saml_assertion() # Parse the returned assertion and extract the authorized roles aws_roles = [] root = ElementTree.fromstring( base64.b64decode(self.saml.saml_response)) namespace = "{urn:oasis:names:tc:SAML:2.0:assertion}" role_name = "https://aws.amazon.com/SAML/Attributes/Role" for attr in root.iter(namespace + "Attribute"): if attr.get("Name") == role_name: for val in attr.iter(namespace + "AttributeValue"): aws_roles.append(val.text) # Note the format of the attribute value should be role_arn, # principal_arn but lots of blogs list it as principal_arn,role_arn so # let's reverse them if needed aws_roles = [role.split(",") for role in aws_roles] aws_roles = [(role, principal) for role, principal in aws_roles] self.all_roles = aws_roles def get_role(self): """ Prompt the user to choose a Role ARN if more than one is available """ if not self.all_roles: self.get_arns() if not self.all_roles: raise Exception("No roles found") # If I have more than one role, ask the user which one they want, # otherwise just proceed self.role_arn, self.principal_arn = user_role_prompt( self.all_roles, saved_choice=self.config.get("role_arn"), ) def assume_role(self): """Perform an AWS SAML role assumption""" if not self.role_arn: self.get_role() if self.config['region']: self.sts_client = boto3.client("sts", region_name=self.config["region"]) res = self.sts_client.assume_role_with_saml( RoleArn=self.role_arn, PrincipalArn=self.principal_arn, SAMLAssertion=self.saml.saml_response, DurationSeconds=self.duration_seconds ) self.credentials = res def save_credentials(self): """Save the AWS Federation credentials to disk""" if not self.credentials: self.assume_role() creds = self.credentials["Credentials"] cred_file = self._initialize_credentials() cred_config = configparser.ConfigParser() cred_config.read(cred_file) # Update with new credentials name = self.credentials["AssumedRoleUser"]["Arn"] m = re.search('(arn\:aws([\w-]*)\:sts\:\:)(.*)', name) if m is not None: name = m.group(3) name = name.replace(":assumed-role", "") if "profile" in self.config: name = self.config["profile"] # Initialize the profile block if it is undefined if name not in cred_config: cred_config[name] = {} # Set each value specifically instead of overwriting the entire # profile block in case they have other parameters defined cred_config[name]['aws_access_key_id'] = creds["AccessKeyId"] cred_config[name]['aws_secret_access_key'] = creds["SecretAccessKey"] cred_config[name]['aws_session_token'] = creds["SessionToken"] # Set region for this profile if passed in via configuration if self.config['region']: cred_config[name]['region'] = self.config['region'] with open(cred_file, "w") as cred_config_file: cred_config.write(cred_config_file) print("Credentials cached in '{}'".format(cred_file)) print("Expires at {}".format(creds["Expiration"])) print("Use aws cli with --profile " + name) # Reset state in the case of another transaction self.credentials = None def _initialize_credentials(self): cred_file = os.environ.get('AWS_SHARED_CREDENTIALS_FILE', None) if cred_file is None: cred_file = os.path.expanduser("~/.aws/credentials") cred_dir = os.path.expanduser("~/.aws/") if not os.path.exists(cred_dir): os.makedirs(cred_dir) return cred_file