diff --git a/ippisite/ippidb/models.py b/ippisite/ippidb/models.py
index 2a6b08450130d3d0be8d1d5978d508805f0d3ddc..6a886aeac82eaa78fb95f1829fb467729df630d5 100644
--- a/ippisite/ippidb/models.py
+++ b/ippisite/ippidb/models.py
@@ -477,19 +477,17 @@ class PpiComplex(models.Model):
         return "PPI {}, Complex {} ({})".format(self.ppi, self.complex, self.cc_nb)
 
 
-class ValidatedCompoundsManager(models.Manager):
-    """
-    ValidatedCompoundManager filters only compounds from validated
-    contributions (or not coming from contributions) in the results
-    of the database query
-    """
-
-    def get_queryset(self):
-        return (
-            super()
-            .get_queryset()
-            .exclude(compoundaction__ppi__contribution__validated=False)
-        )
+class CompoundsManager(models.Manager):
+    def for_user(self, current_user):
+        qs = self.get_queryset()
+        if current_user.is_anonymous:
+            qs = qs.exclude(compoundaction__ppi__contribution__validated=False)
+        elif not current_user.is_superuser:
+            qs = qs.exclude(
+                Q(compoundaction__ppi__contribution__validated=False),
+                ~Q(compoundaction__ppi__contribution__contributor=current_user),
+            )
+        return qs
 
 
 class Compound(AutoFillableModel):
@@ -497,8 +495,7 @@ class Compound(AutoFillableModel):
     Chemical compound
     """
 
-    objects = models.Manager()
-    validated = ValidatedCompoundsManager()
+    objects = CompoundsManager()
 
     canonical_smile = models.TextField(verbose_name="Canonical Smiles", unique=True)
     is_macrocycle = models.BooleanField(
diff --git a/ippisite/ippidb/tests.py b/ippisite/ippidb/tests.py
index 5e4c8b7ffb9a66c6ffaf981bb5c2e912c604a726..3b0aa612f43d5e3cb5b18861c97374d835f8b3d4 100644
--- a/ippisite/ippidb/tests.py
+++ b/ippisite/ippidb/tests.py
@@ -3,6 +3,7 @@ iPPI-DB unit tests
 """
 import re
 
+from django.contrib.auth import get_user_model
 from django.core.management import call_command
 from django.test import TestCase
 from django.urls import reverse
@@ -16,6 +17,9 @@ from .models import (
     CompoundTanimoto,
     create_tanimoto,
     update_compound_cached_properties,
+    CompoundAction,
+    Ppi,
+    Contribution,
 )
 from .models import DrugBankCompound, Protein
 from .utils import FingerPrinter, mol2smi, smi2mol, smi2inchi, smi2inchikey
@@ -405,6 +409,150 @@ class QueryCompoundViewsTestCase(TestCase):
         self.assertEqual(response.status_code, 200)
 
 
