25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

228 lines
7.4 KiB

  1. from django.core.exceptions import ObjectDoesNotExist
  2. from tastypie import fields
  3. from tastypie.authorization import DjangoAuthorization
  4. from tastypie.constants import ALL, ALL_WITH_RELATIONS
  5. from tastypie.exceptions import Unauthorized
  6. from tastypie.resources import ModelResource
  7. from django_images.models import Thumbnail
  8. from .models import Pin, Image
  9. from users.models import User
  10. def _is_pin_owner(obj_or_list, user):
  11. assert obj_or_list is not None
  12. if not isinstance(obj_or_list, (tuple, list)):
  13. obj_or_list = (obj_or_list,)
  14. results = tuple(
  15. obj.submitter == user
  16. for obj in obj_or_list
  17. if isinstance(obj, Pin)
  18. )
  19. if len(results) <= 0:
  20. raise ValueError(
  21. "You should never check permission on %s with this function."
  22. % obj_or_list
  23. )
  24. return all(results)
  25. def _is_authenticated_and_owner(object_list, bundle):
  26. if bundle.request.user.is_anonymous():
  27. return object_list.none()
  28. return object_list.filter(submitter=bundle.request.user)
  29. class PinryAuthorization(DjangoAuthorization):
  30. """
  31. Pinry-specific Authorization backend with object-level permission checking.
  32. """
  33. def _is_obj_owner(self, object_list, bundle):
  34. klass = self.base_checks(bundle.request, bundle.obj.__class__)
  35. if klass is False:
  36. raise Unauthorized("You are not allowed to access that resource.")
  37. return _is_pin_owner(bundle.obj, bundle.request.user)
  38. def read_list(self, object_list, bundle):
  39. # This assumes a ``QuerySet`` from ``ModelResource``.
  40. return object_list
  41. def read_detail(self, object_list, bundle):
  42. """
  43. User can always read detail of any Pin object.
  44. """
  45. return True
  46. def create_detail(self, object_list, bundle):
  47. return self._is_obj_owner(object_list, bundle)
  48. def update_detail(self, object_list, bundle):
  49. return self._is_obj_owner(object_list, bundle)
  50. def delete_detail(self, object_list, bundle):
  51. return self._is_obj_owner(object_list, bundle)
  52. def update_list(self, object_list, bundle):
  53. return _is_authenticated_and_owner(object_list, bundle)
  54. def delete_list(self, object_list, bundle):
  55. return _is_authenticated_and_owner(object_list, bundle)
  56. class ImageAuthorization(DjangoAuthorization):
  57. """
  58. Pinry-specific Authorization backend with object-level permission checking.
  59. """
  60. def __init__(self):
  61. DjangoAuthorization.__init__(self)
  62. def read_list(self, object_list, bundle):
  63. return object_list
  64. def read_detail(self, object_list, bundle):
  65. """
  66. User can always read detail of any Pin object.
  67. """
  68. return True
  69. def create_detail(self, object_list, bundle):
  70. return bundle.request.user.is_authenticated()
  71. def update_detail(self, object_list, bundle):
  72. return bundle.request.user.is_authenticated()
  73. def delete_detail(self, object_list, bundle):
  74. return bundle.request.user.is_authenticated()
  75. def update_list(self, object_list, bundle):
  76. if not bundle.request.user.is_authenticated():
  77. return object_list.none()
  78. return object_list
  79. def delete_list(self, object_list, bundle):
  80. if not bundle.request.user.is_authenticated():
  81. return object_list.none()
  82. return object_list
  83. class UserResource(ModelResource):
  84. gravatar = fields.CharField(readonly=True)
  85. def dehydrate_gravatar(self, bundle):
  86. return bundle.obj.gravatar
  87. class Meta:
  88. list_allowed_methods = ['get']
  89. filtering = {
  90. 'username': ALL
  91. }
  92. queryset = User.objects.all()
  93. resource_name = 'user'
  94. fields = ['username']
  95. include_resource_uri = False
  96. def filter_generator_for(size):
  97. def wrapped_func(bundle, **kwargs):
  98. if hasattr(bundle.obj, '_prefetched_objects_cache') and 'thumbnail' in bundle.obj._prefetched_objects_cache:
  99. for thumbnail in bundle.obj._prefetched_objects_cache['thumbnail']:
  100. if thumbnail.size == size:
  101. return thumbnail
  102. raise ObjectDoesNotExist()
  103. else:
  104. return bundle.obj.get_by_size(size)
  105. return wrapped_func
  106. class ThumbnailResource(ModelResource):
  107. class Meta:
  108. list_allowed_methods = ['get']
  109. fields = ['image', 'width', 'height']
  110. queryset = Thumbnail.objects.all()
  111. resource_name = 'thumbnail'
  112. include_resource_uri = False
  113. class ImageResource(ModelResource):
  114. standard = fields.ToOneField(
  115. ThumbnailResource, full=True,
  116. attribute=lambda bundle: filter_generator_for('standard')(bundle),
  117. related_name='thumbnail',
  118. )
  119. thumbnail = fields.ToOneField(
  120. ThumbnailResource, full=True,
  121. attribute=lambda bundle: filter_generator_for('thumbnail')(bundle),
  122. related_name='thumbnail',
  123. )
  124. square = fields.ToOneField(
  125. ThumbnailResource, full=True,
  126. attribute=lambda bundle: filter_generator_for('square')(bundle),
  127. related_name='thumbnail',
  128. )
  129. class Meta:
  130. fields = ['image', 'width', 'height']
  131. include_resource_uri = False
  132. resource_name = 'image'
  133. queryset = Image.objects.all()
  134. authorization = ImageAuthorization()
  135. class PinResource(ModelResource):
  136. submitter = fields.ToOneField(UserResource, 'submitter', full=True)
  137. image = fields.ToOneField(ImageResource, 'image', full=True)
  138. tags = fields.ListField()
  139. def hydrate_image(self, bundle):
  140. url = bundle.data.get('url', None)
  141. if url:
  142. image = Image.objects.create_for_url(
  143. url,
  144. referer=bundle.data.get('referer', None),
  145. )
  146. bundle.data['image'] = '/api/v1/image/{}/'.format(image.pk)
  147. return bundle
  148. def hydrate(self, bundle):
  149. """Run some early/generic processing
  150. Make sure that user is authorized to create Pins first, before
  151. we hydrate the Image resource, creating the Image object in process
  152. """
  153. submitter = bundle.data.get('submitter', None)
  154. if not submitter:
  155. bundle.data['submitter'] = '/api/v1/user/{}/'.format(bundle.request.user.pk)
  156. else:
  157. if not '/api/v1/user/{}/'.format(bundle.request.user.pk) == submitter:
  158. raise Unauthorized("You are not authorized to create Pins for other users")
  159. return bundle
  160. def dehydrate_tags(self, bundle):
  161. return list(map(str, bundle.obj.tags.all()))
  162. def build_filters(self, filters=None, ignore_bad_filters=False):
  163. orm_filters = super(PinResource, self).build_filters(filters, ignore_bad_filters=ignore_bad_filters)
  164. if filters and 'tag' in filters:
  165. orm_filters['tags__name__in'] = filters['tag'].split(',')
  166. return orm_filters
  167. def save_m2m(self, bundle):
  168. tags = bundle.data.get('tags', None)
  169. if tags:
  170. bundle.obj.tags.set(*tags)
  171. return super(PinResource, self).save_m2m(bundle)
  172. class Meta:
  173. fields = ['id', 'url', 'origin', 'description', 'referer']
  174. ordering = ['id']
  175. filtering = {
  176. 'submitter': ALL_WITH_RELATIONS
  177. }
  178. queryset = Pin.objects.all().select_related('submitter', 'image'). \
  179. prefetch_related('image__thumbnail_set', 'tags')
  180. resource_name = 'pin'
  181. include_resource_uri = False
  182. always_return_data = True
  183. authorization = PinryAuthorization()