Skip to content

Commit b22f0a9

Browse files
committed
IdP metadata merger: deep-copy old settings, then deep-merge new into old
1 parent c12b178 commit b22f0a9

1 file changed

Lines changed: 35 additions & 2 deletions

File tree

src/onelogin/saml2/idp_metadata_parser.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
Metadata class of OneLogin's Python Toolkit.
77
"""
88

9+
10+
from copy import deepcopy
11+
12+
913
try:
1014
import urllib.request as urllib2
1115
except ImportError:
@@ -174,6 +178,35 @@ def merge_settings(settings, new_metadata_settings):
174178
:returns: merged settings
175179
:rtype: dict
176180
"""
177-
result_settings = settings.copy()
178-
result_settings.update(new_metadata_settings)
181+
for d in (settings, new_metadata_settings):
182+
if not isinstance(d, dict):
183+
raise TypeError('Both arguments must be dictionaries.')
184+
185+
# Guarantee to not modify original data (`settings.copy()` would not
186+
# be sufficient, as it's just a shallow copy).
187+
result_settings = deepcopy(settings)
188+
# Merge `new_metadata_settings` into `result_settings`.
189+
dict_deep_merge(result_settings, new_metadata_settings)
179190
return result_settings
191+
192+
193+
def dict_deep_merge(a, b, path=None):
194+
"""Deep-merge dictionary `b` into dictionary `a`.
195+
196+
Kudos to http://stackoverflow.com/a/7205107/145400
197+
"""
198+
if path is None:
199+
path = []
200+
for key in b:
201+
if key in a:
202+
if isinstance(a[key], dict) and isinstance(b[key], dict):
203+
dict_deep_merge(a[key], b[key], path + [str(key)])
204+
elif a[key] == b[key]:
205+
# Key conflict, but equal value.
206+
pass
207+
else:
208+
# Key/value conflict. Prioritize b over a.
209+
a[key] = b[key]
210+
else:
211+
a[key] = b[key]
212+
return a

0 commit comments

Comments
 (0)