+def create_dummy_user(login, password, admin=False):
+    User = get_user_model()
+    if admin:
+        user = User.objects.create_superuser(username=login, email=f"{login}@ippidb.test", password=password)
+    else:
+        user = User.objects.create_user(username=login, email=f"{login}@ippidb.test", password=password)
+    return user
+
+
+def create_dummy_contribution(compound_id, smiles, user, symmetry, validate=False):
+    c = create_dummy_compound(compound_id, smiles)
+    ppi = Ppi()
+    ppi.symmetry = symmetry
+    ppi.save()
+    ca = CompoundAction()
+    ca.nb_copy_compounds = 1
+    ca.compound = c
+    ca.ppi = ppi
+    ca.save()
+    co = Contribution()
+    co.ppi = ppi
+    co.contributor = user
+    co.validated = validate
+    co.save()
+    return c
+
+
+class QueryCompoundViewsAccessTestCase(TestCase):
+    """
+    Test the visibility of compounds belonging to
+    validated or unvalidated contributions
+    The "visibility matrix" is the following:
+    ============================================================================
+    |Validation status / User| Anonymous | Creator | Another user | Admin user |
+    | Unvalidated            |    No     |   Yes   |     No       |    Yes     |
+    | Validated              |    Yes    |   Yes   |     Yes      |    Yes     |
+    ============================================================================
+    """
+
+    @classmethod
+    def setUpTestData(cls):
+        symmetry = models.Symmetry()
+        symmetry.code = "AS"
+        symmetry.description = "asymmetric"
+        symmetry.save()
+        # create contributor 1
+        cls.user_c1 = create_dummy_user("contributor1", "test1")
+        # create contributor 2
+        cls.user_c2 = create_dummy_user("contributor2", "test2")
+        # create admin
+        cls.user_cA = create_dummy_user("admin", "testA", True)
+        # WHAT ARE THE USE CASES TO BE TESTED?
+        # user is anonymous, contributor, another contributor, admin
+        # compound is not validated, validated
+        create_dummy_contribution(1, "CC", cls.user_c1, symmetry, False)
+        create_dummy_contribution(2, "CCC", cls.user_c1, symmetry, True)
+        call_command("lle_le")
+        call_command("pca")
+
+    def test_compound_detail_unvalidated_unlogged(self):
+        """
+        Unvalidated compound should not be visible
+        if unlogged
+        """
+        url = reverse("compound_card", kwargs={"pk": 1})
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 404)
+
+    def test_compound_detail_unvalidated_logged_creator(self):
+        """
+        Unvalidated compound should be visible
+        if logged as the creator
+        """
+        url = reverse("compound_card", kwargs={"pk": 1})
+        self.client.force_login(self.user_c1)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.client.logout()
+
+    def test_compound_detail_unvalidated_logged_user(self):
+        """
+        Unvalidated compound should not be visible
+        if logged as another user
+        """
+        url = reverse("compound_card", kwargs={"pk": 1})
+        self.client.force_login(self.user_c2)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 404)
+        self.client.logout()
+
+    def test_compound_detail_unvalidated_logged_admin(self):
+        """
+        Unvalidated compound should be visible
+        if logged as an admin user
+        """
+        url = reverse("compound_card", kwargs={"pk": 1})
+        self.client.force_login(self.user_cA)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.client.logout()
+
+    def test_compound_detail_validated_unlogged(self):
+        """
+        Validated compound should be visible
+        if unlogged
+        """
+        url = reverse("compound_card", kwargs={"pk": 2})
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+    def test_compound_detail_validated_logged_creator(self):
+        """
+        Validated compound should be visible
+        if logged as the creator
+        """
+        url = reverse("compound_card", kwargs={"pk": 2})
+        self.client.force_login(self.user_c1)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.client.logout()
+
+    def test_compound_detail_validated_logged_user(self):
+        """
+        Validated compound should be visible
+        if logged as another user
+        """
+        url = reverse("compound_card", kwargs={"pk": 2})
+        self.client.force_login(self.user_c2)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.client.logout()
+
+    def test_compound_detail_validated_logged_admin(self):
+        """
+        Validated compound should be visible
+        if logged as an admin user
+        """
+        url = reverse("compound_card", kwargs={"pk": 2})
+        self.client.force_login(self.user_cA)
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.client.logout()
+
+
 class TestGetDoiInfo(TestCase):
     """
     Test retrieving information for a DOI entry
diff --git a/ippisite/ippidb/views/compound_query.py b/ippisite/ippidb/views/compound_query.py
index b49ebac42d3386bbc45e3aabf01ee256dad1b41a..38ea59b3b2e6aeef5e2e2ca7ec09aea8ce3cdb6d 100644
--- a/ippisite/ippidb/views/compound_query.py
+++ b/ippisite/ippidb/views/compound_query.py
@@ -561,19 +561,12 @@ class CompoundListView(ListView):
         return context
 
     def get_queryset(self):
+        # compounds can be accessed only if they are validated or
+        # if the current user is an admin OR their contributor
+        self.queryset = self.model.objects.for_user(self.request.user)
         self.filter_context = {}
         # get queryset
         qs = super().get_queryset()
-        # compounds can be accessed only if they are validated or
-        # if the current user is an admin OR their contributor
-        current_user = self.request.user
-        if current_user.is_anonymous:
-            qs = qs.exclude(compoundaction__ppi__contribution__validated=False)
-        elif not current_user.is_superuser:
-            qs = qs.exclude(
-                Q(compoundaction__ppi__contribution__validated=False),
-                ~Q(compoundaction__ppi__contribution__contributor=current_user),
-            )
         # add filters
         self.filter_context[
             "disabled"
@@ -745,6 +738,9 @@ class CompoundDetailView(DetailView):
     model = Compound
     template_name = "compound_card.html"
 
+    def get_queryset(self):
+        return self.model.objects.for_user(self.request.user)
+
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         context["le_lle_biplot_data"] = LeLleBiplotData.objects.get().le_lle_biplot_data