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