async def test_client_success_call_oauth(fs, monkeypatch): oauth_client = Client({ "orgUrl": ORG_URL, "authorizationMode": "PrivateKey", "clientId": CLIENT_ID, "scopes": SCOPES, "privateKey": PRIVATE_KEY }) request_executor = oauth_client.get_request_executor() monkeypatch.setattr(OAuth, 'get_access_token', mocks.mock_access_token) req, err = await request_executor.create_request("GET", GET_USERS_CALL, {}, {}) req, res_details, resp_body, error = await oauth_client\ .get_request_executor().fire_request(req) assert error is None assert "User-Agent" in req["headers"] assert "Authorization" in req["headers"] assert req["headers"]["Authorization"].startswith("Bearer") assert res_details.status == 200 assert "application/json" in res_details.headers["Content-Type"] assert resp_body is not None
async def test_response_pagination_with_next(monkeypatch): ssws_client = Client({ "orgUrl": ORG_URL, "token": API_TOKEN }) req, error = await ssws_client.get_request_executor()\ .create_request("GET", GET_USERS_CALL + API_LIMIT, {}, {}) monkeypatch.setattr(RequestExecutor, 'fire_request', mocks.mock_GET_HTTP_Client_response_valid_with_next) result, error = await ssws_client.get_request_executor().execute(req) assert result.get_body() is not None assert result.has_next() monkeypatch.setattr(RequestExecutor, 'fire_request', mocks.mock_GET_HTTP_Client_response_valid) assert await result.next() is not None assert not result.has_next() with pytest.raises(StopAsyncIteration): await result.next()
def test_known_sign_on_mode(): response = copy.deepcopy(SAML_APP_RESP_DICT) config = { "orgUrl": "https://test_org.okta.com", "token": "test_token", "requestExecutor": MockRequestExecutor, } client = Client(config) # check list applications client._request_executor.set_response([response]) event_loop = asyncio.get_event_loop() result, resp, err = event_loop.run_until_complete( client.list_applications()) assert type(result[0]) == SamlApplication assert result[0].as_dict() == EXPECTED_SAML_APP_AS_DICT # check get application client._request_executor.set_response(response) event_loop = asyncio.get_event_loop() result, resp, err = event_loop.run_until_complete( client.get_application("test_id")) assert type(result) == SamlApplication assert result.as_dict() == EXPECTED_SAML_APP_AS_DICT
async def test_client_error_call_oauth(fs, monkeypatch): oauth_client = Client({ "orgUrl": ORG_URL, "authorizationMode": "PrivateKey", "clientId": CLIENT_ID, "scopes": SCOPES, "privateKey": PRIVATE_KEY + "Wrong one" }) monkeypatch.setattr(OAuth, 'get_access_token', mocks.mock_access_token) req, err = await oauth_client.get_request_executor()\ .create_request("GET", GET_USERS_CALL, {}, {}) req, res_details, resp_body, error = await oauth_client\ .get_request_executor().fire_request(req) parsed, error = HTTPClient.check_response_for_error( req["url"], res_details, resp_body) assert parsed is None assert res_details.status == 404 assert isinstance(error, HTTPError) assert error.message.startswith("HTTP 404")
async def test_no_x_reset_header(monkeypatch): client = Client(user_config=CLIENT_CONFIG) monkeypatch.setattr(HTTPClient, 'send_request', mocks.mock_GET_HTTP_Client_response_429_no_x_reset) monkeypatch.setattr(time, 'sleep', mocks.mock_pause_function) users, resp, error = await client.list_users() assert error is not None assert isinstance(error, Exception) assert error.args[0] == ERROR_MESSAGE_429_MISSING_DATE_X_RESET assert users is None assert resp is None
async def test_backoff_calculation(): client = Client(user_config=CLIENT_CONFIG) response_429 = (await mocks.mock_GET_HTTP_Client_response_429())[1] # ^ has a 1 second difference in retry and datetime # backoff should be 2 by Okta standards retry_limit_reset = float(response_429.headers["X-Rate-Limit-Reset"]) date_time = convert_date_time_to_seconds(response_429.headers["Date"]) backoff_time = client.get_request_executor().calculate_backoff( retry_limit_reset, date_time) assert (backoff_time == 2)
async def test_multiple_x_reset_headers(monkeypatch, mocker): client = Client(user_config=CLIENT_CONFIG) backoff_spy = mocker.spy(RequestExecutor, 'calculate_backoff') monkeypatch.setattr(HTTPClient, 'send_request', mocks.mock_GET_HTTP_Client_response_429_multi_x_reset) # the X Rate Limit Resets used are 1 sec and 2 sec after the Date header, # -> the min. one should be used (1 sec. after) and the backoff calculated # should be equal to 2 (by Okta standards) monkeypatch.setattr(time, 'sleep', mocks.mock_pause_function) users, resp, error = await client.list_users() assert error is not None assert users is None assert backoff_spy.spy_return == 2
async def test_response_headers(monkeypatch): ssws_client = Client({ "orgUrl": ORG_URL, "token": API_TOKEN }) req, error = await ssws_client.get_request_executor()\ .create_request("GET", GET_USERS_CALL + API_LIMIT, {}, {}) monkeypatch.setattr(RequestExecutor, 'fire_request', mocks.mock_GET_HTTP_Client_response_valid) result, error = await ssws_client.get_request_executor().execute(req) assert result.get_body() is not None assert result.get_headers() == mocks.MockHTTPResponseDetails().headers
def test_unknown_sign_on_mode(): response = copy.deepcopy(SAML_APP_RESP_DICT) response["signOnMode"] = "UNKNOWN_SIGN_ON_MODE" expected = copy.deepcopy(EXPECTED_SAML_APP_AS_DICT) expected["signOnMode"] = ApplicationSignOnMode("UNKNOWN_SIGN_ON_MODE") expected["settings"] = { "app": {}, "notifications": { "vpn": { "network": { "connection": "DISABLED", "exclude": [], "include": [] } } }, } config = { "orgUrl": "https://test_org.okta.com", "token": "test_token", "requestExecutor": MockRequestExecutor, } client = Client(config) # check list applications client._request_executor.set_response([response]) event_loop = asyncio.get_event_loop() result, resp, err = event_loop.run_until_complete( client.list_applications()) # verify if result fallbacks to generic Application assert type(result[0]) != SamlApplication assert type(result[0]) == Application assert result[0].as_dict() == expected # check get application client._request_executor.set_response(response) event_loop = asyncio.get_event_loop() result, resp, err = event_loop.run_until_complete( client.get_application("test_id")) # verify if result fallbacks to generic Application assert type(result) != SamlApplication assert type(result) == Application assert result.as_dict() == expected
async def test_req_timeout(monkeypatch): this_config = CLIENT_CONFIG.copy() this_config.update({"requestTimeout": 1}) client = Client(user_config=this_config) request = await mocks.mock_GET_HTTP_request() monkeypatch.setattr(HTTPClient, 'send_request', mocks.mock_GET_HTTP_Client_response_429) now = dt.datetime.now(dt.timezone.utc) now = now.replace(microsecond=0) two_sec_before = (now - dt.timedelta(seconds=2)).timestamp() a, b, c, error = await client.get_request_executor().fire_request_helper( request, 0, two_sec_before) assert isinstance(error, Exception) assert "Request Timeout exceeded." == error.args[0]
async def test_max_retries_no_timeout(monkeypatch, mocker): client = Client(user_config=CLIENT_CONFIG) query_params = {"limit": "1"} monkeypatch.setattr(HTTPClient, 'send_request', mocks.mock_GET_HTTP_Client_response_429) monkeypatch.setattr(time, 'sleep', mocks.mock_pause_function) http_spy = mocker.spy(HTTPClient, 'send_request') users, resp, error = await client.list_users(query_params) http_spy.assert_called() assert http_spy.call_count ==\ client.get_request_executor()._max_retries + 1 assert client.get_request_executor()._request_timeout == 0 assert isinstance(error, HTTPError) assert error is not None assert error.status == HTTPStatus.TOO_MANY_REQUESTS assert resp.get_status() == HTTPStatus.TOO_MANY_REQUESTS
async def test_client_error_call_SSWS(fs): ssws_client = Client({ "orgUrl": ORG_URL, "token": API_TOKEN + "wrong token" }) req, error = await ssws_client.get_request_executor()\ .create_request("GET", GET_USERS_CALL, {}, {}) req, res_details, resp_body, error = await ssws_client\ .get_request_executor().fire_request(req) parsed, error = HTTPClient.check_response_for_error( req["url"], res_details, resp_body) assert parsed is None assert res_details.status == 404 assert isinstance(error, HTTPError) assert error.message.startswith("HTTP 404")
async def test_response_pagination_with_next_not_starting_with_api(monkeypatch): ssws_client = Client(CLIENT_CONFIG) req, error = await ssws_client.get_request_executor() \ .create_request("GET", GET_OAUTH_CLIENTS_CALL + API_LIMIT, {}, {}) monkeypatch.setattr(RequestExecutor, 'fire_request', mocks.mock_GET_HTTP_Client_response_valid_with_next) result, error = await ssws_client.get_request_executor().execute(req) assert result.get_body() is not None assert result.has_next() assert result._next.startswith(GET_OAUTH_CLIENTS_CALL) monkeypatch.setattr(RequestExecutor, 'fire_request', mocks.mock_GET_HTTP_Client_response_valid) assert await result.next() is not None assert not result.has_next() with pytest.raises(StopAsyncIteration): await result.next()
async def test_list_supported_security_questions(self): # Instantiate Mock Client client = MockOktaClient(fs) client = Client() # Create User user_profile = models.UserProfile() user_profile.first_name = "John" user_profile.last_name = "Doe-List-Security-Question" user_profile.email = "*****@*****.**" user_profile.login = "******" create_user_req = models.CreateUserRequest({ "credentials": models.UserCredentials({ "password": models.PasswordCredential({"value": "Password150kta"}) }), "profile": user_profile }) created_user, _, err = await client.create_user(create_user_req) assert err is None assert isinstance(created_user, models.User) # Create and add factor sec_q_factor = models.SecurityQuestionUserFactor({ "factorType": models.FactorType.QUESTION, "provider": models.FactorProvider.OKTA, "profile": models.SecurityQuestionUserFactorProfile({ "question": "disliked_food", "answer": "lasagna" }) }) enrolled_factor, _, err = await \ client.enroll_factor(created_user.id, sec_q_factor) assert err is None assert isinstance(enrolled_factor, models.SecurityQuestionUserFactor) # List factor to validate users_factors, _, err = await client.list_factors(created_user.id) assert err is None assert len(users_factors) > 0 and len(users_factors) == 1 assert isinstance(users_factors[0], models.SecurityQuestionUserFactor) assert users_factors[0].factor_type == models.FactorType.QUESTION assert users_factors[0].id == sec_q_factor.id assert users_factors[0].profile.question ==\ sec_q_factor.profile.question assert users_factors[0].profile.answer == sec_q_factor.profile.answer assert users_factors[0].profile.question_text # List Security q's retrieved_questions, _, err = await \ client.list_supported_security_questions(created_user.id) assert err is None assert next( (question for question in retrieved_questions if question.profile.question == enrolled_factor.profile.question)) # Deactivate + delete user _, err = await client.deactivate_user(created_user.id) assert err is None _, err = await client.deactivate_or_delete_user(created_user.id) assert err is None
async def test_reset_factors(self): # Instantiate Mock Client client = MockOktaClient(fs) client = Client() # Create User user_profile = models.UserProfile() user_profile.first_name = "John" user_profile.last_name = "Doe-Reset-Factor" user_profile.email = "*****@*****.**" user_profile.login = "******" create_user_req = models.CreateUserRequest({ "credentials": models.UserCredentials({ "password": models.PasswordCredential({"value": "Password150kta"}) }), "profile": user_profile }) created_user, _, err = await client.create_user(create_user_req) assert err is None assert isinstance(created_user, models.User) # Create and add factor sms_factor = models.SmsUserFactor({ "profile": models.SmsUserFactorProfile({"phoneNumber": "+12345678901"}) }) enrolled_factor, _, err = await \ client.enroll_factor(created_user.id, sms_factor) assert err is None assert isinstance(enrolled_factor, models.SmsUserFactor) # Get factor to validate retrieved_user_factor, _, err = await client.get_factor( created_user.id, enrolled_factor.id) assert err is None assert isinstance(retrieved_user_factor, models.SmsUserFactor) assert retrieved_user_factor.id == enrolled_factor.id assert retrieved_user_factor.factor_type == models.FactorType.SMS assert retrieved_user_factor.profile.phone_number ==\ sms_factor.profile.phone_number # Reset factor _, err = await client.reset_factors(created_user.id) assert err is None # List factors to validate users_factors, _, err = await client.list_factors(created_user.id) assert err is None assert len(users_factors) == 0 # Deactivate + delete user _, err = await client.deactivate_user(created_user.id) assert err is None _, err = await client.deactivate_or_delete_user(created_user.id) assert err is None