Projet

Général

Profil

Télécharger (15,2 ko) Statistiques
| Branche: | Révision:

root / larpe / tags / release-1.1.1 / larpe / liberty.ptl @ d03cb81c

1
import libxml2
2
import urllib
3
import urlparse
4
import httplib
5
import re
6
import os
7

    
8
from quixote import get_field, get_request, get_response, get_session, get_session_manager, redirect
9
from quixote.directory import Directory
10
from quixote.http_request import parse_header
11

    
12
import lasso
13

    
14
from qommon import get_logger
15
from qommon.form import *
16
from qommon.template import *
17
from qommon.liberty import soap_call, SOAPException
18

    
19
import misc
20
from users import User
21
from hosts import Host
22
from federations import Federation
23
import site_authentication
24

    
25
class Liberty(Directory):
26
    _q_exports = ['', 'login', 'assertionConsumer', 'soapEndpoint',
27
            'singleLogout', 'singleLogoutReturn',
28
            'federationTermination', 'federationTerminationReturn',
29
            ('metadata.xml', 'metadata'), 'public_key', 'local_auth']
30

    
31
    def perform_login(self, idp = None):
32
        server = misc.get_lasso_server()
33
        login = lasso.Login(server)
34
        login.initAuthnRequest(idp, lasso.HTTP_METHOD_REDIRECT)
35
        login.request.nameIdPolicy = 'federated'
36
        login.request.forceAuthn = False
37
        login.request.isPassive = False
38
        login.request.consent = 'urn:liberty:consent:obtained'
39
        login.buildAuthnRequestMsg()
40
        return redirect(login.msgUrl)
41

    
42
    def assertionConsumer(self):
43
        server = misc.get_lasso_server()
44
        if not server:
45
            return error_page(_('Liberty support is not yet configured'))
46
        login = lasso.Login(server)
47
        request = get_request()
48
        if request.get_method() == 'GET' or get_field('LAREQ'):
49
            if request.get_method() == 'GET':
50
                login.initRequest(request.get_query(), lasso.HTTP_METHOD_REDIRECT)
51
            else:
52
                login.initRequest(get_field('LAREQ'), lasso.HTTP_METHOD_POST)
53

    
54
            login.buildRequestMsg()
55
            try:
56
                soap_answer = soap_call(login.msgUrl, login.msgBody)
57
            except SOAPException:
58
                return error_page(_('Failure to communicate with identity provider'))
59
            try:
60
                login.processResponseMsg(soap_answer)
61
            except lasso.Error, error:
62
                if error[0] == lasso.LOGIN_ERROR_STATUS_NOT_SUCCESS:
63
                    return error_page(_('Unknown authentication failure'))
64
                if hasattr(lasso, 'LOGIN_ERROR_UNKNOWN_PRINCIPAL'):
65
                    if error[0] == lasso.LOGIN_ERROR_UNKNOWN_PRINCIPAL:
66
                        return error_page(_('Authentication failure; unknown principal'))
67
                return error_page(_("Identity Provider didn't accept artifact transaction."))
68
        else:
69
            login.processAuthnResponseMsg(get_field('LARES'))
70
        login.acceptSso()
71
        session = get_session()
72
        if login.isSessionDirty:
73
            if login.session:
74
                session.lasso_session_dumps[server.providerId] = login.session.dump()
75
            else:
76
                session.lasso_session_dumps[server.providerId] = None
77

    
78
        # Look for an existing user
79
        user = self.lookup_user(session, login)
80

    
81
        # Check if it is for Larpe administration or token
82
        host = Host.get_host_from_url()
83
        if host is None:
84
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
85
        if host.name == 'larpe':
86
            if user:
87
                session.set_user(user.id, server.providerId)
88
            else:
89
                session.name_identifier = login.nameIdentifier.content
90
                session.lasso_anonymous_identity_dump = login.identity.dump()
91
                session.provider_id = server.providerId
92

    
93
            if session.after_url:
94
                # Access to an admin page or token url with parameter
95
                after_url = session.after_url
96
                session.after_url = None
97
                return redirect(after_url)
98

    
99
            if user and user.is_admin:
100
                return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
101
            else:
102
                return redirect('%s/token' % get_request().environ['SCRIPT_NAME'])
103

    
104
        # Set session user
105
        if not user:
106
            user = User()
107
        user.name_identifiers = [ login.nameIdentifier.content ]
108
        user.lasso_dumps = [ login.identity.dump() ]
109
        user.store()
110
        session.set_user(user.id, server.providerId)
111

    
112
        federations = Federation.select(lambda x: host.id == x.host_id \
113
                                        and user.name_identifiers[0] in x.name_identifiers)
114

    
115
        if federations:
116
            return site_authentication.get_site_authentication(host).sso_local_login(federations[0])
117
        else:
118
            response = get_response()
119
            if session.after_url:
120
                after_url = session.after_url
121
                session.after_url = None
122
                return redirect(after_url)
123
            response.set_status(303)
124
            response.headers['location'] = urlparse.urljoin(request.get_url(), str('local_auth'))
125
            response.content_type = 'text/plain'
126
            return 'Your browser should redirect you'
127

    
128
    def lookup_user(self, session, login):
129
        found_users = list(User.select(lambda x: login.nameIdentifier.content in x.name_identifiers))
130
        if found_users:
131
            return found_users[0]
132
        return None
133

    
134
    def singleLogout(self):
135
        request = get_request()
136
        logout = lasso.Logout(misc.get_lasso_server())
137
        if lasso.isLibertyQuery(request.get_query()):
138
            try:
139
                logout.processRequestMsg(request.get_query())
140
            except lasso.Error, error:
141
                if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
142
                    return error_page(_('Failed to check single logout request signature.'))
143
                raise
144
            session = get_session()
145
            if not session.id:
146
  	            # session has not been found, this may be because the user has
147
  	            # its browser configured so that cookies are not sent for
148
  	            # remote queries and IdP is using image-based SLO.
149
  	            # so we look up a session with the appropriate name identifier
150
                for session in get_session_manager().values():
151
                    # This block differs from qommon
152
                    user = session.get_user(logout.server.providerId)
153
                    if user and logout.nameIdentifier.content in user.name_identifiers:
154
                        break
155
                else:
156
                    session = get_session()
157
            return self.slo_idp(logout, session)
158
        else:
159
            return self.slo_sp(logout, get_session())
160

    
161
    def singleLogoutReturn(self):
162
        logout = lasso.Logout(misc.get_lasso_server())
163
        host = Host.get_host_from_url()
164
        if host is None:
165
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
166

    
167
        try:
168
            logout.processResponseMsg(get_request().get_query())
169
        except lasso.Error, error:
170
            if error[0] == lasso.PROFILE_ERROR_INVALID_QUERY:
171
                raise AccessError()
172
            if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
173
                return error_page(_('Failed to check single logout request signature.'))
174
            if hasattr(lasso, 'LOGOUT_ERROR_REQUEST_DENIED') and \
175
                    error[0] == lasso.LOGOUT_ERROR_REQUEST_DENIED:
176
                return redirect(host.get_root_url()) # ignore silently
177
            elif error[0] == lasso.ERROR_UNDEFINED:
178
                # XXX: unknown status; ignoring for now.
179
                return redirect(host.get_root_url()) # ignore silently
180
            raise
181
        return redirect(host.get_root_url())
182

    
183
    def slo_idp(self, logout, session):
184
        '''Single Logout initiated by IdP'''
185
        # This block differs from qommon
186
        if session.lasso_session_dumps.has_key(logout.server.providerId):
187
            logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
188
        user = session.get_user(logout.server.providerId)
189
        if user and user.lasso_dumps:
190
            logout.setIdentityFromDump(user.lasso_dumps[0])
191
        if user and logout.nameIdentifier.content not in user.name_identifiers:
192
            raise 'No appropriate name identifier in user (%s and %s)' % (
193
                    logout.nameIdentifier.content, user.name_identifiers)
194

    
195
        host = Host.get_host_with_provider_id(logout.server.providerId)
196
        if host is not None:
197
            site_authentication.get_site_authentication(host).local_logout(user=user)
198

    
199
        try:
200
            logout.validateRequest()
201
        except lasso.Error, error:
202
            if error[0] != lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
203
                raise
204
        else:
205
            get_session_manager().expire_session(logout.server.providerId)
206

    
207
        logout.buildResponseMsg()
208
        if logout.msgBody: # soap answer
209
            return logout.msgBody
210
        else:
211
            return redirect(logout.msgUrl)
212

    
213
    def slo_sp(self, logout, session):
214
        host = Host.get_host_from_url()
215
        if host is None:
216
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
217

    
218
        if not session.id or not session.users.has_key(logout.server.providerId) \
219
                or not session.lasso_session_dumps.has_key(logout.server.providerId):
220
            get_session_manager().expire_session(logout.server.providerId)
221
            return redirect(host.get_root_url())
222

    
223
        logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
224
        user = session.get_user(logout.server.providerId)
225

    
226
        if host.name != 'larpe' and user:
227
            site_authentication.get_site_authentication(host).local_logout(user=user)
228

    
229
        if user and user.lasso_dumps:
230
            logout.setIdentityFromDump(user.lasso_dumps[0])
231
        else:
232
            get_session_manager().expire_session(logout.server.providerId)
233
            return redirect(host.get_root_url())
234

    
235
        return self.slo_sp_redirect(logout, host)
236

    
237
    def slo_sp_redirect(self, logout, host):
238
        try:
239
            logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
240
        except lasso.Error, error:
