Skip to content

Commit

Permalink
Merge branch 'main' into feature/profile-expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
ruhulio committed Dec 19, 2023
2 parents fc7b053 + 5b46a18 commit 46fd4a7
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 89 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Tokendito

<p align="center">
<img src="https://raw.githubusercontent.com/dowjones/tokendito/main/docs/tokendito.png"/>
</p>
Expand Down Expand Up @@ -26,11 +28,19 @@ tokens into your local `~/.aws/credentials` file.

See [Releases](https://github.com/dowjones/tokendito/releases) for a detailed Changelog.

### Tokendito 2.4.0

Version 2.4.0 of Tokendito introduces the following new features:

- Add support for Okta question MFA.
- Many bug fixes and contributions.

### Tokendito 2.3.0

Version 2.3.0 of Tokendito introduces the following new features:

- Basic OIE support while forcing Classic mode.
- Support for saving the Device Token ID for later reuse.
- Misc bug fixes

Note: This feature currently works with locally enabled OIE organizations, but it does not for Organizations with chained Authentication in mixed OIE/Classic environments.
Expand Down
7 changes: 4 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ tokendito --profile engineer
usage: tokendito [-h] [--version] [--configure] [--username OKTA_USERNAME] [--password OKTA_PASSWORD] [--profile USER_CONFIG_PROFILE] [--config-file USER_CONFIG_FILE]
[--loglevel {DEBUG,INFO,WARN,ERROR}] [--log-output-file USER_LOG_OUTPUT_FILE] [--aws-config-file AWS_CONFIG_FILE] [--aws-output AWS_OUTPUT]
[--aws-profile AWS_PROFILE] [--aws-region AWS_REGION] [--aws-role-arn AWS_ROLE_ARN] [--aws-shared-credentials-file AWS_SHARED_CREDENTIALS_FILE]
[--okta-org OKTA_ORG | --okta-tile OKTA_TILE] [--okta-mfa OKTA_MFA] [--okta-mfa-response OKTA_MFA_RESPONSE] [--use-device-token] [--use-profile-expiration] [--quiet]
[--okta-org OKTA_ORG | --okta-tile OKTA_TILE] [--okta-client-id OKTA_CLIENT_ID] [--okta-mfa OKTA_MFA] [--okta-mfa-response OKTA_MFA_RESPONSE]
[--use-device-token] [--use-profile-expiration] [--quiet]
Gets an STS token to use with the AWS CLI and SDK.
Expand Down Expand Up @@ -111,9 +112,9 @@ options:
Okta tile URL to use.
--okta-client-id OKTA_CLIENT_ID
For OIE enabled Orgs this sets the Okta client ID to replace the value found by tokendito. It is used in the authorize code flow.
--okta-mfa OKTA_MFA Sets the MFA method
--okta-mfa OKTA_MFA Sets the MFA method. You can also use the TOKENDITO_OKTA_MFA environment variable.
--okta-mfa-response OKTA_MFA_RESPONSE
Sets the MFA response to a challenge
Sets the MFA response to a challenge. You can also use the TOKENDITO_OKTA_MFA_RESPONSE environment variable.
--use-device-token Use device token across sessions
--use-profile-expiration
Use profile expiration to bypass re-authenticating
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exclude_lines = [
"break",
"except KeyboardInterrupt:",
"if __name__ == .__main__.:",
"if __package__ is None:",
"if not __package__:",
"logger.debug",
"pragma: no cover",
"print..Invalid input, try again...",
Expand Down
44 changes: 38 additions & 6 deletions tests/unit/test_okta.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def test_bad_session_token(mocker, sample_json_response, sample_headers):
{"_embedded": {"factor": {"factorType": "push"}}},
345,
), # Changed expected value to 2
("OKTA", 321, {"_embedded": {"factor": {"factorType": "question"}}}, 321),
("GOOGLE", 456, {"_embedded": {"factor": {"factorType": "sms"}}}, 456),
],
)
Expand Down Expand Up @@ -543,13 +544,16 @@ def test_create_authz_cookies():
"""Test create_authz_cookies."""
from tokendito import okta

pytest_oauth2_session_data = {"state": "pyteststate"}
pytest_oauth2_session_data = {"state": "pyteststate", "nonce": "pytestnonce"}

pytest_oauth2_config = {
"client_id": "123",
"org": "acme",
"authorization_endpoint": "pytesturl",
"token_endpoint": "tokeneurl",
"nonce": "pytest",
"issuer": "pytest",
"ln": "pytest",
}
assert okta.create_authz_cookies(pytest_oauth2_config, pytest_oauth2_session_data) is None
from tokendito import okta
Expand Down Expand Up @@ -637,6 +641,9 @@ def test_get_authorize_code():
response.url = "https://example.com?code=pytest"
assert okta.get_authorize_code(response, "sessionToken") == "pytest"

response.url = "https//example.com?error=login_required"
assert okta.get_authorize_code(response, None) is None


def test_authorization_code_enabled():
"""Test authorization_code_enabled."""
Expand Down Expand Up @@ -687,12 +694,33 @@ def test_authorize_request(mocker):
assert okta.authorize_request(pytest_oauth2_config, pytest_oauth2_session_data) == "pytest"


