""" django-rules: декларативные правила доступа для feed (лента новостей) Правила используются для проверки permissions на уровне объектов. https://github.com/dfunckt/django-rules """ import rules # ----------------------------------------------------------------------------- # ПРЕДИКАТЫ (predicates) # ----------------------------------------------------------------------------- @rules.predicate def is_superuser(user): """Суперпользователь имеет все права""" return user.is_superuser @rules.predicate def is_post_author(user, post): """Пользователь является автором поста""" if post is None: return False return post.author == user or post.created_by == user @rules.predicate def can_publish_posts(user): """ Пользователь может публиковать посты в ленте. Адаптируйте под вашу логику (по должности, группе и т.д.) """ if not hasattr(user, 'position'): return False position_name = getattr(user.position, 'name', '').lower() # Руководители, PR-менеджеры, секретари могут публиковать return any(keyword in position_name for keyword in [ 'руководитель', 'начальник', 'директор', 'секретарь', 'pr', 'маркетинг', 'коммуникац' ]) @rules.predicate def can_moderate_posts(user): """Пользователь может модерировать посты (удалять чужие, редактировать)""" return can_publish_posts(user) or is_superuser(user) @rules.predicate def is_public_post(user, post): """Пост является публичным (доступен всем)""" if post is None: return False return getattr(post, 'is_public', True) @rules.predicate def is_department_post(user, post): """Пост относится к отделу пользователя""" if post is None or not hasattr(user, 'department'): return False if hasattr(post, 'department'): return post.department == user.department if hasattr(post, 'target_departments'): return user.department in post.target_departments.all() return False @rules.predicate def is_comment_author(user, comment): """Пользователь является автором комментария""" if comment is None: return False return comment.author == user or comment.created_by == user @rules.predicate def can_access_comment_post(user, comment): """Пользователь имеет доступ к посту, где находится комментарий""" if comment is None or not hasattr(comment, 'post'): return False post = comment.post return is_public_post(user, post) or is_department_post(user, post) @rules.predicate def is_post_pinned(user, post): """Пост закреплён (может видеть любой сотрудник)""" if post is None: return False return getattr(post, 'is_pinned', False) # ----------------------------------------------------------------------------- # ПРАВИЛА (rules) # ----------------------------------------------------------------------------- # Просмотр поста rules.add_rule( 'feed.view_post', is_superuser | is_public_post | is_department_post | is_post_pinned | is_post_author # Автор всегда видит свой пост, даже если он не опубликован ) # Создание поста rules.add_rule( 'feed.create_post', is_superuser | can_publish_posts ) # Изменение поста rules.add_rule( 'feed.change_post', is_superuser | is_post_author | can_moderate_posts ) # Удаление поста rules.add_rule( 'feed.delete_post', is_superuser | is_post_author | can_moderate_posts ) # Публикация поста (изменение статуса draft → published) rules.add_rule( 'feed.publish_post', is_superuser | (is_post_author & can_publish_posts) | can_moderate_posts ) # Закрепление поста rules.add_rule( 'feed.pin_post', is_superuser | can_moderate_posts ) # Добавление комментария к посту rules.add_rule( 'feed.comment_post', rules.is_authenticated # Любой авторизованный может комментировать ) # Просмотр комментария rules.add_rule( 'feed.view_comment', is_superuser | can_access_comment_post ) # Изменение комментария rules.add_rule( 'feed.change_comment', is_superuser | is_comment_author ) # Удаление комментария rules.add_rule( 'feed.delete_comment', is_superuser | is_comment_author | can_moderate_posts ) # Лайк/реакция на пост rules.add_rule( 'feed.react_to_post', rules.is_authenticated # Любой авторизованный может ставить реакции ) # Просмотр черновиков (draft posts) rules.add_rule( 'feed.view_drafts', is_superuser | can_publish_posts ) # Просмотр статистики по постам rules.add_rule( 'feed.view_statistics', is_superuser | can_publish_posts ) # ----------------------------------------------------------------------------- # ИСПОЛЬЗОВАНИЕ В КОДЕ # ----------------------------------------------------------------------------- """ # В views: from django.core.exceptions import PermissionDenied import rules def post_detail(request, pk): post = get_object_or_404(Post, pk=pk) if not rules.test_rule('feed.view_post', request.user, post): raise PermissionDenied("У вас нет доступа к этому посту") comments = post.comments.all() return render(request, 'feed/post_detail.html', { 'post': post, 'comments': comments }) def post_create(request): if not rules.test_rule('feed.create_post', request.user, None): raise PermissionDenied("У вас нет прав на создание постов") if request.method == 'POST': post = Post.objects.create( author=request.user, title=request.POST.get('title'), content=request.POST.get('content'), status='draft' ) return redirect('feed:post_detail', pk=post.pk) return render(request, 'feed/post_create.html') def post_publish(request, pk): post = get_object_or_404(Post, pk=pk) if not rules.test_rule('feed.publish_post', request.user, post): return JsonResponse({'error': 'Нет прав на публикацию'}, status=403) post.status = 'published' post.published_at = timezone.now() post.save() return JsonResponse({'success': True}) def add_comment(request, post_pk): post = get_object_or_404(Post, pk=post_pk) if not rules.test_rule('feed.comment_post', request.user, None): return JsonResponse({'error': 'Нет прав на комментирование'}, status=403) comment = Comment.objects.create( post=post, author=request.user, text=request.POST.get('text') ) return JsonResponse({'comment_id': comment.pk}) # В templates: {% load rules %} {# Список постов #} {% for post in posts %} {% has_rule 'feed.view_post' user post as can_view %} {% if can_view %}
{{ post.content|truncatewords:50 }}
Автор: {{ post.author }}, {{ post.created_at|date:"d.m.Y H:i" }}
Читать далее {% has_rule 'feed.change_post' user post as can_edit %} {% if can_edit %} Редактировать {% endif %} {% has_rule 'feed.delete_post' user post as can_delete %} {% if can_delete %} {% endif %} {% has_rule 'feed.pin_post' user post as can_pin %} {% if can_pin and not post.is_pinned %} {% endif %}