Projet

Général

Profil

Télécharger (7,9 ko) Statistiques
| Branche: | Tag: | Révision:

oidc / ckanext / ozwillo_pyoidc / plugin.py @ 2bfcb919

1 c8204b73 Serghei Mihai
import logging
2 25877dab Serghei MIHAI
from routes import redirect_to, url_for
3 c8204b73 Serghei Mihai
4 b169c797 Serghei MIHAI
import ckan.plugins as plugins
5
import ckan.plugins.toolkit as toolkit
6 f1d53ae8 Serghei MIHAI
from ckan.common import session, c, request, response
7 a5f39ab1 Serghei MIHAI
from ckan import model
8 f22ce77a Serghei MIHAI
from ckan.logic.action.create import user_create, member_create
9 c8204b73 Serghei Mihai
import ckan.lib.base as base
10
11 f1d53ae8 Serghei MIHAI
from pylons import config
12 c8204b73 Serghei Mihai
13 b71e8531 Serghei MIHAI
import conf
14
from oidc import create_client
15 c8204b73 Serghei Mihai
16
plugin_config_prefix = 'ckanext.ozwillo_pyoidc.'
17
18
log = logging.getLogger(__name__)
19 1c8b9fc4 Serghei MIHAI
plugin_controller = __name__ + ':OpenidController'
20 c8204b73 Serghei Mihai
21 b699aa44 Serghei MIHAI
_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 25877dab Serghei MIHAI
            'redirect_uris': [url_for(host=request.host,
40
                                      controller=plugin_controller,
41
                                      action='callback',
42
                                      id=g.name,
43
                                      qualified=True)]
44 b699aa44 Serghei MIHAI
        })
45
        return create_client(**params)
46
47 b169c797 Serghei MIHAI
48
class OzwilloPyoidcPlugin(plugins.SingletonPlugin):
49
    plugins.implements(plugins.IConfigurer)
50 c8204b73 Serghei Mihai
    plugins.implements(plugins.IRoutes)
51
    plugins.implements(plugins.IAuthenticator, inherit=True)
52 b169c797 Serghei MIHAI
53 c8204b73 Serghei Mihai
    def before_map(self, map):
54 a5f39ab1 Serghei MIHAI
        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 1ae62674 Serghei MIHAI
        map.connect('/user/slo',
61
                    controller=plugin_controller,
62 ffc3aa92 Serghei MIHAI
                    action='slo')
63 1ae62674 Serghei MIHAI
        map.redirect('/organization/{id:.*}/logout', '/user/_logout')
64
65 c8204b73 Serghei Mihai
        return map
66
67
    def after_map(self, map):
68
        return map
69
70
    def identify(self):
71 a5f39ab1 Serghei MIHAI
        user = session.get('user')
72
        if user and not toolkit.c.userobj:
73
            userobj = model.User.get(user)
74
            toolkit.c.user = userobj.name
75
            toolkit.c.userobj = userobj
76 c8204b73 Serghei Mihai
77
    def login(self):
78 f1d53ae8 Serghei MIHAI
        for cookie in request.cookies:
79
            value = request.cookies.get(cookie)
80
            response.set_cookie(cookie, value, secure=True, httponly=True)
81
82 a5f39ab1 Serghei MIHAI
        if 'organization_id' in session:
83
            g = model.Group.get(session['organization_id'])
84 b699aa44 Serghei MIHAI
            client = Clients.get(g)
85 6388360c Serghei MIHAI
            url, ht_args = client.create_authn_request(conf.ACR_VALUES)
86 a5f39ab1 Serghei MIHAI
            if ht_args:
87
                toolkit.request.headers.update(ht_args)
88 c4dcef5d Serghei MIHAI
            redirect_to(url)
89 a5f39ab1 Serghei MIHAI
        else:
90 c4dcef5d Serghei MIHAI
            redirect_to('/')
91 c8204b73 Serghei Mihai
92
    def logout(self):
93 b87c1c93 Serghei MIHAI
        log.info('Logging out user: %s' % session['user'])
94 7400c5df Serghei MIHAI
        session['user'] = None
95 b87c1c93 Serghei MIHAI
        session.save()
96
        g = model.Group.get(session['organization_id'])
97 992d3237 Serghei MIHAI
        for cookie in request.cookies:
98
            response.delete_cookie(cookie)
99 b87c1c93 Serghei MIHAI
        if g:
100 84922cdf Serghei MIHAI
            org_url = toolkit.url_for(host=request.host,
101
                                      controller='organization',
102
                                      action='read',
103
                                      id=g.name,
104
                                      qualified=True)
105
106 c4dcef5d Serghei MIHAI
            redirect_to(str(org_url))
107 b87c1c93 Serghei MIHAI
        else:
108 c4dcef5d Serghei MIHAI
            redirect_to('/')
109 b169c797 Serghei MIHAI
110
    def update_config(self, config_):
111
        toolkit.add_template_directory(config_, 'templates')
112
        toolkit.add_public_directory(config_, 'public')
113
        toolkit.add_resource('fanstatic', 'ozwillo_pyoidc')
