IdP-initiated logout for current user
[cascardo/ipsilon.git] / tests / testnameid.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 from ipsilon.tools.saml2metadata import SAML2_NAMEID_MAP
7 import os
8 import pwd
9 import sys
10 import re
11 from string import Template
12
13
14 idp_g = {'TEMPLATES': '${TESTDIR}/templates/install',
15          'CONFDIR': '${TESTDIR}/etc',
16          'DATADIR': '${TESTDIR}/lib',
17          'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
18          'STATICDIR': '${ROOTDIR}',
19          'BINDIR': '${ROOTDIR}/ipsilon',
20          'WSGI_SOCKET_PREFIX': '${TESTDIR}/${NAME}/logs/wsgi'}
21
22
23 idp_a = {'hostname': '${ADDRESS}:${PORT}',
24          'admin_user': '${TEST_USER}',
25          'system_user': '${TEST_USER}',
26          'instance': '${NAME}',
27          'secure': 'no',
28          'testauth': 'yes',
29          'pam': 'no',
30          'krb': 'no',
31          'ipa': 'no',
32          'server_debugging': 'True'}
33
34
35 sp_g = {'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
36         'SAML2_TEMPLATE': '${TESTDIR}/templates/install/saml2/sp.conf',
37         'SAML2_CONFFILE': '${TESTDIR}/${NAME}/conf.d/ipsilon-saml.conf',
38         'SAML2_HTTPDIR': '${TESTDIR}/${NAME}/saml2'}
39
40
41 sp_a = {'hostname': '${ADDRESS}:${PORT}',
42         'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata',
43         'saml_secure_setup': 'False',
44         'saml_auth': '/sp',
45         'saml_nameid': '${NAMEID}',
46         'httpd_user': '${TEST_USER}'}
47
48
49 def generate_sp_list():
50     splist = []
51     spport = 45081
52
53     for nameid in SAML2_NAMEID_MAP.keys():
54         nameid = nameid
55         spdata = {'nameid': nameid, 'addr': '127.0.0.11', 'port': str(spport)}
56         splist.append(spdata)
57         spport += 1
58
59     return splist
60
61
62 def get_sp_by_nameid(splist, nameid):
63     for server in splist:
64         if server['nameid'] == nameid:
65             return server
66
67     return None
68
69
70 def convert_to_dict(envlist):
71     values = {}
72     for pair in envlist.split('\n'):
73         if pair.find('=') > 0:
74             (key, value) = pair.split('=', 1)
75             values[key] = value
76     return values
77
78
79 def fixup_sp_httpd(httpdir):
80     location = """
81
82 AddOutputFilter INCLUDES .html
83
84 Alias /sp ${HTTPDIR}/sp
85
86 <Directory ${HTTPDIR}/sp>
87     Require all granted
88     Options +Includes
89 </Directory>
90 """
91     index = """<!--#echo var="REMOTE_USER" -->"""
92
93     t = Template(location)
94     text = t.substitute({'HTTPDIR': httpdir})
95     with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
96         f.write(text)
97
98     os.mkdir(httpdir + '/sp')
99     with open(httpdir + '/sp/index.html', 'w') as f:
100         f.write(index)
101
102
103 class IpsilonTest(IpsilonTestBase):
104
105     def __init__(self):
106         super(IpsilonTest, self).__init__('testnameid', __file__)
107
108     def setup_servers(self, env=None):
109         print "Installing IDP server"
110         name = 'idp1'
111         addr = '127.0.0.10'
112         port = '45080'
113         idp = self.generate_profile(idp_g, idp_a, name, addr, port)
114         conf = self.setup_idp_server(idp, name, addr, port, env)
115
116         print "Starting IDP's httpd server"
117         self.start_http_server(conf, env)
118
119         for spdata in generate_sp_list():
120             nameid = spdata['nameid']
121             addr = spdata['addr']
122             port = spdata['port']
123             print "Installing SP server %s" % nameid
124             sp_prof = self.generate_profile(
125                 sp_g, sp_a, nameid, addr, str(port), nameid
126             )
127             conf = self.setup_sp_server(sp_prof, nameid, addr, str(port), env)
128             fixup_sp_httpd(os.path.dirname(conf))
129
130             print "Starting SP's httpd server"
131             self.start_http_server(conf, env)
132
133
134 if __name__ == '__main__':
135
136     idpname = 'idp1'
137     user = pwd.getpwuid(os.getuid())[0]
138
139     expected = {
140         'x509':        False,   # not supported
141         'transient':   True,
142         'persistent':  True,
143         'windows':     False,   # not supported
144         'encrypted':   False,   # not supported
145         'kerberos':    False,   # no auth with kerberos, no princ
146         'email':       True,
147         'unspecified': True,
148         'entity':      False,   # not supported
149     }
150
151     expected_re = {
152         'x509':        None,    # not supported
153         'transient':   '_[0-9a-f]{32}',
154         'persistent':  '_[0-9a-f]{128}',
155         'windows':     None,    # not supported
156         'encrypted':   None,    # not supported
157         'kerberos':    False,   # no auth with kerberos, no princ
158         'email':       '%s@.*' % user,
159         'unspecified': user,
160         'entity':      False,   # not supported
161     }
162
163     sp_list = generate_sp_list()
164     for sp in sp_list:
165         spname = sp['nameid']
166         spurl = 'http://%s:%s' % (sp['addr'], sp['port'])
167         sess = HttpSessions()
168         sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
169         sess.add_server(spname, spurl)
170
171         print ""
172         print "testnameid: Testing NameID format %s ..." % spname
173
174         print "testnameid: Authenticate to IDP ...",
175         try:
176             sess.auth_to_idp(idpname)
177         except Exception, e:  # pylint: disable=broad-except
178             print >> sys.stderr, " ERROR: %s" % repr(e)
179             sys.exit(1)
180         print " SUCCESS"
181
182         print "testnameid: Add SP Metadata to IDP ...",
183         try:
184             sess.add_sp_metadata(idpname, spname)
185         except Exception, e:  # pylint: disable=broad-except
186             print >> sys.stderr, " ERROR: %s" % repr(e)
187             sys.exit(1)
188         print " SUCCESS"
189
190         print "testnameid: Set supported Name ID formats ...",
191         try:
192             sess.set_sp_default_nameids(idpname, spname, [spname])
193         except Exception, e:  # pylint: disable=broad-except
194             print >> sys.stderr, " ERROR: %s" % repr(e)
195             sys.exit(1)
196         print " SUCCESS"
197
198         print "testnameid: Access SP Protected Area ...",
199         try:
200             page = sess.fetch_page(idpname, '%s/sp/' % spurl)
201             if not re.match(expected_re[spname], page.text):
202                 raise ValueError(
203                     'id %s did not match expression %s' %
204                     (id, expected_re[spname])
205                 )
206         except ValueError, e:
207             if expected[spname]:
208                 print >> sys.stderr, " ERROR: %s" % repr(e)
209                 sys.exit(1)
210             print " OK, EXPECTED TO FAIL"
211         else:
212             print " SUCCESS"
213
214         print "testnameid: Try authentication failure ...",
215         newsess = HttpSessions()
216         newsess.add_server(idpname, 'http://127.0.0.10:45080', user, 'wrong')
217         try:
218             newsess.auth_to_idp(idpname)
219             print >> sys.stderr, " ERROR: Authentication should have failed"
220             sys.exit(1)
221         except Exception, e:  # pylint: disable=broad-except
222             print " SUCCESS"
223
224     # Ensure that transient names change with each authentication
225     sp = get_sp_by_nameid(sp_list, 'transient')
226     spname = sp['nameid']
227     spurl = 'http://%s:%s' % (sp['addr'], sp['port'])
228
229     print ""
230     print "testnameid: Testing NameID format %s ..." % spname
231
232     ids = []
233     for i in xrange(4):
234         sess = HttpSessions()
235         sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
236         sess.add_server(spname, spurl)
237         print "testnameid: Authenticate to IDP ...",
238         try:
239             sess.auth_to_idp(idpname)
240         except Exception, e:  # pylint: disable=broad-except
241             print >> sys.stderr, " ERROR: %s" % repr(e)
242             sys.exit(1)
243         else:
244             print " SUCCESS"
245
246         print "testnameid: Access SP ...",
247         try:
248             page = sess.fetch_page(idpname, '%s/sp/' % spurl)
249             t1 = page.text
250         except ValueError, e:
251             print >> sys.stderr, " ERROR: %s" % repr(e)
252             sys.exit(1)
253         else:
254             print " SUCCESS"
255
256         print "testnameid: Access SP again ...",
257         try:
258             page = sess.fetch_page(idpname, '%s/sp/' % spurl)
259             t2 = page.text
260         except ValueError, e:
261             print >> sys.stderr, " ERROR: %s" % repr(e)
262             sys.exit(1)
263         else:
264             print " SUCCESS"
265
266         print "testnameid: Ensure ID is consistent between requests ...",
267         if t1 != t2:
268             print >> sys.stderr, " ERROR: New ID between reqeusts"
269         else:
270             print " SUCCESS"
271
272         ids.append(t1)
273
274     print "testnameid: Ensure uniqueness across sessions ...",
275     if len(ids) != len(set(ids)):
276         print >> sys.stderr, " ERROR: IDs are not unique between sessions"
277         sys.exit(1)
278     else:
279         print " SUCCESS"
280
281     # Ensure that persistent names remain the same with each authentication
282     sp = get_sp_by_nameid(sp_list, 'persistent')
283     spname = sp['nameid']
284     spurl = 'http://%s:%s' % (sp['addr'], sp['port'])
285
286     print ""
287     print "testnameid: Testing NameID format %s ..." % spname
288
289     ids = []
290     for i in xrange(4):
291         sess = HttpSessions()
292         sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
293         sess.add_server(spname, spurl)
294         print "testnameid: Authenticate to IDP ...",
295         try:
296             sess.auth_to_idp(idpname)
297         except Exception, e:  # pylint: disable=broad-except
298             print >> sys.stderr, " ERROR: %s" % repr(e)
299             sys.exit(1)
300         else:
301             print " SUCCESS"
302
303         print "testnameid: Access SP ...",
304         try:
305             page = sess.fetch_page(idpname, '%s/sp/' % spurl)
306             t1 = page.text
307         except ValueError, e:
308             print >> sys.stderr, " ERROR: %s" % repr(e)
309             sys.exit(1)
310         else:
311             print " SUCCESS"
312
313         print "testnameid: Access SP again ...",
314         try:
315             page = sess.fetch_page(idpname, '%s/sp/' % spurl)
316             t2 = page.text
317         except ValueError, e:
318             print >> sys.stderr, " ERROR: %s" % repr(e)
319             sys.exit(1)
320         else:
321             print " SUCCESS"
322
323         print "testnameid: Ensure ID is consistent between requests ...",
324         if t1 != t2:
325             print >> sys.stderr, " ERROR: New ID between reqeusts"
326         else:
327             print " SUCCESS"
328
329         ids.append(t1)
330
331     print "testnameid: Ensure same ID across sessions ...",
332     if len(set(ids)) != 1:
333         print >> sys.stderr, " ERROR: IDs are not the same between sessions"
334         sys.exit(1)
335     else:
336         print " SUCCESS"