Project

General

Profile

Download (7.98 KB) Statistics
| Branch: | Tag: | Revision:

oidc / ckanext / ozwillo_pyoidc / plugin.py @ 34457e9c

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 cf38fede Serghei Mihai
from ckan.lib.helpers import flash_error
11 c8204b73 Serghei Mihai
12 f1d53ae8 Serghei MIHAI
from pylons import config
13 c8204b73 Serghei Mihai
14 b71e8531 Serghei MIHAI
import conf
15 cf38fede Serghei Mihai
from oidc import create_client, OIDCError
16 c8204b73 Serghei Mihai
17
plugin_config_prefix = 'ckanext.ozwillo_pyoidc.'
18
19
log = logging.getLogger(__name__)
20 1c8b9fc4 Serghei MIHAI
plugin_controller = __name__ + ':OpenidController'
21 c8204b73 Serghei Mihai
22 b699aa44 Serghei MIHAI
23
class Clients(object):
24
25
    @classmethod
26 34457e9c Serghei Mihai
    def get_client(cls, g):
27 b699aa44 Serghei MIHAI
        params = conf.CLIENT.copy()
28
        params['client_registration'].update({
29
            'client_id': g._extras['client_id'].value,
30
            'client_secret': g._extras['client_secret'].value,
31 25877dab Serghei MIHAI
            'redirect_uris': [url_for(host=request.host,
32
                                      controller=plugin_controller,
33
                                      action='callback',
34
                                      id=g.name,
35
                                      qualified=True)]
36 b699aa44 Serghei MIHAI
        })
37
        return create_client(**params)
38
39 b169c797 Serghei MIHAI
40
class OzwilloPyoidcPlugin(plugins.SingletonPlugin):
41
    plugins.implements(plugins.IConfigurer)
42 c8204b73 Serghei Mihai
    plugins.implements(plugins.IRoutes)
43
    plugins.implements(plugins.IAuthenticator, inherit=True)
44 b169c797 Serghei MIHAI
45 c8204b73 Serghei Mihai
    def before_map(self, map):
46 a5f39ab1 Serghei MIHAI
        map.connect('/organization/{id:.*}/sso',
47
                    controller=plugin_controller,
48
                    action='sso')
49
        map.connect('/organization/{id:.*}/callback',
50
                    controller=plugin_controller,
51
                    action='callback')
52 1ae62674 Serghei MIHAI
        map.connect('/user/slo',
53
                    controller=plugin_controller,
54 ffc3aa92 Serghei MIHAI
                    action='slo')
55 1ae62674 Serghei MIHAI
        map.redirect('/organization/{id:.*}/logout', '/user/_logout')
56
57 c8204b73 Serghei Mihai
        return map
58
59
    def after_map(self, map):
60
        return map
61
62
    def identify(self):
63 a5f39ab1 Serghei MIHAI
        user = session.get('user')
64
        if user and not toolkit.c.userobj:
65
            userobj = model.User.get(user)
66
            toolkit.c.user = userobj.name
67
            toolkit.c.userobj = userobj
68 c8204b73 Serghei Mihai
69
    def login(self):
70 f1d53ae8 Serghei MIHAI
        for cookie in request.cookies:
71
            value = request.cookies.get(cookie)
72
            response.set_cookie(cookie, value, secure=True, httponly=True)
73
74 a5f39ab1 Serghei MIHAI
        if 'organization_id' in session:
75
            g = model.Group.get(session['organization_id'])
76 34457e9c Serghei Mihai
            client = Clients.get_client(g)
77
            url, ht_args, state = client.create_authn_request(conf.ACR_VALUES)
78
            session['state'] = state
79
            session.save()
80 a5f39ab1 Serghei MIHAI
            if ht_args:
81
                toolkit.request.headers.update(ht_args)
82 c4dcef5d Serghei MIHAI
            redirect_to(url)
83 a5f39ab1 Serghei MIHAI
        else:
84 c4dcef5d Serghei MIHAI
            redirect_to('/')
85 c8204b73 Serghei Mihai
86
    def logout(self):
87 b87c1c93 Serghei MIHAI
        log.info('Logging out user: %s' % session['user'])