114 c8204b73 Serghei Mihai
115
class OpenidController(base.BaseController):
116
117 a5f39ab1 Serghei MIHAI
    def sso(self, id):
118
        log.info('SSO for organization "%s"' % id)
119
        session['organization_id'] = id
120
        session.save()
121
        log.info('redirecting to login page')
122
        login_url = toolkit.url_for(host=request.host,
123
                                    controller='user',
124
                                    action='login',
125
                                    qualified=True)
126 c4dcef5d Serghei MIHAI
        redirect_to(login_url)
127 a5f39ab1 Serghei MIHAI
128
    def callback(self):
129 b699aa44 Serghei MIHAI
        g = model.Group.get(session['organization_id'])
130
        client = Clients.get(g)
131
        userinfo = client.callback(request.GET)
132 cb408fc1 Serghei MIHAI
        locale = None
133 b699aa44 Serghei MIHAI
        log.info('Received userinfo: %s' % userinfo)
134 856c858c Serghei MIHAI
135 f22ce77a Serghei MIHAI
        if 'locale' in userinfo:
136 cb408fc1 Serghei MIHAI
            locale = userinfo.get('locale', '')
137
            if '-' in locale:
138
                locale, country = locale.split('-')
139
140 f22ce77a Serghei MIHAI
        org_url = str(toolkit.url_for(host=request.host,
141
                                      controller="organization",
142
                                      action='read',
143
                                      id=g.name,
144
                                      locale=locale,
145
                                      qualified=True))
146
        if 'sub' in userinfo:
147
148 cb408fc1 Serghei MIHAI
            userobj = model.User.get(userinfo['sub'])
149 f22ce77a Serghei MIHAI
            if not userobj:
150
                user_dict = {'id': userinfo['sub'],
151
                             'name': userinfo['sub'].replace('-', ''),
152
                             'email': userinfo['email'],
153
                             'password': userinfo['sub']
154
                             }
155
                context = {'ignore_auth': True, 'model': model,
156
                           'session': model.Session}
157
                user_create(context, user_dict)
158
                userobj = model.User.get(userinfo['sub'])
159
                if client.app_admin or client.app_user:
160
                    member_dict = {
161
                        'id': g.id,
162
                        'object': userinfo['sub'],
163
                        'object_type': 'user',
164
                        'capacity': 'admin',
165
                    }
166
167
                    member_create_context = {
168
                        'model': model,
169
                        'user': userobj.name,
170
                        'ignore_auth': True,
171
                        'session': session
172
                    }
173
174
                    member_create(member_create_context, member_dict)
175
176 9b4fdd83 Serghei MIHAI
            if 'given_name' in userinfo:
177
                userobj.fullname = userinfo['given_name']
178
            if 'family_name' in userinfo:
179
                userobj.fullname += ' ' + userinfo['family_name']
180
            userobj.save()
181
182 f22ce77a Serghei MIHAI
            if 'nickname' in userinfo:
183
                userobj.name = userinfo['nickname']
184
            try:
185
                userobj.save()
186
            except Exception, e:
187
                log.warning('Error while saving user name: %s' % e)
188
189 b699aa44 Serghei MIHAI
            session['user'] = userobj.id
190
            session.save()
191
192 f22ce77a Serghei MIHAI
        redirect_to(org_url)
193 1ae62674 Serghei MIHAI
194 1c8b9fc4 Serghei MIHAI
195 1ae62674 Serghei MIHAI
    def slo(self):
196
        """
197
        Revokes the delivered access token. Logs out the user
198
        """
199 ffc3aa92 Serghei MIHAI
200
        if not request.referer or request.host not in request.referer:
201
            redirect_to('/')
202
203 b699aa44 Serghei MIHAI
        g = model.Group.get(session['organization_id'])
204 2bfcb919 Serghei MIHAI
        org_url = url_for(host=request.host,
205
                          controller='organization',
206
                          action='read',
207
                          id=g.name,
208
                          qualified=True)
209 1c8b9fc4 Serghei MIHAI
        org_url = str(org_url)
210
211 ffc3aa92 Serghei MIHAI
        if toolkit.c.user:
212 1c8b9fc4 Serghei MIHAI
            client = Clients.get(g)
213
            logout_url = client.end_session_endpoint
214
215
            redirect_uri = org_url + '/logout'
216
217 838dc9f3 Serghei MIHAI
            if not hasattr(client, 'access_token'):
218
                self.sso(g.name)
219
220 1c8b9fc4 Serghei MIHAI
            # revoke the access token
221
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
222
            data = 'token=' + client.access_token
223
            data += '&token_type_hint=access_token'
224
            client.http_request(client.revocation_endpoint, 'POST',
225
                                data=data, headers=headers)
226
227
            # redirect to IDP logout
228
            logout_url += '?id_token_hint=%s&' % client.id_token
229
            logout_url += 'post_logout_redirect_uri=%s' % redirect_uri
230 82a66e96 Serghei MIHAI
            redirect_to(str(logout_url))
231 c4dcef5d Serghei MIHAI
        redirect_to(org_url)