2013-03-27 2 views
1

У меня возникли проблемы с внедрением авторизации на уровне записи с tastypie 0.9.12+.Django Tastypie Рекордный уровень авторизации

Моя установка выглядит примерно так:

Модель

class UserProfile(models.Model): 
    def __unicode__(self): 
     return self.user.get_full_name() 

    user = models.OneToOneField(User) 

class Sample(models.Model): 
    def __unicode__(self): 
     return '%s' % self.id 

    OPEN = 0 
    CLAIMED = 1 
    CLOSED = 2 
    MANUAL = 3 
    MODIFIED = 4 
    DELETED = 5 
    ERROR = 6 
    RESERVED = 7 
    STATUS_CHOICES = (
     (OPEN, 'Open'), 
     (CLAIMED, 'Claimed'), 
     (CLOSED, 'Closed'), 
     (MANUAL, 'Manual'), 
     (MODIFIED, 'Modified'), 
     (DELETED, 'Deleted'), 
     (ERROR, 'Error'), 
     (RESERVED, 'Reserved'), 
    ) 

    status = models.SmallIntegerField(max_length = 1, default = OPEN, choices = STATUS_CHOICES) 
    user_profile = models.ForeignKey(UserProfile, blank = True, null = True) 

Ресурс

class BaseResource(ModelResource): 
    # Base class with rather strict default settings 
    # All other Resources extend this and override any defaults to higher permissions 
    class Meta: 
     authentication = DjangoAuthentication() 
     authorization = ReadOnlyAuthorization() 
     allowed_methods = [] 

class SampleResource(BaseResource): # BaseResource defines a default Meta, setting allowed_methods and authentication for all other resources in the API 
    UserProfile = fields.ForeignKey(UserProfileResource, 'user_profile', null = True, full = True) 
    class Meta(BaseResource.Meta): 
     queryset = Sample.objects.all() 
     resource_name = 'sample' 
     allowed_methods = ['get', 'post', 'put', 'patch'] 
     authorization = SampleAuthorization() 
     always_return_data = True 

    def dehydrate_status(self, bundle): 
     return Sample.STATUS_CHOICES[bundle.data['status']][1] 

    def hydrate_status(self, bundle): 
     bundle.data['status'] = Sample.__dict__[bundle.data['status'].upper()] 
     return bundle 

Авторизация

class SampleAuthorization(Authorization): 
    # Checks that the records' owner is either None or the logged in user 
    def authorize_user(self, bundle): 
     return bundle.obj.user_profile in (None, self.user_profile(bundle)) 

    def user_profile(self, bundle): 
     return user_profile.objects.get(user = bundle.request.user) 



    def read_list(self, object_list, bundle): 
     print 'Read List' 
     return object_list.filter(Q(user_profile = self.user_profile(bundle)) | Q(user_profile = None)) 

    def read_detail(self, object_list, bundle): 
     print 'Read Detail' 
     return self.authorize_user(bundle) 

    def create_list(self, object_list, bundle): 
     return object_list 

    def create_detail(self, object_list, bundle): 
     return self.authorize_user(bundle) 

    def update_list(self, object_list, bundle): 
     print 'Update List' 
     allowed = [] 
     for obj in object_list: 
      if obj.user_profile in (None, self.user_profile(bundle)): 
       allowed.append(obj) 

     return allowed 

    def update_detail(self, object_list, bundle): 
     print 'Update Detail' 
     print bundle.obj.status, bundle.data['status'] 
     # Compare status stored on the server against the user-set status 
     # If server status is >= user status 
     # Raise Unauthorized 
     if bundle.obj.status >= bundle.data['status']: 
       raise Unauthorized('New status must be higher than current status') 
     return self.authorize_user(bundle) 

    def delete_list(self, object_list, bundle): 
     raise Unauthorized('Deletion not allowed through API') 

    def delete_detail(self, object_list, bundle): 
     raise Unauthorized('Deletion not allowed through API') 

Моя проблема заключается в том, что, как представляется, update_detail вызывается дважды, с разными входами. Запрошенное обновление пытается изменить статус записи, хранящейся на сервере. Новый статус должен быть выше, чем сохраненный статус, или изменение является Несанкционированным.

При выполнении приведенного выше кода, мой результат выглядит следующим образом:

Read Detail 
Update Detail 
0 Claimed 
Update Detail 
1 1 
[27/Mar/2013 09:35:23] "PATCH /api/1.0/sample/1/ HTTP/1.1" 401 0 

На первом проходе, то bundle.obj.status имеет правильное значение, но bundle.data [ «статус»] HAS Не было гидратировано. На втором проходе, bundle.obj.status был изменен на новый статус, а новый статус был гидратирован.

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

Как я могу реализовать авторизацию на уровне записи, если метод дважды вызывается Tastypie с разными входами как для сохраненных, так и для новых значений статуса?

ответ

1

Оказывается, несколько звонков в update_detail была ошибкой в ​​рамках tastypie.

Вопрос был отправлен на github и разрешен в bug fix.

0

Посмотрите на использование django-guardian вместо hardcoding отношения на вашей модели. Что-то вроде следующего класса авторизации будет хорошее начало:

https://gist.github.com/airtonix/5476453

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