Skip to content

Commit 1c4a9f3

Browse files
authored
Merge pull request #425 from alencar/feature/support-safenet-trusted-access-mfa
Support SafeNet Trusted Access MFA
2 parents 4b3c9ee + e6f3db3 commit 1c4a9f3

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ aws-adfs integrates with:
2929
* SMS codes
3030
* Phone call
3131
* [Silverfort](https://www.silverfort.com/) MFA provider
32+
* [Thales/SafeNet Trusted Access](https://cpl.thalesgroup.com/access-management/authentication) MFA provider
33+
* OTP 6 digit codes generated by MobilePASS+ Authenticator app
3234

3335
## Setup Dependencies
3436

Diff for: aws_adfs/_safenet_mfa.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import click
2+
import lxml.etree as ET
3+
4+
import logging
5+
import re
6+
7+
from . import run_command
8+
9+
try:
10+
# Python 3
11+
from urllib.parse import urlparse, parse_qs
12+
except ImportError:
13+
# Python 2
14+
from urlparse import urlparse, parse_qs
15+
16+
from . import roles_assertion_extractor
17+
from .helpers import trace_http_request
18+
19+
20+
def extract(html_response, ssl_verification_enabled, mfa_token_command, mfa_token, session):
21+
"""
22+
:param response: raw http response
23+
:param html_response: html result of parsing http response
24+
:return:
25+
"""
26+
27+
roles_page_url = _action_url_on_validation_success(html_response)
28+
29+
if mfa_token_command:
30+
data = run_command.run_command(mfa_token_command)
31+
safenet_mfa_code = data['mfa_token']
32+
logging.debug(f"using SafeNet MFA token from command: {safenet_mfa_code}")
33+
elif mfa_token:
34+
safenet_mfa_code = mfa_token
35+
logging.debug(f"using SafeNet MFA token from env: {safenet_mfa_code}")
36+
else:
37+
safenet_mfa_code = click.prompt(text='Enter your SafeNet MFA token', type=str, hide_input=True)
38+
39+
click.echo('Going for aws roles', err=True)
40+
return _retrieve_roles_page(
41+
roles_page_url,
42+
_context(html_response),
43+
session,
44+
ssl_verification_enabled,
45+
safenet_mfa_code,
46+
)
47+
48+
def _context(html_response):
49+
context_query = './/input[@name="Context"]'
50+
element = html_response.find(context_query)
51+
return element.get('value')
52+
53+
54+
def _retrieve_roles_page(roles_page_url, context, session, ssl_verification_enabled,
55+
safenet_mfa_code):
56+
response = session.post(
57+
roles_page_url,
58+
verify=ssl_verification_enabled,
59+
allow_redirects=True,
60+
data={
61+
'AuthMethod': 'SafeNet-MFA',
62+
'Context': context,
63+
'SAFENET_PASSWORD': safenet_mfa_code,
64+
}
65+
)
66+
trace_http_request(response)
67+
68+
if response.status_code != 200:
69+
raise click.ClickException(
70+
u'Issues during redirection to aws roles page. The error response {}'.format(
71+
response
72+
)
73+
)
74+
75+
# Save session cookies to avoid having to repeat MFA on each login
76+
session.cookies.save(ignore_discard=True)
77+
78+
html_response = ET.fromstring(response.text, ET.HTMLParser())
79+
return roles_assertion_extractor.extract(html_response)
80+
81+
def _action_url_on_validation_success(html_response):
82+
safenet_mfa_auth_method = './/form[@id="options"]'
83+
element = html_response.find(safenet_mfa_auth_method)
84+
return element.get('action')

Diff for: aws_adfs/authenticator.py

+16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from . import _azure_mfa_authenticator as azure_mfa_auth
1010
from . import _azure_cloud_mfa_authenticator as azure_cloud_mfa_auth
1111
from . import _silverfort_authenticator as silverfort_mfa_auth
12+
from . import _safenet_mfa as safenet_mfa
1213
from . import html_roles_fetcher
1314
from . import roles_assertion_extractor
1415
from .helpers import trace_http_request
@@ -128,6 +129,11 @@ def _silverfort_extractor():
128129
def extract():
129130
return silverfort_mfa_auth.extract(html_response, config.ssl_verification, session)
130131
return extract
132+
133+
def _safenet_extractor():
134+
def extract():
135+
return safenet_mfa.extract(html_response, config.ssl_verification, config.mfa_token_command, config.mfa_token, session)
136+
return extract
131137

132138
if assertfile is None:
133139
chosen_strategy = _plain_extractor
@@ -148,6 +154,8 @@ def extract():
148154
chosen_strategy = _azure_cloud_mfa_extractor
149155
elif _is_silverfort_mfa_authentication(html_response):
150156
chosen_strategy = _silverfort_extractor
157+
elif _is_safenet_mfa_authentication(html_response):
158+
chosen_strategy = _safenet_extractor
151159

152160
return chosen_strategy()
153161

@@ -215,3 +223,11 @@ def _is_silverfort_mfa_authentication(html_response):
215223
element is not None
216224
and element.get('value') == 'SilverfortAdfs'
217225
)
226+
227+
def _is_safenet_mfa_authentication(html_response):
228+
auth_method = './/input[@name="AuthMethod"]'
229+
element = html_response.find(auth_method)
230+
return (
231+
element is not None
232+
and element.get('value') == 'SafeNet-MFA'
233+
)

0 commit comments

Comments
 (0)