a22a1f4139be464a2a909dd8e6dee12909bd76ce
[cascardo/ipsilon.git] / ipsilon / providers / saml2idp.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2014  Simo Sorce <simo@redhat.com>
4 #
5 # see file 'COPYING' for use and warranty information
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 from ipsilon.providers.common import ProviderBase, ProviderPageBase
21 from ipsilon.providers.saml2.auth import AuthenticateRequest
22 from ipsilon.util.user import UserSession
23 import cherrypy
24 import lasso
25 import os
26
27
28 class Redirect(AuthenticateRequest):
29
30     def GET(self, *args, **kwargs):
31
32         query = cherrypy.request.query_string
33
34         login = self.saml2login(query)
35         return self.auth(login)
36
37
38 class POSTAuth(AuthenticateRequest):
39
40     def POST(self, *args, **kwargs):
41
42         request = kwargs.get(lasso.SAML2_FIELD_REQUEST)
43         relaystate = kwargs.get(lasso.SAML2_FIELD_RELAYSTATE)
44
45         login = self.saml2login(request)
46         login.set_msgRelayState(relaystate)
47         return self.auth(login)
48
49
50 class Continue(AuthenticateRequest):
51
52     def GET(self, *args, **kwargs):
53
54         session = UserSession()
55         user = session.get_user()
56         session.nuke_data('login', 'Return')
57         self.stage = session.get_data('saml2', 'stage')
58
59         if user.is_anonymous:
60             self._debug("User is marked anonymous?!")
61             # TODO: Return to SP with auth failed error
62             raise cherrypy.HTTPError(401)
63
64         self._debug('Continue auth for %s' % user.name)
65
66         dump = session.get_data('saml2', 'Request')
67         if not dump:
68             self._debug("Couldn't find Request dump?!")
69             # TODO: Return to SP with auth failed error
70             raise cherrypy.HTTPError(400)
71
72         try:
73             login = lasso.Login.newFromDump(self.cfg.idp, dump)
74         except Exception, e:  # pylint: disable=broad-except
75             self._debug('Failed to load status from dump: %r' % e)
76
77         if not login:
78             self._debug("Empty Request dump?!")
79             # TODO: Return to SP with auth failed error
80             raise cherrypy.HTTPError(400)
81
82         return self.auth(login)
83
84
85 class SSO(ProviderPageBase):
86
87     def __init__(self, *args, **kwargs):
88         super(SSO, self).__init__(*args, **kwargs)
89         self.Redirect = Redirect(*args, **kwargs)
90         self.POST = POSTAuth(*args, **kwargs)
91         self.Continue = Continue(*args, **kwargs)
92
93
94 class SAML2(ProviderPageBase):
95
96     def __init__(self, *args, **kwargs):
97         super(SAML2, self).__init__(*args, **kwargs)
98
99         # Init IDP data
100         try:
101             self.cfg.idp = lasso.Server(self.cfg.idp_metadata_file,
102                                         self.cfg.idp_key_file,
103                                         None,
104                                         self.cfg.idp_certificate_file)
105             self.cfg.idp.role = lasso.PROVIDER_ROLE_IDP
106         except Exception, e:  # pylint: disable=broad-except
107             self._debug('Failed to enable SAML2 provider: %r' % e)
108             return
109
110         # Import all known applications
111         data = self.cfg.get_data()
112         for idval in data:
113             if 'type' not in data[idval] or data[idval]['type'] != 'SP':
114                 continue
115             path = os.path.join(self.cfg.idp_storage_path, str(idval))
116             sp = data[idval]
117             if 'name' in sp:
118                 name = sp['name']
119             else:
120                 name = str(idval)
121             try:
122                 meta = os.path.join(path, 'metadata.xml')
123                 cert = os.path.join(path, 'certificate.pem')
124                 self.cfg.idp.addProvider(lasso.PROVIDER_ROLE_SP, meta, cert)
125                 self._debug('Added SP %s' % name)
126             except Exception, e:  # pylint: disable=broad-except
127                 self._debug('Failed to add SP %s: %r' % (name, e))
128
129         self.SSO = SSO(*args, **kwargs)
130
131
132 class IdpProvider(ProviderBase):
133
134     def __init__(self):
135         super(IdpProvider, self).__init__('saml2', 'saml2')
136         self.page = None
137         self.description = """
138 Provides SAML 2.0 authentication infrastructure. """
139
140         self._options = {
141             'idp storage path': [
142                 """ Path to data storage accessible by the IdP """,
143                 'string',
144                 '/var/lib/ipsilon/saml2'
145             ],
146             'idp metadata file': [
147                 """ The IdP Metadata file genearated at install time. """,
148                 'string',
149                 'metadata.xml'
150             ],
151             'idp certificate file': [
152                 """ The IdP PEM Certificate genearated at install time. """,
153                 'string',
154                 'certificate.pem'
155             ],
156             'idp key file': [
157                 """ The IdP Certificate Key genearated at install time. """,
158                 'string',
159                 'certificate.key'
160             ],
161             'allow self registration': [
162                 """ Allow authenticated users to register applications. """,
163                 'boolean',
164                 True
165             ]
166         }
167
168     @property
169     def allow_self_registration(self):
170         return self.get_config_value('allow self registration')
171
172     @property
173     def idp_storage_path(self):
174         return self.get_config_value('idp storage path')
175
176     @property
177     def idp_metadata_file(self):
178         return os.path.join(self.idp_storage_path,
179                             self.get_config_value('idp metadata file'))
180
181     @property
182     def idp_certificate_file(self):
183         return os.path.join(self.idp_storage_path,
184                             self.get_config_value('idp certificate file'))
185
186     @property
187     def idp_key_file(self):
188         return os.path.join(self.idp_storage_path,
189                             self.get_config_value('idp key file'))
190
191     def get_tree(self, site):
192         self.page = SAML2(site, self)
193         return self.page