Repository: TACC/agavepy

Issue:
Implement files_pems_delete method
**Is your feature request related to a problem? Please describe.**
Part of the changes proposed for release v3.0.0 of `TACC-Cloud/agave-cli`

**Describe the solution you'd like**
Implement a method to remove user permissions from a file or folder. For all but iRODS systems, these permissions are set at the API level and do not reflect *nix or other file system ACL. On iRODS systems, if the system.storageConfig.mirror attribute is set to true then any permissions set via the API will be mirrored onto the remote system.

Discussion:

Patch:
diff --git a/agavepy/agave.py b/agavepy/agave.py
index 4216948..0cb8010 100644
--- a/agavepy/agave.py
+++ b/agavepy/agave.py
@@ -25,7 +25,8 @@ from agavepy.clients import (clients_create, clients_delete, clients_list,
 from agavepy.tokens import token_create, refresh_token
 from agavepy.utils import load_config, save_config
 from agavepy.files import (files_copy, files_delete, files_download, 
-    files_list, files_mkdir, files_move, files_pems_list, files_upload)
+    files_list, files_mkdir, files_move, files_pems_delete, files_pems_list, 
+    files_upload)
 
 
 import sys
@@ -845,6 +846,20 @@ class Agave(object):
         files_move(self.api_server, self.token, source, destination)
 
 
+    def files_pems_delete(self, path):
+        """ Remove user permissions associated with a file or folder.
+
+        These permissions are set at the API level and do not reflect *nix or 
+        other file system ACL.
+        Deletes all permissions on a file except those of the owner.
+        """
+        # Check if tokens need to be refreshed.
+        self.refresh_tokens()
+
+        # Delete api permissions.
+        files_pems_delete(self.api_server, self.token, path)
+
+
     def files_pems_list(self, path):
         """ List the user permissions associated with a file or folder
 
diff --git a/agavepy/files/__init__.py b/agavepy/files/__init__.py
index 426e785..b6b95cb 100644
--- a/agavepy/files/__init__.py
+++ b/agavepy/files/__init__.py
@@ -4,5 +4,6 @@ from .download import files_download
 from .list import files_list
 from .mkdir import files_mkdir
 from .move import files_move
+from .pems_delete import files_pems_delete
 from .pems_list import files_pems_list
 from .upload import files_upload
diff --git a/agavepy/files/pems_delete.py b/agavepy/files/pems_delete.py
new file mode 100644
index 0000000..74ea6dc
--- /dev/null
+++ b/agavepy/files/pems_delete.py
@@ -0,0 +1,32 @@
+"""
+    pems_list.py
+"""
+import requests
+from .exceptions import AgaveFilesError
+from ..utils import handle_bad_response_status_code
+
+
+def files_pems_delete(tenant_url, access_token, path):
+    """ Remove user permissions associated with a file or folder. 
+    
+    These permissions are set at the API level and do not reflect *nix or other
+    file system ACL.
+    Deletes all permissions on a file except those of the owner.
+    """
+    # Set request url.
+    endpoint = "{0}/{1}/{2}".format(tenant_url, "files/v2/pems/system", path)
+
+    # Obtain file path. "path" should include the system name at the begining,
+    # so we get rid of it.
+    destination = '/'.join( path.split('/')[1:] )
+
+    # Make request.
+    try:
+        headers  = {"Authorization":"Bearer {0}".format(access_token)}
+        params   = {"pretty": "true"}
+        resp = requests.delete(endpoint, headers=headers, params=params)
+    except Exception as err:
+        raise AgaveFilesError(err)
+
+    # Handle bad status code.
+    handle_bad_response_status_code(resp)
diff --git a/docs/docsite/files/admin.rst b/docs/docsite/files/admin.rst
index b90c28c..ed7bde0 100644
--- a/docs/docsite/files/admin.rst
+++ b/docs/docsite/files/admin.rst
@@ -13,3 +13,9 @@ or other file system ACL.
     >>> ag.files_pems_list("system-id/some-dir")
     USER     READ   WRITE  EXEC
     username yes    yes    yes
+
+To remove all the permissions on a file, except those of the owner:
+
+.. code-block:: pycon
+
+    agave.files_pems_delete("system-id/some-dir")


Test patch:
diff --git a/tests/files-pems_test.py b/tests/files-pems_test.py
index 41d6abe..068a513 100644
--- a/tests/files-pems_test.py
+++ b/tests/files-pems_test.py
@@ -24,10 +24,28 @@ class MockServerListingsEndpoints(BaseHTTPRequestHandler):
     """ Mock the Agave API
     """
     def do_GET(self):
+        # Check that basic auth is used.
+        authorization = self.headers.get("Authorization")
+        if authorization == "" or authorization is None:
+            self.send_response(400)
+            self.end_headers()
+            return
+
         self.send_response(200)
         self.end_headers()
         self.wfile.write(json.dumps(sample_files_pems_list_response).encode())
 
+    def do_DELETE(self):
+        # Check that basic auth is used.                                        
+        authorization = self.headers.get("Authorization")                       
+        if authorization == "" or authorization is None:                        
+            self.send_response(400)                                             
+            self.end_headers()                                                  
+            return
+
+        self.send_response(200)
+        self.end_headers()
+
 
 
 class TestMockServer(MockServer):
@@ -44,7 +62,7 @@ class TestMockServer(MockServer):
 
 
     def test_files_pems_list(self, capfd):
-        """ Test files listings
+        """ Test permissions listings
         """
         local_uri = "http://localhost:{port}/".format(port=self.mock_server_port)
         agave = Agave(api_server=local_uri)
@@ -52,7 +70,7 @@ class TestMockServer(MockServer):
         agave.created_at = str(int(time.time()))
         agave.expires_in = str(14400)
 
-        # List files.
+        # List permissions.
         agave.files_pems_list("tacc-globalfs-username/")
 
         # Stdout should contain the putput from the command.
@@ -61,3 +79,16 @@ class TestMockServer(MockServer):
         assert "username" in out
         assert "yes" in out
         assert "200" in err 
+
+
+    def test_files_pems_list(self):
+        """ Test permissions deletion
+        """
+        local_uri = "http://localhost:{port}/".format(port=self.mock_server_port)
+        agave = Agave(api_server=local_uri)
+        agave.token = "mock-access-token"
+        agave.created_at = str(int(time.time()))
+        agave.expires_in = str(14400)
+
+        # Delete permissions.
+        agave.files_pems_delete("tacc-globalfs-username/")