Project

General

Profile

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

organization_api / ckanext / ozwillo_organization_api / plugin.py @ 3d50c8c3

1
from hashlib import sha1
2
import hmac
3
import requests
4
import logging
5
import json
6

    
7
import ckan.plugins as plugins
8
import ckan.plugins.toolkit as toolkit
9

    
10
import ckan.logic as logic
11

    
12
from pylons import config
13
from ckan.common import request, _
14
from ckan.logic.action.create import _group_or_org_create as group_or_org_create
15
from ckan.logic.action.create import user_create
16
from ckan.logic.action.delete import _group_or_org_purge
17
from ckan.lib.plugins import DefaultOrganizationForm
18

    
19
plugin_config_prefix = 'ckanext.ozwillo_organization_api.'
20

    
21
log = logging.getLogger(__name__)
22

    
23
def valid_signature_required(func):
24

    
25
    signature_header_name = config.get(plugin_config_prefix + 'signature_header_name',
26
                                       'X-Hub-Signature')
27
    instantiated_secret = config.get(plugin_config_prefix + 'instantiation_secret',
28
                                     'secret')
29

    
30
    def wrapper(context, data):
31
        if signature_header_name in request.headers:
32
            if request.headers[signature_header_name].startswith('sha1='):
33
                algo, received_hmac = request.headers[signature_header_name].rsplit('=')
34
                computed_hmac = hmac.new(instantiated_secret, request.body, sha1).hexdigest()
35
                # the received hmac is uppercase according to
36
                # http://doc.ozwillo.com/#ref-3-2-1
37
                if received_hmac != computed_hmac.upper():
38
                    raise logic.NotAuthorized(_('Invalid HMAC'))
39
            else:
40
                raise logic.ValidationError(_('Invalid HMAC algo'))
41
        else:
42
            raise logic.NotAuthorized(_("No HMAC in the header"))
43
        return func(context, data)
44
    return wrapper
45

    
46
@valid_signature_required
47
def create_organization(context, data_dict):
48
    context['ignore_auth'] = True
49
    model = context['model']
50
    session = context['session']
51

    
52
    destruction_secret = config.get(plugin_config_prefix + 'destruction_secret',
53
                                       'changeme')
54

    
55
    client_id = data_dict.pop('client_id')
56
    client_secret = data_dict.pop('client_secret')
57
    instance_id = data_dict.pop('instance_id')
58

    
59
    # re-mapping received dict
60
    registration_uri = data_dict.pop('instance_registration_uri')
61
    organization = data_dict['organization']
62
    user = data_dict['user']
63
    user_dict = {
64
        'id': user['id'],
65
        'name': user['name'].lower().replace(' ', '').replace('.', ''),
66
        'email': user['email_address'],
67
        'password': user['id']
68
    }
69
    user_obj = model.User.get(user_dict['name'])
70

    
71
    org_dict = {
72
        'type': 'organization',
73
        'name': organization['name'].lower().replace(' ', '-'),
74
        'id': instance_id,
75
        'title': organization['name'],
76
        'user': user_dict['name']
77
    }
78

    
79
    if not user_obj:
80
        user_create(context, user_dict)
81
    context['user'] = user_dict['name']
82

    
83
    try:
84
        delete_uri = toolkit.url_for(host=request.host,
85
                                     controller='api', action='action',
86
                                     logic_function="delete-ozwillo-organization",
87
                                     ver=context['api_version'],
88
                                     qualified=True)
89
        organization_uri = toolkit.url_for(host=request.host,
90
                                           controller='organization',
91
                                           action='read',
92
                                           id=org_dict['name'],
93
                                           qualified=True)
94
        default_icon_url = toolkit.url_for(host=request.host,
95
                                           qualified=True,
96
                                           controller='home',
97
                                           action='index') + 'organization_icon.png'
98

    
99
        group_or_org_create(context, org_dict, is_org=True)
100

    
101
        # setting organization as active explicitely
102
        group = model.Group.get(org_dict['name'])
103
        group.state = 'active'
104
        group.image_url = default_icon_url
105
        group.save()
106
        model.repo.new_revision()
107
        model.GroupExtra(group_id=group.id, key='client_id',
108
                         value=client_id).save()
109
        model.GroupExtra(group_id=group.id, key='client_secret',
110
                         value=client_secret).save()
111
        session.flush()
112

    
113
        # notify about organization creation
114
        services = {'services': [{
115
            'local_id': 'organization',
116
            'name': 'Open Data',
117
            'service_uri': organization_uri + '/sso',
118
            'description': 'Organization ' + org_dict['name'] + ' on CKAN',
119
            'tos_uri': organization_uri,
120
            'policy_uri': organization_uri,
121
            'icon': group.image_url,
122
            'payment_option': 'FREE',
123
            'target_audience': ['PUBLIC_BODIES'],
124
            'contacts': [organization_uri],
125
            'redirect_uris': [organization_uri + '/callback'],
126
            'post_logout_redirect_uris': [organization_uri + '/logout'],
127
            'visible': False}],
128
            'instance_id': instance_id,
129
            'destruction_uri': delete_uri,
130
            'destruction_secret': destruction_secret,
131
            'needed_scopes': [{
132
                'scope_id': 'profile',
133
                'motivation': 'Used to link user to the organization'
134
            }]
135
        }
136
        headers = {'Content-type': 'application/json',
137
                   'Accept': 'application/json'}
138
        requests.post(registration_uri,
139
                      data=json.dumps(services),
140
                      auth=(client_id, client_secret),
141
                      headers=headers
142
                  )
143
    except logic.ValidationError, e:
144
        log.debug('Validation error "%s" occured while creating organization' % e)
145
        raise
146

    
147
@valid_signature_required
148
def delete_organization(context, data_dict):
149
    data_dict['id'] = data_dict.pop('instance_id')
150
    context['ignore_auth'] = True
151
    _group_or_org_purge(context, data_dict, is_org=True)
152

    
153

    
154
class OrganizationForm(plugins.SingletonPlugin, DefaultOrganizationForm):
155
    """
156
    Custom form ignoring 'title' and 'name' organization fields
157
    """
158
    plugins.implements(plugins.IGroupForm)
159

    
160
    def is_fallback(self):
161
        return True
162

    
163
    def group_types(self):
164
        return ('organization',)
165

    
166
    def form_to_db_schema(self):
167
        schema = super(OrganizationForm, self).form_to_db_schema()
168
        del schema['name']
169
        del schema['title']
170
        return schema
171

    
172

    
173
class OzwilloOrganizationApiPlugin(plugins.SingletonPlugin):
174
    """
175
    API for OASIS to create and delete an organization
176
    """
177
    plugins.implements(plugins.IActions)
178
    plugins.implements(plugins.IConfigurer)
179

    
180
    def update_config(self, config):
181
        toolkit.add_template_directory(config, 'templates')
182
        toolkit.add_public_directory(config, 'public')
183

    
184
    def get_actions(self):
185
        return {
186
            'create-ozwillo-organization': create_organization,
187
            'delete-ozwillo-organization': delete_organization
188
        }
(2-2/2)