Проблемы с ForeignKey при использовании POST в Django-Tastypie - PullRequest
9 голосов
/ 27 декабря 2011

Я создаю простой API, используя django-tastypie.Идея в том, что у меня есть два ресурса:

  • A Примечание ресурс, представляющий заметку, оставленную пользователем.Только пользователь, создавший заметку, может редактировать ее.
  • 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

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

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

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 исвяжите его с соответствующей авторизацией.

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

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

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

  1. Запретить использование вкусного пирога POST для создания обратной связи с ресурсом заметки или
  2. Измените схему авторизации, чтобы заметки могли редактировать только их создатели, но комментарии могли создаватьсявообще?

Заранее спасибо за любые идеи.

Редактировать: У меня есть большой жирный хак, который может это сделать.Я уверен, что это безопасно, но я не уверен;Я постараюсь составить несколько запросов, чтобы убедиться.Вместо использования 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 (поскольку мы ожидаем, что он будет соответствовать запросу SELECT, а не UPDATE, который соответствует PUT или POST).Это действительно некрасиво и потенциально небезопасно, если используется неправильно, и я надеюсь на лучшее решение.

Редактировать 2: Насколько я могу судить, прочитав источник вкусного пирога,нет способа отфильтровать авторизацию по запросу, который действительно будет отправлен.

Ответы [ 2 ]

4 голосов
/ 08 мая 2012

Согласно обсуждению 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 голос
/ 16 января 2012

простое решение должно заключаться в проверке внутри apply_limits, является ли запрос ресурсом Note или ресурсом Comment . например что-то вроде

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 только тому же пользователю, когда пользователь обращается к ресурсу Note напрямую, а не через другие связанные ресурсы, такие как Комментарии .

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

...