コード例 #1
0
    def close_alert(self, dataset_name, alert_id):
        '''Delete an alert'''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized(
                'You need to be authenticated to delete an alert')

        alert = DatasetAlert.get(alert_id)
        if not alert.can_close(user):
            raise toolkit.NotAuthorized(
                'You are not allowed to close this alert')

        alert.close(user, toolkit.request.POST.get('comment'))
        DB.add(alert)
        DB.commit()

        alert.notify_response()

        return self.json_response({
            'id': alert.id,
            'user_id': alert.user_id,
            'dataset_id': alert.dataset_id,
            'type': alert.type,
            'comment': alert.comment,
            'created': alert.created,
            'closed': alert.closed,
            'closed_by_id': alert.closed_by_id,
            'close_comment': alert.close_comment,
        })
コード例 #2
0
    def search_sql(self, data_dict):
        '''
        :param sql: the sql statement that will be
        executed against the previously configured backend
         (required, e.g. 'SELECT * FROM table_name')

        :rtype: dictionary
        :param fields: fields/columns and their extra metadata
        :type fields: list of dictionaries
        :param records: list of matching results
        :type records: list of dictionaries
        '''
        try:
            engine = self._get_engine()
            connection = engine.connect()
        except OperationalError as e:

            log.error(e)

            raise ValidationError({
                'connection_parameters':
                    [_('Unable to connect to Database, please check!')]
            })

        sql = data_dict['sql']

        try:
            connection.execute(
                u'SET LOCAL statement_timeout TO {0}'.format(_TIMEOUT))

            results = connection.execute(sql)

            return format_results(results)

        except ProgrammingError as e:
            if e.orig.pgcode == _PG_ERR_CODE['permission_denied']:
                raise toolkit.NotAuthorized({
                    'permissions': ['Not authorized to read resource.']
                })

            def _remove_explain(msg):
                return (msg.replace('EXPLAIN (VERBOSE, FORMAT JSON) ', '')
                        .replace('EXPLAIN ', ''))

            raise ValidationError({
                'query': [_remove_explain(str(e))],
                'info': {
                    'statement': [_remove_explain(e.statement)],
                    'params': [e.params],
                    'orig': [_remove_explain(str(e.orig))]
                }
            })
        except DBAPIError as e:
            if e.orig.pgcode == _PG_ERR_CODE['query_canceled']:
                raise ValidationError({
                    'query': ['Query took too long']
                })
            raise
        finally:
            connection.close()
コード例 #3
0
def get_download_authz_token(context,
                             org_name,
                             package_name,
                             resource_id,
                             activity_id=None):
    # type: (Dict[str, Any], str, str, str, str) -> str
    """Get an authorization token for getting the download URL from LFS
    """
    authorize = toolkit.get_action('authz_authorize')
    if not authorize:
        raise RuntimeError(
            "Cannot find authz_authorize; Is ckanext-authz-service installed?")

    scope = helpers.resource_authz_scope(package_name,
                                         org_name=org_name,
                                         actions='read',
                                         resource_id=resource_id,
                                         activity_id=activity_id)
    log.debug("Requesting authorization token for scope: %s", scope)
    authz_result = authorize(context, {"scopes": [scope]})
    if not authz_result or not authz_result.get('token', False):
        raise RuntimeError("Failed to get authorization token for LFS server")
    log.debug("Granted scopes: %s", authz_result['granted_scopes'])

    if len(authz_result['granted_scopes']) == 0:
        raise toolkit.NotAuthorized(
            "You are not authorized to download this resource")

    return ensure_text(authz_result['token'])
コード例 #4
0
def search_sql(context, data_dict):
    engine = _get_engine(data_dict)
    context['connection'] = engine.connect()
    timeout = context.get('query_timeout', _TIMEOUT)
    _cache_types(context)

    try:
        context['connection'].execute(
            u'SET LOCAL statement_timeout TO {0}'.format(timeout))
        results = context['connection'].execute(
            data_dict['sql'].replace('%', '%%')
        )
        return format_results(context, results, data_dict)

    except ProgrammingError, e:
        if e.orig.pgcode == _PG_ERR_CODE['permission_denied']:
            raise toolkit.NotAuthorized({
                'permissions': ['Not authorized to read resource.']
            })
        raise ValidationError({
            'query': [str(e)],
            'info': {
                'statement': [e.statement],
                'params': [e.params],
                'orig': [str(e.orig)]
            }
        })
コード例 #5
0
def _make_public(data_dict, context, type='package'):
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})

    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False):
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to publish the dataset.']})

    # Check state
    if dataset_dict.get('publication_state', False):
        return {'success': False, 'error': 'Dataset publication state is not empty'}

        # set as public
    dataset_dict['private'] = False
    toolkit.get_action('package_update')(context=context, data_dict=dataset_dict)

    log.info("success making public package {0}".format(package_id))
    return {'success': True, 'error': None}
