Django Tastypie记录级授权 [英] Django Tastypie Record-Level Authorization

查看:132
本文介绍了Django Tastypie记录级授权的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法使用tastypie 0.9.12 +实现记录级授权。



我的设置看起来像这样:



模型

  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 '),
(声明,声明),
(CLOSED,已关闭),
(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)
/ pre>

资源

  BaseResource(ModelResource):
#具有相当严格默认设置的基类
#所有其他资源扩展它并覆盖任何默认值到更高的权限
class Meta:
authentication = DjangoAuthentication()
authorization = ReadOnlyAuthorization()
allowed_methods = []

class SampleResource(BaseResource):#BaseResource定义默认的Meta,为API $中的所有其他资源设置allowed_methods和身份验证b $ b UserProfile = fields.ForeignKey(UserProfileResource,'user_profile',null = True,full = True)
class Meta(BaseResource.Meta):
queryset = Sample.objects.all()
resource_name ='s充足的'
allowed_methods = ['get','post','put','patch']
authorization = SampleAuthorization()
always_return_data = True

def ($)
返回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):
#检查记录的所有者是无或登录用户
def authorize_user(self,bundle):
返回bundle中的bundle.obj.user_profile(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):
打印'读取列表'
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'更新列表'
allowed = []
在object_list中的obj:
如果obj.user_profile in(None,self.user_profile(bundle)):
allowed.append(obj)

return allowed

def update_detail(self,object_list,bundle)
打印'更新细节'
打印bundle.obj.status,bundle.data ['status']
#将服务器上存储的状态与用户设置状态进行比较
#如果服务器状态为> =用户状态
#提高Unauth
如果bundle.obj.status> = bundle.data ['status']:
raise未授权('新状态必须高于当前状态')
return self.authorize_user(

def delete_list(self,object_list,bundle):
raise未授权('通过API删除不允许)

def delete_detail(self,object_list,捆绑):
raise未授权('通过API删除'

我的问题是看来update_detail被调用两次,具有不同的输入。所请求的更新正在尝试更改存储在服务器上的记录的状态。新的状态必须高于存储的状态,或更改是未经授权的。



运行上述代码时,我的输出如下所示:

 详细信息
更新细节
0声明
更新细节
1 1
[27 / Mar / 2013 09:35:23]PATCH /api/1.0/sample/1/ HTTP / 1.1401 0

在第一遍,bundle.obj.status具有正确的值,但是bundle.data ['status']尚未被水合。在第二遍,bundle.obj.status已更改为新状态,并且新状态已被水合。



由于第一次通过状态尚未完成,我无法可靠地比较它们,并且不想手动调用hydrate_status,因为它会混淆整个水合物工艺在后台完成。因为第二遍的值是相同的,无论我设置什么状态,它总是引发未经授权的异常。



如果方法由Tastypie调用两次,存储和新的状态值都有不同的输入,那么我如何实现记录级授权?

解决方案

原来,对update_detail的多次调用是tastypie框架中的一个错误。



问题已在github上提交,并在错误修复中解决。


I'm having trouble implementing record-level authorization with tastypie 0.9.12+.

My setup looks something like this:

Model

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)

Resource

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

Authorization

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')

My problem is that it appears that update_detail is called twice, with different inputs. The update requested is attempting to change the status of the record stored on the server. The new status must be higher than the stored status, or the change is Unauthorized.

When running the above code, my output looks like this:

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

On the first pass, the bundle.obj.status has the correct value, but the bundle.data['status'] HAS NOT been hydrated. On the second pass, the bundle.obj.status has been changed to the new status, and the new status HAS been hydrated.

Because the status hasn't been hydrated on the first pass, I can't compare them reliably, and don't want to manually call hydrate_status as it messes up the entire hydrate process done in the background. Because the values on the second pass are the same, no matter what status I set it to, it always raises the Unauthorized exception.

How can I implement the record-level authorization if the method is called twice by Tastypie with different inputs for both the stored and new status values?

解决方案

Turns out, the multiple calls to update_detail was a bug in the tastypie framework.

Issue was submitted on github and resolved in a bug fix.

这篇关于Django Tastypie记录级授权的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