This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
ckanext-ozwillo-organizatio.../ckanext/ozwillo_organization_api/plugin.py

213 lines
7.6 KiB
Python
Raw Normal View History

from hashlib import sha1
import hmac
import requests
import logging
import json
2015-03-10 17:41:04 +01:00
from slugify import slugify
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit
import ckan.logic as logic
import ckan.lib.base as base
from pylons import config
from ckan.common import request, _
from ckan.logic.action.create import _group_or_org_create as group_or_org_create
from ckan.logic.action.create import user_create
2015-02-03 11:01:54 +01:00
from ckan.logic.action.delete import _group_or_org_purge
from ckan.lib.plugins import DefaultOrganizationForm
plugin_config_prefix = 'ckanext.ozwillo_organization_api.'
log = logging.getLogger(__name__)
def valid_signature_required(func):
signature_header_name = config.get(plugin_config_prefix + 'signature_header_name',
'X-Hub-Signature')
instantiated_secret = config.get(plugin_config_prefix + 'instantiation_secret',
'secret')
def wrapper(context, data):
if signature_header_name in request.headers:
if request.headers[signature_header_name].startswith('sha1='):
algo, received_hmac = request.headers[signature_header_name].rsplit('=')
computed_hmac = hmac.new(instantiated_secret, request.body, sha1).hexdigest()
# the received hmac is uppercase according to
# http://doc.ozwillo.com/#ref-3-2-1
if received_hmac != computed_hmac.upper():
raise logic.NotAuthorized(_('Invalid HMAC'))
else:
raise logic.ValidationError(_('Invalid HMAC algo'))
else:
raise logic.NotAuthorized(_("No HMAC in the header"))
return func(context, data)
return wrapper
@valid_signature_required
def create_organization(context, data_dict):
context['ignore_auth'] = True
model = context['model']
session = context['session']
destruction_secret = config.get(plugin_config_prefix + 'destruction_secret',
'changeme')
client_id = data_dict.pop('client_id')
client_secret = data_dict.pop('client_secret')
instance_id = data_dict.pop('instance_id')
# re-mapping received dict
registration_uri = data_dict.pop('instance_registration_uri')
organization = data_dict['organization']
user = data_dict['user']
user_dict = {
2015-02-11 14:07:10 +01:00
'id': user['id'],
2015-03-12 17:18:43 +01:00
'name': user['id'].replace('-', ''),
'email': user['email_address'],
'password': user['id']
}
user_obj = model.User.get(user_dict['name'])
org_dict = {
'type': 'organization',
2015-03-10 17:41:04 +01:00
'name': slugify(organization['name']),
'id': instance_id,
'title': organization['name'],
'user': user_dict['name']
}
if not user_obj:
user_create(context, user_dict)
context['user'] = user_dict['name']
try:
delete_uri = toolkit.url_for(host=request.host,
controller='api', action='action',
logic_function="delete-ozwillo-organization",
ver=context['api_version'],
qualified=True)
organization_uri = toolkit.url_for(host=request.host,
controller='organization',
action='read',
id=org_dict['name'],
qualified=True)
default_icon_url = toolkit.url_for(host=request.host,
qualified=True,
controller='home',
action='index') + 'organization_icon.png'
group_or_org_create(context, org_dict, is_org=True)
# setting organization as active explicitely
group = model.Group.get(org_dict['name'])
group.state = 'active'
group.image_url = default_icon_url
group.save()
model.repo.new_revision()
model.GroupExtra(group_id=group.id, key='client_id',
value=client_id).save()
model.GroupExtra(group_id=group.id, key='client_secret',
value=client_secret).save()
session.flush()
# notify about organization creation
services = {'services': [{
'local_id': 'organization',
'name': 'Open Data',
2015-02-04 10:12:20 +01:00
'service_uri': organization_uri + '/sso',
'description': 'Organization ' + org_dict['name'] + ' on CKAN',
'tos_uri': organization_uri,
'policy_uri': organization_uri,
'icon': group.image_url,
'payment_option': 'FREE',
'target_audience': ['PUBLIC_BODIES'],
'contacts': [organization_uri],
'redirect_uris': [organization_uri + '/callback'],
'post_logout_redirect_uris': [organization_uri + '/logout'],
2015-02-17 10:38:20 +01:00
'visible': False}],
'instance_id': instance_id,
'destruction_uri': delete_uri,
'destruction_secret': destruction_secret,
'needed_scopes': [{
'scope_id': 'profile',
'motivation': 'Used to link user to the organization'
}]
}
headers = {'Content-type': 'application/json',
'Accept': 'application/json'}
requests.post(registration_uri,
data=json.dumps(services),
auth=(client_id, client_secret),
headers=headers
)
2015-02-06 14:07:56 +01:00
except logic.ValidationError, e:
log.debug('Validation error "%s" occured while creating organization' % e)
raise
@valid_signature_required
def delete_organization(context, data_dict):
2015-02-03 11:01:54 +01:00
data_dict['id'] = data_dict.pop('instance_id')
context['ignore_auth'] = True
_group_or_org_purge(context, data_dict, is_org=True)
class OrganizationForm(plugins.SingletonPlugin, DefaultOrganizationForm):
"""
Custom form ignoring 'title' and 'name' organization fields
"""
plugins.implements(plugins.IGroupForm)
def is_fallback(self):
return True
def group_types(self):
return ('organization',)
def form_to_db_schema(self):
schema = super(OrganizationForm, self).form_to_db_schema()
del schema['name']
del schema['title']
return schema
class ErrorController(base.BaseController):
def error403(self):
return base.abort(403, '')
class OzwilloOrganizationApiPlugin(plugins.SingletonPlugin):
"""
API for OASIS to create and delete an organization
"""
plugins.implements(plugins.IActions)
plugins.implements(plugins.IConfigurer)
plugins.implements(plugins.IRoutes)
def before_map(self, map):
# disable organization and members api
for action in ('member_create', 'member_delete',
'organization_member_delete',
'organization_member_create',
'organization_create',
'organization_update',
'organization_delete'):
map.connect('/api/{ver:.*}/action/%s' % action,
controller=__name__ + ':ErrorController',
action='error403')
return map
def after_map(self, map):
return map
def update_config(self, config):
toolkit.add_template_directory(config, 'templates')
toolkit.add_public_directory(config, 'public')
def get_actions(self):
return {
2015-02-03 10:38:39 +01:00
'create-ozwillo-organization': create_organization,
'delete-ozwillo-organization': delete_organization
}