Projet

Général

Profil

Télécharger (8,07 ko) Statistiques
| Branche: | Tag: | Révision:

oidc / ckanext / ozwillo_pyoidc / plugin.py @ 84fe0501

1
import logging
2
from routes import redirect_to, url_for
3

    
4
import ckan.plugins as plugins
5
import ckan.plugins.toolkit as toolkit
6
from ckan.common import session, c, request, response
7
from ckan import model
8
from ckan.logic.action.create import user_create, member_create
9
import ckan.lib.base as base
10
from ckan.lib.helpers import flash_error
11

    
12
from pylons import config
13

    
14
import conf
15
from oidc import create_client, OIDCError
16

    
17
plugin_config_prefix = 'ckanext.ozwillo_pyoidc.'
18

    
19
log = logging.getLogger(__name__)
20
plugin_controller = __name__ + ':OpenidController'
21

    
22

    
23
class Clients(object):
24

    
25
    @classmethod
26
    def get_client(cls, g):
27
        params = conf.CLIENT.copy()
28
        params['srv_discovery_url'] = config.get('%s.ozwillo_discovery_url' % __name__)
29
        params['client_registration'].update({
30
            'client_id': g._extras['client_id'].value,
31
            'client_secret': g._extras['client_secret'].value,
32
            'redirect_uris': [url_for(host=request.host,
33
                                      controller=plugin_controller,
34
                                      action='callback',
35
                                      id=g.name,
36
                                      qualified=True)]
37
        })
38
        return create_client(**params)
39

    
40

    
41
class OzwilloPyoidcPlugin(plugins.SingletonPlugin):
42
    plugins.implements(plugins.IConfigurer)
43
    plugins.implements(plugins.IRoutes)
44
    plugins.implements(plugins.IAuthenticator, inherit=True)
45

    
46
    def before_map(self, map):
47
        map.connect('/organization/{id:.*}/sso',
48
                    controller=plugin_controller,
49
                    action='sso')
50
        map.connect('/organization/{id:.*}/callback',
51
                    controller=plugin_controller,
52
                    action='callback')
53
        map.connect('/user/slo',
54
                    controller=plugin_controller,
55
                    action='slo')
56
        map.redirect('/organization/{id:.*}/logout', '/user/_logout')
57

    
58
        return map
59

    
60
    def after_map(self, map):
61
        return map
62

    
63
    def identify(self):
64
        user = session.get('user')
65
        if user and not toolkit.c.userobj:
66
            userobj = model.User.get(user)
67
            toolkit.c.user = userobj.name
68
            toolkit.c.userobj = userobj
69

    
70
    def login(self):
71
        for cookie in request.cookies:
72
            value = request.cookies.get(cookie)
73
            response.set_cookie(cookie, value, secure=True, httponly=True)
74

    
75
        if 'organization_id' in session:
76
            g = model.Group.get(session['organization_id'])
77
            client = Clients.get_client(g)
78
            url, ht_args, state = client.create_authn_request(conf.ACR_VALUES)
79
            session['state'] = state
80
            session.save()
81
            if ht_args:
82
                toolkit.request.headers.update(ht_args)
83
            redirect_to(url)
84
        else:
85
            redirect_to('/')
86

    
87
    def logout(self):
88
        log.info('Logging out user: %s' % session['user'])
89
        session['user'] = None
90
        session.save()
91
        g = model.Group.get(session['organization_id'])
92
        for cookie in request.cookies:
93
            response.delete_cookie(cookie)
94
        if g:
95
            org_url = toolkit.url_for(host=request.host,
96
                                      controller='organization',
97
                                      action='read',
98
                                      id=g.name,
99
                                      qualified=True)
100

    
101
            redirect_to(str(org_url))
102
        else:
103
            redirect_to('/')
104

    
105
    def update_config(self, config_):
106
        toolkit.add_template_directory(config_, 'templates')
107
        toolkit.add_public_directory(config_, 'public')
108
        toolkit.add_resource('fanstatic', 'ozwillo_pyoidc')
109

    
110
class OpenidController(base.BaseController):
111

    
112
    def sso(self, id):
