Exemplo n.º 1
0
class RHSignURL(RH):
    """Create a persistent signed URL for a user.

    This build a url and adds a signature that authenticates the user without
    requiring them to have session cookies. It is meant for cases where the
    user actively requests a persistent link to use outside their browser,
    e.g. for a calendar feed.

    When called without authentication, no token is added, so it just behaves
    as the normal ``url_for`` in order to allow client-side code to be more
    straightforward and always call this API regardless of whether the user is
    authenticated or not.
    """

    @use_kwargs({
        'endpoint': fields.String(required=True,
                                  validate=validate_with_message(lambda ep: ep in current_app.view_functions,
                                                                 'Invalid endpoint')),
        'params': fields.Dict(keys=fields.String(), load_default={},
                              validate=validate_with_message(lambda params: not any(x.startswith('_') for x in params),
                                                             'Params starting with an underscore are not allowed'))
    })
    def _process(self, endpoint, params):
        try:
            if session.user:
                url = signed_url_for_user(session.user, endpoint, _external=True, **params)
                Logger.get('url_signing').info("%s signed URL for endpoint '%s' with params %r", session.user, endpoint,
                                               params)
            else:
                url = url_for(endpoint, _external=True, **params)
        except BuildError as exc:
            # if building fails for a valid endpoint we can be pretty sure that it's due to
            # some required params missing
            abort(422, messages={'params': [str(exc)]})
        return jsonify(url=url)
Exemplo n.º 2
0
class RHAPISearch(RH):
    """API for searching across all records with the current search provider.

    Besides pagination, filters or placeholders may be passed as query parameters.
    Since `type` may be a list, the results from the search provider are not mixed with
    the InternalSearch.
    """
    @use_kwargs(
        {
            'page':
            fields.Int(missing=None),
            'q':
            fields.String(required=True),
            'type':
            fields.List(EnumField(SearchTarget), missing=None),
            'admin_override_enabled':
            fields.Bool(
                missing=False,
                validate=validate_with_message(
                    lambda value: session.user and session.user.is_admin,
                    'Restricted to admins')),
        },
        location='query',
        unknown=INCLUDE)
    def _process(self, page, q, type, **params):
        search_provider = get_search_provider()
        if type == [SearchTarget.category]:
            search_provider = InternalSearch
        result = search_provider().search(q, session.user, page, type,
                                          **params)
        return ResultSchema().dump(result)
Exemplo n.º 3
0
class RHUserSearch(RHProtected):
    """Search for users based on given criteria."""
    def _serialize_pending_user(self, entry):
        first_name = entry.data.get('first_name') or ''
        last_name = entry.data.get('last_name') or ''
        full_name = f'{first_name} {last_name}'.strip() or 'Unknown'
        affiliation = entry.data.get('affiliation') or ''
        email = entry.data['email'].lower()
        ext_id = f'{entry.provider.name}:{entry.identifier}'
        # detailed data to put in redis to create a pending user if needed
        self.externals[ext_id] = {
            'first_name': first_name,
            'last_name': last_name,
            'email': email,
            'affiliation': affiliation,
            'phone': entry.data.get('phone') or '',
            'address': entry.data.get('address') or '',
        }
        # simple data for the search results
        return {
            '_ext_id': ext_id,
            'id': None,
            'identifier': f'ExternalUser:{ext_id}',
            'email': email,
            'affiliation': affiliation,
            'full_name': full_name,
            'first_name': first_name,
            'last_name': last_name,
        }

    def _serialize_entry(self, entry):
        if isinstance(entry, User):
            return search_result_schema.dump(entry)
        else:
            return self._serialize_pending_user(entry)

    def _process_pending_users(self, results):
        cache = make_scoped_cache('external-user')
        for entry in results:
            ext_id = entry.pop('_ext_id', None)
            if ext_id is not None:
                cache.set(ext_id, self.externals[ext_id], timeout=86400)

    @use_kwargs(
        {
            'first_name': fields.Str(validate=validate.Length(min=1)),
            'last_name': fields.Str(validate=validate.Length(min=1)),
            'email': fields.Str(validate=lambda s: len(s) > 3),
            'affiliation': fields.Str(validate=validate.Length(min=1)),
            'exact': fields.Bool(missing=False),
            'external': fields.Bool(missing=False),
            'favorites_first': fields.Bool(missing=False)
        },
        validate=validate_with_message(
            lambda args: args.keys(
            ) & {'first_name', 'last_name', 'email', 'affiliation'},
            'No criteria provided'),
        location='query')
    def _process(self, exact, external, favorites_first, **criteria):
        matches = search_users(exact=exact,
                               include_pending=True,
                               external=external,
                               **criteria)
        self.externals = {}
        results = sorted((self._serialize_entry(entry) for entry in matches),
                         key=itemgetter('full_name'))
        if favorites_first:
            favorites = {u.id for u in session.user.favorite_users}
            results.sort(key=lambda x: x['id'] not in favorites)
        total = len(results)
        results = results[:10]
        self._process_pending_users(results)
        return jsonify(users=results, total=total)
Exemplo n.º 4
0
                unaccent_match(getattr(EventPerson, k), v, exact))

        # wrap as subquery so we can apply order regardless of distinct-by-id
        query = query.from_self()
        query = query.order_by(
            db.func.lower(
                db.func.indico.indico_unaccent(EventPerson.first_name)),
            db.func.lower(db.func.indico.indico_unaccent(
                EventPerson.last_name)),
            EventPerson.id,
        )
        return query.limit(10).all(), query.count()

    @use_kwargs(
        {
            'first_name': fields.Str(validate=validate.Length(min=1)),
            'last_name': fields.Str(validate=validate.Length(min=1)),
            'email': fields.Str(validate=lambda s: len(s) > 3),
            'affiliation': fields.Str(validate=validate.Length(min=1)),
            'exact': fields.Bool(missing=False),
        },
        validate=validate_with_message(
            lambda args: args.keys(
            ) & {'first_name', 'last_name', 'email', 'affiliation'},
            'No criteria provided'),
        location='query')
    def _process(self, exact, **criteria):
        matches, total = self._search_event_persons(exact=exact, **criteria)
        return jsonify(users=EventPersonSchema().dump(matches, many=True),
                       total=total)