Projet

Général

Profil

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

oidc / ckanext / ozwillo_pyoidc / plugin.py @ f22ce77a

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

    
11
from pylons import config
12

    
13
import conf
14
from oidc import create_client
15

    
16
plugin_config_prefix = 'ckanext.ozwillo_pyoidc.'
17

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

    
21
_CLIENTS = {}
22

    
23
class Clients(object):
24

    
25
    @classmethod
26
    def get(cls, g):
27
        global _CLIENTS
28
        if g.id in _CLIENTS:
29
            return _CLIENTS.get(g.id)
30
        client = cls().get_client(g)
31
        _CLIENTS.update({g.id: client})
32
        return client
33

    
34
    def get_client(self, g):
35
        params = conf.CLIENT.copy()
36
        params['client_registration'].update({
37
            'client_id': g._extras['client_id'].value,
38
            'client_secret': g._extras['client_secret'].value,
39
            'redirect_uris': [url_for(host=request.host,
40
                                      controller=plugin_controller,
41
                                      action='callback',
42
                                      id=g.name,
43
                                      qualified=True)]
44
        })
45
        return create_client(**params)
46

    
47

    
48
class OzwilloPyoidcPlugin(plugins.SingletonPlugin):
49
    plugins.implements(plugins.IConfigurer)
50
    plugins.implements(plugins.IRoutes)
51
    plugins.implements(plugins.IAuthenticator, inherit=True)
52

    
53
    def before_map(self, map):
54
        map.connect('/organization/{id:.*}/sso',
55
                    controller=plugin_controller,
56
                    action='sso')
57
        map.connect('/organization/{id:.*}/callback',
58
                    controller=plugin_controller,
59
                    action='callback')
60
        map.connect('/logout', controller=plugin_controller,
61
                    action='logout')
62
        map.connect('/user/slo',
63
                    controller=plugin_controller,
64
                    action='slo',
65
                    conditions={'method': ['POST']})
66
        map.redirect('/organization/{id:.*}/logout', '/user/_logout')
67

    
68
        return map
69

    
70
    def after_map(self, map):
71
        return map
72

    
73
    def identify(self):
74
        user = session.get('user')
75
        if user and not toolkit.c.userobj:
76
            userobj = model.User.get(user)
77
            toolkit.c.user = userobj.name
78
            toolkit.c.userobj = userobj
79

    
80
    def login(self):
81
        for cookie in request.cookies:
82
            value = request.cookies.get(cookie)
83
            response.set_cookie(cookie, value, secure=True, httponly=True)
84

    
85
        if 'organization_id' in session:
86
            g = model.Group.get(session['organization_id'])
87
            client = Clients.get(g)
88
            url, ht_args = client.create_authn_request(conf.ACR_VALUES)
89
            if ht_args:
90
                toolkit.request.headers.update(ht_args)
91
            redirect_to(url)
92
        else:
93
            redirect_to('/')
94

    
95
    def logout(self):
96
        log.info('Logging out user: %s' % session['user'])
97
        session['user'] = None
98
        session.save()
99
        g = model.Group.get(session['organization_id'])
100
        for cookie in request.cookies:
101
            response.delete_cookie(cookie)
102
        if g:
103
            org_url = toolkit.url_for(host=request.host,
104
                                      controller='organization',
105
                                      action='read',
106
                                      id=g.name,
107
                                      qualified=True)
108

    
109
            redirect_to(str(org_url))
110
        else:
111
            redirect_to('/')
112

    
113
    def update_config(self, config_):
114
        toolkit.add_template_directory(config_, 'templates')
115
        toolkit.add_public_directory(config_, 'public')
116
        toolkit.add_resource('fanstatic', 'ozwillo_pyoidc')
117

    
118
class OpenidController(base.BaseController):
119

    
120
    def sso(self, id):
121
        log.info('SSO for organization "%s"' % id)
122
        session['organization_id'] = id
123
        session.save()
124
        log.info('redirecting to login page')
125
        login_url = toolkit.url_for(host=request.host,
126
                                    controller='user',
127
                                    action='login',
128
                                    qualified=True)
129
        redirect_to(login_url)
130

    
131
    def callback(self):
132
        g = model.Group.get(session['organization_id'])
133
        client = Clients.get(g)
134
        userinfo = client.callback(request.GET)
135
        locale = None
136
        log.info('Received userinfo: %s' % userinfo)
137

    
138
        if 'locale' in userinfo:
139
            locale = userinfo.get('locale', '')
140
            if '-' in locale:
141
                locale, country = locale.split('-')
142

    
143
        org_url = str(toolkit.url_for(host=request.host,
144
                                      controller="organization",
145
                                      action='read',
146
                                      id=g.name,
147
                                      locale=locale,
148
                                      qualified=True))
149
        if 'sub' in userinfo:
150

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

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

    
177
                    member_create(member_create_context, member_dict)
178

    
179
            if 'nickname' in userinfo:
180
                userobj.name = userinfo['nickname']
181
            try:
182
                userobj.save()
183
            except Exception, e:
184
                log.warning('Error while saving user name: %s' % e)
185

    
186
            if 'given_name' in userinfo:
187
                userobj.fullname = userinfo['given_name']
188
            if 'family_name' in userinfo:
189
                userobj.fullname += ' ' + userinfo['family_name']
190
            userobj.save()
191
            session['user'] = userobj.id
192
            session.save()
193

    
194
        redirect_to(org_url)
195

    
196
    def logout(self):
197
        toolkit.c.slo_url = toolkit.url_for(host=request.host,
198
                                            controller=plugin_controller,
199
                                            action="slo",
200
                                            qualified=True)
201
        return base.render('logout_confirm.html')
202

    
203
    def slo(self):
204
        """
205
        Revokes the delivered access token. Logs out the user
206
        """
207
        g = model.Group.get(session['organization_id'])
208
        org_url = toolkit.url_for(host=request.host,
209
                                  controller='organization',
210
                                  action='read',
211
                                  id=g.name,
212
                                  qualified=True)
213
        org_url = str(org_url)
214

    
215
        if toolkit.c.user and request.method == 'POST':
216
            client = Clients.get(g)
217
            logout_url = client.end_session_endpoint
218

    
219
            redirect_uri = org_url + '/logout'
220

    
221
            if not hasattr(client, 'access_token'):
222
                self.sso(g.name)
223

    
224
            # revoke the access token
225
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
226
            data = 'token=' + client.access_token
227
            data += '&token_type_hint=access_token'
228
            client.http_request(client.revocation_endpoint, 'POST',
229
                                data=data, headers=headers)
230

    
231
            # redirect to IDP logout
232
            logout_url += '?id_token_hint=%s&' % client.id_token
233
            logout_url += 'post_logout_redirect_uri=%s' % redirect_uri
234
            redirect_to(str(logout_url))
235
        redirect_to(org_url)
(4-4/4)