113
        log.info('SSO for organization "%s"' % id)
114
        session['organization_id'] = id
115
        session.save()
116
        log.info('redirecting to login page')
117
        login_url = toolkit.url_for(host=request.host,
118
                                    controller='user',
119
                                    action='login',
120
                                    qualified=True)
121
        redirect_to(login_url)
122

    
123
    def callback(self):
124
        g = model.Group.get(session['organization_id'])
125
        client = Clients.get_client(g)
126
        org_url = str(toolkit.url_for(controller="organization",
127
                                      action='read',
128
                                      id=g.name))
129
        try:
130
            userinfo, app_admin, app_user, access_token, id_token \
131
                = client.callback(session['state'], request.GET)
132
            session['access_token'] = access_token
133
            session['id_token'] = id_token
134
            session.save()
135
        except OIDCError, e:
136
            flash_error('Login failed')
137
            redirect_to(org_url, qualified=True)
138
        locale = None
139
        log.info('Received userinfo: %s' % userinfo)
140

    
141
        if 'locale' in userinfo:
142
            locale = userinfo.get('locale', '')
143
            if '-' in locale:
144
                locale, country = locale.split('-')
145

    
146
        org_url = str(toolkit.url_for(org_url, locale=locale, qualified=True))
147
        if 'sub' in userinfo:
148

    
149
            userobj = model.User.get(userinfo['sub'])
150
            if not userobj:
151
                user_dict = {'id': userinfo['sub'],
152
                             'name': userinfo['sub'].replace('-', ''),
153
                             'email': userinfo['email'],
154
                             'password': userinfo['sub']
155
                             }
156
                context = {'ignore_auth': True, 'model': model,
157
                           'session': model.Session}
158
                user_create(context, user_dict)
159
                userobj = model.User.get(userinfo['sub'])
160
                if app_admin or app_user:
161
                    member_dict = {
162
                        'id': g.id,
163
                        'object': userinfo['sub'],
164
                        'object_type': 'user',
165
                        'capacity': 'admin',
166
                    }
167

    
168
                    member_create_context = {
169
                        'model': model,
170
                        'user': userobj.name,
171
                        'ignore_auth': True,
172
                        'session': session
173
                    }
174

    
175
                    member_create(member_create_context, member_dict)
176

    
177
            if 'given_name' in userinfo:
178
                userobj.fullname = userinfo['given_name']
179
            if 'family_name' in userinfo:
180
                userobj.fullname += ' ' + userinfo['family_name']
181
            userobj.save()
182

    
183
            if 'nickname' in userinfo:
184
                userobj.name = userinfo['nickname']
185
            try:
186
                userobj.save()
187
            except Exception, e:
188
                log.warning('Error while saving user name: %s' % e)
189

    
190
            session['user'] = userobj.id
191
            session.save()
192

    
193
        redirect_to(org_url)
194

    
195

    
196
    def slo(self):
197
        """
198
        Revokes the delivered access token. Logs out the user
199
        """
200

    
201
        if not request.referer or request.host not in request.referer:
202
            redirect_to('/')
203

    
204
        g = model.Group.get(session['organization_id'])
205
        org_url = url_for(host=request.host,
206
                          controller='organization',
207
                          action='read',
208
                          id=g.name,
209
                          qualified=True)
210
        org_url = str(org_url)
211

    
212
        if toolkit.c.user:
213
            client = Clients.get_client(g)
214
            logout_url = client.end_session_endpoint
215

    
216
            redirect_uri = org_url + '/logout'
217

    
218
            # revoke the access token
219
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
220
            data = 'token=' + session.get('access_token')
221
            data += '&token_type_hint=access_token'
222
            client.http_request(client.revocation_endpoint, 'POST',
223
                                data=data, headers=headers)
224

    
225
            # redirect to IDP logout
226
            logout_url += '?id_token_hint=%s&' % session.get('id_token')
227
            logout_url += 'post_logout_redirect_uri=%s' % redirect_uri
228
            redirect_to(str(logout_url))
229
        redirect_to(org_url)
(4-4/4)