コード例 #6
0
    def alert(self, dataset_name):
        '''
        Put an alert aka. a signalement on a dataset.
        '''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized('Alert creation requires an user')

        dataset = Package.by_name(dataset_name)

        alert_type = toolkit.request.POST['type']
        comment = toolkit.request.POST['comment']
        alert = DatasetAlert(dataset, user, alert_type, comment)
        DB.add(alert)
        DB.commit()

        alert.notify_admins()

        return self.json_response({
            'id': alert.id,
            'user_id': alert.user_id,
            'dataset_id': alert.dataset_id,
            'type': alert.type,
            'comment': alert.comment,
            'created': alert.created
        })
コード例 #7
0
ファイル: action.py プロジェクト: datopian/ckanext-opnv
def user_show(context, data_dict):
    '''Forbid anonymous access to user info.
    '''
    if context.get('user') or context.get('ignore_auth'):
        return logic.action.get.user_show(context, data_dict)
    else:
        raise toolkit.NotAuthorized(
            'You must be logged in to perform this action.')
コード例 #8
0
ファイル: db.py プロジェクト: yuriprym/ckan
def search_sql(context, data_dict):
    engine = _get_engine(data_dict)
    context['connection'] = engine.connect()
    timeout = context.get('query_timeout', _TIMEOUT)
    _cache_types(context)

    sql = data_dict['sql'].replace('%', '%%')

    try:

        context['connection'].execute(
            u'SET LOCAL statement_timeout TO {0}'.format(timeout))

        table_names = datastore_helpers.get_table_names_from_sql(context, sql)
        log.debug('Tables involved in input SQL: {0!r}'.format(table_names))

        system_tables = [t for t in table_names if t.startswith('pg_')]
        if len(system_tables):
            raise toolkit.NotAuthorized({
                'permissions': ['Not authorized to access system tables']
            })

        results = context['connection'].execute(sql)

        return format_results(context, results, data_dict)

    except ProgrammingError, e:
        if e.orig.pgcode == _PG_ERR_CODE['permission_denied']:
            raise toolkit.NotAuthorized({
                'permissions': ['Not authorized to read resource.']
            })

        def _remove_explain(msg):
            return (msg.replace('EXPLAIN (FORMAT JSON) ', '')
                       .replace('EXPLAIN ', ''))

        raise ValidationError({
            'query': [_remove_explain(str(e))],
            'info': {
                'statement': [_remove_explain(e.statement)],
                'params': [e.params],
                'orig': [_remove_explain(str(e.orig))]
            }
        })
コード例 #9
0
def user_show(context, data_dict):
    '''Forbid anonymous access to user info.
    API call works with POST request with authorization header and id
    of desired user supplied.
    '''
    if context.get('user'):
        return logic.action.get.user_show(context, data_dict)
    else:
        raise toolkit.NotAuthorized(
            'You must be logged in to perform this action.')
コード例 #10
0
def update_short_url(context, data_dict):
    """
    Update a short url. Raises validation error if any
    :param context:
    :param data_dict:
    :return:
    """
    if toolkit.config.get('ckanext.url_shortner_key', None) != data_dict.get(
            'token_key', '').strip():
        raise toolkit.NotAuthorized("No token_key parameter provided")
    md = UrlShorten.update_entry(**data_dict)
    return md.as_dict()
コード例 #11
0
def delete_short_url(context, data_dict):
    """
    Delete an entry (soft delete) or raises validation error if any
    :param context:
    :param data_dict:
    :return:
    """
    if toolkit.config.get('ckanext.url_shortner_key', None) != data_dict.get(
            'token_key', '').strip():
        raise toolkit.NotAuthorized("No token_key parameter provided")
    _ = UrlShorten.delete_entry(**data_dict)
    return {u"msg": u"Entry {} deleted".format(data_dict.get(u'id', ''))}
コード例 #12
0
    def membership_accept(self, request_id):
        '''Accept a membership request'''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized(
                'Membership validation requires an user')

        membership_request = MembershipRequest.get(request_id)
        membership = membership_request.accept(user)

        return self.json_response(membership)
コード例 #13
0
    def _toggle_featured(self, reuse_id, featured=None):
        '''
        Mark or unmark a reuse as featured
        '''
        user = toolkit.c.userobj
        if not user or not user.sysadmin:
            raise toolkit.NotAuthorized()

        reuse = Related.get(reuse_id)
        reuse.featured = featured if featured is not None else (
            0 if reuse.featured else 1)
        self.commit()
        return reuse

        return self.json_response(reuse)
コード例 #14
0
    def membership_refuse(self, request_id):
        '''Refuse a membership request'''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized(
                'Membership validation requires an user')

        comment = toolkit.request.params.get('comment')

        membership_request = MembershipRequest.get(request_id)
        membership_request.refuse(user, comment)

        return self.json_response({})
