Repository: googleapis/google-cloud-python

Issue:
[Storage] make_public() and public_url() don't describe how they work/when they work.
Documentation for `make_public()` and `public_url()` don't describe how they work/when they work.

Specifically `make_public()` uses ACLs to make the object public and `public_url()` only works when the object has necessary permissions granted to all users in the form of `ACL` or a project/bucket has an IAM policy assigning `allUsers` the role `roles/storage.objectViewer`. Users should stay away from project-level IAM policies with allUsers roles/storage.objectViewer` that would not be good. 

(noting this down as I don't want to forget it.)


Discussion:

Resolving patch:
diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py
--- a/storage/google/cloud/storage/blob.py
+++ b/storage/google/cloud/storage/blob.py
@@ -256,7 +256,10 @@ def user_project(self):
 
     @property
     def public_url(self):
-        """The public URL for this blob's object.
+        """The public URL for this blob.
+
+        Use :meth:`make_public` to enable anonymous access via the returned
+        URL.
 
         :rtype: `string`
         :returns: The public URL for this blob.
@@ -1336,7 +1339,7 @@ def test_iam_permissions(self, permissions, client=None):
         return resp.get('permissions', [])
 
     def make_public(self, client=None):
-        """Make this blob public giving all users read access.
+        """Update blob's ACL, granting read access to anonymous users.
 
         :type client: :class:`~google.cloud.storage.client.Client` or
                       ``NoneType``
@@ -1347,8 +1350,7 @@ def make_public(self, client=None):
         self.acl.save(client=client)
 
     def make_private(self, client=None):
-        """Make this blob private by removing `allusers` read access.
-        Does not revoke access for anything other than the `allusers` group.
+        """Update blob's ACL, revoking read access for anonymous users.
 
         :type client: :class:`~google.cloud.storage.client.Client` or
                       ``NoneType``
diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py
--- a/storage/google/cloud/storage/bucket.py
+++ b/storage/google/cloud/storage/bucket.py
@@ -1285,11 +1285,7 @@ def test_iam_permissions(self, permissions, client=None):
         return resp.get('permissions', [])
 
     def make_public(self, recursive=False, future=False, client=None):
-        """Make a bucket public.
-
-        If ``recursive=True`` and the bucket contains more than 256
-        objects / blobs this will cowardly refuse to make the objects public.
-        This is to prevent extremely long runtime of this method.
+        """Update bucket's ACL, granting read access to anonymous users.
 
         :type recursive: bool
         :param recursive: If True, this will make all blobs inside the bucket
@@ -1303,6 +1299,14 @@ def make_public(self, recursive=False, future=False, client=None):
                       ``NoneType``
         :param client: Optional. The client to use.  If not passed, falls back
                        to the ``client`` stored on the current bucket.
+
+        :raises ValueError:
+            If ``recursive`` is True, and the bucket contains more than 256
+            blobs.  This is to prevent extremely long runtime of this
+            method.  For such buckets, iterate over the blobs returned by
+            :meth:`list_blobs` and call
+            :meth:`~google.cloud.storage.blob.Blob.make_public`
+            for each blob.
         """
         self.acl.all().grant_read()
         self.acl.save(client=client)
@@ -1321,10 +1325,11 @@ def make_public(self, recursive=False, future=False, client=None):
                 client=client))
             if len(blobs) > self._MAX_OBJECTS_FOR_ITERATION:
                 message = (
-                    'Refusing to make public recursively with more than '
-                    '%d objects. If you actually want to make every object '
-                    'in this bucket public, please do it on the objects '
-                    'yourself.'
+                    "Refusing to make public recursively with more than "
+                    "%d objects. If you actually want to make every object "
+                    "in this bucket public, iterate through the blobs "
+                    "returned by 'Bucket.list_blobs()' and call "
+                    "'make_public' on each one."
                 ) % (self._MAX_OBJECTS_FOR_ITERATION,)
                 raise ValueError(message)
 
@@ -1333,11 +1338,7 @@ def make_public(self, recursive=False, future=False, client=None):
                 blob.acl.save(client=client)
 
     def make_private(self, recursive=False, future=False, client=None):
-        """Undo the `make_public` method and make the bucket private.
-
-        If ``recursive=True`` and the bucket contains more than 256
-        objects / blobs this will cowardly refuse to make the objects private.
-        This is to prevent extremely long runtime of this method.
+        """Update bucket's ACL, revoking read access for anonymous users.
 
         :type recursive: bool
         :param recursive: If True, this will make all blobs inside the bucket
@@ -1351,6 +1352,14 @@ def make_private(self, recursive=False, future=False, client=None):
                       ``NoneType``
         :param client: Optional. The client to use.  If not passed, falls back
                        to the ``client`` stored on the current bucket.
+
+        :raises ValueError:
+            If ``recursive`` is True, and the bucket contains more than 256
+            blobs.  This is to prevent extremely long runtime of this
+            method.  For such buckets, iterate over the blobs returned by
+            :meth:`list_blobs` and call
+            :meth:`~google.cloud.storage.blob.Blob.make_private`
+            for each blob.
         """
         self.acl.all().revoke_read()
         self.acl.save(client=client)
@@ -1371,8 +1380,9 @@ def make_private(self, recursive=False, future=False, client=None):
                 message = (
                     'Refusing to make private recursively with more than '
                     '%d objects. If you actually want to make every object '
-                    'in this bucket private, please do it on the objects '
-                    'yourself.'
+                    "in this bucket private, iterate through the blobs "
+                    "returned by 'Bucket.list_blobs()' and call "
+                    "'make_private' on each one."
                 ) % (self._MAX_OBJECTS_FOR_ITERATION,)
                 raise ValueError(message)
 


Test patch: