Repository: hydroshare/hydroshare

Issue:
Resource without content files should not be made public
It is possible to create resources without content that are public.  Ways to do this seem to be

As reported by @horsburgh . I used the EnviroDIY website to create this resource:
https://www.hydroshare.org/resource/6e97cc54c6ce448ab32367388a12b793/
Something went wrong in copying the data file over, so there are no content files in this resource. However, the resource is public! 

Also I created a new composite resource, and gave it a title, abstract and keywords, without adding any files.  I get the message that it can be made public.  I can click the public button, and even though I get a warning about it needing file content, it still gets made public.

I was able to do the same with a generic resource.


Discussion:
According to Craig (@darksinge), he found the error in EnviroDIY that prevented the data file from being copied into the HydroShare resource, but the ability to create a public resource with no content is something that needs to be fixed on the HydroShare side.

Resolving patch:
diff --git a/hs_core/models.py b/hs_core/models.py
--- a/hs_core/models.py
+++ b/hs_core/models.py
@@ -1647,7 +1647,7 @@ def can_be_public_or_discoverable(self):
 
         and False otherwise
         """
-        has_files = self.has_required_content_files
+        has_files = self.has_required_content_files()
         has_metadata = self.has_required_metadata
         return has_files and has_metadata
 
@@ -1678,7 +1678,7 @@ def set_discoverable(self, value, user=None):
 
         # check that there is sufficient resource content
         has_metadata = self.has_required_metadata
-        has_files = self.has_required_content_files
+        has_files = self.has_required_content_files()
         if value and not (has_metadata and has_files):
 
             if not has_metadata and not has_files:
@@ -1730,7 +1730,7 @@ def set_public(self, value, user=None):
 
         # check that there is sufficient resource content
         has_metadata = self.has_required_metadata
-        has_files = self.has_required_content_files
+        has_files = self.has_required_content_files()
         if value and not (has_metadata and has_files):
 
             if not has_metadata and not has_files:
diff --git a/hs_core/views/resource_rest_api.py b/hs_core/views/resource_rest_api.py
--- a/hs_core/views/resource_rest_api.py
+++ b/hs_core/views/resource_rest_api.py
@@ -8,6 +8,7 @@
 
 from django.core.urlresolvers import reverse
 from django.core.exceptions import ObjectDoesNotExist, SuspiciousFileOperation
+from django.core.exceptions import ValidationError as CoreValidationError
 from django.http import HttpResponseRedirect
 from django.shortcuts import redirect
 from django.contrib.sites.models import Site
@@ -639,7 +640,7 @@ def put(self, request, pk):
         res = get_resource_by_shortkey(pk)
         try:
             res.set_public(validated_request_data['public'], request.user)
-        except ValidationError:
+        except CoreValidationError:
             return Response(data={'resource_id': pk}, status=status.HTTP_403_FORBIDDEN)
 
         return Response(data={'resource_id': pk}, status=status.HTTP_200_OK)


Test patch:
diff --git a/hs_core/tests/api/rest/test_set_access_rules.py b/hs_core/tests/api/rest/test_set_access_rules.py
--- a/hs_core/tests/api/rest/test_set_access_rules.py
+++ b/hs_core/tests/api/rest/test_set_access_rules.py
@@ -50,12 +50,13 @@ def test_DEPRECATED_set_access_rules(self):
 
         access_url = "/hsapi/resource/accessRules/{res_id}/".format(res_id=res_id)
         response = self.client.put(access_url, {'public': True})
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
+        # this test resource does not have content file, so it cannot be made public
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
 
         response = self.client.get(sysmeta_url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         content = json.loads(response.content)
-        self.assertTrue(content['public'])
+        self.assertFalse(content['public'])
 
     def test_get_access_rules_via_sysmeta(self):
         rtype = 'GenericResource'