コード例 #15
0
def _approve(data_dict, context, type='package'):
    log.debug('_approve: Approving "{0}" ({1})'.format(data_dict['id'], data_dict.get('name', '')))
    # a dataset id i s necessary
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})
    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # DOI has to be already reserved (minted)
    doi = dataset_dict.get('doi', '')
    default_prefix = config.get('datacite_publication.doi_prefix', '10.xxxxx')
    allowed_prefixes = config.get('datacite_publication.custom_prefix', '').split(' ') + [default_prefix]

    log.debug('_approve: Doi "{0}" ({1})'.format(doi, ', '.join(allowed_prefixes)))

    doi_prefix = doi.split('/')[0].strip()

    if (not doi) or (len(doi) <= 0) or (doi_prefix not in allowed_prefixes):
        raise toolkit.ValidationError(
            {'doi': 'dataset has no valid minted DOI [' + ', '.join(allowed_prefixes) + ']/*'})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False) or not helpers.datacite_publication_is_admin():
        log.error('ERROR approving dataset, current user is not authorized: isAdmin = {0}'.format(
            helpers.datacite_publication_is_admin()))
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to approve the dataset (admin only).']})

    # change publication state
    dataset_dict['publication_state'] = 'approved'
    dataset_dict['private'] = False
    context['message'] = APPROVAL_MESSAGE + " for dataset {0}".format(package_id)
    toolkit.get_action('package_update')(context=context, data_dict=dataset_dict)

    # save activity
    _add_activity(dataset_dict, APPROVAL_MESSAGE, context)

    # notify owner and involved users
    dataset_owner = dataset_dict.get('creator_user_id', '')
    datacite_approved_mail(dataset_owner, dataset_dict, context)

    return {'success': True, 'error': None}
コード例 #16
0
    def check_access(self, action_name, context, data_dict=None):
        """
        Check whether the user has the privilege to perform the named action,
        before calling the core check_access function.
        """
        check_auth = not context.get('ignore_auth', False)
        if check_auth:
            check_context = context.copy()
            check_context.setdefault('session', check_context['model'].Session)  # because ckan.lib.helpers.check_access does not add the session to the context *smh*
            check_data_dict = {
                'user_id': context['user'],
                'action': action_name,
            }
            privilege_check = action.user_privilege_check(check_context, check_data_dict)
            if not privilege_check['success']:
                raise tk.NotAuthorized(privilege_check['msg'])

        return self.core_check_access(action_name, context, data_dict)
コード例 #17
0
ファイル: logic.py プロジェクト: EnviDat/ckanext-passwordless
def _request_token(user_id):
    if toolkit.c.user:
        # Don't offer the reset form if already logged in
        log.warning("User already logged in {}".format(toolkit.c.user))
        raise toolkit.NotAuthorized('user already logged in, logout first')

    context = {'user': toolkit.c.user}

    data_dict = {'id': user_id}
    user_obj = None
    try:
        user_dict = toolkit.get_action('user_show')(context, data_dict)
        user_obj = context['user_obj']
    except logic.NotFound:
        # Try searching the user
        del data_dict['id']
        data_dict['q'] = user_id

        if user_id and len(user_id) > 2:
            user_list = toolkit.get_action('user_list')(context, data_dict)
            if len(user_list) == 1:
                # This is ugly, but we need the user object for the mailer,
                # and user_list does not return them
                del data_dict['q']
                data_dict['id'] = user_list[0]['id']
                user_dict = toolkit.get_action('user_show')(context, data_dict)
                user_obj = context['user_obj']
            elif len(user_list) > 1:
                raise logic.NotFound('"%s" matched several users' % user_id)
            else:
                raise logic.NotFound('No such user: %s' % user_id)
        else:
            raise logic.NotFound('No such user: %s' % user_id)

    if user_obj:
        try:
            passwordless_send_reset_link(user_obj)

        except mailer.MailerException as e:
            log.error('Could not send token link: %s' % str(e))
            raise mailer.MailerException(
                'could not send token link by mail: %s' % str(e))

    return
コード例 #18
0
ファイル: logic.py プロジェクト: EnviDat/ckanext-passwordless
def _reset(context, data_dict):
    # No one is logged in already
    if toolkit.c.user:
        log.warning("User already logged in {}".format(toolkit.c.user))
        raise toolkit.NotAuthorized('user already logged in, logout first')

    # Check email is present
    try:
        email = data_dict['email']
        email = email.lower()
    except KeyError:
        raise toolkit.ValidationError({'email': 'missing email'})

    # Check email is valid
    if not util.check_email(email):
        raise toolkit.ValidationError({'email': 'invalid email'})

    # control attempts (exception raised on fail)
    _check_reset_attempts(email.encode())

    # get existing user from email
    user = util.get_user(email)
    # log.debug('passwordless_request_reset: USER is = ' + str(user))

    if not user:
        # A user with this email address doesn't yet exist in CKAN,
        # so create one.
        user = _create_user(email)
        log.debug('passwordless_request_reset: created user = '******'state') == 'deleted':
            raise toolkit.ValidationError(
                {'user': '******'.format(email)})
        # token request
        _request_token(user.get('id'))
    else:
        raise toolkit.ValidationError(
            {'user': '******'})

    log.debug("controller redirecting: user.login, email =  " + str(email))
    return "reset successful"