88 7400c5df Serghei MIHAI
        session['user'] = None
89 b87c1c93 Serghei MIHAI
        session.save()
90
        g = model.Group.get(session['organization_id'])
91 992d3237 Serghei MIHAI
        for cookie in request.cookies:
92
            response.delete_cookie(cookie)
93 b87c1c93 Serghei MIHAI
        if g:
94 84922cdf Serghei MIHAI
            org_url = toolkit.url_for(host=request.host,
95
                                      controller='organization',
96
                                      action='read',
97
                                      id=g.name,
98
                                      qualified=True)
99
100 c4dcef5d Serghei MIHAI
            redirect_to(str(org_url))
101 b87c1c93 Serghei MIHAI
        else:
102 c4dcef5d Serghei MIHAI
            redirect_to('/')
103 b169c797 Serghei MIHAI
104
    def update_config(self, config_):
105
        toolkit.add_template_directory(config_, 'templates')
106
        toolkit.add_public_directory(config_, 'public')
107
        toolkit.add_resource('fanstatic', 'ozwillo_pyoidc')
108 c8204b73 Serghei Mihai
109
class OpenidController(base.BaseController):
110
111 a5f39ab1 Serghei MIHAI
    def sso(self, id):
112
        log.info('SSO for organization "%s"' % id)
113
        session['organization_id'] = id
114
        session.save()
115
        log.info('redirecting to login page')
116
        login_url = toolkit.url_for(host=request.host,
117
                                    controller='user',
118
                                    action='login',
119
                                    qualified=True)
120 c4dcef5d Serghei MIHAI
        redirect_to(login_url)
121 a5f39ab1 Serghei MIHAI
122
    def callback(self):
123 b699aa44 Serghei MIHAI
        g = model.Group.get(session['organization_id'])
124 34457e9c Serghei Mihai
        client = Clients.get_client(g)
125 cf38fede Serghei Mihai
        org_url = str(toolkit.url_for(controller="organization",
126
                                      action='read',
127
                                      id=g.name))
128
        try:
129 34457e9c Serghei Mihai
            userinfo, app_admin, app_user, access_token, id_token \
130
                = client.callback(session['state'], request.GET)
131
            session['access_token'] = access_token
132
            session['id_token'] = id_token
133
            session.save()
134 cf38fede Serghei Mihai
        except OIDCError, e:
135
            flash_error('Login failed')
136
            redirect_to(org_url, qualified=True)
137 cb408fc1 Serghei MIHAI
        locale = None
138 b699aa44 Serghei MIHAI
        log.info('Received userinfo: %s' % userinfo)
139 856c858c Serghei MIHAI
140 f22ce77a Serghei MIHAI
        if 'locale' in userinfo:
141 cb408fc1 Serghei MIHAI
            locale = userinfo.get('locale', '')
142
            if '-' in locale:
143
                locale, country = locale.split('-')
144
145 e5e6f14a Serghei Mihai
        org_url = str(toolkit.url_for(org_url, locale=locale, qualified=True))
146 f22ce77a Serghei MIHAI
        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 34457e9c Serghei Mihai
                if app_admin or app_user:
160 f22ce77a Serghei MIHAI
                    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 34457e9c Serghei Mihai
            client = Clients.get_client(g)
213 1c8b9fc4 Serghei MIHAI
            logout_url = client.end_session_endpoint
214
215
            redirect_uri = org_url + '/logout'
216
217
            # revoke the access token
218
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
219 34457e9c Serghei Mihai
            data = 'token=' + session.get('access_token')
220 1c8b9fc4 Serghei MIHAI
            data += '&token_type_hint=access_token'
221
            client.http_request(client.revocation_endpoint, 'POST',
222
                                data=data, headers=headers)
223
224
            # redirect to IDP logout
225 34457e9c Serghei Mihai
            logout_url += '?id_token_hint=%s&' % session.get('id_token')
226 1c8b9fc4 Serghei MIHAI
            logout_url += 'post_logout_redirect_uri=%s' % redirect_uri
227 82a66e96 Serghei MIHAI
            redirect_to(str(logout_url))
228 c4dcef5d Serghei MIHAI
        redirect_to(org_url)