Skip to content

Commit efec162

Browse files
Merge pull request #191 from c00kiemon5ter/refactor-target-entity-id
Decouple SAMLBackend from SAMLMirrorFrontend
2 parents d861e87 + d0be0a2 commit efec162

File tree

6 files changed

+37
-28
lines changed

6 files changed

+37
-28
lines changed

src/satosa/backends/saml2.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import functools
66
import json
77
import logging
8-
from base64 import urlsafe_b64encode, urlsafe_b64decode
8+
from base64 import urlsafe_b64encode
99
from urllib.parse import urlparse
1010

1111
from saml2.client_base import Base
@@ -88,18 +88,18 @@ def start_auth(self, context, internal_req):
8888
:rtype: satosa.response.Response
8989
"""
9090

91+
target_entity_id = context.get_decoration(Context.KEY_TARGET_ENTITYID)
92+
if target_entity_id:
93+
entity_id = target_entity_id
94+
return self.authn_request(context, entity_id)
95+
9196
# if there is only one IdP in the metadata, bypass the discovery service
9297
idps = self.sp.metadata.identity_providers()
9398
if len(idps) == 1 and "mdq" not in self.config["sp_config"]["metadata"]:
94-
return self.authn_request(context, idps[0])
95-
96-
entity_id = context.get_decoration(
97-
Context.KEY_MIRROR_TARGET_ENTITYID)
98-
if None is entity_id:
99-
return self.disco_query()
99+
entity_id = idps[0]
100+
return self.authn_request(context, entity_id)
100101

101-
entity_id = urlsafe_b64decode(entity_id).decode("utf-8")
102-
return self.authn_request(context, entity_id)
102+
return self.disco_query()
103103

104104
def disco_query(self):
105105
"""

src/satosa/context.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class Context(object):
1616
Holds information about the current request.
1717
"""
1818
KEY_BACKEND_METADATA_STORE = 'metadata_store'
19-
KEY_MIRROR_TARGET_ENTITYID = 'mirror_target_entity_id'
19+
KEY_TARGET_ENTITYID = 'target_entity_id'
2020

2121
def __init__(self):
2222
self._path = None
@@ -63,6 +63,10 @@ def path(self, p):
6363
raise ValueError("path can't start with '/'")
6464
self._path = p
6565

66+
def target_entity_id_from_path(self):
67+
target_entity_id = self.path.split("/")[1]
68+
return target_entity_id
69+
6670
def decorate(self, key, value):
6771
"""
6872
Add information to the context

src/satosa/frontends/saml2.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import functools
66
import json
77
import logging
8+
from base64 import urlsafe_b64decode
89
from urllib.parse import urlparse
910

1011
from saml2 import SAMLError, xmldsig
@@ -199,27 +200,27 @@ def _handle_authn_request(self, context, binding_in, idp):
199200
satosa_logging(logger, logging.ERROR, "Could not find necessary info about entity: %s" % e, context.state)
200201
return ServiceError("Incorrect request from requester: %s" % e)
201202

203+
requester = resp_args["sp_entity_id"]
202204
context.state[self.name] = self._create_state_data(context, idp.response_args(authn_req),
203205
context.request.get("RelayState"))
204206

205207
if authn_req.name_id_policy and authn_req.name_id_policy.format:
206208
name_format = saml_name_id_format_to_hash_type(authn_req.name_id_policy.format)
207209
else:
208210
# default to name id format from metadata, or just transient name id
209-
name_format_from_metadata = idp.metadata[resp_args["sp_entity_id"]]["spsso_descriptor"][0].get(
210-
"name_id_format")
211+
name_format_from_metadata = idp.metadata[requester]["spsso_descriptor"][0].get("name_id_format")
211212
if name_format_from_metadata:
212213
name_format = saml_name_id_format_to_hash_type(name_format_from_metadata[0]["text"])
213214
else:
214215
name_format = UserIdHashType.transient
215216

216-
requester_name = self._get_sp_display_name(idp, resp_args["sp_entity_id"])
217-
internal_req = InternalRequest(name_format, resp_args["sp_entity_id"], requester_name)
217+
requester_name = self._get_sp_display_name(idp, requester)
218+
internal_req = InternalRequest(name_format, requester, requester_name)
218219

219220
idp_policy = idp.config.getattr("policy", "idp")
220221
if idp_policy:
221-
approved_attributes = self._get_approved_attributes(idp, idp_policy, internal_req.requester, context.state)
222-
internal_req.approved_attributes = approved_attributes
222+
internal_req.approved_attributes = self._get_approved_attributes(
223+
idp, idp_policy, requester, context.state)
223224

