2011-12-26 3 views
9

Я создаю простой API, используя django-tastypie. Идея заключается в том у меня есть два ресурса:Проблемы с ForeignKey с использованием POST в Django-Tastypie

  • Примечание ресурс, представляющий записку, оставленную пользователем. Только пользователь, создавший заметку, может ее редактировать.
  • A Комментарий Ресурс. Комментарии могут быть оставлены на любой ноте любым пользователем.

TL; DR: Я не может ограничить Примечание редактирования создателя записки в то же время позволяя любому пользователю комментировать Примечание.

Я использую следующие настройки для проверки подлинности:

class CreatedByEditAuthorization(Authorization): 
    def is_authorized(self, request, object=None, **kwargs): 
     return True 

    def apply_limits(self, request, object_list): 
     if request and request.method != 'GET' and hasattr(request, 'user'): 
      return object_list.filter(created_by=request.user) 
     return object_list 

Короче говоря, пользователь разрешен только для редактирования объектов, для которых они равны свойству CREATED_BY (они могут редактировать только объекты, созданные ими) ,

Это связано следующим образом:

class NoteResource(ModelResource): 
    comments = fields.ToManyField('myapp.api.resources.CommentResource', 'comments', null=True, blank=True) 
    created_by = fields.ToOneField('account.api.resources.UserResource', 'created_by') 

    def obj_create(self, bundle, request, **kwargs): 
     return super(HapResource, self).obj_create(bundle, request, created_by=request.user) 

    class Meta: 
     queryset = Note.objects.all() 
     allowed_methods = ['get', 'put', 'post'] 
     authorization = CreatedByEditAuthorization() 

так вот, когда создается объект, я автоматически присоединять текущего пользователя к атрибуту created_by и связать его с надлежащей авторизации.

A Comment Ресурс прост и имеет только ForeignKey до Note ресурса.

Проблема заключается в следующем: Если пользователь A создает примечание, и пользователь B пытается прокомментировать эту ноту, tastypie отправляет (или имитирует) запрос POST для редактирования этой заметки. Эта попытка отвергается, так как пользователь B не создает заметку, поэтому создание комментария не выполняется.

Вопрос заключается в следующем: Есть ли способ либо:

  1. Предотвращение tastypie с помощью POST, чтобы создать реверс-отношение к Note ресурса или
  2. изменения схемы авторизации поэтому Notes можно редактировать только их создателем, но комментарии могут быть созданы вообще?

Заранее благодарим за любые идеи.

Редактировать: У меня есть большой толстый взлом, который может это сделать. Я уверен, что это безопасно, но я не уверен. Я попытаюсь построить некоторые запросы, чтобы убедиться. Вместо того, чтобы использовать fields.ForeignKey в Comment относиться к Note, создать настраиваемое поле:

class SafeForeignKey(fields.ForeignKey): 
    def build_related_resource(self, value, request=None, related_obj=None, related_name=None): 
     temp = request.method 
     if isinstance(value, basestring): 
      request.method = 'GET' 
     ret = super(SafeForeignKey, self).build_related_resource(value, request, related_obj, related_name) 
     request.method = temp 
     return ret 

Каждый раз, когда мы пытаемся построить этот связанный ресурс, мы отмечаем запрос как GET (так как мы ожидаем, что он должен быть согласован с a SELECT, а не UPDATE, который соответствует PUT или POST). Это очень уродливое и потенциально опасное, если оно используется неправильно, и я надеюсь на лучшее решение.

Редактировать 2: От чтения источника тастипирования, насколько я могу судить, нет способа фильтровать разрешение по запросу, которое будет фактически отправлено.

+0

Пара вопросов - Вы используете contrib.comments? Вы используете аутентификацию, а также авторизацию? У меня есть то, что похоже на очень похожую настройку (без комментария CommentResource), которая отлично работает при публикации нового комментария к другому объекту пользователя. – JamesO

+0

@JamesO Нет, наши комментарии несколько богаче, чем предоставляет contrib.comments (и есть другие данные, связанные с сообщением, имеющим ту же проблему). В настоящее время мы просто используем встроенную аутентификацию() (т. Е. Все аутентифицируются). –