コード例 #19
0
    def membership_request(self, org_name):
        '''Request membership for an organization'''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized('Membership request requires an user')

        organization = Group.by_name(org_name)

        comment = toolkit.request.params.get('comment')
        membership_request = MembershipRequest(user, organization, comment)

        DB.add(membership_request)
        DB.commit()

        membership_request.notify_admins()

        return self.json_response({})
コード例 #20
0
    def get_upload_authz_token(self, dataset_id):
        # type: (str) -> str
        """Get an authorization token to upload the file to LFS
        """
        authorize = toolkit.get_action('authz_authorize')
        if not authorize:
            raise RuntimeError(
                "Cannot find authz_authorize; Is ckanext-authz-service installed?"
            )

        context = {'ignore_auth': True, 'auth_user_obj': self._user}
        scope = helpers.resource_authz_scope(dataset_id, actions='write')
        authz_result = authorize(context, {"scopes": [scope]})

        if not authz_result or not authz_result.get('token', False):
            raise RuntimeError(
                "Failed to get authorization token for LFS server")

        if len(authz_result['granted_scopes']) == 0:
            raise toolkit.NotAuthorized(
                "You are not authorized to upload resources")

        return authz_result['token']
コード例 #21
0
def _finish_manually(data_dict, context, type='package'):
    log.debug('_finish_manually: Finishing "{0}" ({1})'.format(data_dict['id'], data_dict.get('name', '')))
    # a dataset id i s necessary
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})
    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # DOI has to be already reserved (minted)
    if not dataset_dict.get('doi', None):
        raise toolkit.ValidationError({'doi': 'dataset has no valid minted DOI'})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False) or not helpers.datacite_publication_is_admin():
        log.error('ERROR finishing publication dataset, current user is not authorized: isAdmin = {0}'.format(
            helpers.datacite_publication_is_admin()))
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to finish manually the dataset publication (admin only).']})

    # change publication state
    dataset_dict['publication_state'] = 'published'
    dataset_dict['private'] = False
    context['message'] = FINISH_MESSAGE + " for dataset {0}".format(package_id)
    toolkit.get_action('package_update')(context=context, data_dict=dataset_dict)

    # save activity
    _add_activity(dataset_dict, FINISH_MESSAGE, context)

    # notify owner and involved users
    dataset_owner = dataset_dict.get('creator_user_id', '')
    datacite_finished_mail(dataset_owner, dataset_dict, context)

    return {'success': True, 'error': None}
コード例 #22
0
ファイル: actions.py プロジェクト: chris48s/ckanext-unhcr
def package_get_microdata_collections(context, data_dict):
    default_error = 'Unknown microdata error'

    # Check access
    toolkit.check_access('sysadmin', context)
    api_key = config.get('ckanext.unhcr.microdata_api_key')
    if not api_key:
        raise toolkit.NotAuthorized('Microdata API Key is not set')

    try:

        # Get collections
        headers = {'X-Api-Key': api_key}
        url = 'https://microdata.unhcr.org/index.php/api/collections'
        response = requests.get(url, headers=headers).json()
        if response.get('status') != 'success':
            raise RuntimeError(str(response.get('errors', default_error)))
        collections = response['collections']

    except requests.exceptions.HTTPError:
        log.exception(exception)
        raise RuntimeError('Microdata connection failed')

    return collections
コード例 #23
0
ファイル: logic.py プロジェクト: EnviDat/ckanext-passwordless
def _login(context, data_dict):
    if toolkit.c.user:
        # Don't offer the reset form if already logged in
        log.warning("User already logged in")
        raise toolkit.NotAuthorized('user already logged in, logout first')

    # Check if parameters are present
    try:
        user_id = data_dict.get('id')
        if not user_id:
            email = data_dict['email'].lower()
            # Check email is valid
            if not util.check_email(email):
                raise toolkit.ValidationError({'email': 'invalid email'})
            # get the user id
            user_id = util.get_user_id(email)
            if not user_id:
                raise toolkit.ValidationError({
                    'email':
                    'email does not correspond to a registered user'
                })
    except KeyError:
        raise toolkit.ValidationError({'email': 'missing email'})
    try:
        orig_key = data_dict['key']
    except KeyError:
        raise toolkit.ValidationError({'key': 'missing token'})

    if len(orig_key) <= 32 and not orig_key.startswith("b'"):
        key = "b'{0}'".format(orig_key)
    else:
        key = orig_key
    log.debug('login: {0} ({1}) => {2}'.format(user_id, orig_key, key))

    # get whether to return context (UI) or just a message (API)
    return_context = data_dict.get('return_context', False)

    try:
        data_dict = {'id': user_id}
        user_dict = logic.get_action('user_show')(context, data_dict)
        user_obj = context['user_obj']
        email = user_dict.get('email', user_obj.email)
    except logic.NotFound:
        raise logic.NotFound('"%s" matched several users' % user_id)
    except toolkit.NotAuthorized:
        raise toolkit.NotAuthorized('Exception (Not Authorized) email = ' +
                                    str(email) + 'id = ' + str(user_id))
    if not user_obj or not mailer.verify_reset_link(user_obj, key):
        raise toolkit.ValidationError({'key': 'token provided is not valid'})

    flask.session['ckanext-passwordless-user'] = user_dict['name']

    # remove token
    mailer.create_reset_key(user_obj)

    # log the user in programmatically
    try:
        _set_repoze_user_only(user_dict['name'])
    except TypeError as e:
        log.warning("Exception at login: {0}".format(e))

    # delete attempts from Redis
    log.debug("Redis: reset attempts for {0}".format(email))
    redis_conn = connect_to_redis()
    redis_conn.delete(email)

    # make sure the master API key exists
    apikey = util.renew_master_token(user_dict['name'])

    # return message or context
    if return_context:
        return context
    else:
        user_obj = context.get('user_obj', None)
        result_json = {
            'user': {
                'email': user_obj.email,
                'id': user_obj.id,
                'name': user_obj.name,
                'apikey': apikey,
                'fullname': user_obj.fullname
            },
            'message': "login success"
        }
        return result_json