224225
return self.auth_req_callback_func(context, internal_req)
225226

@@ -504,8 +505,7 @@ def _load_idp_dynamic_endpoints(self, context):
504505
:param context:
505506
:return: An idp server
506507
"""
507-
target_entity_id = context.path.split("/")[1]
508-
context.decorate(Context.KEY_MIRROR_TARGET_ENTITYID, target_entity_id)
508+
target_entity_id = context.target_entity_id_from_path()
509509
idp_conf_file = self._load_endpoints_to_config(context.target_backend, target_entity_id)
510510
idp_config = IdPConfig().load(idp_conf_file, metadata_construction=False)
511511
return Server(config=idp_config)
@@ -535,6 +535,10 @@ def handle_authn_request(self, context, binding_in):
535535
:type binding_in: str
536536
:rtype: satosa.response.Response
537537
"""
538+
target_entity_id = context.target_entity_id_from_path()
539+
target_entity_id = urlsafe_b64decode(target_entity_id).decode()
540+
context.decorate(Context.KEY_TARGET_ENTITYID, target_entity_id)
541+
538542
idp = self._load_idp_dynamic_endpoints(context)
539543
return self._handle_authn_request(context, binding_in, idp)
540544

@@ -549,7 +553,7 @@ def _create_state_data(self, context, resp_args, relay_state):
549553
:rtype: dict[str, dict[str, str] | str]
550554
"""
551555
state = super()._create_state_data(context, resp_args, relay_state)
552-
state["target_entity_id"] = context.path.split("/")[1]
556+
state["target_entity_id"] = context.target_entity_id_from_path()
553557
return state
554558

555559
def handle_backend_error(self, exception):

src/satosa/micro_services/custom_routing.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ class DecideIfRequesterIsAllowed(RequestMicroService):
3939
"""
4040
Decide whether a requester is allowed to send an authentication request to the target entity.
4141
42-
This micro service currently only works with `SAMLMirrorFrontend`.
42+
This micro service currently only works when a target entityid is set.
43+
Currently, a target entityid is set only when the `SAMLMirrorFrontend` is
44+
used.
4345
"""
4446
def __init__(self, config, *args, **kwargs):
4547
super().__init__(*args, **kwargs)
@@ -58,11 +60,12 @@ def _b64_url(self, data):
5860
return urlsafe_b64encode(data.encode("utf-8")).decode("utf-8")
5961

6062
def process(self, context, data):
61-
target_entity_id = context.get_decoration(
62-
Context.KEY_MIRROR_TARGET_ENTITYID)
63+
target_entity_id = context.get_decoration(Context.KEY_TARGET_ENTITYID)
6364
if None is target_entity_id:
64-
logger.error("DecideIfRequesterIsAllowed can only be used with SAMLMirrorFrontend")
65-
raise SATOSAError("DecideIfRequesterIsAllowed can only be used with SAMLMirrorFrontend")
65+
msg_tpl = "{name} can only be used when a target entityid is set"
66+
msg = msg_tpl.format(name=self.__class__.__name__)
67+
logger.error(msg)
68+
raise SATOSAError(msg)
6669

6770
target_specific_rules = self.rules.get(target_entity_id)
6871
# default to allowing everything if there are no specific rules

tests/satosa/backends/test_saml2.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,7 @@ def test_full_flow(self, context, idp_conf, sp_conf):
144144
def test_start_auth_redirects_directly_to_mirrored_idp(
145145
self, context, idp_conf):
146146
entityid = idp_conf["entityid"]
147-
entityid_bytes = entityid.encode("utf-8")
148-
entityid_b64_str = urlsafe_b64encode(entityid_bytes).decode("utf-8")
149-
context.decorate(Context.KEY_MIRROR_TARGET_ENTITYID, entityid_b64_str)
147+
context.decorate(Context.KEY_TARGET_ENTITYID, entityid)
150148

151149
resp = self.samlbackend.start_auth(context, InternalRequest(None, None))
152150
self.assert_redirect_to_idp(resp, idp_conf)

tests/satosa/micro_services/test_custom_routing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
def target_context(context):
1515
entityid_bytes = TARGET_ENTITY.encode("utf-8")
1616
entityid_b64_str = urlsafe_b64encode(entityid_bytes).decode("utf-8")
17-
context.decorate(Context.KEY_MIRROR_TARGET_ENTITYID, entityid_b64_str)
17+
context.decorate(Context.KEY_TARGET_ENTITYID, entityid_b64_str)
1818
return context
1919

2020

0 commit comments

Comments
 (0)