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

Support SafeNet Trusted Access MFA #425

Merged
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ aws-adfs integrates with:
* SMS codes
* Phone call
* [Silverfort](https://www.silverfort.com/) MFA provider
* [Thales/SafeNet Trusted Access](https://cpl.thalesgroup.com/access-management/authentication) MFA provider
* OTP 6 digit codes generated by MobilePASS+ Authenticator app

## Setup Dependencies

Expand Down
84 changes: 84 additions & 0 deletions aws_adfs/_safenet_mfa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import click
import lxml.etree as ET

import logging
import re

from . import run_command

try:
# Python 3
from urllib.parse import urlparse, parse_qs
except ImportError:
# Python 2
from urlparse import urlparse, parse_qs

from . import roles_assertion_extractor
from .helpers import trace_http_request


def extract(html_response, ssl_verification_enabled, mfa_token_command, mfa_token, session):
"""
:param response: raw http response
:param html_response: html result of parsing http response
:return:
"""

roles_page_url = _action_url_on_validation_success(html_response)

if mfa_token_command:
data = run_command.run_command(mfa_token_command)
safenet_mfa_code = data['mfa_token']
logging.debug(f"using SafeNet MFA token from command: {safenet_mfa_code}")
elif mfa_token:
safenet_mfa_code = mfa_token
logging.debug(f"using SafeNet MFA token from env: {safenet_mfa_code}")
else:
safenet_mfa_code = click.prompt(text='Enter your SafeNet MFA token', type=str, hide_input=True)

click.echo('Going for aws roles', err=True)
return _retrieve_roles_page(
roles_page_url,
_context(html_response),
session,
ssl_verification_enabled,
safenet_mfa_code,
)

def _context(html_response):
context_query = './/input[@name="Context"]'
element = html_response.find(context_query)
return element.get('value')


def _retrieve_roles_page(roles_page_url, context, session, ssl_verification_enabled,
safenet_mfa_code):
response = session.post(
roles_page_url,
verify=ssl_verification_enabled,
allow_redirects=True,
data={
'AuthMethod': 'SafeNet-MFA',
'Context': context,
'SAFENET_PASSWORD': safenet_mfa_code,
}
)
trace_http_request(response)

if response.status_code != 200:
raise click.ClickException(
u'Issues during redirection to aws roles page. The error response {}'.format(
response
)
)

# Save session cookies to avoid having to repeat MFA on each login
session.cookies.save(ignore_discard=True)

html_response = ET.fromstring(response.text, ET.HTMLParser())
return roles_assertion_extractor.extract(html_response)

def _action_url_on_validation_success(html_response):
safenet_mfa_auth_method = './/form[@id="options"]'
element = html_response.find(safenet_mfa_auth_method)
return element.get('action')
16 changes: 16 additions & 0 deletions aws_adfs/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from . import _azure_mfa_authenticator as azure_mfa_auth
from . import _azure_cloud_mfa_authenticator as azure_cloud_mfa_auth
from . import _silverfort_authenticator as silverfort_mfa_auth
from . import _safenet_mfa as safenet_mfa
from . import html_roles_fetcher
from . import roles_assertion_extractor
from .helpers import trace_http_request
Expand Down Expand Up @@ -128,6 +129,11 @@ def _silverfort_extractor():
def extract():
return silverfort_mfa_auth.extract(html_response, config.ssl_verification, session)
return extract

def _safenet_extractor():
def extract():
return safenet_mfa.extract(html_response, config.ssl_verification, config.mfa_token_command, config.mfa_token, session)
return extract

if assertfile is None:
chosen_strategy = _plain_extractor
Expand All @@ -148,6 +154,8 @@ def extract():
chosen_strategy = _azure_cloud_mfa_extractor
elif _is_silverfort_mfa_authentication(html_response):
chosen_strategy = _silverfort_extractor
elif _is_safenet_mfa_authentication(html_response):
chosen_strategy = _safenet_extractor

return chosen_strategy()

Expand Down Expand Up @@ -215,3 +223,11 @@ def _is_silverfort_mfa_authentication(html_response):
element is not None
and element.get('value') == 'SilverfortAdfs'
)

def _is_safenet_mfa_authentication(html_response):
auth_method = './/input[@name="AuthMethod"]'
element = html_response.find(auth_method)
return (
element is not None
and element.get('value') == 'SafeNet-MFA'
)
Loading