コード例 #24
0
    def fork(self, dataset_name):
        '''
        Fork this package by duplicating it.

        The new owner will be the user parameter.
        The new package is created and the original will have a new related reference to the fork.
        '''
        if not toolkit.request.method == 'POST':
            raise toolkit.abort(400, 'Expected POST method')

        user = toolkit.c.userobj
        if not user:
            raise toolkit.NotAuthorized('Membership request requires an user')

        dataset = Package.by_name(dataset_name)

        name_width = min(len(dataset.name), 88)
        forked_name = '{name}-fork-{hash}'.format(
            name=dataset.name[:name_width],
            hash=str(uuid1())[:6],
        )

        orgs = user.get_groups('organization')
        resources = [{
            'url': r.url,
            'description': r.description,
            'format': r.format,
            'name': r.name,
            'resource_type': r.resource_type,
            'mimetype': r.mimetype,
        } for r in dataset.resources]
        groups = [{'id': g.id} for g in dataset.get_groups()]
        tags = [{
            'name': t.name,
            'vocabulary_id': t.vocabulary_id
        } for t in dataset.get_tags()]
        extras = [{
            'key': key,
            'value': value
        } for key, value in dataset.extras.items() if key != 'supplier_id']

        forked = toolkit.get_action('package_create')(
            data_dict={
                'name': forked_name,
                'title': dataset.title,
                'maintainer': user.fullname,
                'maintainer_email': user.email,
                'license_id': dataset.license_id,
                'notes': dataset.notes,
                'url': dataset.url,
                'version': dataset.version,
                'type': dataset.type,
                'owner_org': orgs[0].id if len(orgs) else None,
                'resources': resources,
                'groups': groups,
                'tags': tags,
                'extras': extras,
            })

        # PackageRole.add_user_to_role(user, model.Role.ADMIN, forked)
        # Manually add the groups to bypass CKAN authorization
        # TODO: Find a better way to handle open groups
        for group in dataset.get_groups():
            group.add_package_by_name(forked_name)
        self.commit()

        # Create the fork relationship
        toolkit.get_action('package_relationship_create')(
            data_dict={
                'type': 'has_derivation',
                'subject': dataset.id,
                'object': forked['id'],
                'comment': 'Fork',
            })

        return self.json_response(Package.by_name(forked_name))
