def default(self, obj): if isinstance(obj, datetime): return obj.strftime('dt(%Y-%m-%dT%H:%M:%SZ)') elif isinstance(obj, object): no_nulls = Utils.remove_nulls(obj.__dict__) formatted = Utils.replace_alt_names(obj, no_nulls) return formatted else: return JSONEncoder.default(self, obj)
def oidc_discovery(self): #headers = {} #request = {} #params = {} url_path = '/.well-known/openid-configuration' response = ApiClient.get_path(self, url_path, request=None, params=None) self.discovery = Utils.deserialize(response.text, AuthResult) return Utils.deserialize(response.text, AuthResult)
def authenticate(self, username, password, relay_state=None, response_type=None, force_mfa=None, context=None): """Begin the authentication process with a username and password :param username: user's username :type username: str :param password: user's password :type password: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :param response_type: the type of session to return (session_token or session_token_url usually) :type response_type: str :param force_mfa: whether to force mfa even if the auth is exempt :type force_mfa: bool :param context: contextual info about the auth request like ip and location :type context: Context :rtype: AuthResult """ request = { 'username': username, 'password': password, 'relayState': relay_state, 'context': context } params = { 'force_mfa': force_mfa, 'response_type': response_type } response = self.post_path('/', request, params=params) return Utils.deserialize(response.text, AuthResult)
def get_users(self, limit=None, q=None, filter_string=None, search_string=None): """Get a list of Users :param limit: maximum number of users to return :type limit: int or None :param q: string to search users' first names, last names, and emails :type q: str or None :param filter_string: string to filter users :type filter_string: str or None :param search_string: string to search users :type search_string: str or None :rtype: list of User """ params = { 'limit': limit, 'q': q, 'filter': filter_string, 'search': search_string, } response = self.get_path('/', params=params) return Utils.deserialize(response.text, self.user_model)
def enroll_factor(self, state_token, factor_type, provider, profile, relay_state=None): """Enroll in an MFA factor during the auth flow. Usually only encountered if MFA is required for authentication :param state_token: current state token from the previous AuthResult :type state_token: str :param factor_type: type of factor (sms, token, question, token:software:totp, token:hardware etc) :type factor_type: str :param provider: factor provider (OKTA, RSA, SYMANTEC, GOOGLE etc) :type provider: str :param profile: factor profile that depends on the factor type :type profile: FactorProfile :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ request = { 'stateToken': state_token, 'factorType': factor_type, 'provider': provider, 'profile': profile, 'relayState': relay_state } response = self.post_path('/factors', request) return Utils.deserialize(response.text, AuthResult)
def enroll_factor(self, state_token, factor_type, provider, profile, relay_state=None): """Enroll in an MFA factor during the auth flow. Usually only encountered if MFA is required for authentication :param state_token: current state token from the previous AuthResult :type state_token: str :param factor_type: type of factor (sms, token, question, token:software:totp, token:hardware etc) :type factor_type: str :param provider: factor provider (OKTA, RSA, SYMANTEC, GOOGLE etc) :type provider: str :param profile: factor profile that depends on the factor type :type profile: FactorProfile :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ request = { 'stateToken': state_token, 'factorType': factor_type, 'provider': provider, 'profile': profile, 'relayState': relay_state } response = ApiClient.post_path(self, '/factors', request) return Utils.deserialize(response.text, AuthResult)
def test_user_applinks_deserialization(self): json_str = ''' [{ "id": "auc9pp3udhKTBhNyS0h7", "label": "Application", "linkUrl": "https://example.okta.com/0oa9pp3udcBnjYs3E0h7/1234", "logoUrl": "https://example.okta.com/assets/img/logos/logo.png", "appName": "application", "appInstanceId": "0oa9pp3udcBnjYs3E0h7", "appAssignmentId": "0ua9pp3udgbxdDted0h7", "credentialsSetup": false, "hidden": false, "sortOrder": 0 }, { "id": "1od6bbdxlpb1BgDTi1h7", "label": "Another application", "linkUrl": "https://example.okta.com/0oa9pp3udcBnjYs3E0h7/5678", "logoUrl": "https://example.okta.com/assets/img/logos/logo.png", "appName": "another_application", "appInstanceId": "1od7bbdxnpb2BgDTi1h7", "appAssignmentId": "1od7bbdxnpb2BgDTi1h7", "credentialsSetup": true, "hidden": true, "sortOrder": 1 }] ''' result = Utils.deserialize(json_str, AppLinks) self.assertEqual(len(result), 2, "Cannot deserialize nested lists of objects") serialized = json.dumps(result, cls=Serializer) self.assertTrue("1od6bbdxlpb1BgDTi1h7" in serialized, "Nested lists aren't serialized properly")
def change_recovery_question(self, uid, password, question, answer): """Changes a user's recovery question & answer by validating the user's current password :param uid: the target user id :type uid: str :param password: the user's current password :type password: str :param question: the new recovery question :type question: str :param answer: the answer to the new recovery question :type answer: str """ data = { 'password': { 'value': password }, 'recovery_question': { 'question': question, 'answer': answer } } response = ApiClient.post_path( self, '/{0}/credentials/change_recovery_question'.format(uid), data) return Utils.deserialize(response.text, LoginCredentials)
def authenticate(self, username, password, relay_state=None, response_type=None, force_mfa=None, context=None): """Begin the authentication process with a username and password :param username: user's username :type username: str :param password: user's password :type password: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :param response_type: the type of session to return (session_token or session_token_url usually) :type response_type: str :param force_mfa: whether to force mfa even if the auth is exempt :type force_mfa: bool :param context: contextual info about the auth request like ip and location :type context: Context :rtype: AuthResult """ request = { 'username': username, 'password': password, 'relayState': relay_state, 'context': context } params = { 'force_mfa': force_mfa, 'response_type': response_type } response = ApiClient.post_path(self, '/', request, params=params) return Utils.deserialize(response.text, AuthResult)
def activate_factor(self, state_token, factor_id, passcode, relay_state=None): """Activate an MFA factor during the auth flow :param state_token: current state token from the previous AuthResult :type state_token: str :param factor_id: target factor id :type factor_id: str :param passcode: passcode required to activate the factor :type passcode: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ url_path = ('/api/v1/authn/factors\ /{0}/lifecycle/activate').format(factor_id) request = { 'stateToken': state_token, 'passCode': passcode, 'relayState': relay_state } response = ApiClient.post_path(self, url_path, request) return Utils.deserialize(response.text, AuthResult)
def default(self, obj): if isinstance(obj, datetime): return obj.strftime('dt(%Y-%m-%dT%H:%M:%SZ)') elif isinstance(obj, object): return Utils.remove_nulls(obj.__dict__) else: return JSONEncoder.default(self, obj)
def forgot_password(auth_client, username, factor_type): """Patched function to forgot password flow from okta.AuthClient """ request = {'username': username, 'factorType': factor_type} response = ApiClient.post_path(auth_client, '/recovery/password', request) return Utils.deserialize(response.text, AuthResult)
def unlock_account_answer(self, state_token, security_answer, relay_state=None): """Unlock an account during an authentication :param state_token: current state token from the previous AuthResult :type state_token: str :param security_answer: answer to the user's security question :type security_answer: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ url_path = '/api/v1/authn/recovery/answer' request = { 'stateToken': state_token, 'securityAnswer': security_answer, 'relayState': relay_state } response = ApiClient.post_path(self, url_path, request) return Utils.deserialize(response.text, AuthResult)
def auth_with_factor(self, state_token, factor_id, passcode, relay_state=None, remember_device=None): """Continue authentication with an MFA attempt :param state_token: current state token from the previous AuthResult :type state_token: str :param factor_id: target factor id :type factor_id: str :param passcode: passcode required for authenticating the factor :type passcode: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :param remember_device: whether to remember this device to avoid requiring MFA next time :type remember_device: bool :rtype: AuthResult """ url_path = '/api/v1/authn/factors/{0}/verify'.format(factor_id) request = { 'stateToken': state_token, 'passCode': passcode, 'relayState': relay_state } params = {'rememberDevice': remember_device} response = ApiClient.post_path(self, url_path, request, params=params) return Utils.deserialize(response.text, AuthResult)
def change_password(self, state_token, old_password, new_password, relay_state=None): """Change a user's password during an authentication flow :param state_token: current state token from the previous AuthResult :type state_token: str :param old_password: user's current password :type old_password: str :param new_password: user's desired password :type new_password: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ url_path = '/api/v1/authn/credentials/change_password' request = { 'stateToken': state_token, 'oldPassword': old_password, 'newPassword': new_password, 'relayState': relay_state } response = ApiClient.post_path(self, url_path, request) return Utils.deserialize(response.text, AuthResult)
def forgot_password_answer(self, state_token, security_answer, new_password, relay_state=None): """Answer the forgot password during an authentication flow :param state_token: current state token from the previous AuthResult :type state_token: str :param security_answer: answer to a user's security question :type security_answer: str :param new_password: user's desired password :type new_password: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :rtype: AuthResult """ url_path = '/api/v1/authn/recovery/answer' request = { 'stateToken': state_token, 'securityAnswer': security_answer, 'newPassword': new_password, 'relayState': relay_state } response = ApiClient.post_path(self, url_path, request) return Utils.deserialize(response.text, AuthResult)
def verify_factor(self, user_id, user_factor_id, activation_token=None, answer=None, passcode=None, next_passcode=None): """Verify an enrolled factor :param user_id: target user id :type user_id: str :param user_factor_id: target factor id :type user_factor_id: str :param activation_token: token required for activation :type activation_token: str :param answer: answer usually required for a question factor :type answer: str :param passcode: code required for verification :type passcode: str :param next_passcode: code usually required for TOTP :type next_passcode: str :return: """ request = { 'activationToken': activation_token, 'answer': answer, 'passCode': passcode, 'nextPassCode': next_passcode } response = ApiClient.post_path(self, '/{0}/factors/{1}/verify'.format(user_id, user_factor_id), request) return Utils.deserialize(response.text, FactorVerificationResponse)
def auth_with_factor(self, state_token, factor_id, passcode, relay_state=None, remember_device=None): """Continue authentication with an MFA attempt :param state_token: current state token from the previous AuthResult :type state_token: str :param factor_id: target factor id :type factor_id: str :param passcode: passcode required for authenticating the factor :type passcode: str :param relay_state: data that will persist for the lifetime of the authentication or recovery token :type relay_state: str or None :param remember_device: whether to remember this device to avoid requiring MFA next time :type remember_device: bool :rtype: AuthResult """ request = { 'stateToken': state_token, 'passCode': passcode, 'relayState': relay_state } params = { 'rememberDevice': remember_device } response = ApiClient.post_path(self, '/factors/{0}/verify'.format(factor_id), request, params=params) return Utils.deserialize(response.text, AuthResult)
def extend_session(self, id): """Extend a session's lifespan :param id: the target session id :rtype: Session """ response = ApiClient.put_path(self, '/{0}'.format(id), None) return Utils.deserialize(response.text, Session)
def validate_session(self, id): """Validate a session :param id: the target session id :rtype: Session """ response = ApiClient.get_path(self, '/{0}'.format(id)) return Utils.deserialize(response.text, Session)
def delete_group(self, gid): """Delete group by target id :param gid: the target group id :type gid: str :return: None """ response = ApiClient.delete_path(self, '/{0}'.format(gid)) return Utils.deserialize(response.text, UserGroup)
def create_group(self, group): """Create a group :param group: the data to create a group :type group: UserGroup :rtype: UserGroup """ response = ApiClient.post_path(self, '/', group) return Utils.deserialize(response.text, UserGroup)
def get_group(self, gid): """Get a single group :param gid: the group id :type gid: str :rtype: UserGroup """ response = ApiClient.get_path(self, '/{0}'.format(gid)) return Utils.deserialize(response.text, UserGroup)
def get_app_instance(self, id): """Get a single app :param id: the app id :type id: str :rtype: AppInstance """ response = ApiClient.get_path(self, '/{0}'.format(id)) return Utils.deserialize(response.text, AppInstance)
def get_user(self, uid): """Get a single user :param uid: the user id or login :type uid: str :rtype: User """ response = ApiClient.get_path(self, '/{0}'.format(uid)) return Utils.deserialize(response.text, User)
def reset_factors(self, uid): """Reset all user factors by target id :param uid: the target user id :type uid: str :return: None """ response = ApiClient.post_path(self, '/{0}/lifecycle/reset_factors'.format(uid)) return Utils.deserialize(response.text, User)
def get_assigned_users_to_app(self, aid): """Get assigned users to an application :param aid: the target app id :type aid: str :rtype: Array of AppUser """ response = ApiClient.get_path(self, '/{0}/users'.format(aid)) return Utils.deserialize(response.text, AppUser)
def get_group_users(self, gid): """Get the users of a group :param gid: the group id :type gid: str :rtype: User """ response = ApiClient.get_path(self, '/{0}/users'.format(gid)) return Utils.deserialize(response.text, User)
def create_app_instance(self, app_instance): """Create a app instance :param app_instance: the data to create a user :type app_instance: AppInstance :rtype: AppInstance """ response = ApiClient.post_path(self, '/', app_instance) return Utils.deserialize(response.text, AppInstance)
def get_lifecycle_factors(self, user_id): """Get enrolled factors for a user :param user_id: target user id :type user_id: str :rtype: list of Factor """ response = ApiClient.get_path(self, '/{0}/factors'.format(user_id)) return Utils.deserialize(response.text, Factor)
def get_user_applinks(self, uid): """Get applinks of a single user :param uid: the user id or login :type uid: str :rtype: AppLinks """ response = ApiClient.get_path(self, '/{0}/appLinks'.format(uid)) return Utils.deserialize(response.text, AppLinks)
def deactivate_org_factor(self, org_factor_id): """Deactivate OrgAuthFactor :param org_factor_id: target factor id :type org_factor_id: str :rtype: OrgAuthFactor """ response = ApiClient.post_path(self, '/factors/{0}/lifecycle/deactivate'.format(org_factor_id)) return Utils.deserialize(response.text, OrgAuthFactor)
def get_available_questions(self, user_id): """Get available factor questions :param user_id: target user id :type user_id: str :rtype: list of Question """ response = ApiClient.get_path(self, '/{0}/factors/questions'.format(user_id)) return Utils.deserialize(response.text, Question)
def get_factors_catalog(self, user_id): """Get available factors for a user :param user_id: target user id :type user_id: str :rtype: list of FactorCatalogEntry """ response = ApiClient.get_path(self, '/{0}/factors/catalog'.format(user_id)) return Utils.deserialize(response.text, FactorCatalogEntry)
def delete_user(self, uid): """Delete user by target id :param uid: the target user id :type uid: str :return: None """ response = ApiClient.delete_path(self, '/{0}'.format(uid)) return Utils.deserialize(response.text, User)
def get_user_groups(self, uid): """Get groups of a single user :param uid: the user id or login :type uid: str :rtype: Groups """ response = ApiClient.get_path(self, '/{0}/groups'.format(uid)) return Utils.deserialize(response.text, UserGroup)
def deactivate_user(self, uid): """Deactivate user by target id :param uid: the target user id :type uid: str :return: User """ response = ApiClient.post_path(self, '/{0}/lifecycle/deactivate'.format(uid)) return Utils.deserialize(response.text, User)
def get_user_admin_roles(self, uid): """Get roles for a single user :param uid: the user id :type uid: str :rtype: Array of Role """ response = self.get_path('/{0}/roles'.format(uid)) return Utils.deserialize(response.text, Role)
def unlock_user(self, uid): """Unlock user by target id :param uid: the target user id :type uid: str :return: User """ response = ApiClient.post_path(self, '/{0}/lifecycle/unlock'.format(uid)) return Utils.deserialize(response.text, User)
def update_factor_device(self, user_id, factor_device_request): """Update a factor device for a user :param user_id: target user id :type user_id: str :param factor_device_request: data to update the factor device :type factor_device_request: FactorDeviceRequest :rtype: FactorDevice """ response = ApiClient.post_path(self, '/{0}/factors/{1}'.format(user_id), factor_device_request) return Utils.deserialize(response.text, FactorDevice)
def get_factor(self, user_id, user_factor_id): """Get information about an enrolled factor :param user_id: target user id :type user_id: str :param user_factor_id: target factor id :type user_factor_id: str :rtype: Factor """ response = ApiClient.get_path(self, '/{0}/factors/{1}'.format(user_id, user_factor_id)) return Utils.deserialize(response.text, Factor)
def update_user_by_id(self, uid, user): """Update a user, defined by an id :param uid: the target user id :type uid: str :param user: the data to update the target user :type user: User :rtype: User """ response = ApiClient.put_path(self, '/{0}'.format(uid), user) return Utils.deserialize(response.text, User)
def activate_org_factor(self, org_factor_id, org_auth_factor=None): """Activate OrgAuthFactor :param org_factor_id: target factor id :type org_factor_id: str :param org_auth_factor: additional factor data :param org_auth_factor: OrgAuthFactor :rtype: OrgAuthFactor """ response = ApiClient.post_path(self, '/factors/{0}/lifecycle/activate'.format(org_factor_id), org_auth_factor) return Utils.deserialize(response.text, OrgAuthFactor)
def update_group_by_id(self, gid, group): """Update a group, defined by an id :param gid: the target group id :type gid: str :param group: the data to update the target group :type group: UserGroup :rtype: UserGroup """ response = ApiClient.put_path(self, '/{0}'.format(gid), group) return Utils.deserialize(response.text, UserGroup)