diff --git a/arkindex/dataimport/api.py b/arkindex/dataimport/api.py
index bf764b476f81e61b48d55ee2894c8268659372bf..587a12fbda62faa63391180de934cd94d8ef417e 100644
--- a/arkindex/dataimport/api.py
+++ b/arkindex/dataimport/api.py
@@ -1,4 +1,5 @@
 from django.db.models import Sum
+from django.http.response import Http404
 from django.shortcuts import get_object_or_404
 from django.core.exceptions import PermissionDenied
 from django.conf import settings
@@ -41,14 +42,13 @@ class DataImportsList(CorpusACLMixin, ListCreateAPIView):
         if serializer.validated_data['mode'] not in (DataImportMode.Images, ):
             raise ValidationError('Unsupported mode for now, sorry.')
 
-        if not self.request.user.is_admin and \
-                Right.Write not in serializer.validated_data['corpus'].get_acl_rights(self.request.user):
+        if not self.has_write_access(serializer.validated_data['corpus']):
             raise PermissionDenied
 
         return super().perform_create(serializer)
 
 
-class DataImportDetails(RetrieveUpdateDestroyAPIView):
+class DataImportDetails(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
     """
     Retrieve and edit a data import
     """
@@ -61,8 +61,7 @@ class DataImportDetails(RetrieveUpdateDestroyAPIView):
 
     def perform_update(self, serializer):
         dataimport = serializer.instance
-        if not self.request.user.is_admin and \
-                Right.Write not in dataimport.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(dataimport.corpus):
             raise PermissionDenied
 
         if dataimport.state not in (DataImportState.Created, DataImportState.Configured):
@@ -86,8 +85,7 @@ class DataImportDetails(RetrieveUpdateDestroyAPIView):
             dataimport.save()
 
     def perform_destroy(self, instance):
-        if not self.request.user.is_admin and \
-                Right.Write not in instance.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(instance.corpus):
             raise PermissionDenied
         if instance.state == DataImportState.Running:
             raise ValidationError("Cannot delete a workflow while it is running")
@@ -109,7 +107,7 @@ class DataImportFailures(ListAPIView):
         ).prefetch_related('dataimport__revision__repo', 'element').order_by('path', 'line')
 
 
-class DataImportDemo(CreateAPIView):
+class DataImportDemo(CorpusACLMixin, CreateAPIView):
     """
     Create, configure and start an Images workflow from a single DataFile
     """
@@ -136,7 +134,7 @@ class DataImportDemo(CreateAPIView):
 
         volume = get_object_or_404(Element, **filters)
 
-        assert self.request.user.is_admin or Right.Write in volume.corpus.get_acl_rights(self.request.user), \
+        assert self.has_write_access(volume.corpus), \
             'Corpus is not writable'
 
         # Start the import
@@ -167,7 +165,7 @@ class DataFileList(CorpusACLMixin, ListAPIView):
         return DataFile.objects.filter(corpus=self.get_corpus(self.kwargs['pk']))
 
 
-class DataFileRetrieve(RetrieveUpdateDestroyAPIView):
+class DataFileRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
     """
     Get one file
     """
@@ -178,19 +176,17 @@ class DataFileRetrieve(RetrieveUpdateDestroyAPIView):
         return DataFile.objects.filter(corpus__in=Corpus.objects.readable(self.request.user))
 
     def perform_update(self, serializer):
-        if not self.request.user.is_admin and \
-                Right.Write not in serializer.instance.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(serializer.instance.corpus):
             raise PermissionDenied
         return super().perform_update(serializer)
 
     def perform_destroy(self, instance):
-        if not self.request.user.is_admin and \
-                Right.Write not in instance.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(instance.corpus):
             raise PermissionDenied
         return super().perform_destroy(instance)
 
 
-class DataFileUpload(APIView):
+class DataFileUpload(CorpusACLMixin, APIView):
     """
     Upload a new file to a corpus
     """
@@ -200,15 +196,10 @@ class DataFileUpload(APIView):
     def post(self, request, pk=None, format=None):
         if 'file' not in request.FILES:
             raise ValidationError({'file': ['No file was sent in the request']})
-        corpus_qs = Corpus.objects.filter(id=pk)
-        if not corpus_qs.exists():
+        try:
+            corpus = self.get_corpus(pk, right=Right.Write)
+        except Http404:
             raise ValidationError({'corpus': ['Corpus not found']})
-        corpus = corpus_qs.get()
-
-        # Check corpus is writable for current user
-        if not self.request.user.is_admin and \
-                Right.Write not in corpus.get_acl_rights(self.request.user):
-            raise PermissionDenied
 
         file_obj = request.FILES['file']
 
@@ -293,7 +284,7 @@ class AvailableRepositoriesList(ListCreateAPIView):
         return Response(data={'import_id': str(dataimport.id)}, status=status.HTTP_201_CREATED)
 
 
-class RepositoryRetrieve(RetrieveUpdateDestroyAPIView):
+class RepositoryRetrieve(CorpusACLMixin, RetrieveUpdateDestroyAPIView):
     permission_classes = (IsAuthenticated, )
     serializer_class = RepositorySerializer
 
@@ -304,14 +295,12 @@ class RepositoryRetrieve(RetrieveUpdateDestroyAPIView):
         )
 
     def perform_update(self, serializer):
-        if not self.request.user.is_admin and \
-                Right.Write not in serializer.instance.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(serializer.instance.corpus):
             raise PermissionDenied
         return super().perform_update(self, serializer)
 
     def perform_destroy(self, instance):
-        if not self.request.user.is_admin and \
-                Right.Write not in instance.corpus.get_acl_rights(self.request.user):
+        if not self.has_write_access(instance.corpus):
             raise PermissionDenied
         return super().perform_destroy(self, instance)
 
diff --git a/arkindex/project/mixins.py b/arkindex/project/mixins.py
index 36c82363100dba2abff81252ef7307fe63d8f9ad..6b0232a0a40a74a0c494662330b644f1a8794cab 100644
--- a/arkindex/project/mixins.py
+++ b/arkindex/project/mixins.py
@@ -10,3 +10,17 @@ class CorpusACLMixin(object):
         if right not in corpus.get_acl_rights(self.request.user):
             raise PermissionDenied()
         return corpus
+
+    def has_read_access(self, corpus):
+        assert isinstance(corpus, Corpus)
+        return corpus.public or self.request.user.is_admin or Right.Read in corpus.get_acl_rights(self.request.user)
+
+    def has_write_access(self, corpus):
+        assert isinstance(corpus, Corpus)
+        return self.request.user.is_admin or self.request.user.is_anonymous and \
+            Right.Write in corpus.get_acl_rights(self.request.user)
+
+    def has_admin_access(self, corpus):
+        assert isinstance(corpus, Corpus)
+        return self.request.user.is_admin or self.request.user.is_anonymous and \
+            Right.Admin in corpus.get_acl_rights(self.request.user)