Add test for per-SP allowed and mapping attributes
[cascardo/ipsilon.git] / tests / testmapping.py
1 #!/usr/bin/python
2 # Copyright (C) 2015 Ipsilon Project Contributors
3
4 from helpers.common import IpsilonTestBase  # pylint: disable=relative-import
5 from helpers.http import HttpSessions  # pylint: disable=relative-import
6 import os
7 import sys
8 import pwd
9 from string import Template
10
11 # Test Attribute Mapping and Allowed Attributes and their per-SP
12 # overrides.
13
14
15 idp_g = {'TEMPLATES': '${TESTDIR}/templates/install',
16          'CONFDIR': '${TESTDIR}/etc',
17          'DATADIR': '${TESTDIR}/lib',
18          'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
19          'STATICDIR': '${ROOTDIR}',
20          'BINDIR': '${ROOTDIR}/ipsilon',
21          'WSGI_SOCKET_PREFIX': '${TESTDIR}/${NAME}/logs/wsgi'}
22
23
24 idp_a = {'hostname': '${ADDRESS}:${PORT}',
25          'admin_user': '${TEST_USER}',
26          'system_user': '${TEST_USER}',
27          'instance': '${NAME}',
28          'secure': 'no',
29          'testauth': 'yes',
30          'pam': 'no',
31          'krb': 'no',
32          'ipa': 'no',
33          'server_debugging': 'True'}
34
35
36 sp_g = {'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
37         'SAML2_TEMPLATE': '${TESTDIR}/templates/install/saml2/sp.conf',
38         'SAML2_CONFFILE': '${TESTDIR}/${NAME}/conf.d/ipsilon-saml.conf',
39         'SAML2_HTTPDIR': '${TESTDIR}/${NAME}/saml2'}
40
41
42 sp_a = {'hostname': '${ADDRESS}:${PORT}',
43         'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata',
44         'saml_secure_setup': 'False',
45         'saml_auth': '/sp',
46         'saml_nameid': '${NAMEID}',
47         'httpd_user': '${TEST_USER}'}
48
49 sp_list = [
50     {'name': 'sp1', 'addr': '127.0.0.11', 'port': '45081'},
51 ]
52
53
54 def convert_to_dict(envlist):
55     values = {}
56     for pair in envlist.split('\n'):
57         if pair.find('=') > 0:
58             (key, value) = pair.split('=', 1)
59             if key.startswith('MELLON_') and not key.endswith('_0'):
60                 values[key] = value
61     return values
62
63
64 def check_info_plugin(s, idp_name, urlbase, expected):
65     """
66     Logout, login, fetch SP page to get the info variables and
67     compare the MELLON_ ones to what we expect.  IDP and NAMEID are
68     ignored. The authtest plugin returns no groups.
69     """
70
71     # Log out
72     page = s.fetch_page(idp_name, '%s/%s?%s' % (
73         urlbase, 'saml2/logout',
74         'ReturnTo=%s/open/logged_out.html' % urlbase))
75     page.expected_value('text()', 'Logged out')
76
77     # Fetch the page (with implicit login)
78     page = s.fetch_page(idp_name, '%s/sp/' % spurl)
79
80     # Confirm that the expected values are in the output and that there
81     # are no unexpected MELLON_ vars, and drop the _0 version.
82     data = convert_to_dict(page.text)
83
84     data.pop('MELLON_IDP')
85     data.pop('MELLON_NAME_ID')
86
87     for key in expected.keys():
88         item = data.pop('MELLON_' + key)
89         if item != expected[key]:
90             raise ValueError('Expected %s, got %s' % (expected[key], item))
91
92     if len(data) > 0:
93         raise ValueError('Unexpected values %s' % data)
94
95
96 def fixup_sp_httpd(httpdir):
97     location = """
98
99 AddOutputFilter INCLUDES .html
100
101 Alias /sp ${HTTPDIR}/sp
102
103 <Directory ${HTTPDIR}/sp>
104     Require all granted
105     Options +Includes
106 </Directory>
107
108 Alias /open ${HTTPDIR}/open
109
110 <Directory ${HTTPDIR}/open>
111 </Directory>
112
113 """
114     index = """
115 <!--#printenv  -->
116 """
117     logged_out = """Logged out"""
118
119     t = Template(location)
120     text = t.substitute({'HTTPDIR': httpdir})
121     with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
122         f.write(text)
123
124     os.mkdir(httpdir + '/sp')
125     with open(httpdir + '/sp/index.html', 'w') as f:
126         f.write(index)
127     os.mkdir(httpdir + '/open')
128     with open(httpdir + '/open/logged_out.html', 'w') as f:
129         f.write(logged_out)
130
131
132 class IpsilonTest(IpsilonTestBase):
133
134     def __init__(self):
135         super(IpsilonTest, self).__init__('testmapping', __file__)
136
137     def setup_servers(self, env=None):
138         print "Installing IDP server"
139         name = 'idp1'
140         addr = '127.0.0.10'
141         port = '45080'
142         idp = self.generate_profile(idp_g, idp_a, name, addr, port)
143         conf = self.setup_idp_server(idp, name, addr, port, env)
144
145         print "Starting IDP's httpd server"
146         self.start_http_server(conf, env)
147
148         for spdata in sp_list:
149             addr = spdata['addr']
150             port = spdata['port']
151             name = spdata['name']
152
153             print "Installing SP server %s" % name
154             sp_prof = self.generate_profile(sp_g, sp_a, name, addr, str(port))
155             conf = self.setup_sp_server(sp_prof, name, addr, str(port), env)
156             fixup_sp_httpd(os.path.dirname(conf))
157
158             print "Starting SP's httpd server"
159             self.start_http_server(conf, env)
160
161
162 if __name__ == '__main__':
163
164     idpname = 'idp1'
165     user = pwd.getpwuid(os.getuid())[0]
166     sp = sp_list[0]
167     spurl = 'http://%s:%s' % (sp['addr'], sp['port'])
168
169     # Set global mapping and allowed attributes, then test fetch from
170     # SP.
171     sess = HttpSessions()
172     sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
173     sess.add_server(sp['name'], spurl)
174
175     print "testmapping: Authenticate to IDP ...",
176     try:
177         sess.auth_to_idp(idpname)
178     except Exception, e:  # pylint: disable=broad-except
179         print >> sys.stderr, " ERROR: %s" % repr(e)
180         sys.exit(1)
181     print " SUCCESS"
182
183     print "testmapping: Add SP Metadata to IDP ...",
184     try:
185         sess.add_sp_metadata(idpname, sp['name'])
186     except Exception, e:  # pylint: disable=broad-except
187         print >> sys.stderr, " ERROR: %s" % repr(e)
188         sys.exit(1)
189     print " SUCCESS"
190
191     try:
192         print "testmapping: Test default mapping and attrs ...",
193         expect = {
194             'fullname': 'Test User %s' % user,
195             'surname': user,
196             'givenname': 'Test User',
197             'email': '%s@example.com' % user,
198         }
199         check_info_plugin(sess, idpname, spurl, expect)
200     except Exception, e:  # pylint: disable=broad-except
201         print >> sys.stderr, " ERROR: %s" % repr(e)
202         sys.exit(1)
203     print " SUCCESS"
204
205     print "testmapping: Set default global mapping ...",
206     try:
207         sess.set_attributes_and_mapping(idpname,
208                                         [['*', '*'],
209                                          ['fullname', 'namefull']])
210     except Exception, e:  # pylint: disable=broad-except
211         print >> sys.stderr, " ERROR: %s" % repr(e)
212         sys.exit(1)
213     else:
214         print " SUCCESS"
215
216     try:
217         print "testmapping: Test global mapping ...",
218         expect = {
219             'fullname': 'Test User %s' % user,
220             'namefull': 'Test User %s' % user,
221             'surname': user,
222             'givenname': 'Test User',
223             'email': '%s@example.com' % user,
224         }
225         check_info_plugin(sess, idpname, spurl, expect)
226     except Exception, e:  # pylint: disable=broad-except
227         print >> sys.stderr, " ERROR: %s" % repr(e)
228         sys.exit(1)
229     else:
230         print " SUCCESS"
231
232     print "testmapping: Set default allowed attributes ...",
233     try:
234         sess.set_attributes_and_mapping(idpname, [],
235                                         ['namefull', 'givenname', 'surname'])
236     except Exception, e:  # pylint: disable=broad-except
237         print >> sys.stderr, " ERROR: %s" % repr(e)
238         sys.exit(1)
239     else:
240         print " SUCCESS"
241
242     try:
243         print "testmapping: Test global allowed attributes ...",
244         expect = {
245             'namefull': 'Test User %s' % user,
246             'surname': user,
247             'givenname': 'Test User',
248         }
249         check_info_plugin(sess, idpname, spurl, expect)
250     except Exception, e:  # pylint: disable=broad-except
251         print >> sys.stderr, " ERROR: %s" % repr(e)
252         sys.exit(1)
253     else:
254         print " SUCCESS"
255
256     print "testmapping: Set SP allowed attributes ...",
257     try:
258         sess.set_attributes_and_mapping(idpname, [['*', '*']],
259                                         ['wholename', 'givenname', 'surname',
260                                         'email', 'fullname'], sp['name'])
261     except Exception, e:  # pylint: disable=broad-except
262         print >> sys.stderr, " ERROR: %s" % repr(e)
263         sys.exit(1)
264     else:
265         print " SUCCESS"
266
267     try:
268         print "testmapping: Test SP allowed atributes ...",
269         expect = {
270             'fullname': 'Test User %s' % user,
271             'surname': user,
272             'givenname': 'Test User',
273             'email': '%s@example.com' % user,
274         }
275         check_info_plugin(sess, idpname, spurl, expect)
276     except Exception, e:  # pylint: disable=broad-except
277         print >> sys.stderr, " ERROR: %s" % repr(e)
278         sys.exit(1)
279     else:
280         print " SUCCESS"
281
282     print "testmapping: Set SP attribute mapping ...",
283     try:
284         sess.set_attributes_and_mapping(idpname,
285                                         [['*', '*'],
286                                          ['fullname', 'wholename']],
287                                         ['wholename', 'givenname',
288                                          'surname',
289                                          'email', 'fullname'],
290                                         sp['name'])
291     except Exception, e:  # pylint: disable=broad-except
292         print >> sys.stderr, " ERROR: %s" % repr(e)
293         sys.exit(1)
294     else:
295         print " SUCCESS"
296
297     try:
298         print "testmapping: Test SP attribute mapping ...",
299         expect = {
300             'wholename': 'Test User %s' % user,
301             'fullname': 'Test User %s' % user,
302             'surname': user,
303             'givenname': 'Test User',
304             'email': '%s@example.com' % user,
305         }
306         check_info_plugin(sess, idpname, spurl, expect)
307     except Exception, e:  # pylint: disable=broad-except
308         print >> sys.stderr, " ERROR: %s" % repr(e)
309         sys.exit(1)
310     else:
311         print " SUCCESS"
312
313     print "testmapping: Drop SP attribute mapping ...",
314     try:
315         sess.set_attributes_and_mapping(idpname, [],
316                                         ['givenname', 'surname', 'email',
317                                          'fullname'], sp['name'])
318     except Exception, e:  # pylint: disable=broad-except
319         print >> sys.stderr, " ERROR: %s" % repr(e)
320         sys.exit(1)
321     else:
322         print " SUCCESS"
323
324     try:
325         print "testmapping: Test SP attr mapping with default allowed...",
326         expect = {
327             'fullname': 'Test User %s' % user,
328             'surname': user,
329             'givenname': 'Test User',
330             'email': '%s@example.com' % user,
331         }
332         check_info_plugin(sess, idpname, spurl, expect)
333     except Exception, e:  # pylint: disable=broad-except
334         print >> sys.stderr, " ERROR: %s" % repr(e)
335         sys.exit(1)
336     else:
337         print " SUCCESS"
338
339     print "testmapping: Drop SP allowed attributes ...",
340     try:
341         sess.set_attributes_and_mapping(idpname, [], [], sp['name'])
342     except Exception, e:  # pylint: disable=broad-except
343         print >> sys.stderr, " ERROR: %s" % repr(e)
344         sys.exit(1)
345     else:
346         print " SUCCESS"
347
348     try:
349         print "testmapping: Test mapping, should be back to global...",
350         expect = {
351             'namefull': 'Test User %s' % user,
352             'surname': user,
353             'givenname': 'Test User',
354         }
355         check_info_plugin(sess, idpname, spurl, expect)
356     except Exception, e:  # pylint: disable=broad-except
357         print >> sys.stderr, " ERROR: %s" % repr(e)
358         sys.exit(1)
359     else:
360         print " SUCCESS"