+0

Вы разместили это как проблему на django-tastypie: https://github.com/toastdriven/django-tastypie/issues? Если он действительно пытается обновить родительскую запись каждый раз, когда вы создаете что-то, связанное с ней, это ближе к ошибке, чем к какой-либо функции. –

ответ

4

В соответствии с обсуждением на https://github.com/toastdriven/django-tastypie/issues/480#issuecomment-5561036:

Метод, который определяет, является ли Resource может быть обновлена ​​can_update. Поэтому, чтобы сделать эту работу в «правильном» пути, вам нужно создать подкласс NoteResource:

class SafeNoteResource(NoteResource): 
    def can_update(self): 
     return False 
    class Meta: 
     queryset = Note.objects.all() 
     allowed_methods = ['get'] 
     authorization = Authorization() 
     # You MUST set this to the same resource_name as NoteResource 
     resource_name = 'note' 

затем дайте CommentResource ссылку на ноты стандартным способом: note = fields.ForeignKey(SafeNoteResource, 'note').

1

простое решение должно быть, чтобы проверить внутри apply_limits ли или нет запрос для Примечание ресурса или Комментарий ресурса. например что-то вроде

def apply_limits(self, request, object_list): 
    if request and request.method != 'GET' and hasattr(request, 'user') and getattr(request, 'path','').startswith('/api/v1/note'): 
     return object_list.filter(created_by=request.user) 
    return object_list 

Тогда вы только ограничение доступа к Notes к тому же самому пользователю, когда пользователь получает доступ к Примечания ресурса напрямую, а не через другие соответствующие ресурсы, такие как Комментариями.

обновление: или чуть более безопасным вариантом было бы проверить, что запрос делает не начало с «API/v1/комментарий» - так что вы просто белый список комментариев доступа, а не ничего кроме записки , Однако тот же принцип применяется. Будьте осторожны с этим текстовым сравнением пути запроса, чтобы избежать случаев, когда кто-то просто добавляет/добавляет строку в ваш URL-адрес, чтобы обойти вашу авторизацию. Надеемся, что добавление будет более ограниченным, так как ему нужно нажать правильный URL-адрес в urls.py, поэтому я использовал здесь startswith. Конечно, вам нужно будет отрегулировать строку пути в соответствии с вашими URL-адресами.

+0

кажется, больше, чем то, что я предложил; разрешения на уровне таблиц должны обрабатываться в 'Meta' для корреспондента' Resource'. Кроме того, как вы говорите, сравнение на основе текста может быть опасным (и на самом деле также может вызывать ошибки, если, скажем, вы должны иметь отношение к комментарию с помощью «full = True» - в этом случае URL-адрес может не включать/комментировать /).В смысле того, что возвращается из пакета, вы правы в том, что путь - это действительно единственные релевантные данные, которые у вас есть. –

+0

не уверен, как мое предложение является более сложным, чем изменение метода запроса от POST до GET (не говоря уже о строках добавленного кода). В предлагаемом решении разрешение обрабатывается в правильном месте (которое применяется apply_limits), и проверка URL-адреса может выполняться независимо от того, полнота или нет. Мы изучаем URL-адрес запроса, а не данные пакета. Мое решение можно немного улучшить, добавив какой-то флаг в объект запроса внутри ресурса комментариев obj_create, а затем проверив его в методе Notes apply_limits. – gingerlime

+0

Это большой взлом, потому что он нарушает модульность. Разрешение на уровне таблиц должно быть реализовано на уровне Ресурса, а не запечено в схеме аутентификации. В вышеуказанном хаке вы меняете метод запроса, но только когда следуете за отношением к «Заметке» из «комментария», поэтому вы сохраняете локализованную схему с заданным ресурсом. Перед публикацией взлома POST-GET я рассмотрел возможность использования пути (на самом деле более надежным/последовательным путем путем изменения пути создания путей), но решил против него, поскольку он нарушил модульность. –

Смежные вопросы