def test_generate_oauth2_session_data():
"""Test generate_oauth2_session_data."""
def test_get_nonce(mocker):
"""Test get_nonce."""
from tokendito import okta

response = Mock()
response.text = """
<html>
<script nonce="PYTEST_NONCE" type="text/javascript">'
</html>
"""
mocker.patch.object(HTTP_client, "get", return_value=response)

assert okta.get_nonce("https://acme.com") == "PYTEST_NONCE"

response.text = "nonce-non-present"
mocker.patch.object(HTTP_client, "get", return_value=response)
assert okta.get_nonce("https://acme.com") is None


def test_get_oauth2_session_data(mocker):
"""Test get_oauth2_session_data."""
from tokendito import okta

mocker.patch("tokendito.okta.get_nonce", return_value="ABC")
assert (
okta.generate_oauth2_session_data("acme.com")["redirect_uri"] == "acme.com/enduser/callback"
okta.get_oauth2_session_data("https://acme.com")["redirect_uri"]
== "https://acme.com/enduser/callback"
)


Expand All @@ -709,7 +737,9 @@ def test_get_oauth2_configuration(mocker):
"grant_types_supported": "authorization_code",
"request_parameter_supported": "pytest",
}
pytest_config = Config(okta={"client_id": "test_client_id", "org": "acme"})
pytest_config = Config(
okta={"client_id": "test_client_id", "org": "acme", "username": "pytest"}
)
mocker.patch.object(HTTP_client, "get", return_value=response)
assert okta.get_oauth2_configuration(pytest_config)["org"] == "acme"

Expand Down Expand Up @@ -740,6 +770,8 @@ def test_validate_oauth2_configuration():
"scopes_supported": "pytest",
"response_types_supported": "code",
"request_parameter_supported": "pytest",
"ln": "pytest",
"nonce": "pytest",
}
assert okta.validate_oauth2_configuration(pytest_oauth2_config) is None

Expand Down Expand Up @@ -883,7 +915,7 @@ def test_access_control(mocker):

mocker.patch("tokendito.okta.oie_enabled", return_value=True)
mocker.patch("tokendito.okta.get_oauth2_configuration", return_value=None)
mocker.patch("tokendito.okta.generate_oauth2_session_data", return_value=None)
mocker.patch("tokendito.okta.get_oauth2_session_data", return_value=None)
mocker.patch("tokendito.okta.create_authz_cookies", return_value=None)
mocker.patch("tokendito.okta.idp_authenticate", return_value=None)
mocker.patch("tokendito.okta.idp_authorize", return_value=None)
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ def test_get_username(mocker):
assert val == "pytest_patched"


def test_get_password(mocker):
def test_get_secret_input(mocker):
"""Test whether data sent is the same as data returned."""
from tokendito import user

mocker.patch("tokendito.user.tty_assertion", return_value=True)
mocker.patch("tokendito.user.getpass", return_value="pytest_patched")
val = user.get_password()
val = user.get_secret_input()

assert val == "pytest_patched"

Expand Down
2 changes: 1 addition & 1 deletion tokendito/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# vim: set filetype=python ts=4 sw=4
# -*- coding: utf-8 -*-
"""Tokendito module initialization."""
__version__ = "2.3.3"
__version__ = "2.4.0"
__title__ = "tokendito"
__description__ = "Get AWS STS tokens from Okta SSO"
__long_description_content_type__ = "text/markdown"
Expand Down
2 changes: 1 addition & 1 deletion tokendito/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def main(args=None): # needed for console script
"""Packge entry point."""
if __package__ is None:
if not __package__:
import os.path

path = os.path.dirname(os.path.dirname(__file__))
Expand Down
2 changes: 1 addition & 1 deletion tokendito/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def authenticate_to_roles(config, urls):
saml_xml = okta.extract_saml_response(saml_response_string)
if not saml_xml:
state_token = okta.extract_state_token(saml_response_string)
if "Extra Verification" in saml_response_string and state_token:
if state_token:
logger.info(f"Step-Up authentication required for {url}.")
if okta.step_up_authenticate(config, state_token):
return authenticate_to_roles(config, urls)
Expand Down
6 changes: 6 additions & 0 deletions tokendito/http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def get(self, url, params=None, headers=None, allow_redirects=True):

def post(self, url, data=None, json=None, headers=None, params=None, return_json=False):
"""Perform a POST request."""
response = None
logger.debug(f"POST to {url}")
try:
response = self.session.post(url, data=data, json=json, params=params, headers=headers)
Expand All @@ -95,6 +96,11 @@ def post(self, url, data=None, json=None, headers=None, params=None, return_json
return response
except requests.RequestException as e:
logger.error(f"Error during POST request to {url}. Error: {e}")
if response:
logger.debug(f"Response Headers: {response.headers}")
logger.debug(f"Response Text: {response.text}")
else:
logger.debug("No response received")
sys.exit(1)
except Exception as err:
logger.error(f"The post request to {url} failed with {err}")
Expand Down
Loading

0 comments on commit 46fd4a7

Please sign in to comment.