コード例 #25
0
ファイル: actions.py プロジェクト: chris48s/ckanext-unhcr
def package_publish_microdata(context, data_dict):
    default_error = 'Unknown microdata error'

    # Get data
    dataset_id = data_dict.get('id')
    nation = data_dict.get('nation')
    repoid = data_dict.get('repoid')

    # Check access
    toolkit.check_access('sysadmin', context)
    api_key = config.get('ckanext.unhcr.microdata_api_key')
    if not api_key:
        raise toolkit.NotAuthorized('Microdata API Key is not set')

    # Get dataset/survey
    headers = {'X-Api-Key': api_key}
    dataset = toolkit.get_action('package_show')(context, {'id': dataset_id})
    survey = helpers.convert_dataset_to_microdata_survey(
        dataset, nation, repoid)
    idno = survey['study_desc']['title_statement']['idno']

    try:

        # Publish dataset
        url = 'https://microdata.unhcr.org/index.php/api/datasets/create/survey/%s' % idno
        response = requests.post(url, headers=headers, json=survey).json()
        if response.get('status') != 'success':
            raise RuntimeError(str(response.get('errors', default_error)))
        template = 'https://microdata.unhcr.org/index.php/catalog/%s'
        survey['url'] = template % response['dataset']['id']
        survey['resources'] = []
        survey['files'] = []

        # Pubish resources/files
        file_name_counter = {}
        if dataset.get('resources', []):
            url = 'https://microdata.unhcr.org/index.php/api/datasets/%s/%s'
            for resource in dataset.get('resources', []):

                # resource
                resouce_url = url % (idno, 'resources')
                md_resource = helpers.convert_resource_to_microdata_resource(
                    resource)
                response = requests.post(resouce_url,
                                         headers=headers,
                                         json=md_resource).json()
                if response.get('status') != 'success':
                    raise RuntimeError(
                        str(response.get('errors', default_error)))
                survey['resources'].append(response['resource'])

                # file
                file_url = url % (idno, 'files')
                file_name = resource['url'].split('/')[-1]
                file_path = helpers.get_resource_file_path(resource)
                file_mime = resource['mimetype']
                if not file_name or not file_path:
                    continue
                file_name_counter.setdefault(file_name, 0)
                file_name_counter[file_name] += 1
                if file_name_counter[file_name] > 1:
                    file_name = helpers.add_file_name_suffix(
                        file_name, file_name_counter[file_name] - 1)
                with open(file_path, 'rb') as file_obj:
                    file = (file_name, file_obj, file_mime)
                    response = requests.post(file_url,
                                             headers=headers,
                                             files={
                                                 'file': file
                                             }).json()
                # TODO: update
                # it's a hack to overcome incorrect Microdata responses
                # usopported file types fail this way and we are skipping them
                if not isinstance(response, dict):
                    continue
                if response.get('status') != 'success':
                    raise RuntimeError(
                        str(response.get('errors', default_error)))
                survey['files'].append(response)

    except requests.exceptions.HTTPError:
        log.exception(exception)
        raise RuntimeError('Microdata connection failed')

    return survey
コード例 #26
0
def _publish(data_dict, context, type='package'):
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})

    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False):
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to publish the dataset.']})

    # get user
    ckan_user = _get_username_from_context(context)

    # check if dataset has a DOI already
    existing_doi = dataset_dict.get('doi')
    if existing_doi:
        if helpers.datacite_publication_is_admin(ckan_user):
            return _publish_custom_by_admin(dataset_dict, package_id, ckan_user, context, type)
        else:
            return {'success': False,
                    'error': 'Dataset has already a DOI. Registering of custom DOI is currently not allowed'}

    # Check state
    if dataset_dict.get('publication_state', False):
        return {'success': False, 'error': 'Dataset publication state should be empty to request a new DOI'}

        # mint doi mint_doi(self, ckan_id, ckan_user, prefix_id = None, suffix = None, entity='package')
    minter_name = config.get('datacite_publication.minter', DEAFULT_MINTER)
    package_name, class_name = minter_name.rsplit('.', 1)
    module = importlib.import_module(package_name)
    minter_class = getattr(module, class_name)
    minter = minter_class()

    prefix = config.get('datacite_publication.doi_prefix', '10.xxxxx')

    doi, error = minter.mint(prefix, pkg=dataset_dict, user=ckan_user)

    log.debug("minter got doi={0}, error={1}".format(doi, error))

    if doi:
        # update dataset
        dataset_dict['doi'] = doi
        dataset_dict['private'] = False
        # TODO: check what is the proper state once workflow is complete
        # dataset_dict['publication_state'] = 'reserved'
        dataset_dict['publication_state'] = 'pub_pending'
        context['message'] = REQUEST_MESSAGE + " for dataset {0}".format(package_id)
        toolkit.get_action('package_update')(context=context, data_dict=dataset_dict)

        # save activity
        _add_activity(dataset_dict, REQUEST_MESSAGE, context)

        # notify admin and user
        datacite_publication_requested_mail(ckan_user, dataset_dict)

        log.info("success minting DOI for package {0}, doi {1}".format(package_id, doi))

        return {'success': True, 'error': None}
    else:
        log.error("error minting DOI for package {0}, error{1}".format(package_id, error))
        return {'success': False, 'error': error}

    return {'success': False, 'error': 'Internal error'}
コード例 #27
0
def config(context, data_dict):
    if toolkit.check_access(constants.CONFIG_PRIVILEGE, context, data_dict):
        return {'success': True}
    else:
        raise toolkit.NotAuthorized()