241
            if error[0] == lasso.PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND:
242
                get_session_manager().expire_session()
243
                return redirect(host.get_root_url())
244
            raise
245
        logout.buildRequestMsg()
246
        get_session_manager().expire_session(logout.server.providerId)
247
        return redirect(logout.msgUrl)
248

    
249
    def soapEndpoint(self):
250
        request = get_request()
251
        ctype = request.environ.get('CONTENT_TYPE')
252
        if not ctype:
253
            return
254

    
255
        ctype, ctype_params = parse_header(ctype)
256
        if ctype != 'text/xml':
257
            return
258

    
259
        response = get_response()
260
        response.set_content_type('text/xml')
261

    
262
        length = int(request.environ.get('CONTENT_LENGTH'))
263
        soap_message = request.stdin.read(length)
264

    
265
        request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
266

    
267
        if request_type == lasso.REQUEST_TYPE_LOGOUT:
268
            logout = lasso.Logout(misc.get_lasso_server())
269
            logout.processRequestMsg(soap_message)
270
            name_identifier = logout.nameIdentifier.content
271
            for session in get_session_manager().values():
272
                user = session.get_user(logout.server.providerId)
273
                if user and logout.nameIdentifier.content in user.name_identifiers:
274
                    break
275
            else:
276
                session = None
277
            return self.slo_idp(logout, session)
278

    
279
        if request_type == lasso.REQUEST_TYPE_DEFEDERATION:
280
            defederation = lasso.Defederation(misc.get_lasso_server())
281
            defederation.processNotificationMsg(soap_message)
282
            for session in get_session_manager().values():
283
                user = session.get_user(defederation.server.providerId)
284
                if user and defederation.nameIdentifier.content in user.name_identifiers:
285
                    break
286
            else:
287
                session = None
288
            return self.fedterm(defederation, session)
289

    
290
    def federationTermination(self):
291
        request = get_request()
292
        if not lasso.isLibertyQuery(request.get_query()):
293
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
294

    
295
        defederation = lasso.Defederation(misc.get_lasso_server())
296
        defederation.processNotificationMsg(request.get_query())
297
        return self.fedterm(defederation, get_session())
298

    
299
    def fedterm(self, defederation, session):
300
        if session is not None:
301
            host = Host.get_host_with_provider_id(defederation.server.providerId)
302
            if host is not None:
303
                site_authentication.get_site_authentication(host).local_defederate(session, defederation.server.providerId)
304
            if session.lasso_session_dumps.has_key(defederation.server.providerId):
305
                defederation.setSessionFromDump(session.lasso_session_dumps[defederation.server.providerId])
306
            user = session.get_user(defederation.server.providerId)
307
            if user and user.lasso_dumps:
308
                defederation.setIdentityFromDump(user.lasso_dumps[0])
309
        else:
310
            user = None
311

    
312
        try:
313
            defederation.validateNotification()
314
        except lasso.Error, error:
315
            pass # ignore failure (?)
316
        else:
317
            if user:
318
                if not defederation.identity:
319
                    # if it was the last federation the whole identity dump collapsed
320
                    del user.lasso_dumps[0]
321
                else:
322
                    user.lasso_dumps[0] = defederation.identity.dump()
323
                user.store()
324

    
325
        if user and defederation.nameIdentifier.content:
326
            user.remove_name_identifier(defederation.nameIdentifier.content)
327
            user.store()
328

    
329
        if defederation.isSessionDirty and session is not None:
330
            if not defederation.session:
331
                del session.lasso_session_dumps[defederation.server.providerId]
332
            else:
333
                session.lasso_session_dumps[defederation.server.providerId] = defederation.session.dump()
334
            session.store()
335

    
336
        get_session_manager().expire_session(defederation.server.providerId)
337

    
338
        if defederation.msgUrl:
339
            return redirect(defederation.msgUrl)
340
        else:
341
            response = get_response()
342
            response.set_status(204)
343
            return ''
344

    
345
    def federationTerminationReturn(self):
346
        host = Host.get_host_from_url()
347
        if host is None:
348
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
349
        return redirect(host.get_return_url())
350

    
351
    def local_auth(self):
352
        host = Host.get_host_from_url()
353
        if host is None:
354
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
355
        return site_authentication.get_site_authentication(host).local_auth
356
    local_auth = property(local_auth)
357

    
358
    def metadata(self):
359
        host = Host.get_host_from_url()
360
        if host is None:
361
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
362
        get_response().set_content_type('text/xml', 'utf-8')
363
        metadata = unicode(open(host.metadata).read(), 'utf-8')
364
        return metadata
365

    
366
    def public_key(self):
367
        host = Host.get_host_from_url()
368
        if host is None:
369
            return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
370
        get_response().set_content_type('text/plain')
371
        public_key = open(host.public_key).read()
372
        return public_key
(9-9/19)