diff --git a/api/permissions.py b/api/permissions.py
index 2153980d9d6bb4202438b406d6bc07aa97dbfbfb..fe1fbd3df590b4a456882411888f5d58dc807933 100644
--- a/api/permissions.py
+++ b/api/permissions.py
@@ -87,3 +87,17 @@ class IsAdminOrReadOnly(permissions.BasePermission):
             request.method in permissions.SAFE_METHODS or
             request.user.is_superuser
         )
+
+
+class IsOwnerOrReadOnly(permissions.BasePermission):
+    """
+    Grant owner and admins write access - all others get read-only.
+    """
+    message = 'You do not have permission to access this resource.'
+
+    def has_permission(self, request, view):
+        return bool(
+            request.method in permissions.SAFE_METHODS or
+            view.get_datasource().owner == request.user or
+            request.user.is_superuser
+        )
diff --git a/api/views/datasources.py b/api/views/datasources.py
index a5d2f9c45c9705cfa57c390889600e4934c802dc..713d1f1e5dcec72b51e1a016de9ac855961e28b1 100644
--- a/api/views/datasources.py
+++ b/api/views/datasources.py
@@ -21,15 +21,20 @@ from provenance import models as prov_models
 
 
 class MetadataItemApiViewset(viewsets.ModelViewSet):
+    """
+    API ViewSet for viewing and managing dynamic metadata items on a data sources.
+    """
     serializer_class = serializers.MetadataItemSerializer
-    permission_classes = [permissions.IsAdminOrReadOnly]
+    permission_classes = [permissions.IsOwnerOrReadOnly]
+
+    def get_datasource(self):
+        return get_object_or_404(models.DataSource, pk=self.kwargs['datasource_pk'])
 
     def get_queryset(self):
         return models.MetadataItem.objects.filter(datasource=self.kwargs['datasource_pk'])
 
     def perform_create(self, serializer):
-        datasource = get_object_or_404(models.DataSource, pk=self.kwargs['datasource_pk'])
-        serializer.save(datasource=datasource)
+        serializer.save(datasource=self.get_datasource())
 
 
 class DataSourceApiViewset(viewsets.ReadOnlyModelViewSet):