コード例 #28
0
def _publish_to_datacite(data_dict, context, type='package'):
    log.debug(
        '_publish_to_datacite: Publishing to datacite "{0}" ({1})'.format(data_dict['id'], data_dict.get('name', '')))
    # a dataset id i s necessary
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})
    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # state has to be approved
    state = dataset_dict.get('publication_state', '')
    if state != 'approved':
        raise toolkit.ValidationError({'publication_state': 'dataset needs to be in state "approved" (by the admin)'})

    # DOI has to be already reserved (minted)
    doi = dataset_dict.get('doi', '')
    default_prefix = config.get('datacite_publication.doi_prefix', '10.xxxxx')
    allowed_prefixes = config.get('datacite_publication.custom_prefix', '').split(' ') + [default_prefix]
    doi_prefix = doi.split('/')[0].strip()

    if (not doi) or (len(doi) <= 0) or (doi_prefix not in allowed_prefixes):
        raise toolkit.ValidationError(
            {'doi': 'dataset has no valid minted DOI [' + ', '.join(allowed_prefixes) + ']/*'})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False) or not helpers.datacite_publication_is_admin():
        log.error(
            'ERROR finishing publication dataset in datacite, current user is not authorized: isAdmin = {0}'.format(
                helpers.datacite_publication_is_admin()))
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to perform the dataset publication to datacite (admin only).']})

    datacite_publisher = DatacitePublisher()

    try:
        doi, error = datacite_publisher.publish(doi, pkg=dataset_dict, context=context)
    except Exception as e:
        log.error("exception publishing package {0} to Datacite, error {1}".format(package_id, traceback.format_exc()))
        return {'success': False, 'error': 'Exception when publishing to DataCite: {0}'.format(e)}
    except:
        log.error("error publishing package {0} to Datacite, error {1}".format(package_id, sys.exc_info()[0]))
        return {'success': False, 'error': 'Unknown error when publishing to DataCite: {0}'.format(sys.exc_info()[0])}

    if error:
        log.error("error publishing package {0} to Datacite, error {1}".format(package_id, error))
        return {'success': False, 'error': error}

    # change publication state
    dataset_dict['publication_state'] = 'published'
    dataset_dict['private'] = False
    context['message'] = FINISH_MESSAGE + " for dataset {0}".format(package_id)
    toolkit.get_action('package_update')(context=context, data_dict=dataset_dict)

    # save activity
    _add_activity(dataset_dict, FINISH_MESSAGE, context)

    # notify owner and involved users
    dataset_owner = dataset_dict.get('creator_user_id', '')
    datacite_finished_mail(dataset_owner, dataset_dict, context)

    return {'success': True, 'error': None}
コード例 #29
0
def _update_in_datacite(data_dict, context, type='package'):
    log.debug(
        '_update_in_datacite: Updating in datacite "{0}" ({1})'.format(data_dict['id'], data_dict.get('name', '')))
    # a dataset id i s necessary
    try:
        id_or_name = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})
    dataset_dict = toolkit.get_action('package_show')(context, {'id': id_or_name})

    # state has to be approved
    state = dataset_dict.get('publication_state', '')
    if state != 'published':
        raise toolkit.ValidationError({'publication_state': 'dataset needs to be in state "published" (in datacite)'})

    # DOI has to be already present
    doi = dataset_dict.get('doi', '')
    default_prefix = config.get('datacite_publication.doi_prefix', '10.xxxxx')
    allowed_prefixes = config.get('datacite_publication.custom_prefix', '').split(' ') + [default_prefix]
    doi_prefix = doi.split('/')[0].strip()

    if (not doi) or (len(doi) <= 0) or (doi_prefix not in allowed_prefixes):
        raise toolkit.ValidationError(
            {'doi': 'dataset has no valid minted DOI [' + ', '.join(allowed_prefixes) + ']/*'})

    # Check authorization
    package_id = dataset_dict.get('package_id', dataset_dict.get('id', id_or_name))
    if not authz.is_authorized(
            'package_update', context,
            {'id': package_id}).get('success', False) or not helpers.datacite_publication_is_admin():
        log.error(
            'ERROR updating publication dataset in datacite, current user is not authorized: isAdmin = {0}'.format(
                helpers.datacite_publication_is_admin()))
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to perform the dataset update to datacite (admin only).']})

    # Get the DOI minter
    minter_name = config.get('datacite_publication.minter', DEAFULT_MINTER)
    package_name, class_name = minter_name.rsplit('.', 1)
    module = importlib.import_module(package_name)
    minter_class = getattr(module, class_name)
    minter = minter_class()

    # check when possible if the association doi-ckan id is valid:
    log.debug("CHECK DOI in minter")
    is_doi_valid_op = getattr(minter, "is_doi_valid", None)
    if callable(is_doi_valid_op):
        valid_doi = minter.is_doi_valid(doi, package_id)
        if not valid_doi:
            return {'success': False, 'error': 'DOI and id do not match to the DOI realisation table in the DB'}

    datacite_publisher = DatacitePublisher()

    try:
        doi, error = datacite_publisher.publish(doi, pkg=dataset_dict, context=context, update=True)
    except Exception as e:
        log.error("exception updating package {0} in Datacite, error {1}".format(package_id, traceback.format_exc()))
        return {'success': False, 'error': 'Exception when updating in DataCite: {0}'.format(e)}
    except:
        log.error("error updating package {0} in Datacite, error {1}".format(package_id, sys.exc_info()[0]))
        return {'success': False, 'error': 'Unknown error when updating in DataCite: {0}'.format(sys.exc_info()[0])}

    if error:
        log.error("error updating package {0} to Datacite, error {1}".format(package_id, error))
        return {'success': False, 'error': error}

    # save activity
    _add_activity(dataset_dict, UPDATE_MESSAGE, context)

    return {'success': True, 'error': None}
