Merge branch 'fix-sibscriptions-permissions' into 'master'
feat: Add permissions to subscriptions to only exposed user's See merge request startinblox/djangoldp-packages/djangoldp-notifications!22
This commit is contained in:
		@ -5,10 +5,54 @@ stages:
 | 
			
		||||
  - test
 | 
			
		||||
  - release
 | 
			
		||||
 | 
			
		||||
variables:
 | 
			
		||||
  SIB_PROJECT_NAME: "sib_test"
 | 
			
		||||
  SIB_PROJECT_DIR: "/builds/$SIB_PROJECT_NAME"
 | 
			
		||||
  DJANGO_SETTINGS_MODULE: "$SIB_PROJECT_NAME.settings"
 | 
			
		||||
  SIB_PACKAGE_CONTENT: >
 | 
			
		||||
    {
 | 
			
		||||
      "ldppackages": {
 | 
			
		||||
        "djangoldp_project": "djangoldp_project",
 | 
			
		||||
        "djangoldp_circle": "djangoldp_circle",
 | 
			
		||||
        "djangoldp_notification": "djangoldp_notification",
 | 
			
		||||
        "djangoldp_account": "djangoldp_account",
 | 
			
		||||
        "djangoldp_skill": "djangoldp_skill",
 | 
			
		||||
        "djangoldp_joboffer": "djangoldp_joboffer",
 | 
			
		||||
        "djangoldp_conversation": "djangoldp_conversation",
 | 
			
		||||
        "djangoldp_profile": "djangoldp_profile",
 | 
			
		||||
        "oidc_provider": "git+https://github.com/jblemee/django-oidc-provider.git@develop"
 | 
			
		||||
      },
 | 
			
		||||
      "server": {
 | 
			
		||||
        "site_url": "http://localhost:8000",
 | 
			
		||||
        "allowed_hosts": [
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
        "db_host": "localhost",
 | 
			
		||||
        "db_name": "database",
 | 
			
		||||
        "db_user": "me",
 | 
			
		||||
        "db_pass": "changeit",
 | 
			
		||||
        "smtp_host": "locahost",
 | 
			
		||||
        "smtp_user": "user",
 | 
			
		||||
        "smtp_pass": "changeit",
 | 
			
		||||
        "admin_email": "admin@example.org",
 | 
			
		||||
        "admin_name": "admin",
 | 
			
		||||
        "admin_pass": "admin"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
  stage: test
 | 
			
		||||
  before_script:
 | 
			
		||||
    - export PATH="$PATH:/root/.local/bin" PYTHONPATH="$SIB_PROJECT_DIR"
 | 
			
		||||
    - pip install git+https://git.happy-dev.fr/startinblox/devops/sib.git
 | 
			
		||||
    - cd /builds && DJANGO_SETTINGS_MODULE="" sib startproject $SIB_PROJECT_NAME
 | 
			
		||||
    - echo $SIB_PACKAGE_CONTENT > $SIB_PROJECT_DIR/packages.yml
 | 
			
		||||
    - cd $SIB_PROJECT_DIR
 | 
			
		||||
    - sib install $SIB_PROJECT_NAME
 | 
			
		||||
    - cd $CI_PROJECT_DIR
 | 
			
		||||
    - pip install -e .[dev]
 | 
			
		||||
  script:
 | 
			
		||||
    - echo 'Make your tests here !'
 | 
			
		||||
    - pytest
 | 
			
		||||
  except:
 | 
			
		||||
    - master
 | 
			
		||||
  tags:
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,20 @@
 | 
			
		||||
import logging
 | 
			
		||||
from threading import Thread
 | 
			
		||||
 | 
			
		||||
import requests
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.contrib.admin.models import LogEntry
 | 
			
		||||
from django.contrib.sessions.models import Session
 | 
			
		||||
from django.core.mail import send_mail
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models.signals import post_save
 | 
			
		||||
from django.dispatch import receiver
 | 
			
		||||
from oidc_provider.models import Token
 | 
			
		||||
from django.template import loader
 | 
			
		||||
from django.urls import NoReverseMatch
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from djangoldp.fields import LDPUrlField
 | 
			
		||||
from djangoldp.models import Model
 | 
			
		||||
 | 
			
		||||
from django.template import loader
 | 
			
		||||
from threading import Thread
 | 
			
		||||
 | 
			
		||||
from djangoldp_notification.middlewares import MODEL_MODIFICATION_USER_FIELD
 | 
			
		||||
from .permissions import InboxPermissions
 | 
			
		||||
from djangoldp_notification.permissions import InboxPermissions, SubscriptionsPermissions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Notification(Model):
 | 
			
		||||
@ -50,6 +45,11 @@ class Subscription(Model):
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '{}'.format(self.object)
 | 
			
		||||
 | 
			
		||||
    class Meta(Model.Meta):
 | 
			
		||||
        anonymous_perms = []
 | 
			
		||||
        authenticated_perms = ["add", "view", "delete"]
 | 
			
		||||
        permission_classes = [SubscriptionsPermissions]
 | 
			
		||||
 | 
			
		||||
# --- SUBSCRIPTION SYSTEM ---
 | 
			
		||||
@receiver(post_save, dispatch_uid="callback_notif")
 | 
			
		||||
def send_notification(sender, instance, created, **kwargs):
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
from django.contrib.auth import get_user_model
 | 
			
		||||
from djangoldp.permissions import LDPPermissions
 | 
			
		||||
from rest_framework.reverse import reverse
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InboxPermissions(LDPPermissions):
 | 
			
		||||
@ -35,4 +37,26 @@ class InboxPermissions(LDPPermissions):
 | 
			
		||||
            if not perm.split('.')[1].split('_')[0] in self.user_permissions(request.user, model, obj):
 | 
			
		||||
                return False
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SubscriptionsPermissions(LDPPermissions):
 | 
			
		||||
    def has_permission(self, request, view):
 | 
			
		||||
        if request.user.is_anonymous and not request.method == "OPTIONS":
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        if request.method in ["GET", "PATCH", "DELETE", "PUT"]:
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return super().has_permission(request, view)
 | 
			
		||||
 | 
			
		||||
    def has_object_permission(self, request, view, obj):
 | 
			
		||||
        if request.user.is_anonymous and not request.method == "OPTIONS":
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        reverse_path_key = "{}-notification-list".format(get_user_model()._meta.object_name.lower())
 | 
			
		||||
        user_inbox = reverse(reverse_path_key, kwargs={"slug": request.user.slug}, request=request)
 | 
			
		||||
        if obj.inbox == user_inbox:
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								djangoldp_notification/tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								djangoldp_notification/tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
from importlib import import_module
 | 
			
		||||
from os import environ
 | 
			
		||||
 | 
			
		||||
from pytest import fail
 | 
			
		||||
 | 
			
		||||
settings_module = environ.get("DJANGO_SETTINGS_MODULE")
 | 
			
		||||
if settings_module is None or len(settings_module) == 0:
 | 
			
		||||
    fail("DJANGO_SETTINGS_MODULE needs to be defined and point to your SIB app installation settings")
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    import_module(settings_module)
 | 
			
		||||
except ImportError:
 | 
			
		||||
    initial_module = [token for token in settings_module.split(".") if len(token) > 0][0]
 | 
			
		||||
    fail("Unable to import {}. Try to configure PYTHONPATH to point the "
 | 
			
		||||
         "directory containing the {} module".format(settings_module, initial_module))
 | 
			
		||||
							
								
								
									
										47
									
								
								djangoldp_notification/tests/test_models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								djangoldp_notification/tests/test_models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
 | 
			
		||||
from djangoldp.factories import UserFactory
 | 
			
		||||
from test_plus import APITestCase
 | 
			
		||||
 | 
			
		||||
from djangoldp_notification.models import Subscription
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestSubscription(APITestCase):
 | 
			
		||||
    user1 = None
 | 
			
		||||
    user2 = None
 | 
			
		||||
    circle_user1_url = "http://localhost:8000/circles/1"
 | 
			
		||||
    circle_user2_url = "http://localhost:8000/circles/2"
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.user1 = UserFactory(username="karl_marx", password="password")
 | 
			
		||||
        Subscription.objects.create(object=self.circle_user1_url, inbox="http://testserver/users/karl_marx/inbox/")
 | 
			
		||||
 | 
			
		||||
        self.user2 = UserFactory(username="piotr_kropotkine", password="password")
 | 
			
		||||
        Subscription.objects.create(object=self.circle_user2_url,
 | 
			
		||||
                                    inbox="http://testserver/users/piotr_kropotkine/inbox/")
 | 
			
		||||
 | 
			
		||||
    def test_not_logged_fails(self):
 | 
			
		||||
        response = self.get("/subscriptions/")
 | 
			
		||||
        self.assert_http_403_forbidden(response)
 | 
			
		||||
        self.assertEqual(response.data.get("detail"), "Authentication credentials were not provided.")
 | 
			
		||||
 | 
			
		||||
    def test_logged_in_succeeds(self):
 | 
			
		||||
        with self.login(self.user1):
 | 
			
		||||
            result = self.get("/subscriptions/").data.get("ldp:contains")
 | 
			
		||||
            expected = [OrderedDict({
 | 
			
		||||
                "@id": "http://localhost:8000/subscriptions/1/",
 | 
			
		||||
                "object": self.circle_user1_url,
 | 
			
		||||
                "inbox": "http://testserver/users/karl_marx/inbox/",
 | 
			
		||||
                "permissions": [{'mode': {'@type': 'view'}}, {'mode': {'@type': 'delete'}}]
 | 
			
		||||
            })]
 | 
			
		||||
            self.assertSequenceEqual(result, expected)
 | 
			
		||||
 | 
			
		||||
        with self.login(self.user2):
 | 
			
		||||
            result = self.get("/subscriptions/").data.get("ldp:contains")
 | 
			
		||||
            expected = [OrderedDict({
 | 
			
		||||
                "@id": "http://localhost:8000/subscriptions/2/",
 | 
			
		||||
                "object": self.circle_user2_url,
 | 
			
		||||
                "inbox": "http://testserver/users/piotr_kropotkine/inbox/",
 | 
			
		||||
                "permissions": [{'mode': {'@type': 'view'}}, {'mode': {'@type': 'delete'}}]
 | 
			
		||||
            })]
 | 
			
		||||
            self.assertSequenceEqual(result, expected)
 | 
			
		||||
		Reference in New Issue
	
	Block a user