Зависимый от пользователя фильтр ресурсов в Python-eve RestApi Framework

Я хочу реализовать «более сложный» фильтр на основе прав доступа пользователя в Python eve restframework.

Проблема

У нас есть аутентификация по токену, учетная запись пользователя извлекается в классе TokenAuth. У пользователя есть несколько контрактов, в каждом контракте есть счета. Я хочу реализовать конечную точку / счета, которая показывает счета его контракта. Мы используем mongodb.

Для лучшего понимания, что-то вроде инструкции SQL "SELECT * FROM bills WHERE bills.contract IN user.contracts"

user { contracts : ["a","b","c"] }
bills { contract: "a" }

Исходная информация

class TokenAuth(TokenAuthBase):
    def check_auth(self, token, allowed_roles, resource, method):
       users = app.data.driver.db['users']
       TokenAuth.account = users.find_one(lookup)
       ...

(Обновить)

Доступ к ресурсам с ограничениями для пользователей¶ (URRA)

В случае отношения пользователь 1: n счет, URRA выполнит эту работу. См. URRA в документации по python eve.

В более сложном случае необходимо написать собственный фильтр-запрос. Мне нужен такой вариант :).

Обновлять

Я нашел решение, см. Обновленный ответ.


person Simon Fakir    schedule 08.03.2014    source источник


Ответы (5)


А как насчет предопределенных фильтров базы данных?

bills = {
    'datasource': {
        'source': 'bills',
        'filter': {'field1': 'value1', 'field2': 'value2'}
    }
}

source - это коллекция mongodb; filter - это запрос mongodb. Он будет прозрачно применяться к любому запросу, достигающему конечной точки (до клиентских запросов).

person Nicola Iarocci    schedule 10.03.2014

Благодаря Никола Яорчи я нашел решение.

Во-первых, я использую собственный класс аутентификации для каждой конечной точки.

class MyCreditNoteAuth(TokenAuthBase):
   def check_auth(self, token, allowed_roles, resource, method):
        account = app.data.driver.db['users'].find_one({'api_access_token': token})

Этот метод извлекает учетную запись пользователя из mongodb, и теперь у меня есть доступ к его идентификаторам контрактов.

Во-вторых, все еще в верхнем методе я обновляю фильтр источника данных при каждом запросе:

mynotes['datasource']['filter']['contract'] = { '$in': account['contracts'] }

Теперь клиент видит только свои «заметки» на данной конечной точке.

person Simon Fakir    schedule 18.03.2014
comment
Вы можете привести более полный пример? Мне сложно это воспроизвести. Благодарность! - person upbeat.linux; 10.12.2014
comment
И это тоже ищу - person Metalstorm; 26.12.2015

Я думаю, что было бы лучше делать это по запросу. Изменение 'DOMAIN' может привести к случаям, когда несколько человек входят в систему и отправляют запросы одновременно, а домен меняется между запросами в зависимости от того, как вы реализуете такие вещи. Может сработать что-то вроде этого:

from eve import Eve
from eve.auth import BasicAuth

def pre_get_api_stuff(resource, request, lookup):
    username = request.authorization['username']
    accounts = app.data.driver.db['accounts']
    account = accounts.find_one({'username': username})
    if resource == 'notes':
        lookup.update({'username': username})

app = Eve(auth=BasicAuth)
app.on_pre_GET += pre_get_api_stuff
person Davis Kirkendall    schedule 15.01.2016

Просматривали ли вы доступ к ресурсам с ограничением для пользователей?

С первого взгляда я бы сказал, что если сохранить токен пользователя (или идентификатор, или что-то еще, что идентифицирует пользователя в вашей настройке) с вашими документами счета, а затем включить URRA в billsendpoint, это поможет.

person Nicola Iarocci    schedule 09.03.2014
comment
Спасибо за ваш ответ. Но дело в том, что URRA требует деблокирования счета пользователем 1: n, в данном случае это пользователь n: m заключает 1: n счетов. Значит, мне нужно заменить фильтр URRA на более сложный? Это возможно? - person Simon Fakir; 10.03.2014

Более полный пример выглядит следующим образом, предположим, что у нас есть следующее в settings.py

note_schema = {
    'user_id': {
        'type': 'objectid',
        'required': True,
        'readonly': True
    }
}
user_schema = {
    'username': {
        'type': 'string',
        'required': True,
        'unique': True,
    },
    'password': {
        'type': 'string',
        'required': True,
    },
    'token': {
        'type': 'string',
        'required': True,
    }
}

DOMAIN = {
    'user': {
        'schema': user_schema,
    },
    'note': {
        'schema': note_schema,
    },
}

И auth поставляется с добавлением фильтра на основе имени пользователя, логика фильтрации здесь проста, но вы можете добавить к фильтру все, что хотите.

class MyAuth(BasicAuth):
    def check_auth(self, token, allowed_roles, resource, method):
        username = get_username_from_token(token)
        if resource == 'note':
            note = app.config['DOMAIN']['note']
            user = app.data.driver.db['user'].find_one({'username': username})
            note['datasource']['filter'] = {'user_id': user['_id']}

        return True
person bbsang    schedule 09.01.2016