Skip to content

Commit cb3afdc

Browse files
authored
Merge pull request #336 from dinkom/stormpath-325
Saml IdP url builder
2 parents 4f65d79 + 7ce5fb9 commit cb3afdc

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

‎stormpath/saml/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .saml_idp_url_builder import SamlIdpUrlBuilder
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
try:
2+
from urllib import urlencode
3+
except ImportError:
4+
from urllib.parse import urlencode
5+
from uuid import uuid4
6+
from datetime import datetime
7+
import jwt
8+
from oauthlib.common import to_unicode
9+
10+
11+
class SamlIdpUrlBuilder(object):
12+
13+
def __init__(self, application):
14+
self.application = application
15+
16+
def _get_service_provider(self):
17+
return self.application.saml_policy.service_provider
18+
19+
def build(self, options=None):
20+
service_provider = self._get_service_provider()
21+
api_key_secret = self.application._client.auth.secret
22+
api_key_id = self.application._client.auth.id
23+
24+
try:
25+
jti = uuid4().get_hex()
26+
except AttributeError:
27+
jti = uuid4().hex
28+
29+
claims = {
30+
'iat': datetime.utcnow(),
31+
'jti': jti,
32+
'iss': api_key_id
33+
}
34+
35+
if options:
36+
if 'cb_uri' in options:
37+
claims['cb_uri'] = options['cb_uri']
38+
39+
if 'ash' in options:
40+
claims['ash'] = options['ash']
41+
42+
if 'onk' in options:
43+
claims['onk'] = options['onk']
44+
45+
if 'state' in options:
46+
claims['state'] = options['state']
47+
48+
jwt_signature = to_unicode(
49+
jwt.encode(
50+
claims, api_key_secret, 'HS256', headers={'kid': api_key_id}),
51+
'UTF-8')
52+
url_params = {'accessToken': jwt_signature}
53+
sso_initiation_endpoint = service_provider.sso_initiation_endpoint.href
54+
55+
encoded_params = urlencode(url_params)
56+
init_url = "%s?%s" % (sso_initiation_endpoint, encoded_params)
57+
58+
return init_url

‎tests/live/test_saml.py‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import jwt
2+
from stormpath.saml import SamlIdpUrlBuilder
3+
from tests.live.base import AuthenticatedLiveBase
4+
5+
6+
class TestSamlIdpUrlBuilder(AuthenticatedLiveBase):
7+
8+
def setUp(self):
9+
super(TestSamlIdpUrlBuilder, self).setUp()
10+
11+
self.app_name = self.get_random_name()
12+
self.app = self.client.applications.create({
13+
'name': self.app_name,
14+
'description': 'test app'
15+
})
16+
17+
def test_build_default_url(self):
18+
saml_idp_url_builder = SamlIdpUrlBuilder(self.app)
19+
url = saml_idp_url_builder.build()
20+
21+
self.assertTrue('accessToken' in url)
22+
23+
token = url.split('accessToken=')[1]
24+
result = jwt.decode(token, self.app._client.auth.secret)
25+
26+
self.assertTrue('iss' in result.keys())
27+
self.assertTrue('iat' in result.keys())
28+
self.assertTrue('jti' in result.keys())
29+
30+
def test_build_uri_with_options(self):
31+
options = {
32+
'cb_uri': 'http://some_cb_uri.com/',
33+
'ash': 'ash',
34+
'onk': 'onk',
35+
'state': 'state'
36+
}
37+
38+
saml_idp_url_builder = SamlIdpUrlBuilder(self.app)
39+
url = saml_idp_url_builder.build(options)
40+
41+
self.assertTrue('accessToken' in url)
42+
43+
token = url.split('accessToken=')[1]
44+
result = jwt.decode(token, self.app._client.auth.secret)
45+
46+
self.assertTrue('cb_uri' in result.keys())
47+
self.assertTrue('ash' in result.keys())
48+
self.assertTrue('onk' in result.keys())
49+
self.assertTrue('state' in result.keys())
50+
51+
self.assertEqual(result['cb_uri'], options['cb_uri'])
52+
self.assertEqual(result['ash'], options['ash'])
53+
self.assertEqual(result['onk'], options['onk'])
54+
self.assertEqual(result['state'], options['state'])

0 commit comments

Comments
 (0)