Project

General

Profile

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

organization_api / ckanext / ozwillo_organization_api / plugin.py @ f5d8f6bc

1 5c8cfd17 Serghei MIHAI
from hashlib import sha1
2
import hmac
3 74d08116 Serghei MIHAI
import requests
4
import logging
5 d443ceb7 Serghei MIHAI
import json
6 ad8a37b8 Serghei MIHAI
from slugify import slugify
7 5c8cfd17 Serghei MIHAI
8
import ckan.plugins as plugins
9 cb821f25 Serghei MIHAI
import ckan.plugins.toolkit as toolkit
10
11 5c8cfd17 Serghei MIHAI
import ckan.logic as logic
12 5c4e0677 Serghei MIHAI
import ckan.lib.base as base
13 5c8cfd17 Serghei MIHAI
14
from pylons import config
15
from ckan.common import request, _
16
from ckan.logic.action.create import _group_or_org_create as group_or_org_create
17 74d08116 Serghei MIHAI
from ckan.logic.action.create import user_create
18 7d5492a9 Serghei MIHAI
from ckan.logic.action.delete import _group_or_org_purge
19 3c33f2ff Serghei MIHAI
from ckan.lib.plugins import DefaultOrganizationForm
20 5c8cfd17 Serghei MIHAI
21 cb821f25 Serghei MIHAI
plugin_config_prefix = 'ckanext.ozwillo_organization_api.'
22
23 74d08116 Serghei MIHAI
log = logging.getLogger(__name__)
24
25 5c8cfd17 Serghei MIHAI
def valid_signature_required(func):
26 cb821f25 Serghei MIHAI
27 5c8cfd17 Serghei MIHAI
    signature_header_name = config.get(plugin_config_prefix + 'signature_header_name',
28
                                       'X-Hub-Signature')
29 458a2c0b Serghei MIHAI
    instantiated_secret = config.get(plugin_config_prefix + 'instantiation_secret',
30 5c8cfd17 Serghei MIHAI
                                     'secret')
31
32
    def wrapper(context, data):
33
        if signature_header_name in request.headers:
34
            if request.headers[signature_header_name].startswith('sha1='):
35 cb821f25 Serghei MIHAI
                algo, received_hmac = request.headers[signature_header_name].rsplit('=')
36 74d08116 Serghei MIHAI
                computed_hmac = hmac.new(instantiated_secret, request.body, sha1).hexdigest()
37 cb821f25 Serghei MIHAI
                # the received hmac is uppercase according to
38
                # http://doc.ozwillo.com/#ref-3-2-1
39
                if received_hmac != computed_hmac.upper():
40 5c8cfd17 Serghei MIHAI
                    raise logic.NotAuthorized(_('Invalid HMAC'))
41
            else:
42
                raise logic.ValidationError(_('Invalid HMAC algo'))
43
        else:
44
            raise logic.NotAuthorized(_("No HMAC in the header"))
45
        return func(context, data)
46
    return wrapper
47
48
@valid_signature_required
49
def create_organization(context, data_dict):
50 74d08116 Serghei MIHAI
    context['ignore_auth'] = True
51
    model = context['model']
52 55fa9cda Serghei MIHAI
    session = context['session']
53 cb821f25 Serghei MIHAI
54
    destruction_secret = config.get(plugin_config_prefix + 'destruction_secret',
55
                                       'changeme')
56
57
    client_id = data_dict.pop('client_id')
58
    client_secret = data_dict.pop('client_secret')
59
    instance_id = data_dict.pop('instance_id')
60
61
    # re-mapping received dict
62
    registration_uri = data_dict.pop('instance_registration_uri')
63
    organization = data_dict['organization']
64 74d08116 Serghei MIHAI
    user = data_dict['user']
65 270f763c Serghei MIHAI
    user_dict = {
66 19d8afdf Serghei MIHAI
        'id': user['id'],
67 60b9fd7f Serghei MIHAI
        'name': user['id'].replace('-', ''),
68 270f763c Serghei MIHAI
        'email': user['email_address'],
69
        'password': user['id']
70
    }
71
    user_obj = model.User.get(user_dict['name'])
72
73 cb821f25 Serghei MIHAI
    org_dict = {
74
        'type': 'organization',
75 ad8a37b8 Serghei MIHAI
        'name': slugify(organization['name']),
76 0225110e Serghei MIHAI
        'id': instance_id,
77 74d08116 Serghei MIHAI
        'title': organization['name'],
78 270f763c Serghei MIHAI
        'user': user_dict['name']
79 74d08116 Serghei MIHAI
    }
80
81
    if not user_obj:
82
        user_create(context, user_dict)
83
    context['user'] = user_dict['name']
84
85 cb821f25 Serghei MIHAI
    try:
86 c314bc51 Serghei MIHAI
        delete_uri = toolkit.url_for(host=request.host,
87
                                     controller='api', action='action',
88 55388392 Serghei MIHAI
                                     logic_function="delete-ozwillo-organization",
89 cb821f25 Serghei MIHAI
                                     ver=context['api_version'],
90
                                     qualified=True)
91 74d08116 Serghei MIHAI
        organization_uri = toolkit.url_for(host=request.host,
92
                                           controller='organization',
93
                                           action='read',
94
                                           id=org_dict['name'],
95
                                           qualified=True)
96 d443ceb7 Serghei MIHAI
        default_icon_url = toolkit.url_for(host=request.host,
97
                                           qualified=True,
98
                                           controller='home',
99 b34cfce8 Serghei MIHAI
                                           action='index') + 'opendata.png'
