0001-WIP-support-federation-file-loading-19396.patch
README | ||
---|---|---|
82 | 82 |
the absolute path toward a metadata file. All other keys are override |
83 | 83 |
of generic settings. |
84 | 84 | |
85 |
MELLON_FEDERATIONS |
|
86 |
------------------ |
|
87 | ||
88 |
A list of dictionaries, only one key 'FEDERATION' is mandatory in those |
|
89 |
dictionaries. It should contain the local path or the remote URL for the |
|
90 |
metadata file describing the SAML-based federation to be loaded in mellon. Both |
|
91 |
relative and absolute paths are supported. |
|
92 |
Additional parameters can be given as key/value pairs in the dictionaries, on |
|
93 |
a similar basis as the aforementioned MELLON_IDENTITY_PROVIDERS config. |
|
94 |
For each dictionary describing a federation, these parameters will apply to |
|
95 |
any successfully-loaded provider belonging to that federation. |
|
96 |
These parameters also override the global settings. |
|
97 | ||
85 | 98 |
MELLON_PUBLIC_KEYS |
86 | 99 |
------------------ |
87 | 100 |
mellon/adapters.py | ||
---|---|---|
11 | 11 |
from django.contrib.auth.models import Group |
12 | 12 |
from django.utils import six |
13 | 13 |
from django.utils.encoding import force_text |
14 |
from django.utils.text import slugify |
|
14 | 15 | |
15 | 16 |
from . import utils, app_settings, models |
17 |
from mellon.federation_utils import idp_metadata_store, url2filename, \ |
|
18 |
idp_metadata_extract_entity_id, idp_metadata_is_cached, \ |
|
19 |
idp_metadata_load, idp_settings_store, idp_settings_load, \ |
|
20 |
store_fingerprint |
|
16 | 21 | |
17 | 22 | |
18 | 23 |
class UserCreationError(Exception): |
... | ... | |
25 | 30 | |
26 | 31 |
def get_idp(self, entity_id): |
27 | 32 |
'''Find the first IdP definition matching entity_id''' |
28 |
for idp in self.get_idps(): |
|
29 |
if entity_id == idp['ENTITY_ID']: |
|
30 |
return idp |
|
33 |
idp = {} |
|
34 | ||
35 |
# First, check whether the provider is cached |
|
36 |
if idp_metadata_is_cached(entity_id): |
|
37 |
metadata_content = idp_metadata_load(entity_id) |
|
38 |
idp.update({'METADATA': metadata_content, |
|
39 |
'ENTITY_ID': entity_id}) |
|
40 |
# Extra settings loaded if the provider comes from a federation |
|
41 |
idp.update(idp_settings_load(entity_id) or {}) |
|
42 | ||
43 |
# If not, try to fetch it from the mellon settings |
|
44 |
else: |
|
45 |
for idp in self.get_identity_providers_setting(): |
|
46 |
if not idp.get('METADATA_URL') and not idp.get('METADATA'): |
|
47 |
self.logger.error(u'missing METADATA or METADATA_URL in idp %s', idp or '') |
|
48 |
continue |
|
49 | ||
50 |
elif 'METADATA_URL' in idp and 'METADATA' not in idp: |
|
51 |
metadata = utils.get_metadata_from_url(idp) |
|
52 |
if not metadata: |
|
53 |
continue |
|
54 |
idp['METADATA'] = metadata |
|
55 | ||
56 |
if 'ENTITY_ID' not in idp: |
|
57 |
if idp['METADATA'].startswith('/') or idp['METADATA'].startswith('./'): |
|
58 |
# In case the entity ID isn't provided in the settings, it |
|
59 |
# needs to be fetched from the content of the metadata file |
|
60 |
metadata_path = idp['METADATA'] |
|
61 |
if 'FEDERATION' in idp: |
|
62 |
metadata_path = default_storage.path(metadata_path) |
|
63 |
content = open(metadata_path, 'r').read() |
|
64 |
else: |
|
65 |
content = idp['METADATA'] |
|
66 |
idp['ENTITY_ID'] = idp_metadata_extract_entity_id(content) |
|
67 | ||
68 |
if idp['ENTITY_ID'] == entity_id: |
|
69 |
break |
|
70 | ||
71 |
return idp.copy() |
|
31 | 72 | |
32 | 73 |
def get_identity_providers_setting(self): |
33 |
return app_settings.IDENTITY_PROVIDERS |
|
74 |
# First, providers from federation as declared in the mellon settings |
|
75 |
for federation_data in self.get_federations(): |
|
76 |
if not isinstance(federation_data, dict) or \ |
|
77 |
'FEDERATION' not in federation_data: |
|
78 |
continue |
|
79 |
fed_extra_attrs = federation_data.copy() |
|
80 |
# Federation can be declared as URLs. If so, their content needs |
|
81 |
# to be fetched and cached |
|
82 |
fed_filepath, _ = utils.get_federation_metadata(federation_data.get('FEDERATION')) |
|
83 | ||
84 |
try: |
|
85 |
tree = ET.parse(fed_filepath) |
|
86 |
root = tree.getroot() |
|
87 |
for child in root: |
|
88 |
provider = {} |
|
89 |
entity_id = idp_metadata_extract_entity_id(ET.tostring(child)) |
|
90 |
if not entity_id: |
|
91 |
# The XML tag wasn't an IDPSSODescriptor |
|
92 |
continue |
|
93 |
# Store the metadata content in cache |
|
94 |
provider['METADATA'] = idp_metadata_store(ET.tostring(child).decode('utf-8')) |
|
95 |
provider['ENTITY_ID'] = entity_id |
|
96 |
# Add in each provider the federation-wise configuration |
|
97 |
provider.update(fed_extra_attrs) |
|
98 |
idp_settings_store(provider) |
|
99 |
if entity_id: |
|
100 |
store_fingerprint(entity_id) |
|
101 |
yield provider |
|
102 |
except: |
|
103 |
self.logger.error('Couldn\'t load federation metadata file %r', |
|
104 |
fed_filepath) |
|
105 |
continue |
|
106 | ||
107 |
# Then, the non-federated providers |
|
108 |
for extra_provider in app_settings.IDENTITY_PROVIDERS: |
|
109 |
if 'ENTITY_ID' in extra_provider: |
|
110 |
entity_id = extra_provider.get('ENTITY_ID') |
|
111 |
else: |
|
112 |
if 'METADATA' in extra_provider: |
|
113 |
metadata = extra_provider.get('METADATA') |
|
114 |
elif 'METADATA_URL' in extra_provider: |
|
115 |
metadata = utils.get_metadata_from_url(extra_provider) |
|
116 |
else: |
|
117 |
continue |
|
118 |
entity_id = idp_metadata_extract_entity_id(metadata) |
|
119 | ||
120 |
if entity_id: |
|
121 |
store_fingerprint(entity_id) |
|
122 | ||
123 |
yield extra_provider |
|
124 | ||
125 |
def get_federations(self): |
|
126 |
for federation in getattr(app_settings, 'FEDERATIONS', []): |
|
127 |
yield federation |
|
34 | 128 | |
35 | 129 |
def get_idps(self): |
36 | 130 |
for i, idp in enumerate(self.get_identity_providers_setting()): |
37 | 131 |
if 'METADATA_URL' in idp and 'METADATA' not in idp: |
38 |
verify_ssl_certificate = utils.get_setting( |
|
39 |
idp, 'VERIFY_SSL_CERTIFICATE') |
|
40 |
try: |
|
41 |
response = requests.get(idp['METADATA_URL'], verify=verify_ssl_certificate) |
|
42 |
response.raise_for_status() |
|
43 |
except requests.exceptions.RequestException as e: |
|
44 |
self.logger.error( |
|
45 |
u'retrieval of metadata URL %r failed with error %s for %d-th idp', |
|
46 |
idp['METADATA_URL'], e, i) |
|
132 |
md_content = utils.get_metadata_from_url(idp) |
|
133 | ||
134 |
if not md_content: |
|
47 | 135 |
continue |
48 |
idp['METADATA'] = response.text |
|
49 |
elif 'METADATA' in idp: |
|
50 |
if idp['METADATA'].startswith('/'): |
|
51 |
idp['METADATA'] = open(idp['METADATA']).read() |
|
52 |
else: |
|
136 | ||
137 |
if 'FEDERATION' in idp: |
|
138 |
# IdPs from federation are cached on filesystem |
|
139 |
# only the filename is kept in memory |
|
140 |
idp['METADATA'] = idp_metadata_store(md_content) |
|
141 |
entity_id = idp.get('ENTITY_ID') |
|
142 |
if not entity_id: |
|
143 |
idp['ENTITY_ID'] = idp_metadata_extract_entity_id(md_content) |
|
144 |
# load federation-specific configuration |
|
145 |
idp.update(idp_settings_load(idp.get('ENTITY_ID'))) |
|
146 |
else: |
|
147 |
idp['METADATA'] = md_content |
|
148 | ||
149 |
elif idp.get('METADATA', '').startswith('/') or \ |
|
150 |
idp.get('METADATA', '').startswith('./') and \ |
|
151 |
'FEDERATION' not in idp: |
|
152 |
idp['METADATA'] = open(idp['METADATA'], 'r').read() |
|
153 | ||
154 |
elif not idp.get('METADATA'): |
|
53 | 155 |
self.logger.error(u'missing METADATA or METADATA_URL in %d-th idp', i) |
54 | 156 |
continue |
55 |
if 'ENTITY_ID' not in idp: |
|
56 |
try: |
|
57 |
doc = ET.fromstring(idp['METADATA']) |
|
58 |
except (TypeError, ET.ParseError): |
|
59 |
self.logger.error(u'METADATA of %d-th idp is invalid', i) |
|
60 |
continue |
|
61 |
if doc.tag != '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF: |
|
62 |
self.logger.error(u'METADATA of %d-th idp has no EntityDescriptor root tag', i) |
|
63 |
continue |
|
64 | ||
65 |
if not 'entityID' in doc.attrib: |
|
66 |
self.logger.error( |
|
67 |
u'METADATA of %d-th idp has no entityID attribute on its root tag', i) |
|
68 |
continue |
|
69 |
idp['ENTITY_ID'] = doc.attrib['entityID'] |
|
70 | 157 |
yield idp |
71 | 158 | |
72 | 159 |
def authorize(self, idp, saml_attributes): |
mellon/app_settings.py | ||
---|---|---|
38 | 38 |
'LOGIN_URL': 'mellon_login', |
39 | 39 |
'LOGOUT_URL': 'mellon_logout', |
40 | 40 |
'ARTIFACT_RESOLVE_TIMEOUT': 10.0, |
41 |
'FEDERATIONS': [], |
|
41 | 42 |
} |
42 | 43 | |
44 |
@property |
|
45 |
def FEDERATIONS(self): |
|
46 |
from django.conf import settings |
|
47 |
if settings.hasattr('MELLON_FEDERATIONS'): |
|
48 |
federations = settings.MELLON_FEDERATIONS |
|
49 |
if isinstance(federations, dict): |
|
50 |
federations = [federations] |
|
51 |
return federations |
|
52 | ||
43 | 53 |
@property |
44 | 54 |
def IDENTITY_PROVIDERS(self): |
45 | 55 |
from django.conf import settings |
56 |
idps = [] |
|
46 | 57 |
try: |
47 |
idps = settings.MELLON_IDENTITY_PROVIDERS |
|
58 |
if hasattr(settings, 'MELLON_IDENTITY_PROVIDERS'): |
|
59 |
idps = settings.MELLON_IDENTITY_PROVIDERS |
|
60 |
elif not hasattr(settings, 'MELLON_FEDERATIONS'): |
|
61 |
raise AttributeError |
|
48 | 62 |
except AttributeError: |
49 | 63 |
return [] |
50 | 64 |
if isinstance(idps, dict): |
mellon/federation_utils.py | ||
---|---|---|
1 |
import base64 |
|
2 |
import fcntl |
|
3 |
import hashlib |
|
4 |
import json |
|
5 |
import lasso |
|
6 |
import logging |
|
7 |
import os |
|
8 |
import os.path |
|
9 |
import requests |
|
10 |
import tempfile |
|
11 | ||
12 | ||
13 |
from datetime import datetime, timedelta |
|
14 |
from django.core.files.storage import default_storage |
|
15 |
from django.utils.text import slugify |
|
16 |
from hashlib import sha1 |
|
17 |
from xml.etree import ElementTree as ET |
|
18 | ||
19 | ||
20 |
def truncate_unique(s, length=250): |
|
21 |
if len(s) < length: |
|
22 |
return s |
|
23 |
md5 = hashlib.md5(s.encode('ascii')).hexdigest() |
|
24 |
# we should be the first and last characters from the URL |
|
25 |
l = int((length - len(md5)) / 2 - 2) # four additional characters |
|
26 |
assert l > 20 |
|
27 |
return s[:l] + '...' + s[-l:] + '_' + md5 |
|
28 | ||
29 | ||
30 |
def url2filename(url): |
|
31 |
return truncate_unique(slugify(url), 230) |
|
32 | ||
33 | ||
34 |
def get_entity_id_from_fingerprint(fingerprint): |
|
35 |
from .utils import reload_idps |
|
36 |
logger = logging.getLogger(__name__) |
|
37 |
fingerprint_b32 = base64.b32encode(fingerprint) |
|
38 |
main_md_dir = default_storage.path('metadata-cache') |
|
39 |
artifact_dir = os.path.join(main_md_dir, 'fingerprints') |
|
40 |
artifact_path = os.path.join(artifact_dir, fingerprint_b32.decode('utf-8')) |
|
41 | ||
42 |
if not os.path.exists(artifact_path) or \ |
|
43 |
not os.path.exists(artifact_dir) or \ |
|
44 |
not os.path.exists(main_md_dir) or \ |
|
45 |
os.stat(artifact_dir).st_mtime > 3600: |
|
46 |
reload_idps() |
|
47 | ||
48 |
if not os.path.exists(artifact_path): |
|
49 |
logger.info('no entity ID found for artifact %s', fingerprint) |
|
50 |
return |
|
51 | ||
52 |
try: |
|
53 |
mapping = dict() |
|
54 |
with open(artifact_path, 'r+') as f: |
|
55 |
try: |
|
56 |
# Shared yet blocking lock, as obtaining the entity id is critical |
|
57 |
# to the SAML artifact resolution. |
|
58 |
fcntl.lockf(f, fcntl.LOCK_SH) |
|
59 |
except: |
|
60 |
logger.exception(u"failed to acquire shared blocking lock on " |
|
61 |
"the entity_id fingerprint cache file") |
|
62 |
else: |
|
63 |
entity_id = f.read() |
|
64 |
finally: |
|
65 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
66 |
except: |
|
67 |
logger.exception(u"could read the entity_id fingerprint cache file") |
|
68 | ||
69 |
return entity_id |
|
70 | ||
71 | ||
72 |
def store_fingerprint(entity_id): |
|
73 |
"""Adds an entry in the <sha1(entity_id), entity_id> mapping. |
|
74 |
The mapping cache file may be created in not already existing. |
|
75 |
""" |
|
76 |
logger = logging.getLogger(__name__) |
|
77 |
m = sha1() |
|
78 |
m.update(entity_id.encode('utf-8')) |
|
79 |
fingerprint = m.digest() |
|
80 |
fingerprint_b32 = base64.b32encode(fingerprint) |
|
81 |
unix_path = default_storage.path(os.path.join('metadata-cache/fingerprints/', fingerprint_b32.decode('utf-8'))) |
|
82 |
dirname = os.path.dirname(unix_path) |
|
83 | ||
84 |
try: |
|
85 |
if not os.path.exists(dirname): |
|
86 |
os.makedirs(dirname) |
|
87 |
with open(unix_path, 'w') as f: |
|
88 |
try: |
|
89 |
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
90 |
except: |
|
91 |
logger.exception(u"failed to acquire the exclusive non blocking " |
|
92 |
"lock on the entity_id fingerprint cache file") |
|
93 |
return |
|
94 |
else: |
|
95 |
with tempfile.NamedTemporaryFile(mode='w', dir=os.path.dirname(unix_path), delete=False) as temp: |
|
96 |
try: |
|
97 |
temp.write(entity_id) |
|
98 |
temp.flush() |
|
99 |
os.rename(temp.name, unix_path) |
|
100 |
pass |
|
101 |
except: |
|
102 |
logger.error('Could\'nt store fingerprint for entity ID %s', entity_id) |
|
103 |
os.unlink(temp.name) |
|
104 |
finally: |
|
105 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
106 |
except: |
|
107 |
logger.exception(u"could create the intermediate metadata cache " |
|
108 |
"folder") |
|
109 | ||
110 | ||
111 |
def load_federation_cache(url): |
|
112 |
logger = logging.getLogger(__name__) |
|
113 |
try: |
|
114 |
filename = url2filename(url) |
|
115 |
path = os.path.join('metadata-cache', filename) |
|
116 | ||
117 |
unix_path = default_storage.path(path) |
|
118 |
dirname = os.path.dirname(unix_path) |
|
119 |
if not os.path.exists(dirname): |
|
120 |
os.makedirs(dirname) |
|
121 |
f = open(unix_path, 'w') |
|
122 |
try: |
|
123 |
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
124 |
except IOError: |
|
125 |
return |
|
126 |
else: |
|
127 |
with tempfile.NamedTemporaryFile(dir=os.path.dirname(unix_path), delete=False) as temp: |
|
128 |
try: |
|
129 |
# increase modified time by one hour to prevent too many updates |
|
130 |
st = os.stat(unix_path) |
|
131 |
os.utime(unix_path, (st.st_atime, st.st_mtime + 3600)) |
|
132 |
response = requests.get(url) |
|
133 |
response.raise_for_status() |
|
134 |
temp.write(response.content) |
|
135 |
temp.flush() |
|
136 |
os.rename(temp.name, unix_path) |
|
137 |
except: |
|
138 |
logger.error('Could\'nt fetch %r', url) |
|
139 |
os.unlink(temp.name) |
|
140 |
finally: |
|
141 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
142 |
finally: |
|
143 |
f.close() |
|
144 |
except OSError: |
|
145 |
logger.exception(u"could create the intermediary 'metadata-cache' " |
|
146 |
"folder") |
|
147 |
return |
|
148 |
except: |
|
149 |
logger.exception(u'failed to load federation from %s', url) |
|
150 | ||
151 | ||
152 |
def get_federation_from_url(url, update_cache=False): |
|
153 |
logger = logging.getLogger(__name__) |
|
154 |
filename = url2filename(url) |
|
155 |
filepath = os.path.join('metadata-cache', filename) |
|
156 |
if not default_storage.exists(filepath) or update_cache or \ |
|
157 |
default_storage.created_time(filepath) < datetime.now() - timedelta(days=1): |
|
158 |
load_federation_cache(url) |
|
159 |
else: |
|
160 |
logger.warning('federation %s has not been loaded', url) |
|
161 |
return default_storage.path(filepath) |
|
162 | ||
163 | ||
164 |
def idp_metadata_filepath(entity_id): |
|
165 |
filename = url2filename(entity_id) |
|
166 |
filepath = os.path.join('./metadata-cache', filename) |
|
167 |
return filepath |
|
168 | ||
169 | ||
170 |
def idp_settings_filepath(entity_id): |
|
171 |
filename = url2filename(entity_id) + "_settings.json" |
|
172 |
filepath = os.path.join('./metadata-cache', filename) |
|
173 |
return filepath |
|
174 | ||
175 | ||
176 |
def idp_metadata_is_cached(entity_id): |
|
177 |
filepath = idp_metadata_filepath(entity_id) |
|
178 |
if not default_storage.exists(filepath): |
|
179 |
return False |
|
180 |
return True |
|
181 | ||
182 | ||
183 |
def idp_metadata_is_file(metadata): |
|
184 |
# XXX too restrictive (e.g. 'metadata/http-somemetadataserver-com-md00.xml' |
|
185 |
# could be a file too...) |
|
186 |
# On the opposite, `if "http://" in metadata or "https://" in metadata:" is |
|
187 |
# equally restrictive. |
|
188 |
# Using a URLValidator doesn't seem adequate either. |
|
189 |
if metadata.startswith('/') or metadata.startswith('./'): |
|
190 |
return True |
|
191 | ||
192 | ||
193 |
def idp_metadata_needs_refresh(entity_id, update_cache=False): |
|
194 |
filepath = idp_metadata_filepath(entity_id) |
|
195 |
if not default_storage.exists(filepath) or update_cache or \ |
|
196 |
default_storage.created_time(filepath) < datetime.now() - timedelta(days=1): |
|
197 |
return True |
|
198 |
return False |
|
199 | ||
200 | ||
201 |
def idp_settings_needs_refresh(entity_id, update_cache=False): |
|
202 |
filepath = idp_settings_filepath(entity_id) |
|
203 |
if not default_storage.exists(filepath) or update_cache or \ |
|
204 |
default_storage.created_time(filepath) < datetime.now() - timedelta(days=1): |
|
205 |
return True |
|
206 |
return False |
|
207 | ||
208 | ||
209 |
def idp_metadata_store(metadata_content): |
|
210 |
entity_id = idp_metadata_extract_entity_id(metadata_content) |
|
211 |
if not entity_id: |
|
212 |
return |
|
213 |
logger = logging.getLogger(__name__) |
|
214 |
filepath = idp_metadata_filepath(entity_id) |
|
215 | ||
216 |
dirname = os.path.dirname(filepath) |
|
217 |
if not default_storage.exists(dirname): |
|
218 |
os.makedirs(default_storage.path(dirname)) |
|
219 | ||
220 |
if idp_metadata_needs_refresh(entity_id): |
|
221 |
with open(default_storage.path(filepath), 'w') as f: |
|
222 |
try: |
|
223 |
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
224 |
f.write(metadata_content) |
|
225 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
226 |
except: |
|
227 |
logger.error('Couldn\'t store metadata for EntityID %r', |
|
228 |
entity_id) |
|
229 |
return |
|
230 |
return default_storage.path(filepath) |
|
231 | ||
232 | ||
233 |
def idp_metadata_load(entity_id): |
|
234 |
logger = logging.getLogger(__name__) |
|
235 |
filepath = idp_metadata_filepath(entity_id) |
|
236 |
if default_storage.exists(filepath): |
|
237 |
logger.info('Loading metadata for EntityID %r', entity_id) |
|
238 |
with open(default_storage.path(filepath), 'r') as f: |
|
239 |
return f.read() |
|
240 |
else: |
|
241 |
logger.warning('No metadata file for EntityID %r', entity_id) |
|
242 | ||
243 | ||
244 |
def idp_settings_store(idp): |
|
245 |
""" |
|
246 |
Stores an IDP settings when loaded from a federation. |
|
247 |
""" |
|
248 |
logger = logging.getLogger(__name__) |
|
249 |
entity_id = idp.get('ENTITY_ID') |
|
250 |
filepath = idp_settings_filepath(entity_id) |
|
251 |
idp_settings = {} |
|
252 | ||
253 |
if not entity_id: |
|
254 |
return |
|
255 | ||
256 |
dirname = os.path.dirname(filepath) |
|
257 |
if not default_storage.exists(dirname): |
|
258 |
os.makedirs(default_storage.path(dirname)) |
|
259 | ||
260 |
for key, value in idp.items(): |
|
261 |
if key not in ('METADATA', 'ENTITY_ID'): |
|
262 |
idp_settings.update({key: value}) |
|
263 | ||
264 |
if idp_settings_needs_refresh(entity_id) and idp_settings: |
|
265 |
with open(default_storage.path(filepath), 'w') as f: |
|
266 |
try: |
|
267 |
fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) |
|
268 |
f.write(json.dumps(idp_settings)) |
|
269 |
fcntl.lockf(f, fcntl.LOCK_UN) |
|
270 |
except: |
|
271 |
logger.error('Couldn\'t store settings for EntityID %r', |
|
272 |
entity_id) |
|
273 | ||
274 | ||
275 |
def idp_settings_load(entity_id): |
|
276 |
logger = logging.getLogger(__name__) |
|
277 |
filepath = idp_settings_filepath(entity_id) |
|
278 |
if default_storage.exists(filepath): |
|
279 |
logger.info('Loading JSON settings for EntityID %r', entity_id) |
|
280 |
with open(default_storage.path(filepath), 'r') as f: |
|
281 |
try: |
|
282 |
idp_settings = json.loads(f.read()) |
|
283 |
except: |
|
284 |
logger.warning('Couldn\'t load JSON settings for EntityID %r', |
|
285 |
entity_id) |
|
286 |
else: |
|
287 |
return idp_settings |
|
288 |
else: |
|
289 |
logger.warning('No JSON settings file for EntityID %r', entity_id) |
|
290 | ||
291 |
return {} |
|
292 | ||
293 | ||
294 |
def idp_metadata_extract_entity_id(metadata_content): |
|
295 |
logger = logging.getLogger(__name__) |
|
296 |
try: |
|
297 |
doc = ET.fromstring(metadata_content) |
|
298 |
except (TypeError, ET.ParseError): |
|
299 |
logger.error(u'METADATA of idp %r is invalid', metadata_content) |
|
300 |
return |
|
301 |
if doc.tag != '{%s}EntityDescriptor' % lasso.SAML2_METADATA_HREF: |
|
302 |
logger.error(u'METADATA of idp %r has no EntityDescriptor root tag', |
|
303 |
metadata_content) |
|
304 |
return |
|
305 |
if not 'entityID' in doc.attrib: |
|
306 |
logger.error( |
|
307 |
u'METADATA of idp %r has no entityID attribute on its root tag', |
|
308 |
metadata_content) |
|
309 |
return |
|
310 |
return doc.attrib['entityID'] |
mellon/middleware.py | ||
---|---|---|
23 | 23 |
# Skip mellon views |
24 | 24 |
if request.resolver_match.url_name and request.resolver_match.url_name.startswith('mellon_'): |
25 | 25 |
return |
26 |
if not any(utils.get_idps()):
|
|
26 |
if not app_settings.FEDERATIONS and not app_settings.IDENTITY_PROVIDERS:
|
|
27 | 27 |
return |
28 | 28 |
if not app_settings.OPENED_SESSION_COOKIE_NAME: |
29 | 29 |
return |
mellon/utils.py | ||
---|---|---|
3 | 3 |
import importlib |
4 | 4 |
from functools import wraps |
5 | 5 |
import isodate |
6 |
import requests |
|
7 |
import requests.exceptions |
|
6 | 8 |
from xml.parsers import expat |
9 |
import os |
|
7 | 10 | |
8 | 11 |
from django.contrib import auth |
12 |
from django.core.exceptions import ValidationError |
|
13 |
from django.core.files.storage import default_storage |
|
9 | 14 |
from django.core.urlresolvers import reverse |
15 |
from django.core.validators import URLValidator |
|
10 | 16 |
from django.template.loader import render_to_string |
11 | 17 |
from django.utils.timezone import make_aware, now, make_naive, is_aware, get_default_timezone |
12 | 18 |
from django.conf import settings |
... | ... | |
14 | 20 |
import lasso |
15 | 21 | |
16 | 22 |
from . import app_settings |
23 |
from .federation_utils import get_federation_from_url, idp_metadata_is_file, \ |
|
24 |
idp_metadata_load, idp_metadata_extract_entity_id, \ |
|
25 |
get_entity_id_from_fingerprint |
|
17 | 26 | |
18 | 27 | |
19 | 28 |
def create_metadata(request): |
... | ... | |
47 | 56 | |
48 | 57 | |
49 | 58 |
def create_server(request): |
59 |
metadata = create_metadata(request) |
|
60 |
if app_settings.PRIVATE_KEY: |
|
61 |
private_key = app_settings.PRIVATE_KEY |
|
62 |
private_key_password = app_settings.PRIVATE_KEY_PASSWORD |
|
63 |
elif app_settings.PRIVATE_KEYS: |
|
64 |
private_key = app_settings.PRIVATE_KEYS[0] |
|
65 |
private_key_password = None |
|
66 |
if isinstance(private_key, (tuple, list)): |
|
67 |
private_key_password = private_key[1] |
|
68 |
private_key = private_key[0] |
|
69 |
else: # no signature |
|
70 |
private_key = None |
|
71 |
private_key_password = None |
|
72 |
server = lasso.Server.newFromBuffers(metadata, private_key_content=private_key, |
|
73 |
private_key_password=private_key_password) |
|
74 |
server.setEncryptionPrivateKeyWithPassword(private_key, private_key_password) |
|
75 |
private_keys = app_settings.PRIVATE_KEYS |
|
76 |
# skip first key if it is already loaded |
|
77 |
if not app_settings.PRIVATE_KEY: |
|
78 |
private_keys = app_settings.PRIVATE_KEYS[1:] |
|
79 |
for key in private_keys: |
|
80 |
password = None |
|
81 |
if isinstance(key, (tuple, list)): |
|
82 |
password = key[1] |
|
83 |
key = key[0] |
|
84 |
server.setEncryptionPrivateKeyWithPassword(key, password) |
|
85 |
return server |
|
86 | ||
87 | ||
88 |
def get_federation_metadata(federation): |
|
50 | 89 |
logger = logging.getLogger(__name__) |
51 |
root = request.build_absolute_uri('/') |
|
52 |
cache = getattr(settings, '_MELLON_SERVER_CACHE', {}) |
|
53 |
if root not in cache: |
|
54 |
metadata = create_metadata(request) |
|
55 |
if app_settings.PRIVATE_KEY: |
|
56 |
private_key = app_settings.PRIVATE_KEY |
|
57 |
private_key_password = app_settings.PRIVATE_KEY_PASSWORD |
|
58 |
elif app_settings.PRIVATE_KEYS: |
|
59 |
private_key = app_settings.PRIVATE_KEYS[0] |
|
60 |
private_key_password = None |
|
61 |
if isinstance(private_key, (tuple, list)): |
|
62 |
private_key_password = private_key[1] |
|
63 |
private_key = private_key[0] |
|
64 |
else: # no signature |
|
65 |
private_key = None |
|
66 |
private_key_password = None |
|
67 |
server = lasso.Server.newFromBuffers(metadata, private_key_content=private_key, |
|
68 |
private_key_password=private_key_password) |
|
69 |
server.setEncryptionPrivateKeyWithPassword(private_key, private_key_password) |
|
70 |
private_keys = app_settings.PRIVATE_KEYS |
|
71 |
# skip first key if it is already loaded |
|
72 |
if not app_settings.PRIVATE_KEY: |
|
73 |
private_keys = app_settings.PRIVATE_KEYS[1:] |
|
74 |
for key in private_keys: |
|
75 |
password = None |
|
76 |
if isinstance(key, (tuple, list)): |
|
77 |
password = key[1] |
|
78 |
key = key[0] |
|
79 |
server.setEncryptionPrivateKeyWithPassword(key, password) |
|
80 |
for idp in get_idps(): |
|
81 |
try: |
|
82 |
server.addProviderFromBuffer(lasso.PROVIDER_ROLE_IDP, idp['METADATA']) |
|
83 |
except lasso.Error as e: |
|
84 |
logger.error(u'bad metadata in idp %r', idp['ENTITY_ID']) |
|
85 |
logger.debug(u'lasso error: %s', e) |
|
86 |
continue |
|
87 |
cache[root] = server |
|
88 |
settings._MELLON_SERVER_CACHE = cache |
|
89 |
return settings._MELLON_SERVER_CACHE.get(root) |
|
90 | ||
91 | ||
92 |
def create_login(request): |
|
93 |
server = create_server(request) |
|
90 |
fedmd = None |
|
91 |
pemcert = None |
|
92 |
if (isinstance(federation, tuple) and len(federation) == 2): |
|
93 |
logger.info('Loading local cert-based federation %r', |
|
94 |
federation) |
|
95 |
if federation[1].endswith('.pem'): |
|
96 |
fedmd = federation[0] |
|
97 |
pemcert = federation[1] |
|
98 |
else: |
|
99 |
urlval = URLValidator() |
|
100 |
try: |
|
101 |
urlval(federation) |
|
102 |
except ValidationError: |
|
103 |
logger.info('Loading file-based federation %s', |
|
104 |
federation) |
|
105 |
fedmd = federation |
|
106 |
else: |
|
107 |
logger.info('Fetching and loading url-based federation %s', |
|
108 |
federation) |
|
109 |
fedmd = get_federation_from_url(federation) |
|
110 |
return (fedmd, pemcert) |
|
111 | ||
112 | ||
113 |
def create_login(request, server=None): |
|
114 |
if not server: |
|
115 |
server = create_server(request) |
|
94 | 116 |
login = lasso.Login(server) |
95 | 117 |
if not app_settings.PRIVATE_KEY and not app_settings.PRIVATE_KEYS: |
96 | 118 |
login.setSignatureHint(lasso.PROFILE_SIGNATURE_HINT_FORBID) |
... | ... | |
113 | 135 |
yield idp |
114 | 136 | |
115 | 137 | |
138 |
def get_federations(): |
|
139 |
for adapter in get_adapters(): |
|
140 |
if hasattr(adapter, 'get_federations'): |
|
141 |
for federation in adapter.get_federations(): |
|
142 |
yield federation |
|
143 | ||
144 | ||
116 | 145 |
def flatten_datetime(d): |
117 | 146 |
d = d.copy() |
118 | 147 |
for key, value in d.items(): |
... | ... | |
180 | 209 |
return idp.get(name) or getattr(app_settings, name, default) |
181 | 210 | |
182 | 211 | |
183 |
def create_logout(request): |
|
212 |
def create_logout(request, server=None):
|
|
184 | 213 |
logger = logging.getLogger(__name__) |
185 |
server = create_server(request) |
|
214 |
if not server: |
|
215 |
server = create_server(request) |
|
186 | 216 |
mellon_session = request.session.get('mellon_session', {}) |
187 | 217 |
entity_id = mellon_session.get('issuer') |
188 | 218 |
session_index = mellon_session.get('session_index') |
... | ... | |
259 | 289 |
parser.XmlDeclHandler = xmlDeclHandler |
260 | 290 |
parser.Parse(content, True) |
261 | 291 |
return xml_encoding |
292 | ||
293 | ||
294 |
def add_service_provider(server, entity_id): |
|
295 |
logger = logging.getLogger(__name__) |
|
296 | ||
297 |
metadata_dir = default_storage.path('metadata-cache') |
|
298 | ||
299 |
if not os.path.exists(metadata_dir) or \ |
|
300 |
os.stat(metadata_dir).st_mtime > 3600: |
|
301 |
reload_idps() |
|
302 | ||
303 |
idp = get_idp(entity_id) |
|
304 |
if not idp or not idp.get('METADATA'): |
|
305 |
logger.error('no valid idp found for entity ID %r', entity_id) |
|
306 |
return |
|
307 | ||
308 |
metadata = idp['METADATA'] |
|
309 |
try: |
|
310 |
if metadata.startswith('/') or metadata.startswith('./') or \ |
|
311 |
('FEDERATION' in idp and idp_metadata_is_file(metadata)): |
|
312 |
# Simply call the adequate built-in lasso routine |
|
313 |
server.addProvider(lasso.PROVIDER_ROLE_IDP, metadata) |
|
314 |
else: |
|
315 |
# The metadata supplied is directly the content buffer: |
|
316 |
server.addProviderFromBuffer(lasso.PROVIDER_ROLE_IDP, metadata) |
|
317 |
except lasso.ServerAddProviderFailedError as e: |
|
318 |
logger.error('Error %s: Failed to load idp %s', e, metadata) |
|
319 | ||
320 | ||
321 |
def create_loaded_server(request, entity_id = None): |
|
322 |
payload = None |
|
323 | ||
324 |
if not entity_id: |
|
325 |
if request.method == 'GET': |
|
326 |
if 'SAMLResponse' in request.GET or 'SAMLRequest' in request.GET: |
|
327 |
payload = request.META['QUERY_STRING'] |
|
328 |
elif 'SAMLart' in request.GET: |
|
329 |
entity_id = get_entity_id_from_fingerprint(request.GET['SAMLart']) |
|
330 |
elif request.method == 'POST': |
|
331 |
if 'SAMLResponse' in request.POST: |
|
332 |
payload = request.POST['SAMLResponse'] |
|
333 |
elif 'SAMLRequest' in request.POST: |
|
334 |
payload = request.POST['SAMLRequest'] |
|
335 | ||
336 |
if not entity_id and payload: |
|
337 |
entity_id = lasso.profileGetIssuer(payload) |
|
338 | ||
339 |
server = create_server(request) |
|
340 |
add_service_provider(server, entity_id) |
|
341 | ||
342 |
return server |
|
343 | ||
344 | ||
345 |
def get_metadata_from_url(idp): |
|
346 |
logger = logging.getLogger(__name__) |
|
347 | ||
348 |
verify_ssl_certificate = get_setting( |
|
349 |
idp, 'VERIFY_SSL_CERTIFICATE') |
|
350 | ||
351 |
try: |
|
352 |
response = requests.get(idp['METADATA_URL'], verify=verify_ssl_certificate) |
|
353 |
response.raise_for_status() |
|
354 |
except requests.exceptions.RequestException as e: |
|
355 |
logger.error( |
|
356 |
u'retrieval of metadata URL %r failed with error %s', |
|
357 |
idp['METADATA_URL'], e) |
|
358 |
else: |
|
359 |
return response.content.decode('utf-8') |
|
360 | ||
361 | ||
362 |
def reload_idps(): |
|
363 |
for idp in get_idps(): |
|
364 |
pass |
mellon/views.py | ||
---|---|---|
1 |
import base64 |
|
2 |
import binascii |
|
1 | 3 |
import logging |
2 | 4 |
import requests |
3 | 5 |
import lasso |
... | ... | |
19 | 21 |
from django.db import transaction |
20 | 22 |
from django.utils.translation import ugettext as _ |
21 | 23 | |
22 |
from . import app_settings, utils |
|
24 |
from . import app_settings, utils, federation_utils |
|
25 |
from .federation_utils import idp_metadata_load, get_entity_id_from_fingerprint |
|
23 | 26 | |
24 | 27 | |
25 | 28 |
lasso.setFlag('thin-sessions') |
... | ... | |
111 | 114 |
if not utils.is_nonnull(request.POST['SAMLResponse']): |
112 | 115 |
return HttpResponseBadRequest('SAMLResponse contains a null character') |
113 | 116 |
self.log.info('Got SAML Response', extra={'saml_response': request.POST['SAMLResponse']}) |
114 |
self.profile = login = utils.create_login(request) |
|
117 | ||
118 |
server = utils.create_loaded_server(request) |
|
119 |
self.profile = login = utils.create_login(request, server) |
|
120 | ||
115 | 121 |
idp_message = None |
116 | 122 |
status_codes = [] |
123 | ||
117 | 124 |
# prevent null characters in SAMLResponse |
118 | 125 |
try: |
119 | 126 |
login.processAuthnResponseMsg(request.POST['SAMLResponse']) |
... | ... | |
233 | 240 | |
234 | 241 |
def continue_sso_artifact(self, request, method): |
235 | 242 |
idp_message = None |
243 |
entity_id = None |
|
236 | 244 |
status_codes = [] |
237 | 245 | |
238 | 246 |
if method == lasso.HTTP_METHOD_ARTIFACT_GET: |
... | ... | |
244 | 252 |
artifact = request.POST['SAMLart'] |
245 | 253 |
relay_state = request.POST.get('RelayState') |
246 | 254 | |
247 |
self.profile = login = utils.create_login(request) |
|
255 |
try: |
|
256 |
decoded_artifact = base64.b64decode(artifact) |
|
257 |
except: |
|
258 |
self.log.warning(u'artifact %r is not base64-encoded', artifact) |
|
259 |
# let error be caught later, during request initialization |
|
260 |
else: |
|
261 |
fingerprint = decoded_artifact[4:24] |
|
262 |
entity_id = get_entity_id_from_fingerprint(fingerprint) |
|
263 | ||
264 |
if entity_id: |
|
265 |
server = utils.create_server(request) |
|
266 |
idp = utils.get_idp(entity_id) |
|
267 |
utils.add_service_provider(server, idp) |
|
268 |
else: |
|
269 |
self.log.warning('no entity id found for artifact %s', artifact) |
|
270 |
self.log.warning('loading full set of providers') |
|
271 |
server = utils.create_loaded_server(request) |
|
272 | ||
273 |
self.profile = login = utils.create_login(request, server) |
|
274 | ||
248 | 275 |
if relay_state and utils.is_nonnull(relay_state): |
249 | 276 |
login.msgRelayState = relay_state |
277 | ||
250 | 278 |
try: |
251 | 279 |
login.initRequest(message, method) |
252 | 280 |
except lasso.ProfileInvalidArtifactError: |
... | ... | |
349 | 377 |
idp = self.get_idp(request) |
350 | 378 |
if idp is None: |
351 | 379 |
return HttpResponseBadRequest('no idp found') |
352 |
self.profile = login = utils.create_login(request) |
|
353 |
self.log.debug('authenticating to %r', idp['ENTITY_ID']) |
|
380 |
entity_id = idp.get('ENTITY_ID') or federation_utils.idp_metadata_extract_entity_id(idp.get('METADATA')) |
|
381 |
self.log.debug('authenticating to %r', entity_id) |
|
382 | ||
383 |
server = utils.create_loaded_server(request, entity_id) |
|
384 |
self.profile = login = utils.create_login(request, server) |
|
385 | ||
354 | 386 |
try: |
355 |
login.initAuthnRequest(idp['ENTITY_ID'], lasso.HTTP_METHOD_REDIRECT)
|
|
387 |
login.initAuthnRequest(entity_id, lasso.HTTP_METHOD_REDIRECT)
|
|
356 | 388 |
authn_request = login.request |
357 | 389 |
# configure NameID policy |
358 | 390 |
policy = authn_request.nameIdPolicy |
... | ... | |
409 | 441 | |
410 | 442 |
def idp_logout(self, request): |
411 | 443 |
'''Handle logout request emitted by the IdP''' |
412 |
self.profile = logout = utils.create_logout(request) |
|
444 |
server = utils.create_loaded_server(request) |
|
445 |
self.profile = logout = utils.create_logout(request, server) |
|
413 | 446 |
try: |
414 | 447 |
logout.processRequestMsg(request.META['QUERY_STRING']) |
415 | 448 |
except lasso.Error as e: |
... | ... | |
438 | 471 |
try: |
439 | 472 |
issuer = request.session.get('mellon_session', {}).get('issuer') |
440 | 473 |
if issuer: |
441 |
self.profile = logout = utils.create_logout(request) |
|
474 |
server = utils.create_loaded_server(request) |
|
475 |
self.profile = logout = utils.create_logout(request, server) |
|
442 | 476 |
try: |
443 | 477 |
if 'lasso_session_dump' in request.session: |
444 | 478 |
logout.setSessionFromDump(request.session['lasso_session_dump']) |
... | ... | |
464 | 498 | |
465 | 499 |
def sp_logout_response(self, request): |
466 | 500 |
'''Launch a logout request to the identity provider''' |
467 |
self.profile = logout = utils.create_logout(request) |
|
501 |
server = utils.create_loaded_server(request) |
|
502 |
self.profile = logout = utils.create_logout(request, server) |
|
468 | 503 |
# the user shouldn't be logged anymore at this point but it may happen |
469 | 504 |
# that a concurrent SSO happened in the meantime, so we do another |
470 | 505 |
# logout to make sure. |
setup.py | ||
---|---|---|
94 | 94 |
'django>=1.5,<2.0', |
95 | 95 |
'requests', |
96 | 96 |
'isodate', |
97 |
'pytz', |
|
97 | 98 |
], |
98 | 99 |
setup_requires=[ |
99 | 100 |
'django>=1.5,<2.0', |
tests/conftest.py | ||
---|---|---|
42 | 42 |
caplog.handler.stream = py.io.TextIO() |
43 | 43 |
caplog.handler.records = [] |
44 | 44 |
return caplog |
45 | ||
46 | ||
47 |
# XXX temporary workaround |
|
48 |
# non-federated IdPs shouldn't have their MD cached |
|
49 |
@pytest.fixture(autouse=True) |
|
50 |
def mellon_settings(settings, tmpdir): |
|
51 |
settings.MEDIA_ROOT = str(tmpdir) |
tests/dummy_md.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:pyff="http://pyff.io/NS" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xrd="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_20171018T113001Z" Name="https://federation.renater.fr/" cacheDuration="PT1H" validUntil="2017-10-27T11:30:01Z"><ds:Signature> |
|
2 |
<ds:SignedInfo> |
|
3 |
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> |
|
4 |
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> |
|
5 |
<ds:Reference URI=""> |
|
6 |
<ds:Transforms> |
|
7 |
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> |
|
8 |
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> |
|
9 |
</ds:Transforms> |
|
10 |
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> |
|
11 |
<ds:DigestValue>JKdLdd5yGvkFdb1fCAByMMnurIKYhZepRouZfOjIUrg=</ds:DigestValue> |
|
12 |
</ds:Reference> |
|
13 |
</ds:SignedInfo> |
|
14 |
<ds:SignatureValue> |
|
15 |
OTexfi8c63TsP1V9j5m6digA2NomUfqBtT8pPKhwdqEDQS5qLh6fxvT+wWkP6JaIhkP8nxwpbArl |
|
16 |
7cUHkRv5ibZzcknIAjXYMhsSTtFQUq89OMcDHtZHG54jiKyHPhu2+XEbvv6DsAYanYC6SHEnGjNG |
|
17 |
opnOEUB2XqeycsvvTQQIuWZEoABTVcKYyk2CW7Ij5EUmPOAPiidtbt8lzrtkV6dwLbkyoEbChAyj |
|
18 |
emrL/oS01aJgT9sQoJxR8lyRMGiZ/BwQqYTareiKwOXLPdGThzsfZXD8de9T1xuysILaAM7sHPJV |
|
19 |
QfrQJm80Zo2MM/GnhJTO9rc4m3kRnRhqmA6qMw== |
|
20 |
</ds:SignatureValue> |
|
21 |
<ds:KeyInfo> |
|
22 |
<ds:KeyValue> |
|
23 |
<ds:RSAKeyValue> |
|
24 |
<ds:Modulus> |
|
25 |
71+vTf66BPgYUF7sm4T++W69qMVyGQn9wNqpBLc6sp53eq/JRTOUD26Yehjsld5qN52Bv2r5QG7o |
|
26 |
4VU123akXUYzupvq1f+tmF9NwYa7MPEPFzCzJHhNXjZNRxcsW1WLW34fhQCm0oak3oSPoNo5qeGi |
|
27 |
jNsTSkgSt1mPH0P8d95af2VJnT6zbrclxvH4emqpT9oGLsWqKWLlIbZ7u1PUjuNVwLHuj909/apm |
|
28 |
C13RBIpV52fey4qey34bnRHdCTknZeN/TJLTJ9hMWzz9TbdjfIFaiF7MeY+OYRXzUJeQuHHMu/2I |
|
29 |
emkoR26mYi6irvmx8AdPcPCwcRKw2Ca4xLhbNw== |
|
30 |
</ds:Modulus> |
|
31 |
<ds:Exponent>AQAB</ds:Exponent> |
|
32 |
</ds:RSAKeyValue> |
|
33 |
</ds:KeyValue> |
|
34 |
<ds:X509Data> |
|
35 |
<ds:X509Certificate> |
|
36 |
MIIC9zCCAd+gAwIBAgIEfe6j3jANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDEyFTQU1MIE1ldGFk |
|
37 |
YXRhIFNpZ25pbmcgQ2VydGlmaWNhdGUwHhcNMTYwNzI5MDczNjM4WhcNMjYwNjA3MDczNjM4WjAs |
|
38 |
MSowKAYDVQQDEyFTQU1MIE1ldGFkYXRhIFNpZ25pbmcgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb3 |
|
39 |
DQEBAQUAA4IBDwAwggEKAoIBAQDvX69N/roE+BhQXuybhP75br2oxXIZCf3A2qkEtzqynnd6r8lF |
|
40 |
M5QPbph6GOyV3mo3nYG/avlAbujhVTXbdqRdRjO6m+rV/62YX03Bhrsw8Q8XMLMkeE1eNk1HFyxb |
|
41 |
VYtbfh+FAKbShqTehI+g2jmp4aKM2xNKSBK3WY8fQ/x33lp/ZUmdPrNutyXG8fh6aqlP2gYuxaop |
|
42 |
YuUhtnu7U9SO41XAse6P3T39qmYLXdEEilXnZ97Lip7LfhudEd0JOSdl439MktMn2ExbPP1Nt2N8 |
|
43 |
gVqIXsx5j45hFfNQl5C4ccy7/Yh6aShHbqZiLqKu+bHwB09w8LBxErDYJrjEuFs3AgMBAAGjITAf |
|
44 |
MB0GA1UdDgQWBBTT88iZzWO+hN9SBUkpx871lmTuLTANBgkqhkiG9w0BAQsFAAOCAQEABoPpODry |
|
45 |
XwiM5jjtqk6veR02FevCKHpZP6Od7Kqcfs6lg5LcQmGUOgpmW3Gg4UMjBYkgARsT2Nsnah1CJqa8 |
|
46 |
cjvv8p5KEIhY0hVS8iMJnrb3PDeiFSeP4xSfct/6z/ebV4+QFl22bsm2zpAC6BpFz8+IJ/jAmQzT |
|
47 |
Vob4MAUeQPnwwzm3xz6yanLZx7BK5cfrTCa+hrarNQCboRjXPwiejF8WRCxpgRHH6yNs5QH/Z6o5 |
|
48 |
e3tUP7uEpn2Ob+kcLsEMGb9DghkoDAgkHCOZeTy+7hgxt+/T94cLTa58gVtvEOnd0GuL7Vfd+IVd |
|
49 |
XgSard8RfR3OyZlf6M4aSGQA73sskQ== |
|
50 |
</ds:X509Certificate> |
|
51 |
</ds:X509Data> |
|
52 |
</ds:KeyInfo> |
|
53 |
</ds:Signature><md:EntityDescriptor entityID="https://aishib.agropolis.fr/idp/shibboleth"> |
|
54 |
<md:Extensions> |
|
55 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2013-06-06T11:49:20Z"> |
|
56 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
57 |
</mdrpi:RegistrationInfo> |
|
58 |
</md:Extensions> |
|
59 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
60 |
<md:Extensions> |
|
61 |
<shibmd:Scope regexp="false">agropolis.fr</shibmd:Scope> |
|
62 | ||
63 |
<mdui:UIInfo> |
|
64 | ||
65 |
<mdui:DisplayName xml:lang="en">Agropolis International</mdui:DisplayName> |
|
66 | ||
67 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAACPUlEQVQoz21SPW8TQRR8u7d3ju34Er6UCHACRqK4Bjk0oDSEjiYSBQVS8k9AkWgp+AuUASGEaCASkoPcgARVMF8iTnxJwMFJfL47397u3u6jsGVFwLzmFTN6ozdDEBEAAOD4kiRJsVgkhMA/YMfZzc3NVstnzGq1/KXlJfgf2Ii9XquVZ2YWbi5Io4MsbfPeZK4wRm0AOH6KICIivq2tV6/O5cYLHztbW9FvYTKuZaLk7PjpW+UrJXtspCHGmGazCQBnZ8svtj/0tbAINYhcy1DyThpRhHtztyedwkBDAWBnZ6dSqbzZ+7THu0IraTJplDSZ0JnQ2Y+482jj1cgSRUTHdrhW9fa3SKaxErFKIyUiKaIsjVQaKf5s631HxP1+X2vNwjB0J9zd+OgnD044Ra4lI5ZBw7XqyeRQREciaqe9xoE/K/OFfIENnqBM1uZBqlWBOYxQjYZnqieTThru8zCU3AASIAjIXNellGLePkjCUPICy1mEDAShSg7SqCsTBtQ7WfY3vlarVUYpdUsuAs5PXV7dfpe3HEYtjSbNZJwJoRUi3rl4fSrvfufctm0ySA0RD0V84+WDL1EbCCDCYIDApeKZ+uKK6faDIPA8jw7jIORUbry2eH+5Mu8ARTSAkCPW3QvX6osrE+B8bjQ8zxu2aACDaIwxxuzzsLbbePj88a8k0Mb4vr/2ei3LMoOIiGRU0iGGVuDp6pNz5fNSiKnpac/zgAABMuzSXwIggIjdbrdUKjHGYGQaAAD+AJBJecXxnEvWAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjQ4OjE1KzAyOjAwpWiGMQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo0ODoxNSswMjowMNQ1Po0AAAAASUVORK5CYII=</mdui:Logo> |
|
68 |
<mdui:InformationURL xml:lang="fr">http://www.agropolis.fr</mdui:InformationURL> |
|
69 | ||
70 |
<mdui:DisplayName xml:lang="fr">Agropolis International</mdui:DisplayName> |
|
71 | ||
72 |
</mdui:UIInfo> |
|
73 |
</md:Extensions> |
|
74 |
<md:KeyDescriptor use="signing"> |
|
75 |
<ds:KeyInfo> |
|
76 | ||
77 |
<ds:X509Data> |
|
78 |
<ds:X509Certificate> |
|
79 |
MIIDNzCCAh+gAwIBAgIUYY3sGXwChkj2CRy6QFDvkdj2zlAwDQYJKoZIhvcNAQEF |
|
80 |
BQAwHjEcMBoGA1UEAxMTYWlzaGliLmFncm9wb2xpcy5mcjAeFw0xMzA1MTUxMzM3 |
|
81 |
MTJaFw0zMzA1MTUxMzM3MTJaMB4xHDAaBgNVBAMTE2Fpc2hpYi5hZ3JvcG9saXMu |
|
82 |
ZnIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxrDy6lrhIBjcxv16n |
|
83 |
4UJ2cEMYPO4wSmfDwhO6feoSIEuIblYRHE2nQKirMokwD6seF4rbDHyxLXg/ColL |
|
84 |
VLv+0CJteIOZjSCgSN90WzQRrC1Ex5sJfPu6yPEXvW8H1906gEg6ok8rlCIHRGfE |
|
85 |
15pHK5eqxQS5f2n8c2t/Uk33/FBj79/hb3Cd7vE4mdlvReD3AFswC0lV4bPmj3Ka |
|
86 |
KUuMj9xwipwnfWCu6p2/ZJF4M3ADU5grXHJ2Vqmd8DWm5raaObKjYwJddbRBByI8 |
|
87 |
bJJLIwAQQmX4Dh4hf1QKlf2oqWPWVQxLQp0erL1U8IWmj1RG8TTH9xOJl6kkEhYq |
|
88 |
Z2gfAgMBAAGjbTBrMEoGA1UdEQRDMEGCE2Fpc2hpYi5hZ3JvcG9saXMuZnKGKmh0 |
|
89 |
dHBzOi8vYWlzaGliLmFncm9wb2xpcy5mci9pZHAvc2hpYmJvbGV0aDAdBgNVHQ4E |
|
90 |
FgQU9A7iQ8Qo+t2JCpKuOOV9YBoYs4MwDQYJKoZIhvcNAQEFBQADggEBAG0LOW6I |
|
91 |
F+M8n2NpzyQjfVCJCA6QhWjbXrfemiPJFZGZZb2dVmHof4yCpCUYgHOBoZaXPOlB |
|
92 |
nLYsUWvFZ6V2GELZpLHzHSSrYidieW07qQkh1DwcIYpvtZgLviOtT/tCEGsk925f |
|
93 |
DUoGdeIqpqt54WZcW9+TbKicvjg3JT4BFOQ17bFNwPW+YjTbvsWYxen+e0mRp4vM |
|
94 |
V0yMu2f3bccVhePASSZGL3yod3sJ1dPvlrJO9c35BekhtirolVjZqMQ0AYPVifua |
|
95 |
yIU0dWXsZkAOcBL9kZFbJcYRUIxMgvp8U2Zdv1+ZlwOyXnnWDOOh9wjuT7FAyObU |
|
96 |
ChvjHlgZHkvLwJI= |
|
97 |
</ds:X509Certificate> |
|
98 |
</ds:X509Data> |
|
99 | ||
100 |
</ds:KeyInfo> |
|
101 |
</md:KeyDescriptor> |
|
102 | ||
103 | ||
104 | ||
105 | ||
106 | ||
107 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
108 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
109 | ||
110 | ||
111 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://aishib.agropolis.fr/idp/profile/SAML2/POST/SSO"/> |
|
112 | ||
113 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://aishib.agropolis.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
114 | ||
115 | ||
116 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://aishib.agropolis.fr/idp/profile/Shibboleth/SSO"/> |
|
117 | ||
118 | ||
119 |
</md:IDPSSODescriptor> |
|
120 | ||
121 | ||
122 | ||
123 |
<md:Organization> |
|
124 | ||
125 |
<md:OrganizationName xml:lang="en">Agropolis International</md:OrganizationName> |
|
126 |
<md:OrganizationDisplayName xml:lang="en">Agropolis International</md:OrganizationDisplayName> |
|
127 |
<md:OrganizationURL xml:lang="en">http://www.agropolis.fr</md:OrganizationURL> |
|
128 | ||
129 |
</md:Organization> |
|
130 | ||
131 | ||
132 | ||
133 |
<md:ContactPerson contactType="technical"> |
|
134 |
<md:SurName>Jean Cerda</md:SurName> |
|
135 |
<md:EmailAddress>cerda@agropolis.fr</md:EmailAddress> |
|
136 |
</md:ContactPerson> |
|
137 | ||
138 | ||
139 | ||
140 |
<md:ContactPerson contactType="technical"> |
|
141 |
<md:SurName>Jean-Pierre Allano</md:SurName> |
|
142 |
<md:EmailAddress>allano@agropolis.fr</md:EmailAddress> |
|
143 |
</md:ContactPerson> |
|
144 | ||
145 | ||
146 | ||
147 | ||
148 |
</md:EntityDescriptor><md:EntityDescriptor entityID="https://ambre.vetagro-sup.fr/idp/shibboleth"> |
|
149 |
<md:Extensions> |
|
150 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2013-01-14T16:11:53Z"> |
|
151 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
152 |
</mdrpi:RegistrationInfo> |
|
153 |
</md:Extensions> |
|
154 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
155 |
<md:Extensions> |
|
156 |
<shibmd:Scope regexp="false">vetagro-sup.fr</shibmd:Scope> |
|
157 | ||
158 |
<mdui:UIInfo> |
|
159 | ||
160 |
<mdui:DisplayName xml:lang="en">Vetagro Sup</mdui:DisplayName> |
|
161 | ||
162 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAB/klEQVQ4y2NkQAP///+X+/jxI/PO7dujP3/7kf7v778ffDzciw2MDFaqq6t/Y2RkfIysngndAEZGxkezZ8/25xEQqVDTs5eRVrNU4RQUL7x9+7YtumasBjAwMDBISIgbcvBKct998p7h1fvfDKyc4nz//jF6yjAwSBNlwM+fP3/9/PmT4dbl4wysrCwMf//9Z/r7959UcFISD1EGPH107xgfD9sbTg52hrcvHjDw87Aw/Pz+7tLEefNuEmWAm6ffxvu3Ll4zMjZleHz/KsO7lw+f3bh+czs2tVgNsLS0fPfs4b3VnKy//wrycW84e2L/uobmtg0MpIKurt6N////Z8jPzxcnWTMDAwPD79//tbq7+9bhU8OMTfD//8cMP79L1X/8tjxMQYHXT0Uh88aBgyuvEW3Aly+8ntw8jLU3bx20cnH/xPz2jTyvk9OGpTt3NhIOxP//GRgkJGR8tPVeyElIvWA4dfIqg57Bff0nD+eHExULIb5dqkYmYgYm5r8YPH3vMzx//omBk+edpI4+vxVRBnz5zPqbi4vt7a9fv//9+cvAYG79jIGZmZWBg4NJiCgDdh4sfHD/7u9j714Z/BYWiGBQVkxjePNK8+erV5+uYDOABTMM/nP09y9Z8+OHgNjli9ahf//++8vGwrlAS0tp/v///7kYGRm/IasHAIVTy8DG/VpJAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjUwOjA5KzAyOjAwfDtz7wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo1MDowOSswMjowMA1my1MAAAAASUVORK5CYII=</mdui:Logo> |
|
163 |
<mdui:InformationURL xml:lang="fr">http://www.vetagro-sup.fr</mdui:InformationURL> |
|
164 | ||
165 |
<mdui:DisplayName xml:lang="fr">Vetagro Sup</mdui:DisplayName> |
|
166 | ||
167 |
</mdui:UIInfo> |
|
168 |
</md:Extensions> |
|
169 |
<md:KeyDescriptor use="signing"> |
|
170 |
<ds:KeyInfo> |
|
171 | ||
172 |
<ds:X509Data> |
|
173 |
<ds:X509Certificate> |
|
174 |
MIIDPDCCAiSgAwIBAgIVAL9PsuadPSIZcMHNxlK/oevezmzWMA0GCSqGSIb3DQEB |
|
175 |
BQUAMB8xHTAbBgNVBAMTFGFtYnJlLnZldGFncm8tc3VwLmZyMB4XDTEyMTEwODEw |
|
176 |
MTQwNFoXDTMyMTEwODEwMTQwNFowHzEdMBsGA1UEAxMUYW1icmUudmV0YWdyby1z |
|
177 |
dXAuZnIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc/ptfpmkomwmT |
|
178 |
4RsID+1Ce1dX0eUjcLgSOZN8hVpHWLag2ERWkpmvB5aK7BAFcI5i//Gk80tAiasu |
|
179 |
JtlZhBnEw54aTJRGpyL2CVkHyl6SMRxprIi1Ji67IoGqEgUeGaheAxo+tG5e1WSc |
|
180 |
bIbldcSKdwvjAV+7HSB4C6NqLsAzJH25++yaRH2uf2LTD0TDzNR9Q2hVj/VyYWR+ |
|
181 |
K3HWI1Snjn/i7aFfZZhYmBkwHuQOaPhwCM+khikg5XicMsxUhHCMi93UgHGIsdkr |
|
182 |
IEGj4xydBTUKsLaykeuFS8EgXbWwCLGkeX76w8xDoFIpnppU/yFd9v7Zg3EBfn4p |
|
183 |
kTW3GdIjAgMBAAGjbzBtMEwGA1UdEQRFMEOCFGFtYnJlLnZldGFncm8tc3VwLmZy |
|
184 |
hitodHRwczovL2FtYnJlLnZldGFncm8tc3VwLmZyL2lkcC9zaGliYm9sZXRoMB0G |
|
185 |
A1UdDgQWBBTPTqWkVHrHXFjmxMWkNt/sp2h5ozANBgkqhkiG9w0BAQUFAAOCAQEA |
|
186 |
FvXMtfBUmRZCzz8CjanGzr1TBUPmnkrKci5AtkseKw9YlfUmBXTHB01y697nYq6m |
|
187 |
RB6KhvfW212h9CF0IOEEjoadgDhXqGYhq8PnAOtT4Ty3XDy8SbRh8aQWfvnfSngv |
|
188 |
FdpHRiSpj5UXXuT5zTtkf59h58XKtEfCkMbUzvdOgUobJzpD0WISmQHPQnx+Neg6 |
|
189 |
9j7oMRrDiZjS39Om8Imu9xvsnddDM3PlsDBIsvrr1o7K5iLkEdR1YYX0ZNDbiFuw |
|
190 |
QXXl2dwQPB8KrScPUvCe57slU2gFQvvIBzjQysxC6V6TPSuM3A/ee56lACuB3jKj |
|
191 |
oYkHQc5Gj/1rSMLmu9aLMg== |
|
192 |
</ds:X509Certificate> |
|
193 |
</ds:X509Data> |
|
194 | ||
195 |
</ds:KeyInfo> |
|
196 |
</md:KeyDescriptor> |
|
197 | ||
198 | ||
199 | ||
200 | ||
201 | ||
202 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
203 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
204 | ||
205 | ||
206 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://ambre.vetagro-sup.fr/idp/profile/SAML2/POST/SSO"/> |
|
207 | ||
208 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://ambre.vetagro-sup.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
209 | ||
210 | ||
211 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://ambre.vetagro-sup.fr/idp/profile/Shibboleth/SSO"/> |
|
212 | ||
213 | ||
214 |
</md:IDPSSODescriptor> |
|
215 | ||
216 | ||
217 | ||
218 |
<md:Organization> |
|
219 | ||
220 |
<md:OrganizationName xml:lang="en">Vetagro Sup</md:OrganizationName> |
|
221 |
<md:OrganizationDisplayName xml:lang="en">Vetagro Sup</md:OrganizationDisplayName> |
|
222 |
<md:OrganizationURL xml:lang="en">http://www.vetagro-sup.fr</md:OrganizationURL> |
|
223 | ||
224 |
</md:Organization> |
|
225 | ||
226 | ||
227 | ||
228 |
<md:ContactPerson contactType="technical"> |
|
229 |
<md:SurName>Nicolas Aulas</md:SurName> |
|
230 |
<md:EmailAddress>nicolas.aulas@vetagro-sup.fr</md:EmailAddress> |
|
231 |
</md:ContactPerson> |
|
232 | ||
233 | ||
234 | ||
235 | ||
236 | ||
237 | ||
238 |
</md:EntityDescriptor><md:EntityDescriptor entityID="https://antimoine.insa-strasbourg.fr/idp/shibboleth"> |
|
239 |
<md:Extensions> |
|
240 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2014-02-11T08:44:08Z"> |
|
241 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
242 |
</mdrpi:RegistrationInfo> |
|
243 |
</md:Extensions> |
|
244 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
245 |
<md:Extensions> |
|
246 |
<shibmd:Scope regexp="false">insa-strasbourg.fr</shibmd:Scope> |
|
247 | ||
248 |
<mdui:UIInfo> |
|
249 | ||
250 |
<mdui:DisplayName xml:lang="en">INSA Strasbourg</mdui:DisplayName> |
|
251 | ||
252 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAq1BMVEXeAACEhoTWz87nQTn3oqXnIBjvgnvWlpT/5+fnEAjvZWOltrXnNDH3mpT/9/fnCAD/4+fvioz3x8bnVVL3qq3nKCnvkpTnHBjvcXP3sq3vgoT/7+/nFBDnODH3mpz////nDAj3z87vlpTveXv3vr3nAACMmpznRUL3pqXnJCHnFAj/+//nDADvjoz3y87vXVr3rq3nLCnvdXP3srXvhoT/8/fvODH3npz3lpQaie6kAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAAe0lEQVQY053OwQqCQBhF4VM22VSMMWrFGP2kkIpUQ1nz/m8Ws20TeFeHb3WRnzEBBjO8xRjZrURi0TFiy+UITjTpjIKaOndZuDSGxF0j9G1+e7CuLN1nE2F7VI28tAoqMMezP1ieFOOp7RPueBXKSqdnMq8Wg66nPP0LX4uIG4jEwJ0sAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjQ5OjA0KzAyOjAwIHfmJQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo0OTowNCswMjowMFEqXpkAAAAASUVORK5CYII=</mdui:Logo> |
|
253 |
<mdui:InformationURL xml:lang="fr">http://www.insa-strasbourg.fr</mdui:InformationURL> |
|
254 | ||
255 |
<mdui:DisplayName xml:lang="fr">INSA Strasbourg</mdui:DisplayName> |
|
256 | ||
257 |
</mdui:UIInfo> |
|
258 |
</md:Extensions> |
|
259 |
<md:KeyDescriptor use="signing"> |
|
260 |
<ds:KeyInfo> |
|
261 | ||
262 |
<ds:X509Data> |
|
263 |
<ds:X509Certificate> |
|
264 |
MIIDUDCCAjigAwIBAgIVAIbX8U0uAqAhuXm1jWxiFpggtDTDMA0GCSqGSIb3DQEB |
|
265 |
CwUAMCQxIjAgBgNVBAMMGXNvdWZyZS5pbnNhLXN0cmFzYm91cmcuZnIwHhcNMTYw |
|
266 |
OTI3MTIzNjIxWhcNMzYwOTI3MTIzNjIxWjAkMSIwIAYDVQQDDBlzb3VmcmUuaW5z |
|
267 |
YS1zdHJhc2JvdXJnLmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA |
|
268 |
sEE02sLRPAG5N81DMHEeGpI2MYF8yG/RiwH07cFIlLqgV80ewOmi0FWPYijxMb8A |
|
269 |
bmx0RwUMvJBVI6WMxtT9fykhID20k8rWOuYOzvaynzVqCktqVgKoEAxP1PFE9b0n |
|
270 |
iGKFprjjNl9ZD90GOUsxbAO7yXG9Q4WBa/eThl6XkUvNkSaZp5hcdWrgcAdsae3q |
|
271 |
iD/uxFa38NXNNeRLGyfxjd2K5qYSzbwBza9s9TOq1+pfw7sxu3/4BnfQ0RLGO6co |
|
272 |
4tH4Mufh0ome4cyYk4pvW5DOd1AznxDb8HpqvE0zwEsa69c/FDX0akgFZydmc77a |
|
273 |
j6USn6JKjjbO49yGtG1gVQIDAQABo3kwdzAdBgNVHQ4EFgQUjzMsxZYiokPYxper |
|
274 |
9zadM8J0F0kwVgYDVR0RBE8wTYIZc291ZnJlLmluc2Etc3RyYXNib3VyZy5mcoYw |
|
275 |
aHR0cHM6Ly9zb3VmcmUuaW5zYS1zdHJhc2JvdXJnLmZyL2lkcC9zaGliYm9sZXRo |
|
276 |
MA0GCSqGSIb3DQEBCwUAA4IBAQBFJKsiS3yfWuDB/E+iqQ0TuQJzL5+JIcloN0dw |
|
277 |
BFxW3VZOju15zeQ7LwRBg9S4SGLMPJU+LM1lvr68cK9brut/FjF51SETIXEeCWo3 |
|
278 |
7+PIqgOCzraLNinmpU/OtN8ENalOPvpS6Jvbd23qB2t+IqOtZ+j15b0Yq4/on1E3 |
|
279 |
W2F9CVzKpe4EwmmtCPQbe7U1wvhgFylEx797pex8veWs79YSYwqvcKMh79dzl8Fo |
|
280 |
/CgsO5pDrfKmc6SGMkByq75dZj+PqhZDzZ9EFTxbrXOTaS08VRN6a5Rh2iYRnGxq |
|
281 |
yZl66tPcaIm5PHgOEmu5X4lPkUoY+Jt36Gj3SGCbYt8qH5S0 |
|
282 |
</ds:X509Certificate> |
|
283 |
</ds:X509Data> |
|
284 | ||
285 |
</ds:KeyInfo> |
|
286 |
</md:KeyDescriptor> |
|
287 | ||
288 | ||
289 | ||
290 |
<md:KeyDescriptor use="signing"> |
|
291 |
<ds:KeyInfo> |
|
292 |
<ds:X509Data> |
|
293 |
<ds:X509Certificate> |
|
294 |
MIIDXDCCAkSgAwIBAgIVAKI+qiqDCk9wTTqn7OVAoZrvj/CpMA0GCSqGSIb3DQEB |
|
295 |
BQUAMCcxJTAjBgNVBAMTHGFudGltb2luZS5pbnNhLXN0cmFzYm91cmcuZnIwHhcN |
|
296 |
MTQwMTEzMTAzOTU4WhcNMzQwMTEzMTAzOTU4WjAnMSUwIwYDVQQDExxhbnRpbW9p |
|
297 |
bmUuaW5zYS1zdHJhc2JvdXJnLmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB |
|
298 |
CgKCAQEAtuM8lRjlVjjmrHq9VtguaOMQL+Wd99BiOs56kL3Mbctg1FwH69LYThCW |
|
299 |
6dOz6WJg/jU/naF7jEikXKc71xGyu7Ph7Iqa9S5hoXXAT8u/0q2nZDeTOraJqKe1 |
|
300 |
FMF2RzXhEEMyQO3CiKNK9b+tbKoNZS7FQCixMZklWZPt4EcEKd6jyRq1WYX3dpnb |
|
301 |
r9I/aCdhtK/PGvGe5gKTDoTR2HKyWKJTc/obf8x/vlYIEwiaGgdlqI2KiBE0x48n |
|
302 |
zQdP6XVi3T8ZWbnkLmCfgJtP2C8PtEJuwDRAy0Z9N4DSwvxn5YCVYgBLSi0TLa10 |
|
303 |
B/lUqqBezZrTrA9p9Lt8JtGXW5YGHwIDAQABo38wfTBcBgNVHREEVTBTghxhbnRp |
|
304 |
bW9pbmUuaW5zYS1zdHJhc2JvdXJnLmZyhjNodHRwczovL2FudGltb2luZS5pbnNh |
|
305 |
LXN0cmFzYm91cmcuZnIvaWRwL3NoaWJib2xldGgwHQYDVR0OBBYEFLFkjPZUc9JY |
|
306 |
qrWjldJ/iGGkKAt4MA0GCSqGSIb3DQEBBQUAA4IBAQBSk/wU1mRn4VF2ifmy261K |
|
307 |
DK7uX+t1H1hh8S38fKSFU7HoNXJTV3vQnmBOpYIGC1gtvmb+qjqpNtikU2zO84Gq |
|
308 |
Q0bXHxYF2d9RUP89mKaFxE5uNcXFmlOA3ChZY3pMT5zwAPI/T60tGrex7zci7OLn |
|
309 |
JDAQj/q4Yk9ejx6JTFggQSCCVh+oV/SDIMd2p5AY6H3mto3b6XCk7Lssa8a/D30k |
|
310 |
pEkZnhTKdN82eRyynuOR7UDU4tasV4d7Mi/j53f5ihnRcsvwh/pYodjoVYY8cEcZ |
|
311 |
JLnAXYF8coSwh8UN4D/0NHsvTuSOFQc85hGrqacMsvxiQiw9mv01AX5+A5YLEbVQ |
|
312 |
</ds:X509Certificate> |
|
313 |
</ds:X509Data> |
|
314 |
</ds:KeyInfo> |
|
315 |
</md:KeyDescriptor> |
|
316 | ||
317 | ||
318 | ||
319 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/Redirect/SLO"/> |
|
320 | ||
321 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/POST/SLO"/> |
|
322 | ||
323 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/SOAP/SLO"/> |
|
324 | ||
325 | ||
326 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
327 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
328 | ||
329 | ||
330 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/POST/SSO"/> |
|
331 | ||
332 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
333 | ||
334 | ||
335 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://antimoine.insa-strasbourg.fr/idp/profile/Shibboleth/SSO"/> |
|
336 | ||
337 | ||
338 |
</md:IDPSSODescriptor> |
|
339 | ||
340 | ||
341 | ||
342 |
<md:Organization> |
|
343 | ||
344 |
<md:OrganizationName xml:lang="en">INSA Strasbourg</md:OrganizationName> |
|
345 |
<md:OrganizationDisplayName xml:lang="en">INSA Strasbourg</md:OrganizationDisplayName> |
|
346 |
<md:OrganizationURL xml:lang="en">http://www.insa-strasbourg.fr</md:OrganizationURL> |
|
347 | ||
348 |
</md:Organization> |
|
349 | ||
350 | ||
351 | ||
352 |
<md:ContactPerson contactType="technical"> |
|
353 |
<md:SurName>Lahsen BOUZID</md:SurName> |
|
354 |
<md:EmailAddress>lahsen.bouzid@insa-strasbourg.fr</md:EmailAddress> |
|
355 |
</md:ContactPerson> |
|
356 | ||
357 | ||
358 | ||
359 |
<md:ContactPerson contactType="technical"> |
|
360 |
<md:SurName>Simon SCHERRER</md:SurName> |
|
361 |
<md:EmailAddress>simon.scherrer@insa-strasbourg.fr</md:EmailAddress> |
|
362 |
</md:ContactPerson> |
|
363 | ||
364 | ||
365 | ||
366 | ||
367 |
</md:EntityDescriptor></md:EntitiesDescriptor> |
tests/federation-sample.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="UTF-8" standalone="no"?><md:EntitiesDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute" xmlns:mdrpi="urn:oasis:names:tc:SAML:metadata:rpi" xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui" xmlns:pyff="http://pyff.io/NS" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xrd="http://docs.oasis-open.org/ns/xri/xrd-1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="_20171018T113001Z" Name="https://federation.renater.fr/" cacheDuration="PT1H" validUntil="2017-10-27T11:30:01Z"><ds:Signature> |
|
2 |
<ds:SignedInfo> |
|
3 |
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> |
|
4 |
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/> |
|
5 |
<ds:Reference URI=""> |
|
6 |
<ds:Transforms> |
|
7 |
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> |
|
8 |
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/> |
|
9 |
</ds:Transforms> |
|
10 |
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> |
|
11 |
<ds:DigestValue>JKdLdd5yGvkFdb1fCAByMMnurIKYhZepRouZfOjIUrg=</ds:DigestValue> |
|
12 |
</ds:Reference> |
|
13 |
</ds:SignedInfo> |
|
14 |
<ds:SignatureValue> |
|
15 |
OTexfi8c63TsP1V9j5m6digA2NomUfqBtT8pPKhwdqEDQS5qLh6fxvT+wWkP6JaIhkP8nxwpbArl |
|
16 |
7cUHkRv5ibZzcknIAjXYMhsSTtFQUq89OMcDHtZHG54jiKyHPhu2+XEbvv6DsAYanYC6SHEnGjNG |
|
17 |
opnOEUB2XqeycsvvTQQIuWZEoABTVcKYyk2CW7Ij5EUmPOAPiidtbt8lzrtkV6dwLbkyoEbChAyj |
|
18 |
emrL/oS01aJgT9sQoJxR8lyRMGiZ/BwQqYTareiKwOXLPdGThzsfZXD8de9T1xuysILaAM7sHPJV |
|
19 |
QfrQJm80Zo2MM/GnhJTO9rc4m3kRnRhqmA6qMw== |
|
20 |
</ds:SignatureValue> |
|
21 |
<ds:KeyInfo> |
|
22 |
<ds:KeyValue> |
|
23 |
<ds:RSAKeyValue> |
|
24 |
<ds:Modulus> |
|
25 |
71+vTf66BPgYUF7sm4T++W69qMVyGQn9wNqpBLc6sp53eq/JRTOUD26Yehjsld5qN52Bv2r5QG7o |
|
26 |
4VU123akXUYzupvq1f+tmF9NwYa7MPEPFzCzJHhNXjZNRxcsW1WLW34fhQCm0oak3oSPoNo5qeGi |
|
27 |
jNsTSkgSt1mPH0P8d95af2VJnT6zbrclxvH4emqpT9oGLsWqKWLlIbZ7u1PUjuNVwLHuj909/apm |
|
28 |
C13RBIpV52fey4qey34bnRHdCTknZeN/TJLTJ9hMWzz9TbdjfIFaiF7MeY+OYRXzUJeQuHHMu/2I |
|
29 |
emkoR26mYi6irvmx8AdPcPCwcRKw2Ca4xLhbNw== |
|
30 |
</ds:Modulus> |
|
31 |
<ds:Exponent>AQAB</ds:Exponent> |
|
32 |
</ds:RSAKeyValue> |
|
33 |
</ds:KeyValue> |
|
34 |
<ds:X509Data> |
|
35 |
<ds:X509Certificate> |
|
36 |
MIIC9zCCAd+gAwIBAgIEfe6j3jANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDEyFTQU1MIE1ldGFk |
|
37 |
YXRhIFNpZ25pbmcgQ2VydGlmaWNhdGUwHhcNMTYwNzI5MDczNjM4WhcNMjYwNjA3MDczNjM4WjAs |
|
38 |
MSowKAYDVQQDEyFTQU1MIE1ldGFkYXRhIFNpZ25pbmcgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb3 |
|
39 |
DQEBAQUAA4IBDwAwggEKAoIBAQDvX69N/roE+BhQXuybhP75br2oxXIZCf3A2qkEtzqynnd6r8lF |
|
40 |
M5QPbph6GOyV3mo3nYG/avlAbujhVTXbdqRdRjO6m+rV/62YX03Bhrsw8Q8XMLMkeE1eNk1HFyxb |
|
41 |
VYtbfh+FAKbShqTehI+g2jmp4aKM2xNKSBK3WY8fQ/x33lp/ZUmdPrNutyXG8fh6aqlP2gYuxaop |
|
42 |
YuUhtnu7U9SO41XAse6P3T39qmYLXdEEilXnZ97Lip7LfhudEd0JOSdl439MktMn2ExbPP1Nt2N8 |
|
43 |
gVqIXsx5j45hFfNQl5C4ccy7/Yh6aShHbqZiLqKu+bHwB09w8LBxErDYJrjEuFs3AgMBAAGjITAf |
|
44 |
MB0GA1UdDgQWBBTT88iZzWO+hN9SBUkpx871lmTuLTANBgkqhkiG9w0BAQsFAAOCAQEABoPpODry |
|
45 |
XwiM5jjtqk6veR02FevCKHpZP6Od7Kqcfs6lg5LcQmGUOgpmW3Gg4UMjBYkgARsT2Nsnah1CJqa8 |
|
46 |
cjvv8p5KEIhY0hVS8iMJnrb3PDeiFSeP4xSfct/6z/ebV4+QFl22bsm2zpAC6BpFz8+IJ/jAmQzT |
|
47 |
Vob4MAUeQPnwwzm3xz6yanLZx7BK5cfrTCa+hrarNQCboRjXPwiejF8WRCxpgRHH6yNs5QH/Z6o5 |
|
48 |
e3tUP7uEpn2Ob+kcLsEMGb9DghkoDAgkHCOZeTy+7hgxt+/T94cLTa58gVtvEOnd0GuL7Vfd+IVd |
|
49 |
XgSard8RfR3OyZlf6M4aSGQA73sskQ== |
|
50 |
</ds:X509Certificate> |
|
51 |
</ds:X509Data> |
|
52 |
</ds:KeyInfo> |
|
53 |
</ds:Signature><md:EntityDescriptor entityID="https://access-check.edugain.org/simplesaml/saml2/idp/metadata.php"> |
|
54 |
<md:Extensions> |
|
55 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2015-01-30T15:32:58Z"> |
|
56 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
57 |
</mdrpi:RegistrationInfo> |
|
58 |
</md:Extensions> |
|
59 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
60 |
<md:Extensions> |
|
61 |
<shibmd:Scope regexp="false">access-check.edugain.org</shibmd:Scope> |
|
62 | ||
63 |
<mdui:UIInfo> |
|
64 | ||
65 |
<mdui:DisplayName xml:lang="en">eduGAIN Access Check</mdui:DisplayName> |
|
66 | ||
67 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAABbUlEQVQoz52RPW4UQRCFv6ru6fnZNSxCJuVKpFwAicR3IUI+AakvhQhI7JU8M/1T3QQzsg1sAhWV3qtPVaonfPrCX9W+fpTP3y7otzceGJKJxCiujpVUkCv0+m3K967hixUfWgWSHwAPxG6UqtIFbEFHGoCqR721BX+wirO8LVGg+a6amEXXjqxhM5ZxNFPvX1Odocn5ZwB7HCn4hWI42wxD6KWUBVfIZxXd9J2zNhHFmro2Gx3QPKSReGCiV40ESM/A1NcxTDnn7HvLFViXBRZ6MKJnmwaktca/lFzO4fZG7n5e0D9cK4DjFI69yrGbwvhqP7KWp7kR99QrQAn3cW5N5/SQ1rivDgdUCUegFfkDkKF26TG7FijzZoS0sM7kM7D6gncvgG5d7cEdu6wN+v3R/cRwQgaAdnA2vACQvn9Tk6NNqjtwjis1Iyp4JOpafwvu3ZAYa3TV2fBjBnjfe8uLKErOZLk6fU//lcMvhuSrh7MzMwcAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDctMjBUMTA6NTA6MTErMDI6MDCDfj0WAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA3LTIwVDEwOjUwOjExKzAyOjAw8iOFqgAAAABJRU5ErkJggg==</mdui:Logo> |
|
68 |
<mdui:InformationURL xml:lang="fr">http://www.renater.fr</mdui:InformationURL> |
|
69 |
<mdui:Description xml:lang="en">eduGAIN Access Check allows administrators of a Service Provider (SP) registered in eduGAIN to create test accounts with different profiles to validate the behaviour and test federated login. The test accounts can only be used to access own services.</mdui:Description> |
|
70 |
<mdui:DisplayName xml:lang="fr">eduGAIN Access Check</mdui:DisplayName> |
|
71 |
<mdui:Description xml:lang="fr">eduGAIN Access Check allows administrators of a Service Provider (SP) registered in eduGAIN to create test accounts with different profiles to validate the behaviour and test federated login. The test accounts can only be used to access own services.</mdui:Description> |
|
72 |
</mdui:UIInfo> |
|
73 |
</md:Extensions> |
|
74 |
<md:KeyDescriptor use="signing"> |
|
75 |
<ds:KeyInfo> |
|
76 | ||
77 |
<ds:X509Data> |
|
78 |
<ds:X509Certificate> |
|
79 |
MIID2zCCAsOgAwIBAgIJAJpdV2MFitUqMA0GCSqGSIb3DQEBBQUAMIGDMQswCQYD |
|
80 |
VQQGEwJGUjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MQ4wDAYDVQQKDAVHRUFOVDEd |
|
81 |
MBsGA1UEAwwUdGVzdC1pZHAuZWR1Z2Fpbi5vcmcxLjAsBgkqhkiG9w0BCQEWH3Rl |
|
82 |
c3RpZHBhY2NvdW50bWFuYWdlckBnZWFudC5uZXQwHhcNMTQxMjE4MTAxODU5WhcN |
|
83 |
MjQxMjE3MTAxODU5WjCBgzELMAkGA1UEBhMCRlIxFTATBgNVBAcMDERlZmF1bHQg |
|
84 |
Q2l0eTEOMAwGA1UECgwFR0VBTlQxHTAbBgNVBAMMFHRlc3QtaWRwLmVkdWdhaW4u |
|
85 |
b3JnMS4wLAYJKoZIhvcNAQkBFh90ZXN0aWRwYWNjb3VudG1hbmFnZXJAZ2VhbnQu |
|
86 |
bmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo48FFP0P/81e3WHb |
|
87 |
U91F/TYDZC/JypEqO2XQNH50baXpk2JrJFVFOWdgdK6qWHsLznuxngRsfOasAaVA |
|
88 |
Ob1Bf3g2xgPUd2htSLxds+o/Y24DOM6ZairxbWJk2rOvLhJFchlrcNWCpMtUCkfJ |
|
89 |
xmqGmeo93XAud5byj3wQ1NuH2o8rjTPAkMgQdr8D2b8EG1NYEH00AqRlXZTFCWGL |
|
90 |
KDEuZwyta6vgMQYT4K6UF/F+HWF2wzbmVgRTHguJ0rzNqz6t+9CtLkhyZO+/57Ro |
|
91 |
4U0ikshVWkUOENPKCnB1t+ebs/AsNozbIGA/HcdtwUwDgIowv/K0hdnLDC1vz6/S |
|
92 |
F3rnGQIDAQABo1AwTjAdBgNVHQ4EFgQUgWN9jmJxOEHYU5m8D0atl895HxowHwYD |
|
93 |
VR0jBBgwFoAUgWN9jmJxOEHYU5m8D0atl895HxowDAYDVR0TBAUwAwEB/zANBgkq |
|
94 |
hkiG9w0BAQUFAAOCAQEAXvlBHMaBK6m0PQNanTqGBRdRAFt8Xkr5texD5mPTmS/7 |
|
95 |
nqnxlN0orqYWGCaARmQE+T77EB2a2n9g2s130pUXwJxcbUwIOdPKH6CMKEHT/512 |
|
96 |
bndJXQ3DyhkuVSLtRFOdfleIhi8qUkNC9FWxM4jDHDTTQtNEHnCjFxlhxw+ri5QJ |
|
97 |
AVKpH9MkcuIkM6Jx+QhNwTDwCRIJffoDOH420yR5EWx/sQ4tjKQGiFOPv/WHFjXd |
|
98 |
LqHU+X8ErzxeNmUHHST6pHePWRCMtoPTdCPhEroJhou6NMHh8ylQOIVHt6gggc7r |
|
99 |
kUWMUybDUxPp49qMeNkdKqFPby2aW7ouKRoOXuxZhg== |
|
100 |
</ds:X509Certificate> |
|
101 |
</ds:X509Data> |
|
102 | ||
103 |
</ds:KeyInfo> |
|
104 |
</md:KeyDescriptor> |
|
105 | ||
106 | ||
107 | ||
108 | ||
109 | ||
110 | ||
111 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
112 | ||
113 | ||
114 | ||
115 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://access-check.edugain.org/simplesaml/saml2/idp/SSOService.php"/> |
|
116 | ||
117 | ||
118 | ||
119 |
</md:IDPSSODescriptor> |
|
120 | ||
121 | ||
122 | ||
123 |
<md:Organization> |
|
124 | ||
125 |
<md:OrganizationName xml:lang="en">eduGAIN Access Check</md:OrganizationName> |
|
126 |
<md:OrganizationDisplayName xml:lang="en">eduGAIN Access Check</md:OrganizationDisplayName> |
|
127 |
<md:OrganizationURL xml:lang="en">http://www.renater.fr</md:OrganizationURL> |
|
128 | ||
129 |
</md:Organization> |
|
130 | ||
131 | ||
132 |
<md:ContactPerson contactType="technical"> |
|
133 |
<md:EmailAddress>edugain-integration@geant.net</md:EmailAddress> |
|
134 |
</md:ContactPerson> |
|
135 | ||
136 | ||
137 |
</md:EntityDescriptor><md:EntityDescriptor entityID="https://aishib.agropolis.fr/idp/shibboleth"> |
|
138 |
<md:Extensions> |
|
139 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2013-06-06T11:49:20Z"> |
|
140 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
141 |
</mdrpi:RegistrationInfo> |
|
142 |
</md:Extensions> |
|
143 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
144 |
<md:Extensions> |
|
145 |
<shibmd:Scope regexp="false">agropolis.fr</shibmd:Scope> |
|
146 | ||
147 |
<mdui:UIInfo> |
|
148 | ||
149 |
<mdui:DisplayName xml:lang="en">Agropolis International</mdui:DisplayName> |
|
150 | ||
151 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAACPUlEQVQoz21SPW8TQRR8u7d3ju34Er6UCHACRqK4Bjk0oDSEjiYSBQVS8k9AkWgp+AuUASGEaCASkoPcgARVMF8iTnxJwMFJfL47397u3u6jsGVFwLzmFTN6ozdDEBEAAOD4kiRJsVgkhMA/YMfZzc3NVstnzGq1/KXlJfgf2Ii9XquVZ2YWbi5Io4MsbfPeZK4wRm0AOH6KICIivq2tV6/O5cYLHztbW9FvYTKuZaLk7PjpW+UrJXtspCHGmGazCQBnZ8svtj/0tbAINYhcy1DyThpRhHtztyedwkBDAWBnZ6dSqbzZ+7THu0IraTJplDSZ0JnQ2Y+482jj1cgSRUTHdrhW9fa3SKaxErFKIyUiKaIsjVQaKf5s631HxP1+X2vNwjB0J9zd+OgnD044Ra4lI5ZBw7XqyeRQREciaqe9xoE/K/OFfIENnqBM1uZBqlWBOYxQjYZnqieTThru8zCU3AASIAjIXNellGLePkjCUPICy1mEDAShSg7SqCsTBtQ7WfY3vlarVUYpdUsuAs5PXV7dfpe3HEYtjSbNZJwJoRUi3rl4fSrvfufctm0ySA0RD0V84+WDL1EbCCDCYIDApeKZ+uKK6faDIPA8jw7jIORUbry2eH+5Mu8ARTSAkCPW3QvX6osrE+B8bjQ8zxu2aACDaIwxxuzzsLbbePj88a8k0Mb4vr/2ei3LMoOIiGRU0iGGVuDp6pNz5fNSiKnpac/zgAABMuzSXwIggIjdbrdUKjHGYGQaAAD+AJBJecXxnEvWAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjQ4OjE1KzAyOjAwpWiGMQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo0ODoxNSswMjowMNQ1Po0AAAAASUVORK5CYII=</mdui:Logo> |
|
152 |
<mdui:InformationURL xml:lang="fr">http://www.agropolis.fr</mdui:InformationURL> |
|
153 | ||
154 |
<mdui:DisplayName xml:lang="fr">Agropolis International</mdui:DisplayName> |
|
155 | ||
156 |
</mdui:UIInfo> |
|
157 |
</md:Extensions> |
|
158 |
<md:KeyDescriptor use="signing"> |
|
159 |
<ds:KeyInfo> |
|
160 | ||
161 |
<ds:X509Data> |
|
162 |
<ds:X509Certificate> |
|
163 |
MIIDNzCCAh+gAwIBAgIUYY3sGXwChkj2CRy6QFDvkdj2zlAwDQYJKoZIhvcNAQEF |
|
164 |
BQAwHjEcMBoGA1UEAxMTYWlzaGliLmFncm9wb2xpcy5mcjAeFw0xMzA1MTUxMzM3 |
|
165 |
MTJaFw0zMzA1MTUxMzM3MTJaMB4xHDAaBgNVBAMTE2Fpc2hpYi5hZ3JvcG9saXMu |
|
166 |
ZnIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxrDy6lrhIBjcxv16n |
|
167 |
4UJ2cEMYPO4wSmfDwhO6feoSIEuIblYRHE2nQKirMokwD6seF4rbDHyxLXg/ColL |
|
168 |
VLv+0CJteIOZjSCgSN90WzQRrC1Ex5sJfPu6yPEXvW8H1906gEg6ok8rlCIHRGfE |
|
169 |
15pHK5eqxQS5f2n8c2t/Uk33/FBj79/hb3Cd7vE4mdlvReD3AFswC0lV4bPmj3Ka |
|
170 |
KUuMj9xwipwnfWCu6p2/ZJF4M3ADU5grXHJ2Vqmd8DWm5raaObKjYwJddbRBByI8 |
|
171 |
bJJLIwAQQmX4Dh4hf1QKlf2oqWPWVQxLQp0erL1U8IWmj1RG8TTH9xOJl6kkEhYq |
|
172 |
Z2gfAgMBAAGjbTBrMEoGA1UdEQRDMEGCE2Fpc2hpYi5hZ3JvcG9saXMuZnKGKmh0 |
|
173 |
dHBzOi8vYWlzaGliLmFncm9wb2xpcy5mci9pZHAvc2hpYmJvbGV0aDAdBgNVHQ4E |
|
174 |
FgQU9A7iQ8Qo+t2JCpKuOOV9YBoYs4MwDQYJKoZIhvcNAQEFBQADggEBAG0LOW6I |
|
175 |
F+M8n2NpzyQjfVCJCA6QhWjbXrfemiPJFZGZZb2dVmHof4yCpCUYgHOBoZaXPOlB |
|
176 |
nLYsUWvFZ6V2GELZpLHzHSSrYidieW07qQkh1DwcIYpvtZgLviOtT/tCEGsk925f |
|
177 |
DUoGdeIqpqt54WZcW9+TbKicvjg3JT4BFOQ17bFNwPW+YjTbvsWYxen+e0mRp4vM |
|
178 |
V0yMu2f3bccVhePASSZGL3yod3sJ1dPvlrJO9c35BekhtirolVjZqMQ0AYPVifua |
|
179 |
yIU0dWXsZkAOcBL9kZFbJcYRUIxMgvp8U2Zdv1+ZlwOyXnnWDOOh9wjuT7FAyObU |
|
180 |
ChvjHlgZHkvLwJI= |
|
181 |
</ds:X509Certificate> |
|
182 |
</ds:X509Data> |
|
183 | ||
184 |
</ds:KeyInfo> |
|
185 |
</md:KeyDescriptor> |
|
186 | ||
187 | ||
188 | ||
189 | ||
190 | ||
191 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
192 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
193 | ||
194 | ||
195 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://aishib.agropolis.fr/idp/profile/SAML2/POST/SSO"/> |
|
196 | ||
197 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://aishib.agropolis.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
198 | ||
199 | ||
200 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://aishib.agropolis.fr/idp/profile/Shibboleth/SSO"/> |
|
201 | ||
202 | ||
203 |
</md:IDPSSODescriptor> |
|
204 | ||
205 | ||
206 | ||
207 |
<md:Organization> |
|
208 | ||
209 |
<md:OrganizationName xml:lang="en">Agropolis International</md:OrganizationName> |
|
210 |
<md:OrganizationDisplayName xml:lang="en">Agropolis International</md:OrganizationDisplayName> |
|
211 |
<md:OrganizationURL xml:lang="en">http://www.agropolis.fr</md:OrganizationURL> |
|
212 | ||
213 |
</md:Organization> |
|
214 | ||
215 | ||
216 | ||
217 |
<md:ContactPerson contactType="technical"> |
|
218 |
<md:SurName>Jean Cerda</md:SurName> |
|
219 |
<md:EmailAddress>cerda@agropolis.fr</md:EmailAddress> |
|
220 |
</md:ContactPerson> |
|
221 | ||
222 | ||
223 | ||
224 |
<md:ContactPerson contactType="technical"> |
|
225 |
<md:SurName>Jean-Pierre Allano</md:SurName> |
|
226 |
<md:EmailAddress>allano@agropolis.fr</md:EmailAddress> |
|
227 |
</md:ContactPerson> |
|
228 | ||
229 | ||
230 | ||
231 | ||
232 |
</md:EntityDescriptor><md:EntityDescriptor entityID="https://ambre.vetagro-sup.fr/idp/shibboleth"> |
|
233 |
<md:Extensions> |
|
234 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2013-01-14T16:11:53Z"> |
|
235 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
236 |
</mdrpi:RegistrationInfo> |
|
237 |
</md:Extensions> |
|
238 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
239 |
<md:Extensions> |
|
240 |
<shibmd:Scope regexp="false">vetagro-sup.fr</shibmd:Scope> |
|
241 | ||
242 |
<mdui:UIInfo> |
|
243 | ||
244 |
<mdui:DisplayName xml:lang="en">Vetagro Sup</mdui:DisplayName> |
|
245 | ||
246 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAB/klEQVQ4y2NkQAP///+X+/jxI/PO7dujP3/7kf7v778ffDzciw2MDFaqq6t/Y2RkfIysngndAEZGxkezZ8/25xEQqVDTs5eRVrNU4RQUL7x9+7YtumasBjAwMDBISIgbcvBKct998p7h1fvfDKyc4nz//jF6yjAwSBNlwM+fP3/9/PmT4dbl4wysrCwMf//9Z/r7959UcFISD1EGPH107xgfD9sbTg52hrcvHjDw87Aw/Pz+7tLEefNuEmWAm6ffxvu3Ll4zMjZleHz/KsO7lw+f3bh+czs2tVgNsLS0fPfs4b3VnKy//wrycW84e2L/uobmtg0MpIKurt6N////Z8jPzxcnWTMDAwPD79//tbq7+9bhU8OMTfD//8cMP79L1X/8tjxMQYHXT0Uh88aBgyuvEW3Aly+8ntw8jLU3bx20cnH/xPz2jTyvk9OGpTt3NhIOxP//GRgkJGR8tPVeyElIvWA4dfIqg57Bff0nD+eHExULIb5dqkYmYgYm5r8YPH3vMzx//omBk+edpI4+vxVRBnz5zPqbi4vt7a9fv//9+cvAYG79jIGZmZWBg4NJiCgDdh4sfHD/7u9j714Z/BYWiGBQVkxjePNK8+erV5+uYDOABTMM/nP09y9Z8+OHgNjli9ahf//++8vGwrlAS0tp/v///7kYGRm/IasHAIVTy8DG/VpJAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjUwOjA5KzAyOjAwfDtz7wAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo1MDowOSswMjowMA1my1MAAAAASUVORK5CYII=</mdui:Logo> |
|
247 |
<mdui:InformationURL xml:lang="fr">http://www.vetagro-sup.fr</mdui:InformationURL> |
|
248 | ||
249 |
<mdui:DisplayName xml:lang="fr">Vetagro Sup</mdui:DisplayName> |
|
250 | ||
251 |
</mdui:UIInfo> |
|
252 |
</md:Extensions> |
|
253 |
<md:KeyDescriptor use="signing"> |
|
254 |
<ds:KeyInfo> |
|
255 | ||
256 |
<ds:X509Data> |
|
257 |
<ds:X509Certificate> |
|
258 |
MIIDPDCCAiSgAwIBAgIVAL9PsuadPSIZcMHNxlK/oevezmzWMA0GCSqGSIb3DQEB |
|
259 |
BQUAMB8xHTAbBgNVBAMTFGFtYnJlLnZldGFncm8tc3VwLmZyMB4XDTEyMTEwODEw |
|
260 |
MTQwNFoXDTMyMTEwODEwMTQwNFowHzEdMBsGA1UEAxMUYW1icmUudmV0YWdyby1z |
|
261 |
dXAuZnIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc/ptfpmkomwmT |
|
262 |
4RsID+1Ce1dX0eUjcLgSOZN8hVpHWLag2ERWkpmvB5aK7BAFcI5i//Gk80tAiasu |
|
263 |
JtlZhBnEw54aTJRGpyL2CVkHyl6SMRxprIi1Ji67IoGqEgUeGaheAxo+tG5e1WSc |
|
264 |
bIbldcSKdwvjAV+7HSB4C6NqLsAzJH25++yaRH2uf2LTD0TDzNR9Q2hVj/VyYWR+ |
|
265 |
K3HWI1Snjn/i7aFfZZhYmBkwHuQOaPhwCM+khikg5XicMsxUhHCMi93UgHGIsdkr |
|
266 |
IEGj4xydBTUKsLaykeuFS8EgXbWwCLGkeX76w8xDoFIpnppU/yFd9v7Zg3EBfn4p |
|
267 |
kTW3GdIjAgMBAAGjbzBtMEwGA1UdEQRFMEOCFGFtYnJlLnZldGFncm8tc3VwLmZy |
|
268 |
hitodHRwczovL2FtYnJlLnZldGFncm8tc3VwLmZyL2lkcC9zaGliYm9sZXRoMB0G |
|
269 |
A1UdDgQWBBTPTqWkVHrHXFjmxMWkNt/sp2h5ozANBgkqhkiG9w0BAQUFAAOCAQEA |
|
270 |
FvXMtfBUmRZCzz8CjanGzr1TBUPmnkrKci5AtkseKw9YlfUmBXTHB01y697nYq6m |
|
271 |
RB6KhvfW212h9CF0IOEEjoadgDhXqGYhq8PnAOtT4Ty3XDy8SbRh8aQWfvnfSngv |
|
272 |
FdpHRiSpj5UXXuT5zTtkf59h58XKtEfCkMbUzvdOgUobJzpD0WISmQHPQnx+Neg6 |
|
273 |
9j7oMRrDiZjS39Om8Imu9xvsnddDM3PlsDBIsvrr1o7K5iLkEdR1YYX0ZNDbiFuw |
|
274 |
QXXl2dwQPB8KrScPUvCe57slU2gFQvvIBzjQysxC6V6TPSuM3A/ee56lACuB3jKj |
|
275 |
oYkHQc5Gj/1rSMLmu9aLMg== |
|
276 |
</ds:X509Certificate> |
|
277 |
</ds:X509Data> |
|
278 | ||
279 |
</ds:KeyInfo> |
|
280 |
</md:KeyDescriptor> |
|
281 | ||
282 | ||
283 | ||
284 | ||
285 | ||
286 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
287 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
288 | ||
289 | ||
290 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://ambre.vetagro-sup.fr/idp/profile/SAML2/POST/SSO"/> |
|
291 | ||
292 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://ambre.vetagro-sup.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
293 | ||
294 | ||
295 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://ambre.vetagro-sup.fr/idp/profile/Shibboleth/SSO"/> |
|
296 | ||
297 | ||
298 |
</md:IDPSSODescriptor> |
|
299 | ||
300 | ||
301 | ||
302 |
<md:Organization> |
|
303 | ||
304 |
<md:OrganizationName xml:lang="en">Vetagro Sup</md:OrganizationName> |
|
305 |
<md:OrganizationDisplayName xml:lang="en">Vetagro Sup</md:OrganizationDisplayName> |
|
306 |
<md:OrganizationURL xml:lang="en">http://www.vetagro-sup.fr</md:OrganizationURL> |
|
307 | ||
308 |
</md:Organization> |
|
309 | ||
310 | ||
311 | ||
312 |
<md:ContactPerson contactType="technical"> |
|
313 |
<md:SurName>Nicolas Aulas</md:SurName> |
|
314 |
<md:EmailAddress>nicolas.aulas@vetagro-sup.fr</md:EmailAddress> |
|
315 |
</md:ContactPerson> |
|
316 | ||
317 | ||
318 | ||
319 | ||
320 | ||
321 | ||
322 |
</md:EntityDescriptor><md:EntityDescriptor entityID="https://antimoine.insa-strasbourg.fr/idp/shibboleth"> |
|
323 |
<md:Extensions> |
|
324 |
<mdrpi:RegistrationInfo registrationAuthority="https://federation.renater.fr/" registrationInstant="2014-02-11T08:44:08Z"> |
|
325 |
<mdrpi:RegistrationPolicy xml:lang="en">https://services.renater.fr/federation/en/metadata_registration_practice_statement</mdrpi:RegistrationPolicy> |
|
326 |
</mdrpi:RegistrationInfo> |
|
327 |
</md:Extensions> |
|
328 |
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:mace:shibboleth:1.0 urn:oasis:names:tc:SAML:2.0:protocol"> |
|
329 |
<md:Extensions> |
|
330 |
<shibmd:Scope regexp="false">insa-strasbourg.fr</shibmd:Scope> |
|
331 | ||
332 |
<mdui:UIInfo> |
|
333 | ||
334 |
<mdui:DisplayName xml:lang="en">INSA Strasbourg</mdui:DisplayName> |
|
335 | ||
336 |
<mdui:Logo height="16" width="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAq1BMVEXeAACEhoTWz87nQTn3oqXnIBjvgnvWlpT/5+fnEAjvZWOltrXnNDH3mpT/9/fnCAD/4+fvioz3x8bnVVL3qq3nKCnvkpTnHBjvcXP3sq3vgoT/7+/nFBDnODH3mpz////nDAj3z87vlpTveXv3vr3nAACMmpznRUL3pqXnJCHnFAj/+//nDADvjoz3y87vXVr3rq3nLCnvdXP3srXvhoT/8/fvODH3npz3lpQaie6kAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAAe0lEQVQY053OwQqCQBhF4VM22VSMMWrFGP2kkIpUQ1nz/m8Ws20TeFeHb3WRnzEBBjO8xRjZrURi0TFiy+UITjTpjIKaOndZuDSGxF0j9G1+e7CuLN1nE2F7VI28tAoqMMezP1ieFOOp7RPueBXKSqdnMq8Wg66nPP0LX4uIG4jEwJ0sAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA3LTIwVDEwOjQ5OjA0KzAyOjAwIHfmJQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNy0yMFQxMDo0OTowNCswMjowMFEqXpkAAAAASUVORK5CYII=</mdui:Logo> |
|
337 |
<mdui:InformationURL xml:lang="fr">http://www.insa-strasbourg.fr</mdui:InformationURL> |
|
338 | ||
339 |
<mdui:DisplayName xml:lang="fr">INSA Strasbourg</mdui:DisplayName> |
|
340 | ||
341 |
</mdui:UIInfo> |
|
342 |
</md:Extensions> |
|
343 |
<md:KeyDescriptor use="signing"> |
|
344 |
<ds:KeyInfo> |
|
345 | ||
346 |
<ds:X509Data> |
|
347 |
<ds:X509Certificate> |
|
348 |
MIIDUDCCAjigAwIBAgIVAIbX8U0uAqAhuXm1jWxiFpggtDTDMA0GCSqGSIb3DQEB |
|
349 |
CwUAMCQxIjAgBgNVBAMMGXNvdWZyZS5pbnNhLXN0cmFzYm91cmcuZnIwHhcNMTYw |
|
350 |
OTI3MTIzNjIxWhcNMzYwOTI3MTIzNjIxWjAkMSIwIAYDVQQDDBlzb3VmcmUuaW5z |
|
351 |
YS1zdHJhc2JvdXJnLmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA |
|
352 |
sEE02sLRPAG5N81DMHEeGpI2MYF8yG/RiwH07cFIlLqgV80ewOmi0FWPYijxMb8A |
|
353 |
bmx0RwUMvJBVI6WMxtT9fykhID20k8rWOuYOzvaynzVqCktqVgKoEAxP1PFE9b0n |
|
354 |
iGKFprjjNl9ZD90GOUsxbAO7yXG9Q4WBa/eThl6XkUvNkSaZp5hcdWrgcAdsae3q |
|
355 |
iD/uxFa38NXNNeRLGyfxjd2K5qYSzbwBza9s9TOq1+pfw7sxu3/4BnfQ0RLGO6co |
|
356 |
4tH4Mufh0ome4cyYk4pvW5DOd1AznxDb8HpqvE0zwEsa69c/FDX0akgFZydmc77a |
|
357 |
j6USn6JKjjbO49yGtG1gVQIDAQABo3kwdzAdBgNVHQ4EFgQUjzMsxZYiokPYxper |
|
358 |
9zadM8J0F0kwVgYDVR0RBE8wTYIZc291ZnJlLmluc2Etc3RyYXNib3VyZy5mcoYw |
|
359 |
aHR0cHM6Ly9zb3VmcmUuaW5zYS1zdHJhc2JvdXJnLmZyL2lkcC9zaGliYm9sZXRo |
|
360 |
MA0GCSqGSIb3DQEBCwUAA4IBAQBFJKsiS3yfWuDB/E+iqQ0TuQJzL5+JIcloN0dw |
|
361 |
BFxW3VZOju15zeQ7LwRBg9S4SGLMPJU+LM1lvr68cK9brut/FjF51SETIXEeCWo3 |
|
362 |
7+PIqgOCzraLNinmpU/OtN8ENalOPvpS6Jvbd23qB2t+IqOtZ+j15b0Yq4/on1E3 |
|
363 |
W2F9CVzKpe4EwmmtCPQbe7U1wvhgFylEx797pex8veWs79YSYwqvcKMh79dzl8Fo |
|
364 |
/CgsO5pDrfKmc6SGMkByq75dZj+PqhZDzZ9EFTxbrXOTaS08VRN6a5Rh2iYRnGxq |
|
365 |
yZl66tPcaIm5PHgOEmu5X4lPkUoY+Jt36Gj3SGCbYt8qH5S0 |
|
366 |
</ds:X509Certificate> |
|
367 |
</ds:X509Data> |
|
368 | ||
369 |
</ds:KeyInfo> |
|
370 |
</md:KeyDescriptor> |
|
371 | ||
372 | ||
373 | ||
374 |
<md:KeyDescriptor use="signing"> |
|
375 |
<ds:KeyInfo> |
|
376 |
<ds:X509Data> |
|
377 |
<ds:X509Certificate> |
|
378 |
MIIDXDCCAkSgAwIBAgIVAKI+qiqDCk9wTTqn7OVAoZrvj/CpMA0GCSqGSIb3DQEB |
|
379 |
BQUAMCcxJTAjBgNVBAMTHGFudGltb2luZS5pbnNhLXN0cmFzYm91cmcuZnIwHhcN |
|
380 |
MTQwMTEzMTAzOTU4WhcNMzQwMTEzMTAzOTU4WjAnMSUwIwYDVQQDExxhbnRpbW9p |
|
381 |
bmUuaW5zYS1zdHJhc2JvdXJnLmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB |
|
382 |
CgKCAQEAtuM8lRjlVjjmrHq9VtguaOMQL+Wd99BiOs56kL3Mbctg1FwH69LYThCW |
|
383 |
6dOz6WJg/jU/naF7jEikXKc71xGyu7Ph7Iqa9S5hoXXAT8u/0q2nZDeTOraJqKe1 |
|
384 |
FMF2RzXhEEMyQO3CiKNK9b+tbKoNZS7FQCixMZklWZPt4EcEKd6jyRq1WYX3dpnb |
|
385 |
r9I/aCdhtK/PGvGe5gKTDoTR2HKyWKJTc/obf8x/vlYIEwiaGgdlqI2KiBE0x48n |
|
386 |
zQdP6XVi3T8ZWbnkLmCfgJtP2C8PtEJuwDRAy0Z9N4DSwvxn5YCVYgBLSi0TLa10 |
|
387 |
B/lUqqBezZrTrA9p9Lt8JtGXW5YGHwIDAQABo38wfTBcBgNVHREEVTBTghxhbnRp |
|
388 |
bW9pbmUuaW5zYS1zdHJhc2JvdXJnLmZyhjNodHRwczovL2FudGltb2luZS5pbnNh |
|
389 |
LXN0cmFzYm91cmcuZnIvaWRwL3NoaWJib2xldGgwHQYDVR0OBBYEFLFkjPZUc9JY |
|
390 |
qrWjldJ/iGGkKAt4MA0GCSqGSIb3DQEBBQUAA4IBAQBSk/wU1mRn4VF2ifmy261K |
|
391 |
DK7uX+t1H1hh8S38fKSFU7HoNXJTV3vQnmBOpYIGC1gtvmb+qjqpNtikU2zO84Gq |
|
392 |
Q0bXHxYF2d9RUP89mKaFxE5uNcXFmlOA3ChZY3pMT5zwAPI/T60tGrex7zci7OLn |
|
393 |
JDAQj/q4Yk9ejx6JTFggQSCCVh+oV/SDIMd2p5AY6H3mto3b6XCk7Lssa8a/D30k |
|
394 |
pEkZnhTKdN82eRyynuOR7UDU4tasV4d7Mi/j53f5ihnRcsvwh/pYodjoVYY8cEcZ |
|
395 |
JLnAXYF8coSwh8UN4D/0NHsvTuSOFQc85hGrqacMsvxiQiw9mv01AX5+A5YLEbVQ |
|
396 |
</ds:X509Certificate> |
|
397 |
</ds:X509Data> |
|
398 |
</ds:KeyInfo> |
|
399 |
</md:KeyDescriptor> |
|
400 | ||
401 | ||
402 | ||
403 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/Redirect/SLO"/> |
|
404 | ||
405 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/POST/SLO"/> |
|
406 | ||
407 |
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/SOAP/SLO"/> |
|
408 | ||
409 | ||
410 |
<md:NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</md:NameIDFormat> |
|
411 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> |
|
412 | ||
413 | ||
414 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/POST/SSO"/> |
|
415 | ||
416 |
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://antimoine.insa-strasbourg.fr/idp/profile/SAML2/Redirect/SSO"/> |
|
417 | ||
418 | ||
419 |
<md:SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://antimoine.insa-strasbourg.fr/idp/profile/Shibboleth/SSO"/> |
|
420 | ||
421 | ||
422 |
</md:IDPSSODescriptor> |
|
423 | ||
424 | ||
425 | ||
426 |
<md:Organization> |
|
427 | ||
428 |
<md:OrganizationName xml:lang="en">INSA Strasbourg</md:OrganizationName> |
|
429 |
<md:OrganizationDisplayName xml:lang="en">INSA Strasbourg</md:OrganizationDisplayName> |
|
430 |
<md:OrganizationURL xml:lang="en">http://www.insa-strasbourg.fr</md:OrganizationURL> |
|
431 | ||
432 |
</md:Organization> |
|
433 | ||
434 | ||
435 | ||
436 |
<md:ContactPerson contactType="technical"> |
|
437 |
<md:SurName>Lahsen BOUZID</md:SurName> |
|
438 |
<md:EmailAddress>lahsen.bouzid@insa-strasbourg.fr</md:EmailAddress> |
|
439 |
</md:ContactPerson> |
|
440 | ||
441 | ||
442 | ||
443 |
<md:ContactPerson contactType="technical"> |
|
444 |
<md:SurName>Simon SCHERRER</md:SurName> |
|
445 |
<md:EmailAddress>simon.scherrer@insa-strasbourg.fr</md:EmailAddress> |
|
446 |
</md:ContactPerson> |
|
447 | ||
448 | ||
449 | ||
450 | ||
451 |
</md:EntityDescriptor> |
|
452 |
|
|
453 |
<md:EntityDescriptor entityID="http://idp5/metadata"> |
|
454 |
<md:IDPSSODescriptor |
|
455 |
WantAuthnRequestsSigned="true" |
|
456 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
457 |
<md:KeyDescriptor use="signing"> |
|
458 |
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> |
|
459 |
<ds:X509Data><ds:X509Certificate> |
|
460 |
MIIDnjCCAoagAwIBAgIBATANBgkqhkiG9w0BAQUFADBUMQswCQYDVQQGEwJGUjEP |
|
461 |
MA0GA1UECBMGRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczETMBEGA1UEChMKRW50cm91 |
|
462 |
dmVydDEPMA0GA1UEAxMGRGFtaWVuMB4XDTA2MTAyNzA5MDc1NFoXDTExMTAyNjA5 |
|
463 |
MDc1NFowVDELMAkGA1UEBhMCRlIxDzANBgNVBAgTBkZyYW5jZTEOMAwGA1UEBxMF |
|
464 |
UGFyaXMxEzARBgNVBAoTCkVudHJvdXZlcnQxDzANBgNVBAMTBkRhbWllbjCCASIw |
|
465 |
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM06Hx6VgHYR9wUf/tZVVTRkVWNq |
|
466 |
h9x+PvHA2qH4OYMuqGs4Af6lU2YsZvnrmRdcFWv0+UkdAgXhReCWAZgtB1pd/W9m |
|
467 |
6qDRldCCyysow6xPPKRz/pOTwRXm/fM0QGPeXzwzj34BXOIOuFu+n764vKn18d+u |
|
468 |
uVAEzk1576pxTp4pQPzJfdNLrLeQ8vyCshoFU+MYJtp1UA+h2JoO0Y8oGvywbUxH |
|
469 |
ioHN5PvnzObfAM4XaDQohmfxM9Uc7Wp4xKAc1nUq5hwBrHpjFMRSz6UCfMoJSGIi |
|
470 |
+3xJMkNCjL0XEw5NKVc5jRKkzSkN5j8KTM/k1jPPsDHPRYzbWWhnNtd6JlkCAwEA |
|
471 |
AaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0 |
|
472 |
ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFP2WWMDShux3iF74+SoO1xf6qhqaMB8G |
|
473 |
A1UdIwQYMBaAFGjl6TRXbQDHzSlZu+e8VeBaZMB5MA0GCSqGSIb3DQEBBQUAA4IB |
|
474 |
AQAZ/imK7UMognXbs5RfSB8cMW6iNAI+JZqe9XWjvtmLfIIPbHM96o953SiFvrvQ |
|
475 |
BZjGmmPMK3UH29cjzDx1R/RQaYTyMrHyTePLh3BMd5mpJ/9eeJCSxPzE2ECqWRUa |
|
476 |
pkjukecFXqmRItwgTxSIUE9QkpzvuQRb268PwmgroE0mwtiREADnvTFkLkdiEMew |
|
477 |
fiYxZfJJLPBqwlkw/7f1SyzXoPXnz5QbNwDmrHelga6rKSprYKb3pueqaIe8j/AP |
|
478 |
NC1/bzp8cGOcJ88BD5+Ny6qgPVCrMLE5twQumJ12V3SvjGNtzFBvg2c/9S5OmVqR |
|
479 |
LlTxKnCrWAXftSm1rNtewTsF |
|
480 |
</ds:X509Certificate></ds:X509Data> |
|
481 |
</ds:KeyInfo> |
|
482 |
</md:KeyDescriptor> |
|
483 |
<md:ArtifactResolutionService isDefault="true" index="0" |
|
484 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
485 |
Location="http://idp5/artifact" /> |
|
486 |
<md:SingleLogoutService |
|
487 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
488 |
Location="http://idp5/singleLogoutSOAP" /> |
|
489 |
<md:SingleLogoutService |
|
490 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
491 |
Location="http://idp5/singleLogout" |
|
492 |
ResponseLocation="http://idp5/singleLogoutReturn" /> |
|
493 |
<md:ManageNameIDService |
|
494 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
495 |
Location="http://idp5/manageNameIdSOAP" /> |
|
496 |
<md:ManageNameIDService |
|
497 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
498 |
Location="http://idp5/manageNameId" |
|
499 |
ResponseLocation="http://idp5/manageNameIdReturn" /> |
|
500 |
<md:SingleSignOnService |
|
501 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" |
|
502 |
Location="http://idp5/singleSignOn" /> |
|
503 |
<md:SingleSignOnService |
|
504 |
Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" |
|
505 |
Location="http://idp5/singleSignOnSOAP" /> |
|
506 |
</md:IDPSSODescriptor> |
|
507 |
<md:AuthnAuthorityDescriptor |
|
508 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
509 |
<md:AuthnQueryService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://idp6/authnQueryService"/> |
|
510 |
<md:AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://idp6/authnAuthAssertionIDRequestService"/> |
|
511 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat> |
|
512 |
</md:AuthnAuthorityDescriptor> |
|
513 |
<md:PDPDescriptor |
|
514 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
515 |
<md:AuthzService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://idp6/authzService"/> |
|
516 |
<md:AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://idp6/PDPAuthAssertionIDRequestService"/> |
|
517 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:kerberos</md:NameIDFormat> |
|
518 |
</md:PDPDescriptor> |
|
519 |
<md:AttributeAuthorityDescriptor |
|
520 |
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> |
|
521 |
<md:AttributeService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://idp6/attributeService"/> |
|
522 |
<md:AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://idp6/AttributeAuthAssertionIDRequestService"/> |
|
523 |
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat> |
|
524 |
</md:AttributeAuthorityDescriptor> |
|
525 |
<md:Organization> |
|
526 |
<md:OrganizationName xml:lang="en">Entr'ouvert</md:OrganizationName> |
|
527 |
</md:Organization> |
|
528 | ||
529 |
</md:EntityDescriptor> |
|
530 |
</md:EntitiesDescriptor> |
tests/test_federation_utils.py | ||
---|---|---|
1 |
import base64 |
|
2 |
import os |
|
3 |
import time |
|
4 | ||
5 |
from django.core.files.storage import default_storage |
|
6 |
from django.utils.text import slugify |
|
7 |
from hashlib import sha1 |
|
8 |
from httmock import HTTMock |
|
9 |
from mellon.federation_utils import get_federation_from_url, truncate_unique, \ |
|
10 |
store_fingerprint, get_entity_id_from_fingerprint |
|
11 |
from utils import sample_federation_response |
|
12 | ||
13 | ||
14 |
def test_federation_metadata_cache(): |
|
15 |
url = u'https://dummy.mdserver/metadata.xml' |
|
16 |
filepath = default_storage.path(os.path.join('metadata-cache/', truncate_unique(slugify(url)))) |
|
17 | ||
18 |
with HTTMock(sample_federation_response): |
|
19 |
tmp = get_federation_from_url(url) |
|
20 | ||
21 |
assert default_storage.path(tmp) == filepath |
|
22 | ||
23 |
st = os.stat(filepath) |
|
24 | ||
25 |
assert os.path.isfile(filepath) |
|
26 |
assert st.st_mtime < time.time() + 3600 |
|
27 | ||
28 |
with HTTMock(sample_federation_response): |
|
29 |
get_federation_from_url(url) |
|
30 |
stnew = os.stat(filepath) |
|
31 | ||
32 |
assert stnew.st_ctime == st.st_ctime |
|
33 |
assert stnew.st_mtime == st.st_mtime |
|
34 | ||
35 |
storig = os.stat(os.path.join('tests', 'federation-sample.xml')) |
|
36 | ||
37 |
assert storig.st_size == st.st_size |
|
38 | ||
39 | ||
40 |
def test_artifact_mapping_cache(): |
|
41 |
entity_id = u'https://dummy.provider.nowhere/' |
|
42 | ||
43 |
store_fingerprint(entity_id) |
|
44 | ||
45 |
m = sha1() |
|
46 |
m.update(entity_id.encode('utf-8')) |
|
47 |
fingerprint = m.digest() |
|
48 |
fingerprint_b32 = base64.b32encode(fingerprint) |
|
49 |
unix_path = default_storage.path(os.path.join('metadata-cache/fingerprints/', fingerprint_b32.decode('utf-8'))) |
|
50 | ||
51 |
assert os.path.exists(unix_path) |
|
52 |
assert entity_id == get_entity_id_from_fingerprint(fingerprint) |
tests/test_sso_slo.py | ||
---|---|---|
9 | 9 |
from django.utils import six |
10 | 10 |
from django.utils.six.moves.urllib import parse as urlparse |
11 | 11 | |
12 |
from mellon.utils import create_metadata |
|
12 |
from mellon.utils import create_metadata, create_server |
|
13 |
from django.utils.http import urlencode |
|
13 | 14 | |
14 | 15 |
from httmock import all_requests, HTTMock, response as mock_response |
15 | 16 | |
... | ... | |
21 | 22 |
return open('tests/metadata.xml').read() |
22 | 23 | |
23 | 24 | |
25 |
@fixture |
|
26 |
def federation_metadata(): |
|
27 |
return './tests/federation-sample.xml' |
|
28 | ||
29 | ||
24 | 30 |
@fixture |
25 | 31 |
def idp_private_key(): |
26 | 32 |
return open('tests/idp-private-key.pem').read() |
... | ... | |
48 | 54 |
return private_settings |
49 | 55 | |
50 | 56 | |
57 |
@fixture |
|
58 |
def federated_sp_settings(private_settings, federation_metadata, sp_private_key, public_key): |
|
59 |
private_settings.MELLON_FEDERATIONS = [{ |
|
60 |
'FEDERATION': federation_metadata, |
|
61 |
}] |
|
62 |
private_settings.MELLON_PUBLIC_KEYS = [public_key] |
|
63 |
private_settings.MELLON_PRIVATE_KEYS = [sp_private_key] |
|
64 |
private_settings.MELLON_NAME_ID_POLICY_FORMAT = lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT |
|
65 |
private_settings.LOGIN_REDIRECT_URL = '/' |
|
66 |
return private_settings |
|
67 | ||
68 | ||
51 | 69 |
@fixture |
52 | 70 |
def sp_metadata(sp_settings, rf): |
53 | 71 |
request = rf.get('/') |
54 | 72 |
return create_metadata(request) |
55 | 73 | |
56 | 74 | |
75 |
@fixture |
|
76 |
def federated_sp_metadata(federated_sp_settings, rf): |
|
77 |
request = rf.get('/') |
|
78 |
return create_metadata(request) |
|
79 | ||
80 | ||
57 | 81 |
class MockIdp(object): |
58 | 82 |
def __init__(self, idp_metadata, private_key, sp_metadata): |
59 | 83 |
self.server = server = lasso.Server.newFromBuffers(idp_metadata, private_key) |
... | ... | |
120 | 144 |
return MockIdp(idp_metadata, idp_private_key, sp_metadata) |
121 | 145 | |
122 | 146 | |
147 |
@fixture |
|
148 |
def federated_idp(federated_sp_settings, idp_metadata, idp_private_key, federated_sp_metadata): |
|
149 |
return MockIdp(idp_metadata, idp_private_key, federated_sp_metadata) |
|
150 | ||
151 | ||
123 | 152 |
def test_sso_slo(db, app, idp, caplog, sp_settings): |
124 | 153 |
response = app.get(reverse('mellon_login') + '?next=/whatever/') |
125 | 154 |
url, body, relay_state = idp.process_authn_request_redirect(response['Location']) |
... | ... | |
210 | 239 |
assert 'created new user' in caplog.text |
211 | 240 |
assert 'logged in using SAML' in caplog.text |
212 | 241 |
assert response['Location'].endswith('/whatever/') |
242 | ||
243 | ||
244 |
def test_login_federation(db, app, federated_idp, caplog, federated_sp_settings): |
|
245 |
qs = urlencode({ |
|
246 |
'entityID': 'http://idp5/metadata', |
|
247 |
}) |
|
248 |
response = app.get('/login/?' + qs) |
|
249 |
url, body, _ = federated_idp.process_authn_request_redirect(response['Location']) |
|
250 |
assert url.endswith(reverse('mellon_login')) |
|
251 |
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body}) |
|
252 |
assert 'created new user' in caplog.text |
|
253 |
assert 'logged in using SAML' in caplog.text |
|
254 |
assert response['Location'].endswith(federated_sp_settings.LOGIN_REDIRECT_URL) |
|
255 | ||
256 | ||
257 |
def test_sso_artifact_federation(db, app, caplog, federated_sp_settings, idp_metadata, idp_private_key, rf): |
|
258 |
qs = urlencode({ |
|
259 |
'entityID': 'http://idp5/metadata', |
|
260 |
}) |
|
261 |
federated_sp_settings.MELLON_DEFAULT_ASSERTION_CONSUMER_BINDING = 'artifact' |
|
262 |
request = rf.get('/') |
|
263 |
federated_sp_metadata = create_metadata(request) |
|
264 |
idp = MockIdp(idp_metadata, idp_private_key, federated_sp_metadata) |
|
265 |
response = app.get('/login/?' + qs) |
|
266 |
url, body, _ = idp.process_authn_request_redirect(response['Location']) |
|
267 |
assert body is None |
|
268 |
assert reverse('mellon_login') in url |
|
269 |
assert 'SAMLart' in url |
|
270 |
acs_artifact_url = url.split('testserver', 1)[1] |
|
271 |
with HTTMock(idp.mock_artifact_resolver()): |
|
272 |
response = app.get(acs_artifact_url) |
|
273 |
assert 'created new user' in caplog.text |
|
274 |
assert 'logged in using SAML' in caplog.text |
|
275 |
assert response['Location'].endswith(federated_sp_settings.LOGIN_REDIRECT_URL) |
|
276 |
# force delog |
|
277 |
app.session.flush() |
|
278 |
assert 'dead artifact' not in caplog.text |
|
279 |
with HTTMock(idp.mock_artifact_resolver()): |
|
280 |
response = app.get(acs_artifact_url) |
|
281 |
# verify retry login was asked |
|
282 |
assert 'dead artifact' in caplog.text |
|
283 |
assert response.status_code == 302 |
|
284 |
assert reverse('mellon_login') in url |
|
285 |
response = response.follow() |
|
286 |
url, body, _ = idp.process_authn_request_redirect(response['Location']) |
|
287 |
reset_caplog(caplog) |
|
288 |
# verify caplog has been cleaned |
|
289 |
assert 'created new user' not in caplog.text |
|
290 |
assert body is None |
|
291 |
assert reverse('mellon_login') in url |
|
292 |
assert 'SAMLart' in url |
|
293 |
acs_artifact_url = url.split('testserver', 1)[1] |
|
294 |
with HTTMock(idp.mock_artifact_resolver()): |
|
295 |
response = app.get(acs_artifact_url) |
|
296 |
assert 'created new user' in caplog.text |
|
297 |
assert 'logged in using SAML' in caplog.text |
|
298 |
assert response['Location'].endswith(federated_sp_settings.LOGIN_REDIRECT_URL) |
tests/test_utils.py | ||
---|---|---|
1 |
import re |
|
2 | 1 |
import datetime |
2 |
import logging |
|
3 |
import os |
|
4 |
import re |
|
3 | 5 | |
4 | 6 |
import mock |
5 | 7 |
import lasso |
6 | 8 |
import requests.exceptions |
7 | 9 |
from httmock import HTTMock |
8 | 10 | |
9 |
from mellon.utils import create_server, create_metadata, iso8601_to_datetime, flatten_datetime |
|
11 |
from mellon.utils import create_server, create_metadata, iso8601_to_datetime, \ |
|
12 |
flatten_datetime, get_idp, create_loaded_server |
|
10 | 13 |
import mellon.utils |
11 | 14 |
from xml_utils import assert_xml_constraints |
12 | 15 | |
13 |
from utils import error_500, metadata_response |
|
16 |
from utils import error_500, metadata_response, sample_federation_response, \ |
|
17 |
html_response, dummy_md_response |
|
14 | 18 | |
15 | 19 | |
16 |
def test_create_server_connection_error(mocker, rf, private_settings, caplog): |
|
20 |
def test_create_server_connection_error_lazy(mocker, rf, private_settings, caplog):
|
|
17 | 21 |
mocker.patch('requests.get', |
18 | 22 |
side_effect=requests.exceptions.ConnectionError('connection error')) |
19 | 23 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
... | ... | |
23 | 27 |
] |
24 | 28 |
request = rf.get('/') |
25 | 29 |
create_server(request) |
26 |
assert 'connection error' in caplog.text |
|
30 |
assert 'failed with error' not in caplog.text |
|
31 |
create_loaded_server(request) |
|
32 |
assert 'failed with error' in caplog.text |
|
27 | 33 | |
28 | 34 | |
29 |
def test_create_server_internal_server_error(mocker, rf, private_settings, caplog): |
|
35 |
def test_create_server_internal_server_error_lazy(mocker, rf, private_settings, caplog):
|
|
30 | 36 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
31 | 37 |
{ |
32 | 38 |
'METADATA_URL': 'http://example.com/metadata', |
33 | 39 |
} |
34 | 40 |
] |
35 | 41 |
request = rf.get('/') |
36 |
assert not 'failed with error' in caplog.text
|
|
42 |
assert 'failed with error' not in caplog.text
|
|
37 | 43 |
with HTTMock(error_500): |
38 | 44 |
create_server(request) |
45 |
assert 'failed with error' not in caplog.text |
|
46 |
with HTTMock(error_500): |
|
47 |
create_loaded_server(request) |
|
39 | 48 |
assert 'failed with error' in caplog.text |
40 | 49 | |
41 | 50 | |
42 |
def test_create_server_invalid_metadata(mocker, rf, private_settings, caplog): |
|
51 |
def test_load_federation_file_lazy(mocker, rf, private_settings, caplog, tmpdir): |
|
52 |
private_settings.MELLON_FEDERATIONS = [ |
|
53 |
{'FEDERATION': 'tests/federation-sample.xml'}, |
|
54 |
] |
|
55 |
request = rf.get('/') |
|
56 |
assert 'failed with error' not in caplog.text |
|
57 |
with HTTMock(html_response): |
|
58 |
server = create_server(request) |
|
59 |
assert len(server.providers) == 0 |
|
60 |
with HTTMock(html_response): |
|
61 |
server = create_loaded_server(request) |
|
62 |
assert len(server.providers) == 1 |
|
63 | ||
64 | ||
65 |
def test_load_federation_url_lazy(mocker, rf, private_settings, caplog, tmpdir): |
|
66 |
private_settings.MELLON_FEDERATIONS = [ |
|
67 |
{'FEDERATION': 'https://dummy.server/metadata.xml'}, |
|
68 |
] |
|
69 |
request = rf.get('/') |
|
70 |
assert 'failed with error' not in caplog.text |
|
71 |
with HTTMock(dummy_md_response): |
|
72 |
server = create_server(request) |
|
73 |
assert len(server.providers) == 0 |
|
74 |
with HTTMock(dummy_md_response): |
|
75 |
server = create_loaded_server(request) |
|
76 |
assert len(server.providers) == 1 |
|
77 | ||
78 | ||
79 |
def test_federation_parameters_lazy(mocker, rf, private_settings, caplog, tmpdir): |
|
80 |
private_settings.MELLON_FEDERATIONS = [{ |
|
81 |
'FEDERATION': 'tests/federation-sample.xml', |
|
82 |
'VERIFY_SSL_CERTIFICATE': False, |
|
83 |
'ERROR_REDIRECT_AFTER_TIMEOUT': 150, |
|
84 |
'PROVISION': True |
|
85 |
}] |
|
86 |
request = rf.get('/') |
|
87 |
assert 'failed with error' not in caplog.text |
|
88 |
with HTTMock(html_response): |
|
89 |
server = create_server(request) |
|
90 |
assert len(server.providers) == 0 |
|
91 |
with HTTMock(dummy_md_response): |
|
92 |
server = create_loaded_server(request) |
|
93 |
assert len(server.providers) == 1 |
|
94 |
for entity_id in server.providers.keys(): |
|
95 |
idp = get_idp(entity_id) |
|
96 |
assert idp |
|
97 |
assert idp['VERIFY_SSL_CERTIFICATE'] is False |
|
98 |
assert idp['ERROR_REDIRECT_AFTER_TIMEOUT'] == 150 |
|
99 |
assert idp['PROVISION'] is True |
|
100 | ||
101 | ||
102 |
def test_create_server_invalid_metadata_lazy(mocker, rf, private_settings, caplog): |
|
103 |
caplog.set_level(logging.DEBUG) |
|
43 | 104 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
44 | 105 |
{ |
45 | 106 |
'METADATA': 'xxx', |
... | ... | |
49 | 110 |
assert not 'failed with error' in caplog.text |
50 | 111 |
with HTTMock(error_500): |
51 | 112 |
create_server(request) |
52 |
assert len(caplog.records) == 1 |
|
53 |
assert re.search('METADATA.*is invalid', caplog.text) |
|
113 |
assert len(caplog.records) == 0 |
|
114 |
assert not re.search('METADATA.*is invalid|bad metadata in idp|Failed to add new provider.', caplog.text) |
|
115 | ||
116 |
# Server created for one single provider: |
|
117 |
with HTTMock(error_500): |
|
118 |
create_loaded_server(request) |
|
119 |
assert len(caplog.records) == 5 |
|
120 |
assert re.search('METADATA.*is invalid|bad metadata in idp|Failed to add new provider.', caplog.text) |
|
54 | 121 | |
55 | 122 | |
56 | 123 |
def test_create_server_invalid_metadata_file(mocker, rf, private_settings, caplog): |
... | ... | |
67 | 134 |
assert len(server.providers) == 0 |
68 | 135 | |
69 | 136 | |
70 |
def test_create_server_good_metadata_file(mocker, rf, private_settings, caplog): |
|
137 |
def test_create_server_good_metadata_file_lazy(mocker, rf, private_settings, caplog):
|
|
71 | 138 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
72 | 139 |
{ |
73 |
'METADATA': '/xxx',
|
|
140 |
'METADATA': './tests/metadata.xml',
|
|
74 | 141 |
} |
75 | 142 |
] |
76 | 143 |
request = rf.get('/') |
77 |
with mock.patch( |
|
78 |
'mellon.adapters.open', mock.mock_open(read_data=open('tests/metadata.xml').read()), |
|
79 |
create=True): |
|
144 |
with HTTMock(html_response): |
|
80 | 145 |
server = create_server(request) |
81 | 146 |
assert 'ERROR' not in caplog.text |
147 |
assert len(server.providers) == 0 |
|
148 |
with HTTMock(html_response): |
|
149 |
server = create_loaded_server(request) |
|
82 | 150 |
assert len(server.providers) == 1 |
83 | 151 | |
84 | 152 | |
85 |
def test_create_server_good_metadata(mocker, rf, private_settings, caplog): |
|
153 |
def test_create_server_good_metadata_lazy(mocker, rf, private_settings, caplog):
|
|
86 | 154 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
87 | 155 |
{ |
88 | 156 |
'METADATA': open('tests/metadata.xml').read(), |
... | ... | |
92 | 160 |
assert not 'failed with error' in caplog.text |
93 | 161 |
server = create_server(request) |
94 | 162 |
assert 'ERROR' not in caplog.text |
163 |
assert len(server.providers) == 0 |
|
164 |
server = create_loaded_server(request) |
|
95 | 165 |
assert len(server.providers) == 1 |
96 | 166 | |
97 | 167 | |
98 |
def test_create_server_invalid_idp_dict(mocker, rf, private_settings, caplog): |
|
168 |
def test_create_server_invalid_idp_dict_lazy(mocker, rf, private_settings, caplog):
|
|
99 | 169 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
100 | 170 |
{ |
101 | 171 |
} |
... | ... | |
103 | 173 |
request = rf.get('/') |
104 | 174 |
assert not 'failed with error' in caplog.text |
105 | 175 |
create_server(request) |
106 |
assert 'missing METADATA' in caplog.text |
|
176 |
assert 'missing METADATA' not in caplog.text |
|
177 |
server = create_loaded_server(request) |
|
178 |
assert not len(server.providers) |
|
107 | 179 | |
108 | 180 | |
109 |
def test_create_server_good_metadata_url(mocker, rf, private_settings, caplog): |
|
181 |
def test_create_server_good_metadata_url_lazy(mocker, rf, private_settings, caplog):
|
|
110 | 182 |
private_settings.MELLON_IDENTITY_PROVIDERS = [ |
111 | 183 |
{ |
112 | 184 |
'METADATA_URL': 'http://example.com/metadata', |
... | ... | |
118 | 190 |
with HTTMock(metadata_response): |
119 | 191 |
server = create_server(request) |
120 | 192 |
assert 'ERROR' not in caplog.text |
193 |
assert len(server.providers) == 0 |
|
194 | ||
195 |
with HTTMock(dummy_md_response): |
|
196 |
server = create_loaded_server(request) |
|
121 | 197 |
assert len(server.providers) == 1 |
122 | 198 | |
123 | 199 |
tests/utils.py | ||
---|---|---|
13 | 13 | |
14 | 14 |
@all_requests |
15 | 15 |
def metadata_response(url, request): |
16 |
return response(200, content=open('tests/metadata.xml').read()) |
|
16 |
return response(200, content=open('tests/metadata.xml', 'r').read()) |
|
17 | ||
18 | ||
19 |
@all_requests |
|
20 |
def dummy_md_response(url, request): |
|
21 |
return response(200, content=open('tests/dummy_md.xml', 'r').read()) |
|
22 | ||
23 | ||
24 |
@all_requests |
|
25 |
def sample_federation_response(url, request): |
|
26 |
return response(200, content=open('tests/federation-sample.xml', 'r').read()) |
|
17 | 27 | |
18 | 28 | |
19 | 29 |
def reset_caplog(cap): |
20 |
- |