Skip to content

Commit b78ab3e

Browse files
new: SAML2 frontend+backend: support reloading metadata
Using the reload_metadata method added into pysaml2 in IdentityPython/pysaml2#809, support reloading metadata when triggered via an externally exposed URL (as `/<module_name>/reload-metadata`) This is off by default (URL not exposed) and needs to be explicitly enabled by setting the newly introduced config option `enable_metadata_reload` for the SAML modules to `true` (or `yes`). The loaded config is already preserved in the modules, so can be easily used to provide a reference copy of the metadata configuration to the `reload_metadata` method. This is implemented separately for the SAML2 Backend and SAML2 Frontend (applying to all three SAML2 Frontend classes). This will complete the missing functionality identified in IdentityPython/pysaml2#808
1 parent 961dd99 commit b78ab3e

File tree

6 files changed

+47
-1
lines changed

6 files changed

+47
-1
lines changed

Diff for: example/plugins/backends/saml2_backend.yaml.example

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ config:
1515
memorize_idp: no
1616
use_memorized_idp_when_force_authn: no
1717
send_requester_id: no
18+
enable_metadata_reload: no
1819

1920
sp_config:
2021
key_file: backend.key

Diff for: example/plugins/frontends/saml2_frontend.yaml.example

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ config:
1919
# domain: .example.com
2020

2121
entityid_endpoint: true
22+
enable_metadata_reload: no
2223

2324
idp_config:
2425
organization: {display_name: Example Identities, name: Example Identities Org., url: 'http://www.example.com'}

Diff for: example/plugins/frontends/saml2_virtualcofrontend.yaml.example

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ config:
9494
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': sso/post
9595
'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect': sso/redirect
9696

97+
enable_metadata_reload: no
98+
9799
# If configured and not false or empty the common domain cookie _saml_idp will be set
98100
# with or have appended the IdP used for authentication. The default is not to set the
99101
# cookie. If the value is a dictionary with key 'domain' then the domain for the cookie

Diff for: src/satosa/backends/saml2.py

+15
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class SAMLBackend(BackendModule, SAMLBaseModule):
8282
KEY_SAML_DISCOVERY_SERVICE_URL = 'saml_discovery_service_url'
8383
KEY_SAML_DISCOVERY_SERVICE_POLICY = 'saml_discovery_service_policy'
8484
KEY_SP_CONFIG = 'sp_config'
85+
KEY_METADATA = 'metadata'
8586
KEY_SEND_REQUESTER_ID = 'send_requester_id'
8687
KEY_MIRROR_FORCE_AUTHN = 'mirror_force_authn'
8788
KEY_MEMORIZE_IDP = 'memorize_idp'
@@ -479,8 +480,22 @@ def register_endpoints(self):
479480
url_map.append(("^{0}".format(parsed_entity_id.path[1:]),
480481
self._metadata_endpoint))
481482

483+
if self.enable_metadata_reload():
484+
url_map.append(
485+
("^%s/%s$" % (self.name, "reload-metadata"), self._reload_metadata))
486+
482487
return url_map
483488

489+
def _reload_metadata(self, context):
490+
"""
491+
Reload SAML metadata
492+
"""
493+
logger.debug("Reloading metadata")
494+
res = self.sp.reload_metadata(copy.deepcopy(self.config[SAMLBackend.KEY_SP_CONFIG][SAMLBackend.KEY_METADATA]))
495+
message = "Metadata reload %s" % ("OK" if res else "failed")
496+
status = "200 OK" if res else "500 FAILED"
497+
return Response(message=message, status=status)
498+
484499
def get_metadata_desc(self):
485500
"""
486501
See super class satosa.backends.backend_base.BackendModule#get_metadata_desc

Diff for: src/satosa/base.py

+10
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ def run(self, context):
261261

262262
class SAMLBaseModule(object):
263263
KEY_ENTITYID_ENDPOINT = 'entityid_endpoint'
264+
KEY_ENABLE_METADATA_RELOAD = 'enable_metadata_reload'
264265
KEY_ATTRIBUTE_PROFILE = 'attribute_profile'
265266
KEY_ACR_MAPPING = 'acr_mapping'
266267
VALUE_ATTRIBUTE_PROFILE_DEFAULT = 'saml'
@@ -276,6 +277,15 @@ def expose_entityid_endpoint(self):
276277
value = self.config.get(self.KEY_ENTITYID_ENDPOINT, False)
277278
return bool(value)
278279

280+
def enable_metadata_reload(self):
281+
"""
282+
Check whether metadata reload has been enabled in config
283+
284+
return: bool
285+
"""
286+
value = self.config.get(self.KEY_ENABLE_METADATA_RELOAD, False)
287+
return bool(value)
288+
279289

280290
class SAMLEIDASBaseModule(SAMLBaseModule):
281291
VALUE_ATTRIBUTE_PROFILE_DEFAULT = 'eidas'

Diff for: src/satosa/frontends/saml2.py

+18-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class SAMLFrontend(FrontendModule, SAMLBaseModule):
6464
KEY_CUSTOM_ATTR_RELEASE = 'custom_attribute_release'
6565
KEY_ENDPOINTS = 'endpoints'
6666
KEY_IDP_CONFIG = 'idp_config'
67+
KEY_METADATA = 'metadata'
6768

6869
def __init__(self, auth_req_callback_func, internal_attributes, config, base_url, name):
6970
self._validate_config(config)
@@ -113,12 +114,18 @@ def register_endpoints(self, backend_names):
113114
:type backend_names: list[str]
114115
:rtype: list[(str, ((satosa.context.Context, Any) -> satosa.response.Response, Any))]
115116
"""
117+
url_map = []
118+
119+
if self.enable_metadata_reload():
120+
url_map.append(
121+
("^%s/%s$" % (self.name, "reload-metadata"), self._reload_metadata))
122+
116123
self.idp_config = self._build_idp_config_endpoints(
117124
self.config[self.KEY_IDP_CONFIG], backend_names)
118125
# Create the idp
119126
idp_config = IdPConfig().load(copy.deepcopy(self.idp_config))
120127
self.idp = Server(config=idp_config)
121-
return self._register_endpoints(backend_names)
128+
return self._register_endpoints(backend_names) + url_map
122129

123130
def _create_state_data(self, context, resp_args, relay_state):
124131
"""
@@ -484,6 +491,16 @@ def _metadata_endpoint(self, context):
484491
None).decode("utf-8")
485492
return Response(metadata_string, content="text/xml")
486493

494+
def _reload_metadata(self, context):
495+
"""
496+
Reload SAML metadata
497+
"""
498+
logger.debug("Reloading metadata")
499+
res = self.idp.reload_metadata(copy.deepcopy(self.config[SAMLFrontend.KEY_IDP_CONFIG][SAMLFrontend.KEY_METADATA]))
500+
message = "Metadata reload %s" % ("OK" if res else "failed")
501+
status = "200 OK" if res else "500 FAILED"
502+
return Response(message=message, status=status)
503+
487504
def _register_endpoints(self, providers):
488505
"""
489506
Register methods to endpoints

0 commit comments

Comments
 (0)