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

add sp support for encrypted attributes #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/sp.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def get_default_login_return_url(self):
'sso_url': 'http://localhost:8000/saml/login/',
'slo_url': 'http://localhost:8000/saml/logout/',
'certificate': IDP_CERTIFICATE,
# for decrypting attributes with xmlsec1
# 'encrypted_attributes': {
# 'xmlsec1_path': 'xmlsec1',
# 'sp_key_path': '../testd/keys/sample/sp-private-key.pem',
# },
},
},
]
Expand Down
12 changes: 10 additions & 2 deletions flask_saml2/sp/idphandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def __init__(
sso_url: Optional[str] = None,
slo_url: Optional[str] = None,
certificate: Optional[X509] = None,
encrypted_attributes: Mapping[str, str] = None,
**kwargs,
):
"""
Expand All @@ -99,11 +100,17 @@ def __init__(

The ``sso_url``, ``slo_url``, and ``certificate`` can all be found in
the IdP's metadata.

``encrypted_attributes`` is a map with extra configuration for decrypting
the saml EncryptedAttributes tag using xmlsec1. this requires installing
the xmlsec1 program to work. the map consists of ``xmlsec1_path`` and
``sp_key_path``, which are paths to an xmlsec1 binary and your sp's key
for decrypting the attributes
"""
super().__init__(**kwargs)

self.sp = sp
self.entity_id = entity_id
self.encrypted_attributes = encrypted_attributes

if display_name is not None:
self.display_name = display_name
Expand Down Expand Up @@ -219,7 +226,8 @@ def get_response_parser(self, saml_response):
"""
return ResponseParser(
self.decode_saml_string(saml_response),
certificate=self.certificate)
certificate=self.certificate,
encrypted_attributes=self.encrypted_attributes)

def get_auth_data(self, response: ResponseParser) -> AuthData:
"""
Expand Down
13 changes: 12 additions & 1 deletion flask_saml2/sp/parser.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import subprocess
from typing import Mapping, Optional

from flask_saml2.types import XmlNode
Expand All @@ -6,6 +7,16 @@


class ResponseParser(XmlParser):
def __init__(self, xml_string, *args, encrypted_attributes=None, **kwargs):
super().__init__(xml_string, *args, **kwargs)
if encrypted_attributes:
xml_string = subprocess.check_output([
encrypted_attributes['xmlsec1_path'],
'--decrypt',
'--privkey-pem', encrypted_attributes['sp_key_path'],
'/dev/stdin'], input=xml_string)
self.xml_string = xml_string
self.xml_tree = self.parse_request(self.xml_string)

def is_signed(self):
sig = self.xml_tree.xpath('/samlp:Response/ds:Signature', namespaces=self.get_namespace_map())
Expand Down Expand Up @@ -36,7 +47,7 @@ def issue_instant(self) -> str:

@cached_property
def assertion(self) -> XmlNode:
return self._xpath_xml_tree('/samlp:Response/saml:Assertion')[0]
return self._xpath_xml_tree('.//saml:Assertion')[0]

@cached_property
def subject(self) -> XmlNode:
Expand Down