コード例 #30
0
def _publish_resource(data_dict, context):

    # validate the id and get the resource data
    try:
        id = data_dict['id']
    except KeyError:
        raise toolkit.ValidationError({'id': 'missing id'})
    resource_dict = toolkit.get_action('resource_show')(context, {'id': id})

    package_id = data_dict.get('package_id')
    package_dict = toolkit.get_action('package_show')(context, {'id': package_id})

    # Check authorization
    ckan_user = _get_username_from_context(context)
    if not helpers.datacite_publication_is_admin(ckan_user):
        raise toolkit.NotAuthorized({
            'permissions': ['Not authorized to publish the resource (admins only).']})

    # check if it is an update
    update = data_dict.get('update', False)
    log.debug('_publish_resource: *UPDATE* = {0}'.format(update))

    # state to publish has to be not yet published
    state = resource_dict.get('publication_state', '')
    if not update and state == 'published':
        return {'success': False, 'error': 'Resource is already in state "published"'}

    # state to update has to be not published
    if update and state != 'published':
        return {'success': False, 'error': 'Resource is should be in state "published" for updating'}

    # DOI has to be already present and valid
    custom_doi = resource_dict.get('doi', '')
    if (not custom_doi) or (len(custom_doi) <= 0):
        log.error('_publish_resource: resource has no minted DOI')
        return {'success': False, 'error': 'Custom DOI not valid: empty'}

    default_prefix = config.get('datacite_publication.doi_prefix', '')
    allowed_prefixes = config.get('datacite_publication.custom_prefix', '').split(' ') + [default_prefix]
    custom_prefix = custom_doi.split('/')[0].strip()
    if custom_prefix not in allowed_prefixes:
        log.error('_publish_resource: resource has no valid minted DOI [' + ', '.join(allowed_prefixes) + ']/*')
        return {'success': False, 'error': 'Custom DOI not valid: prefix not allowed'}

    custom_suffix = custom_doi.split('/')[1].strip()
    if (not custom_suffix) or (len(custom_suffix) <= 0):
        log.error('_publish_resource: resource has an empty DOI suffix')
        return {'success': False, 'error': 'Custom DOI not valid: suffix empty'}

    if update:
        log.info("updating CUSTOM resource DOI by an Admin {0}/{1}, allowed: {2}".format(custom_prefix, custom_suffix,
                                                                                          allowed_prefixes))
    else:
        log.info("publishing CUSTOM resource DOI by an Admin {0}/{1}, allowed: {2}".format(custom_prefix, custom_suffix,
                                                                                           allowed_prefixes))

    # Get the DOI minter
    minter_name = config.get('datacite_publication.minter', DEAFULT_MINTER)
    package_name, class_name = minter_name.rsplit('.', 1)
    module = importlib.import_module(package_name)
    minter_class = getattr(module, class_name)
    minter = minter_class()

    if update:
        doi = custom_doi
        # check when possible if the association doi-ckan id is valid:
        log.debug("CHECK DOI in minter")
        is_doi_valid_op = getattr(minter, "is_doi_valid", None)
        if callable(is_doi_valid_op):
            valid_doi = minter.is_doi_valid(doi, id, entity_type='resource')
            if not valid_doi:
                return {'success': False, 'error': 'DOI and id do not match to the DOI realisation table in the DB'}
    else:
        # Mint custom DOI
        doi, error = minter.mint(custom_prefix, pkg=resource_dict, user=ckan_user,
                                 suffix=custom_suffix, entity='resource')
        log.debug("minter got doi={0}, error={1}".format(doi, error))

        if error:
            log.error("error minting DOI for resource {0}, error{1}".format(id, error))
            return {'success': False, 'error': error.split('DETAIL')[0]}
        log.info("success saving custom minted DOI for resource {0}, doi {1}".format(id, doi))

    # Publish to DataCite
    datacite_publisher = DatacitePublisher()

    try:
        doi, error = datacite_publisher.publish_resource(doi, resource=resource_dict, package=package_dict,
                                                         context=context, update=update)
    except Exception as e:
        log.error("exception publishing resource {0} to Datacite, error {1}".format(id, traceback.format_exc()))
        return {'success': False, 'error': 'Exception when publishing to DataCite: {0}'.format(e)}
    except:
        log.error("error publishing resource {0} to Datacite, error {1}".format(id, sys.exc_info()[0]))
        return {'success': False, 'error': 'Unknown error when publishing to DataCite: {0}'.format(sys.exc_info()[0])}

    if error:
        log.error("error publishing resource {0} to Datacite, error {1}".format(id, error))
        return {'success': False, 'error': error}

    if not update:
        # update dataset publication state
        resource_patch = {'id': id, 'publication_state': 'published'}
        resource_patched = toolkit.get_action('resource_patch')(context, resource_patch)

    return {'success': True, 'error': None}