efc4934e3950546f1e9aa43a003d3df32736bc74
[cascardo/ipsilon.git] / tests / testlogout.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2015 Ipsilon project Contributors, for license see COPYING
4
5 from helpers.common import IpsilonTestBase  # pylint: disable=relative-import
6 from helpers.http import HttpSessions  # pylint: disable=relative-import
7 import os
8 import pwd
9 import sys
10 from string import Template
11
12
13 idp_g = {'TEMPLATES': '${TESTDIR}/templates/install',
14          'CONFDIR': '${TESTDIR}/etc',
15          'DATADIR': '${TESTDIR}/lib',
16          'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
17          'STATICDIR': '${ROOTDIR}',
18          'BINDIR': '${ROOTDIR}/ipsilon',
19          'WSGI_SOCKET_PREFIX': '${TESTDIR}/${NAME}/logs/wsgi'}
20
21
22 idp_a = {'hostname': '${ADDRESS}:${PORT}',
23          'admin_user': '${TEST_USER}',
24          'system_user': '${TEST_USER}',
25          'instance': '${NAME}',
26          'secure': 'no',
27          'testauth': 'yes',
28          'pam': 'no',
29          'gssapi': 'no',
30          'ipa': 'no',
31          'server_debugging': 'True'}
32
33
34 sp_g = {'HTTPDCONFD': '${TESTDIR}/${NAME}/conf.d',
35         'SAML2_TEMPLATE': '${TESTDIR}/templates/install/saml2/sp.conf',
36         'SAML2_CONFFILE': '${TESTDIR}/${NAME}/conf.d/ipsilon-saml.conf',
37         'SAML2_HTTPDIR': '${TESTDIR}/${NAME}/saml2'}
38
39
40 sp_a = {'hostname': '${ADDRESS}:${PORT}',
41         'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata',
42         'saml_secure_setup': 'False',
43         'saml_auth': '/sp',
44         'httpd_user': '${TEST_USER}'}
45
46
47 sp_b = {'hostname': '${ADDRESS}:${PORT}',
48         'saml_idp_metadata': 'http://127.0.0.10:45080/idp1/saml2/metadata',
49         'saml_secure_setup': 'False',
50         'no_saml_soap_logout': 'True',
51         'saml_auth': '/sp',
52         'httpd_user': '${TEST_USER}'}
53
54
55 # Global list of SP's
56 splist = [
57     {
58         'nameid': 'sp1',
59         'addr': '127.0.0.11',
60         'port': '45081',
61     },
62     {
63         'nameid': 'sp2',
64         'addr': '127.0.0.11',
65         'port': '45082',
66     },
67     {
68         'nameid': 'sp3',
69         'addr': '127.0.0.11',
70         'port': '45083',
71     },
72     {
73         'nameid': 'sp4',
74         'addr': '127.0.0.11',
75         'port': '45084',
76     },
77     {
78         'nameid': 'sp5',
79         'addr': '127.0.0.11',
80         'port': '45085',
81     },
82 ]
83
84
85 def fixup_sp_httpd(httpdir):
86     location = """
87
88 Alias /sp ${HTTPDIR}/sp
89
90 <Directory ${HTTPDIR}/sp>
91     Require all granted
92 </Directory>
93
94 Alias /open ${HTTPDIR}/open
95
96 <Directory ${HTTPDIR}/open>
97 </Directory>
98 """
99     index = """WORKS!"""
100     logged_out = """Logged out"""
101
102     t = Template(location)
103     text = t.substitute({'HTTPDIR': httpdir})
104     with open(httpdir + '/conf.d/ipsilon-saml.conf', 'a') as f:
105         f.write(text)
106
107     os.mkdir(httpdir + '/sp')
108     with open(httpdir + '/sp/index.html', 'w') as f:
109         f.write(index)
110     os.mkdir(httpdir + '/open')
111     with open(httpdir + '/open/logged_out.html', 'w') as f:
112         f.write(logged_out)
113
114
115 def ensure_logout(session, idp_name, sp_url):
116     """
117     Fetch the secure page without following redirects. If we get
118     a 303 then we should be redirected to the IDP for authentication
119     which means we aren't logged in.
120
121     Returns nothing or raises exception on error
122     """
123     try:
124         logout_page = session.fetch_page(idp_name, sp_url,
125                                          follow_redirect=False)
126         if logout_page.result.status_code != 303:
127             raise ValueError('Still logged into url')
128     except ValueError:
129         raise
130
131     return True
132
133
134 class IpsilonTest(IpsilonTestBase):
135
136     def __init__(self):
137         super(IpsilonTest, self).__init__('testlogout', __file__)
138
139     def setup_servers(self, env=None):
140         print "Installing IDP server"
141         name = 'idp1'
142         addr = '127.0.0.10'
143         port = '45080'
144         idp = self.generate_profile(idp_g, idp_a, name, addr, port)
145         conf = self.setup_idp_server(idp, name, addr, port, env)
146
147         print "Starting IDP's httpd server"
148         self.start_http_server(conf, env)
149
150         for spdata in splist:
151             nameid = spdata['nameid']
152             addr = spdata['addr']
153             port = spdata['port']
154             print "Installing SP server %s" % nameid
155
156             # Configure sp3 and sp4 for only HTTP Redirect to test
157             # that a mix of SOAP and HTTP Redirect will play nice
158             # together.
159             if nameid in ['sp3', 'sp4']:
160                 sp_prof = self.generate_profile(
161                     sp_g, sp_b, nameid, addr, str(port), nameid
162                 )
163             else:
164                 sp_prof = self.generate_profile(
165                     sp_g, sp_a, nameid, addr, str(port), nameid
166                 )
167             conf = self.setup_sp_server(sp_prof, nameid, addr, str(port), env)
168             fixup_sp_httpd(os.path.dirname(conf))
169
170             print "Starting SP's httpd server"
171             self.start_http_server(conf, env)
172
173
174 if __name__ == '__main__':
175
176     idpname = 'idp1'
177     user = pwd.getpwuid(os.getuid())[0]
178
179     sess = HttpSessions()
180     sess.add_server(idpname, 'http://127.0.0.10:45080', user, 'ipsilon')
181     for sp in splist:
182         spname = sp['nameid']
183         spurl = 'http://%s:%s' % (sp['addr'], sp['port'])
184         sess.add_server(spname, spurl)
185
186     print "testlogout: Authenticate to IDP ...",
187     try:
188         sess.auth_to_idp(idpname)
189     except Exception, e:  # pylint: disable=broad-except
190         print >> sys.stderr, " ERROR: %s" % repr(e)
191         sys.exit(1)
192     print " SUCCESS"
193
194     for sp in splist:
195         spname = sp['nameid']
196         print "testlogout: Add SP Metadata for %s to IDP ..." % spname,
197         try:
198             sess.add_sp_metadata(idpname, spname)
199         except Exception, e:  # pylint: disable=broad-except
200             print >> sys.stderr, " ERROR: %s" % repr(e)
201             sys.exit(1)
202         print " SUCCESS"
203
204     print "testlogout: Logout without logging into SP ...",
205     try:
206         page = sess.fetch_page(idpname, '%s/%s?%s' % (
207             'http://127.0.0.11:45081', 'saml2/logout',
208             'ReturnTo=http://127.0.0.11:45081/open/logged_out.html'))
209         page.expected_value('text()', 'Logged out')
210     except ValueError, e:
211         print >> sys.stderr, " ERROR: %s" % repr(e)
212         sys.exit(1)
213     print " SUCCESS"
214
215     print "testlogout: Access SP Protected Area ...",
216     try:
217         page = sess.fetch_page(idpname, 'http://127.0.0.11:45081/sp/')
218         page.expected_value('text()', 'WORKS!')
219     except ValueError, e:
220         print >> sys.stderr, " ERROR: %s" % repr(e)
221         sys.exit(1)
222     print " SUCCESS"
223
224     print "testlogout: Logout from SP ...",
225     try:
226         page = sess.fetch_page(idpname, '%s/%s?%s' % (
227             'http://127.0.0.11:45081', 'saml2/logout',
228             'ReturnTo=http://127.0.0.11:45081/open/logged_out.html'))
229         page.expected_value('text()', 'Logged out')
230     except ValueError, e:
231         print >> sys.stderr, " ERROR: %s" % repr(e)
232         sys.exit(1)
233     print " SUCCESS"
234
235     print "testlogout: Try logout again ...",
236     try:
237         page = sess.fetch_page(idpname, '%s/%s?%s' % (
238             'http://127.0.0.11:45081', 'saml2/logout',
239             'ReturnTo=http://127.0.0.11:45081/open/logged_out.html'))
240         page.expected_value('text()', 'Logged out')
241     except ValueError, e:
242         print >> sys.stderr, " ERROR: %s" % repr(e)
243         sys.exit(1)
244     print " SUCCESS"
245
246     print "testlogout: Ensure logout ...",
247     try:
248         ensure_logout(sess, idpname, 'http://127.0.0.11:45081/sp/')
249     except ValueError, e:
250         print >> sys.stderr, " ERROR: %s" % repr(e)
251         sys.exit(1)
252     print " SUCCESS"
253
254     # Test logout from each of the SP's in the list to ensure that the
255     # order of logout doesn't matter.
256     for sporder in splist:
257         print "testlogout: Access SP Protected Area of each SP ...",
258         for sp in splist:
259             spname = sp['nameid']
260             spurl = 'http://%s:%s/sp/' % (sp['addr'], sp['port'])
261             try:
262                 page = sess.fetch_page(idpname, spurl)
263                 page.expected_value('text()', 'WORKS!')
264             except ValueError, e:
265                 print >> sys.stderr, " ERROR: %s" % repr(e)
266                 sys.exit(1)
267         print " SUCCESS"
268
269         print "testlogout: Initiate logout from %s ..." % sporder['nameid'],
270         try:
271             logouturl = 'http://%s:%s' % (sp['addr'], sp['port'])
272             page = sess.fetch_page(idpname, '%s/%s?%s' % (
273                 logouturl, 'saml2/logout',
274                 'ReturnTo=http://127.0.0.11:45081/open/logged_out.html'))
275             page.expected_value('text()', 'Logged out')
276         except ValueError, e:
277             print >> sys.stderr, " ERROR: %s" % repr(e)
278             sys.exit(1)
279         print " SUCCESS"
280
281         print "testlogout: Ensure logout of each SP ...",
282         for sp in splist:
283             spname = sp['nameid']
284             spurl = 'http://%s:%s/sp/' % (sp['addr'], sp['port'])
285             try:
286                 ensure_logout(sess, idpname, spurl)
287             except ValueError, e:
288                 print >> sys.stderr, " ERROR: %s" % repr(e)
289                 sys.exit(1)
290         print " SUCCESS"
291
292     # Test IdP-initiated logout
293     print "testlogout: Access SP Protected Area of SP1...",
294     try:
295         page = sess.fetch_page(idpname, 'http://127.0.0.11:45081/sp/')
296         page.expected_value('text()', 'WORKS!')
297     except ValueError, e:
298         print >> sys.stderr, " ERROR: %s" % repr(e)
299         sys.exit(1)
300     print " SUCCESS"
301
302     print "testlogout: Access SP Protected Area of SP2...",
303     try:
304         page = sess.fetch_page(idpname, 'http://127.0.0.11:45082/sp/')
305         page.expected_value('text()', 'WORKS!')
306     except ValueError, e:
307         print >> sys.stderr, " ERROR: %s" % repr(e)
308         sys.exit(1)
309     print " SUCCESS"
310
311     print "testlogout: Access the IdP...",
312     try:
313         page = sess.fetch_page(idpname, 'http://127.0.0.10:45080/%s' % idpname)
314         page.expected_value('//div[@id="welcome"]/p/text()',
315                             'Welcome %s!' % user)
316     except ValueError, e:
317         print >> sys.stderr, " ERROR: %s" % repr(e)
318         sys.exit(1)
319     print " SUCCESS"
320
321     print "testlogout: IdP-initiated logout ...",
322     try:
323         page = sess.fetch_page(idpname,
324                                'http://127.0.0.10:45080/%s/logout' % idpname)
325         page.expected_value('//div[@id="content"]/p/a/text()', 'Log In')
326     except ValueError, e:
327         print >> sys.stderr, " ERROR: %s" % repr(e)
328         sys.exit(1)
329     print " SUCCESS"
330
331     print "testlogout: Ensure logout of SP1 ...",
332     try:
333         ensure_logout(sess, idpname, 'http://127.0.0.11:45081/sp/')
334     except ValueError, e:
335         print >> sys.stderr, " ERROR: %s" % repr(e)
336         sys.exit(1)
337     print " SUCCESS"
338
339     print "testlogout: Ensure logout of SP2 ...",
340     try:
341         ensure_logout(sess, idpname, 'http://127.0.0.11:45082/sp/')
342     except ValueError, e:
343         print >> sys.stderr, " ERROR: %s" % repr(e)
344         sys.exit(1)
345     print " SUCCESS"
346
347     print "testlogout: Access the IdP...",
348     try:
349         page = sess.fetch_page(idpname,
350                                'http://127.0.0.10:45080/%s/login' % idpname)
351         page.expected_value('//div[@id="welcome"]/p/text()',
352                             'Welcome %s!' % user)
353     except ValueError, e:
354         print >> sys.stderr, " ERROR: %s" % repr(e)
355         sys.exit(1)
356     print " SUCCESS"
357
358     print "testlogout: IdP-initiated logout with no SP sessions...",
359     try:
360         page = sess.fetch_page(idpname,
361                                'http://127.0.0.10:45080/%s/logout' % idpname)
362         page.expected_value('//div[@id="logout"]/p//text()',
363                             'Successfully logged out.')
364     except ValueError, e:
365         print >> sys.stderr, " ERROR: %s" % repr(e)
366         sys.exit(1)
367     print " SUCCESS"