Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable use of HOTP for authenticating #222

Merged
merged 5 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dds_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class DDSEndpoint:

# Authentication - user and project
ENCRYPTED_TOKEN = BASE_ENDPOINT + "/user/encrypted_token"
SECOND_FACTOR = BASE_ENDPOINT + "/user/second_factor"

# S3Connector keys
S3KEYS = BASE_ENDPOINT + "/s3/proj"
Expand Down
60 changes: 55 additions & 5 deletions dds_cli/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ def __authenticate_user(self):
message="Non-empty password needed to be able to authenticate."
)

# Project passed in to add it to the token. Can be None.
try:
response = requests.get(
dds_cli.DDSEndpoint.ENCRYPTED_TOKEN,
Expand All @@ -112,13 +111,64 @@ def __authenticate_user(self):
except simplejson.JSONDecodeError as err:
raise dds_cli.exceptions.ApiResponseError(message=str(err))

# Raise exceptions to log info if not ok response
if not response.ok:
message = response_json.get("message", "Unexpected error!")
if response.status_code == 401:
raise exceptions.AuthenticationError(message=message)
raise exceptions.AuthenticationError(
"Authentication failed, incorrect username and/or password."
)
else:
raise dds_cli.exceptions.ApiResponseError(
message=f"API returned an error: {response_json.get('message', 'Unknown Error!')}"
)

# Token received from API needs to be completed with a mfa timestamp
partial_auth_token = response_json.get("token")

# Verify 2fa email token
LOG.info(
"Please enter the one-time authentication code sent to your email address (leave empty to exit):"
)
done = False
while not done:
entered_one_time_code = rich.prompt.Prompt.ask("Authentication one-time code")
if entered_one_time_code == "":
raise exceptions.AuthenticationError(
message="Exited due to no one-time authentication code entered."
)

raise exceptions.ApiResponseError(message=message)
if not entered_one_time_code.isdigit():
LOG.info("Please enter a valid one-time code. It should consist of only digits.")
continue
if len(entered_one_time_code) != 8:
LOG.info(
f"Please enter a valid one-time code. It should consist of 8 digits (you entered {len(entered_one_time_code)} digits)."
)
continue

try:
response = requests.get(
dds_cli.DDSEndpoint.SECOND_FACTOR,
headers={"Authorization": f"Bearer {partial_auth_token}"},
json={"HOTP": entered_one_time_code},
timeout=dds_cli.DDSEndpoint.TIMEOUT,
)
response_json = response.json()
except requests.exceptions.RequestException as err:
raise exceptions.ApiRequestError(message=str(err)) from err

if response.ok:
# Step out of the while-loop
done = True
if not response.ok:
message = response_json.get("message", "Unexpected error!")
if response.status_code == 401:
try_again = rich.prompt.Confirm.ask(
"Second factor authentication failed, would you like to try again?"
)
if not try_again:
raise exceptions.AuthenticationError(message="Exited due to user choice.")
else:
raise exceptions.ApiResponseError(message=message)

# Get token from response
token = response_json.get("token")
Expand Down
2 changes: 1 addition & 1 deletion dds_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def format_api_response(response, key, magnitude=None, iec_standard=False):
response /= base
else:
# utilize the given magnitude
response /= base ** magnitude
response /= base**magnitude

if key == "Size":
unit = "B" # lock
Expand Down