def test_register_valid_rules(self): rules_list = [ # Dummy model {'codename':'can_ship', 'model':Dummy, 'field_name':'canShip', 'view_param_pk':'idView', 'description':"Only supplier has the authorization to ship"}, ] for params in rules_list: registry.register(**params)
def test_user_is_tested_for_simple_rule_by_field_name(self): self.create_fixtures() registry.register( 'mock_permission', MockModel, field_name='mock_simple_permission') back = ObjectPermissionBackend() res = back.has_perm(self.user, 'mock_permission', self.model) self.assertEqual(res, True)
def test_non_boolean_permissions_raises(self): self.create_fixtures() registry.register('mock_non_boolean_permission', MockModel) back = ObjectPermissionBackend() self.assertRaises( NotBooleanPermission, back.has_perm, self.user, 'mock_non_boolean_permission', self.model)
def test_non_callable_permission_raises(self): self.create_fixtures() registry.register('not_callable', MockModel) back = ObjectPermissionBackend() self.assertRaises( NotBooleanPermission, back.has_perm, self.user, 'not_callable', self.model)
def test_register_valid_rules_compact_style(self): rules_list = [ # Dummy model {'codename':'canShip', 'model':Dummy}, ] for params in rules_list: registry.register(**params)
def register(perm, func, model=GlobalPermission): """ Function to register global permissions or permissions against a particular model. """ model.add_to_class(perm, func) rulez_registry.register(perm, model)
def __call__(self, func): from . import registry # Add function to the model self.model.add_to_class(self.codename, func) # Register the function registry.register(self.codename, self.model) # Pass it along return func
def test_tag_syntax(self): registry.register("mock_positive_permission", MockModel) # TODO: error messages from template tag a bit are confusing. self.assertRaisesRegexp(TemplateSyntaxError, "tag requires exactly three arguments", self.render_template, "{% load rulez_perms %}{% rulez_perms mock_positive_permission object %}", {}) self.assertRaisesRegexp(TemplateSyntaxError, "tag requires exactly three arguments", self.render_template, "{% load rulez_perms %}{% rulez_perms mock_positive_permission object can %}", {}) self.assertRaisesRegexp(TemplateSyntaxError, "third argument to tag must be 'as'", self.render_template, "{% load rulez_perms %}{% rulez_perms mock_positive_permission object can can %}", {})
def assertNoHeCant(self, permission, user): registry.register(permission, MockModel) rendered = self.render_template( "{% load rulez_perms %}" "{% rulez_perms " + permission + " object as can %}" "{% if can %}yes he can{% else %}no he can't{% endif %}", { "user": user, "object": MockModel() } ) self.assertEqual(rendered, "no he can't")
def setUp(self): try: self.anonymous = User.objects.get_or_create(id=settings.ANONYMOUS_USER_ID, username='******', is_active=True)[0] except Exception: self.fail("You need to define an ANONYMOUS_USER_ID in your settings file") self.user = User.objects.get_or_create(username='******', is_active=True)[0] self.otherUser = User.objects.get_or_create(username='******', is_active=True)[0] self.superuser = User.objects.get_or_create(username='******', is_active=True, is_superuser=True)[0] self.not_active_superuser = User.objects.get_or_create(username='******', is_active=False, is_superuser=True)[0] self.obj = Dummy.objects.get_or_create(supplier=self.user)[0] # self.ctype = ContentType.objects.get_for_model(self.obj) registry.register(codename='can_ship', field_name='canShip', model=self.obj.__class__, view_param_pk='idDummy', description="Only supplier have the authorization to ship")
def __call__(self, cls): import inspect from . import registry if not self.codenames: # No codenames were passed; register all can_FOO functions for codename, unused in inspect.getmembers(cls): if codename.startswith('can_'): registry.register(codename, cls) else: # Register all provided names for codename in self.codenames: registry.register(codename, cls) # Pass it along return cls
def test_register_invalid_rules_NonexistentFieldName(self): rules_list = [ # Dummy model {'codename':'can_ship', 'model':Dummy, 'field_name':'canSship', 'view_param_pk':'idView', 'description':"Only supplier has the authorization to ship"}, ] for params in rules_list: self.assertRaises(NonexistentFieldName, lambda: registry.register(**params))
qs = cls.objects if not cls.can_save(user): qs = qs.filter( Q(released_at__isnull=True) | Q(released_at__lte=timezone.now())) # Show "all cohorts" and "current cohort" exhibitions to non-superusers if not user or not user.is_authenticated() or not user.is_superuser: cohort = Cohort.objects.get_current(user) qs = qs.filter(Q(cohort__isnull=True) | Q(cohort=cohort)) return qs registry.register('can_see', Exhibition) registry.register('can_save', Exhibition) @receiver(post_init, sender=Exhibition) def post_init(sender, instance=None, **kwargs): '''Store initial image, to detect changes in post_save, post_delete''' if instance: instance.__init_image = instance.image @receiver(post_delete, sender=Exhibition) def post_delete(sender, instance=None, **kwargs): '''Delete orphan image, if any''' if instance: if instance.__init_image:
def test_rule_is_registered(self): registry.register('mock_permission', MockModel) # if it's been registered properly we should be able to get() something res = registry.get('mock_permission', MockModel) self.assertNotEqual(res, None) self.assertNotEqual(res, {})
# send email # logger.info('Sending ReviewDocument invite email to: %s' % u) m = ReviewerReminderEmail(recipients=((u.get_full_name(), u.email,),), from_tuple=(from_user.get_full_name(), from_user.email,)) m.process(subject=m.subject, item=self.document.item, document=self.document, from_name=from_user.get_full_name(), action_url=ABSOLUTE_BASE_URL(path=self.get_absolute_url(user=u))) def can_read(self, user): return user in self.participants def can_edit(self, user): return user in self.participants def can_delete(self, user): return user in self.participants rulez_registry.register("can_read", ReviewDocument) rulez_registry.register("can_edit", ReviewDocument) rulez_registry.register("can_delete", ReviewDocument) from .signals import (#set_item_review_percentage_complete, #reset_item_review_percentage_complete_on_complete, #reset_item_review_percentage_complete_on_delete, ensure_matter_participants_are_in_reviewdocument_participants, on_reviewer_add, on_reviewer_remove,)
"tool": self.workspace.tools.filter(slug=self.tool_slug).first().slug, "slug": self.slug, }, ) def can_read(self, user): return user in self.workspace.participants.all() def can_edit(self, user): return user in self.workspace.participants.all() def can_delete(self, user): return user.profile.is_lawyer and user in self.workspace.participants.all() rulez_registry.register("can_read", EightyThreeB) rulez_registry.register("can_edit", EightyThreeB) rulez_registry.register("can_delete", EightyThreeB) class Attachment(IsDeletedMixin, models.Model): eightythreeb = models.ForeignKey("eightythreeb.EightyThreeB") attachment = models.FileField(upload_to=_upload_file, blank=True, storage=_managed_S3BotoStorage()) def can_delete(self, user): return user == self.eightythreeb.user rulez_registry.register("can_delete", Attachment) from .signals import * # noqa
return super(ItemRequestRevisionView, self).pre_save(obj=obj, **kwargs) def post_save(self, obj, **kwargs): """ Send the email to the items responsible_party """ self.object.send_document_requested_emails(from_user=self.request.user) # if is_requested is True the activity has to be created (similar to send_document_requested_emails()) if self.object.is_requested is True: user = self.object.responsible_party if user: self.matter.actions.request_user_upload_revision(item=self.object, adding_user=self.request.user, added_user=user) super(ItemRequestRevisionView, self).post_save(obj=obj, **kwargs) def can_read(self, user): return user in self.matter.participants.all() def can_edit(self, user): return user.matter_permissions(matter=self.matter).has_permission(manage_document_reviews=True) is True def can_delete(self, user): return user.matter_permissions(matter=self.matter).has_permission(manage_document_reviews=True) is True rulez_registry.register("can_read", ItemRequestRevisionView) rulez_registry.register("can_edit", ItemRequestRevisionView) rulez_registry.register("can_delete", ItemRequestRevisionView)
lookup_field = 'pk' def can_read(self, user): obj = self.get_object() return user in obj.document.item.matter.participants.all() def can_edit(self, user): obj = self.get_object() return user.matter_permissions(matter=obj.document.item.matter).has_permission(manage_document_reviews=True) is True def can_delete(self, user): obj = self.get_object() return user.matter_permissions(matter=obj.document.item.matter).has_permission(manage_document_reviews=True) is True rulez_registry.register("can_read", ReviewEndpoint) rulez_registry.register("can_edit", ReviewEndpoint) rulez_registry.register("can_delete", ReviewEndpoint) class BaseReviewerOrSignerMixin(generics.GenericAPIView): """ Provides the object to access .signers or .reviewers and their required functionality """ model = Revision # to allow us to use get_object generically serializer_class = ReviewSerializer # as we are returning the revision and not the item lookup_field = 'slug' lookup_url_kwarg = 'item_slug' def get_objects(self, **kwargs):
invite.inviting_user = from_user invite.save(update_fields=['inviting_user']) # send the invite url action_url = ABSOLUTE_BASE_URL(invite.get_absolute_url()) m = SignerReminderEmail(recipients=((signer.get_full_name(), signer.email,),)) m.process(subject=subject, message=message, item=self.document.item, document=self.document, from_name=from_user.get_full_name(), action_url=action_url) def can_read(self, user): return user in set(self.signers.all() | self.document.item.matter.participants.all()) def can_edit(self, user): return user in self.document.item.matter.participants.all() def can_delete(self, user): return user in self.document.item.matter.participants.all() rulez_registry.register("can_read", SignDocument) rulez_registry.register("can_edit", SignDocument) rulez_registry.register("can_delete", SignDocument) from .signals import (ensure_matter_participants_are_in_signdocument_participants, on_signer_add, on_signer_remove,)
return u'%s' % self.name def get_absolute_url(self): return self.item.get_absolute_url() def can_read(self, user): return self.pk is None \ or user in self.item.matter.participants.all() \ or user == self.created_by \ or user in self.assigned_to.all() \ or user.matter_permissions(matter=self.item.matter).has_permission(manage_items=True) def can_edit(self, user): return self.pk is None \ or user == self.created_by \ or user in self.assigned_to.all() \ or user.matter_permissions(matter=self.item.matter).has_permission(manage_items=True) def can_delete(self, user): return user == self.created_by \ or user.matter_permissions(matter=self.item.matter).has_permission(manage_items=True) rulez_registry.register("can_read", Task) rulez_registry.register("can_edit", Task) rulez_registry.register("can_delete", Task) # Signals post_save.connect(post_save_update_task_complete_count_in_item, sender=Task, dispatch_uid='task.post_save.post_save_update_task_complete_count_in_item') post_delete.connect(post_delete_update_task_complete_count_in_item, sender=Task, dispatch_uid='task.post_delete.post_delete_update_task_complete_count_in_item')
def test_inactive_user_can_never_have_any_permissions(self): self.create_fixtures() registry.register('mock_permission', MockModel) back = ObjectPermissionBackend() res = back.has_perm(self.inactive_user, 'mock_permission', self.model) self.assertEqual(res, False)
# Authenticated users can see shared or own artwork if (user and user.is_authenticated()): return qs.filter(Q(author__exact=user.id) | Q(shared__gt=0)) # Public can only see shared artwork else: return qs.filter(Q(shared__gt=0)) # Return queryset filtered to own artwork @classmethod def can_save_queryset(cls, qs=None, user=None): if not qs: qs = cls.objects if (user and user.is_authenticated()): # Authors can submit any unshared artwork return qs.filter(author__exact=user.id, shared__exact=0) else: return qs.none() registry.register('can_see', Artwork) registry.register('can_save', Artwork) class ArtworkForm(forms.ModelForm): class Meta: model = Artwork fields = ['title', 'code'] widgets = {'code': HiddenInput}
def test_rules_returns_False_for_None_obj(self): self.create_fixtures() registry.register('mock_permission', MockModel) back = ObjectPermissionBackend() res = back.has_perm(self.user, 'mock_permission', None) self.assertEqual(res, False)
if self.request.user.profile.is_lawyer: obj.lawyer = self.request.user return super(MatterEndpoint, self).pre_save(obj=obj) def can_read(self, user): return user.profile.user_class in ['lawyer', 'customer'] def can_edit(self, user): return user.profile.is_lawyer def can_delete(self, user): return user.profile.is_lawyer rulez_registry.register("can_read", MatterEndpoint) rulez_registry.register("can_edit", MatterEndpoint) rulez_registry.register("can_delete", MatterEndpoint) """ Custom Api Endpoints """ class MatterExportView(generics.CreateAPIView, MatterMixin): @property def provider_name(self): if self.provider == 'box': return 'box.com'
return self.TODO_STATUS_CHOICES.get_desc_by_value(self.status) @property def original_name(self): return self.data.get('name', 'No original name was found, was created by user') @property def item_hash_num(self): return '#{primary}-{secondary}'.format(primary=int(self.data.get('sort_position', 0)), secondary=int(self.data.get('sort_position_by_cat', 0))) def get_absolute_url(self): # need to import this HERE for some reason? from django.core.urlresolvers import reverse return reverse('todo:item', kwargs={'project_uuid': self.project.uuid, 'slug': self.slug}) registry.register("can_read", ToDo) registry.register("can_edit", ToDo) registry.register("can_delete", ToDo) class Attachment(models.Model): """ Files that can be attached to our todo items """ uuid = models.CharField(max_length=255, blank=True, null=True, db_index=True) uploaded_by = models.ForeignKey('auth.User', related_name='atatchments_uploaded') deleted_by = models.ForeignKey('auth.User', blank=True, null=True, related_name='atatchments_deleted') attachment = FPFileField(upload_to=_attachment_upload_file, additional_params=None) project = models.ForeignKey('project.Project', related_name='attachments') todo = models.ForeignKey('todo.ToDo', blank=True, null=True, related_name='attachments') data = JSONField(default={})
return super(SignatureEndpoint, self).pre_delete(obj=obj, **kwargs) def can_read(self, user): return user.profile.user_class in ['lawyer', 'customer'] def can_edit(self, user): obj = self.get_object() return user.matter_permissions(matter=obj.matter).has_permission(manage_signature_requests=True) is True def can_delete(self, user): obj = self.get_object() return user.matter_permissions(matter=obj.matter).has_permission(manage_signature_requests=True) is True rulez_registry.register("can_read", SignatureEndpoint) rulez_registry.register("can_edit", SignatureEndpoint) rulez_registry.register("can_delete", SignatureEndpoint) class ItemRevisionSignersView(generics.ListAPIView, generics.CreateAPIView, BaseReviewerOrSignerMixin): """ /matters/:matter_slug/items/:item_slug/revision/signers/ (GET,POST) [lawyer,customer] to list, create signers """ serializer_class = SignatureSerializer # as we are returning the revision and not the item def get_queryset_provider(self): return self.revision.signers
return Response(status=status) def can_read(self, user): return user in self.get_object().participants.all() or user == self.get_object().lawyer def can_edit(self, user): role = self.request.DATA.get('role') if not role: return False # manage_participants overrides manage_clients if user.matter_permissions(matter=self.matter).has_permission(manage_participants=True) is True: return True elif ROLES.get_value_by_name(role.lower()) == ROLES.client: return user.matter_permissions(matter=self.matter).has_permission(manage_clients=True) is True def can_delete(self, user): user_to_work_on = User.objects.get(username=self.kwargs.get('username')) # manage_participants overrides manage_clients if user.matter_permissions(matter=self.matter).has_permission(manage_participants=True) is True \ or user_to_work_on == self.request.user: return True elif user_to_work_on.matter_permissions(matter=self.matter).role == ROLES.client: return user.matter_permissions(matter=self.matter).has_permission(manage_clients=True) is True rulez_registry.register("can_read", MatterParticipant) rulez_registry.register("can_edit", MatterParticipant) rulez_registry.register("can_delete", MatterParticipant)
return super(BoxViewSet, self).create(request) def can_read(self, user): return user.is_authenticated() def can_edit(self, user): if self.request.method == 'POST': return True return user in self.get_object().users.all() or user.is_staff def can_delete(self, user): return user == self.get_object().owner or user.is_staff rulez_registry.register("can_read", BoxViewSet) rulez_registry.register("can_edit", BoxViewSet) rulez_registry.register("can_delete", BoxViewSet) class BoxRegistrationEndpoint(generics.CreateAPIView): model = Box serializer_class = BoxSerializer def create(self, request, **kwargs): status_code = status.HTTP_200_OK request_data = request.data.copy() request_data['remote_ip'] = request.META.get('HTTP_X_FORWARDED_FOR', request.META.get('REMOTE_ADDR'))
def can_see_queryset(cls, qs=None, user=None): if not qs: qs = cls.objects if not cls.can_save(user): qs = qs.filter(Q(released_at__isnull=True) | Q(released_at__lte=timezone.now())) # Show "all cohorts" and "current cohort" exhibitions to non-superusers if not user or not user.is_authenticated() or not user.is_superuser: cohort = Cohort.objects.get_current(user) qs = qs.filter(Q(cohort__isnull=True) | Q(cohort=cohort)) return qs registry.register('can_see', Exhibition) registry.register('can_save', Exhibition) @receiver(post_init, sender=Exhibition) def post_init(sender, instance=None, **kwargs): '''Store initial image, to detect changes in post_save, post_delete''' if instance: instance.__init_image = instance.image @receiver(post_delete, sender=Exhibition) def post_delete(sender, instance=None, **kwargs): '''Delete orphan image, if any''' if instance: if instance.__init_image: instance.__init_image.storage.delete(instance.__init_image.name)
from geoposition.fields import GeopositionField class Hive(models.Model): uuid = UUIDField(auto=True, db_index=True) users = models.ManyToManyField('auth.User') name = models.CharField(max_length=128, db_index=True, null=True, blank=True) description = models.CharField(max_length=255) project = models.ForeignKey('project.Project', null=True, blank=True) position = GeopositionField(default='51.1935462,6.4479122999999845') sensors = models.ManyToManyField('sensor.Sensor') is_public = models.BooleanField(default=False, db_index=True) data = JSONField(default={}) def __unicode__(self): return '%s - %s' % (self.name, self.description) def can_read(self, user): return user.is_authenticated() def can_edit(self, user): return user in self.users.all() or user.is_staff def can_delete(self, user): return user in self.users.all() or user.is_staff rulez_registry.register("can_read", Hive) rulez_registry.register("can_edit", Hive) rulez_registry.register("can_delete", Hive)
qs = cls.objects if user and user.is_authenticated(): qs = qs.filter(voted_by=user) if submission: if isinstance(submission, (list, tuple)): qs = qs.filter(submission__id__in=submission) else: qs = qs.filter(submission=submission) elif exhibition: qs = qs.filter(submission__exhibition=exhibition) else: qs = qs.none() return qs registry.register('can_delete', Vote) @receiver(post_save, sender=Vote) def post_save(sender, instance=None, created=False, **kwargs): if created and (instance.status == Vote.THUMBS_UP): instance.submission.score = F('score') + 1 instance.submission.save() @receiver(post_delete, sender=Vote) def post_delete(sender, instance=None, **kwargs): if instance.status == Vote.THUMBS_UP: instance.submission.score = F('score') - 1 instance.submission.save() class VoteForm(forms.ModelForm):
def test_rules_returns_False_for_inexistant_rule(self): self.create_fixtures() registry.register('mock_permission', MockModel) back = ObjectPermissionBackend() res = back.has_perm(self.user, 'whatever_permission', self.model) self.assertEqual(res, False)
@classmethod def can_see_queryset(cls, qs=None, user=None, exhibition=None): if not qs: qs = cls.objects if exhibition: if isinstance(exhibition, Exhibition): exhibition = exhibition.id qs = qs.filter(exhibition_id=exhibition) else: cohort = Cohort.objects.get_current(user=user) qs = qs.filter(Q(exhibition__cohort__isnull=True) | Q(exhibition__cohort=cohort)) return qs registry.register('can_see', Submission) registry.register('can_save', Submission) registry.register('can_vote', Submission) @receiver(post_save, sender=Submission) def post_save(sender, instance=None, **kwargs): '''Update artwork.shared to submission id''' if instance: from artwork.models import Artwork Artwork.objects.filter(id__exact=instance.artwork_id).update(shared=instance.id) @receiver(post_delete, sender=Submission) def post_delete(sender, instance=None, **kwargs): '''Decrement artwork.shared, and delete existing votes.'''
if comment_object.actor == request.user and comment_object.timestamp > \ timezone.now() - datetime.timedelta(minutes=settings.EDIT_COMMENTS_DURATION): comment_object.data['comment'] = comment comment_object.save(update_fields=['data']) return Response(status=http_status.HTTP_200_OK) return Response(status=http_status.HTTP_403_FORBIDDEN, data={'reason': u'Your comment is too old to be edited.'}) else: return Response(status=http_status.HTTP_400_BAD_REQUEST, data={'reason': 'You should send a comment.'}) def get_object(self): return get_object_or_404(self.model, pk=self.kwargs.get('id')) def delete(self, request, *args, **kwargs): # lawyer can delete at any time # customer can ONLY delete if it not older than DELETE_COMMENTS_DURATION self.object = self.get_object() if request.user.profile.is_lawyer or self.object.timestamp > \ timezone.now() - datetime.timedelta(minutes=settings.DELETE_COMMENTS_DURATION): # TODO: check if lawyer from different matter needs to be blocked self.object.delete() return Response(status=http_status.HTTP_204_NO_CONTENT) return Response(status=http_status.HTTP_403_FORBIDDEN) def can_edit(self, user): return user.profile.user_class in ['lawyer', 'customer'] rulez_registry.register("can_edit", ItemCommentEndpoint)
middle = [pending, done][self.approval == 'A'] representation = self.admin_type.name + self.by_admin.username \ + middle + self.for_user.username return representation def can_be_approved_by(self, user_obj): ''' Returns a bool, depending on whether the acting admin/user CAN approve the user, given the requirements of business logic. ''' # ADMIN TYPE 2 can approve ALL if user_obj.groups.filter(name=ADMIN_TYPE_TWO).exists(): return True # ADMIN TYPE 1 users from his city only if user_obj.groups.filter(name=ADMIN_TYPE_ONE).exists(): if user_obj.location == self.for_user.location: return True # Not an admin # OR, admin is type 1, and user is not in his location return False def mark_approved(self): self.approval = 'A' self.save() registry.register('can_be_approved_by', Approval)
qs = cls.objects if exhibition: if isinstance(exhibition, Exhibition): exhibition = exhibition.id qs = qs.filter(exhibition_id=exhibition) else: cohort = Cohort.objects.get_current(user=user) qs = qs.filter( Q(exhibition__cohort__isnull=True) | Q(exhibition__cohort=cohort)) return qs registry.register('can_see', Submission) registry.register('can_save', Submission) registry.register('can_vote', Submission) @receiver(post_save, sender=Submission) def post_save(sender, instance=None, **kwargs): '''Update artwork.shared to submission id''' if instance: from artwork.models import Artwork Artwork.objects.filter(id__exact=instance.artwork_id).update( shared=instance.id) @receiver(post_delete, sender=Submission) def post_delete(sender, instance=None, **kwargs):