100 74d08116 Serghei MIHAI
101 cb821f25 Serghei MIHAI
        group_or_org_create(context, org_dict, is_org=True)
102
103 74d08116 Serghei MIHAI
        # setting organization as active explicitely
104
        group = model.Group.get(org_dict['name'])
105
        group.state = 'active'
106 d443ceb7 Serghei MIHAI
        group.image_url = default_icon_url
107 74d08116 Serghei MIHAI
        group.save()
108 55fa9cda Serghei MIHAI
        model.repo.new_revision()
109
        model.GroupExtra(group_id=group.id, key='client_id',
110
                         value=client_id).save()
111
        model.GroupExtra(group_id=group.id, key='client_secret',
112
                         value=client_secret).save()
113
        session.flush()
114 74d08116 Serghei MIHAI
115 cb821f25 Serghei MIHAI
        # notify about organization creation
116
        services = {'services': [{
117
            'local_id': 'organization',
118 dca00d04 Serghei MIHAI
            'name': 'Open Data',
119 472417ae Serghei MIHAI
            'service_uri': organization_uri + '/sso',
120 d443ceb7 Serghei MIHAI
            'description': 'Organization ' + org_dict['name'] + ' on CKAN',
121
            'tos_uri': organization_uri,
122
            'policy_uri': organization_uri,
123
            'icon': group.image_url,
124
            'payment_option': 'FREE',
125
            'target_audience': ['PUBLIC_BODIES'],
126
            'contacts': [organization_uri],
127 a5322fb2 Serghei MIHAI
            'redirect_uris': [organization_uri + '/callback'],
128 eecd60db Serghei MIHAI
            'post_logout_redirect_uris': [organization_uri + '/logout'],
129 00985cc5 Serghei MIHAI
            'visible': False}],
130 cb821f25 Serghei MIHAI
            'instance_id': instance_id,
131
            'destruction_uri': delete_uri,
132
            'destruction_secret': destruction_secret,
133
            'needed_scopes': [{
134
                'scope_id': 'profile',
135
                'motivation': 'Used to link user to the organization'
136
            }]
137
        }
138 606ae999 Serghei MIHAI
        headers = {'Content-type': 'application/json',
139
                   'Accept': 'application/json'}
140 cb821f25 Serghei MIHAI
        requests.post(registration_uri,
141 d443ceb7 Serghei MIHAI
                      data=json.dumps(services),
142 606ae999 Serghei MIHAI
                      auth=(client_id, client_secret),
143
                      headers=headers
144 cb821f25 Serghei MIHAI
                  )
145 d2732e9e Serghei MIHAI
    except logic.ValidationError, e:
146
        log.debug('Validation error "%s" occured while creating organization' % e)
147
        raise
148 5c8cfd17 Serghei MIHAI
149
@valid_signature_required
150
def delete_organization(context, data_dict):
151 7d5492a9 Serghei MIHAI
    data_dict['id'] = data_dict.pop('instance_id')
152
    context['ignore_auth'] = True
153
    _group_or_org_purge(context, data_dict, is_org=True)
154 5c8cfd17 Serghei MIHAI
155
156 3c33f2ff Serghei MIHAI
class OrganizationForm(plugins.SingletonPlugin, DefaultOrganizationForm):
157
    """
158
    Custom form ignoring 'title' and 'name' organization fields
159
    """
160
    plugins.implements(plugins.IGroupForm)
161
162
    def is_fallback(self):
163 f5d8f6bc Serghei MIHAI
        return False
164 3c33f2ff Serghei MIHAI
165
    def group_types(self):
166
        return ('organization',)
167
168
    def form_to_db_schema(self):
169
        schema = super(OrganizationForm, self).form_to_db_schema()
170
        del schema['name']
171
        del schema['title']
172
        return schema
173
174
175 5c4e0677 Serghei MIHAI
class ErrorController(base.BaseController):
176
    def error403(self):
177
        return base.abort(403, '')
178
179
180 5c8cfd17 Serghei MIHAI
class OzwilloOrganizationApiPlugin(plugins.SingletonPlugin):
181
    """
182
    API for OASIS to create and delete an organization
183
    """
184
    plugins.implements(plugins.IActions)
185 d443ceb7 Serghei MIHAI
    plugins.implements(plugins.IConfigurer)
186 5c4e0677 Serghei MIHAI
    plugins.implements(plugins.IRoutes)
187
188
    def before_map(self, map):
189
        # disable organization and members api
190
        for action in ('member_create', 'member_delete',
191
                       'organization_member_delete',
192
                       'organization_member_create',
193
                       'organization_create',
194
                       'organization_update',
195
                       'organization_delete'):
196
            map.connect('/api/{ver:.*}/action/%s' % action,
197
                        controller=__name__ + ':ErrorController',
198
                        action='error403')
199
        return map
200
201
    def after_map(self, map):
202
        return map
203 d443ceb7 Serghei MIHAI
204
    def update_config(self, config):
205 9afd103f Serghei MIHAI
        toolkit.add_template_directory(config, 'templates')
206 d443ceb7 Serghei MIHAI
        toolkit.add_public_directory(config, 'public')
207 5c8cfd17 Serghei MIHAI
208
    def get_actions(self):
209
        return {
210 f81cd06f Serghei MIHAI
            'create-ozwillo-organization': create_organization,
211
            'delete-ozwillo-organization': delete_organization
212 5c8cfd17 Serghei MIHAI
        }