Repository: hydroshare/hydroshare (language: python)

Issue:
Remove userzone and remove all federation config
Resolves https://github.com/hydroshare/hydroshare/issues/5519 (by removing the userzone and all federation)
Resolves https://github.com/hydroshare/hydroshare/issues/5512 (by adding a GH action for PR builds) -- rebased thanks to @sblack-usu 
Resolves https://github.com/hydroshare/hydroshare/issues/5104 (because no more UserZone accounts)
Resolves https://github.com/hydroshare/hydroshare/issues/5061 (because messaging is updated)
Resolves https://github.com/hydroshare/hydroshare/issues/3915 (because no more copying bags to userzone)
Resolves https://github.com/hydroshare/hydroshare/issues/1930 (because we are moving away from irods federation)
Resolves https://github.com/hydroshare/hydroshare/issues/2585 (because no more irods passwords)

<!--

Please read, and add your text at the bottom of this message.

Thanks for contributing code to HydroShare. In order to maintain code quality and expedite this process, please assist the development team by making sure the following is present in this pull request.

For more information, see https://docs.google.com/document/d/1dzxqlZW5fKNEyQSeKiSFq-SmS-VOPCva95XXkBjPExs

-->

### Pull Request Checklist: 
- [ ] Positive Test Case Written by Dev

<!-- Enter steps that a QA engineer, stakeholder, or user documentation writer would follow to test the positive or "successful" case of the functionality your code provides or fixes -->

- [ ] Automated Testing

<!-- Our Jenkins Instance is set up to automatically test every commit from a pull request. Code coverage must not decrease so new functionality or code paths added during a bug fix must have appropriate tests written. Every test must pass, including PEP8 code formatting tests. -->

- [ ] Sufficient User and Developer Documentation

<!-- Please email your positive test case lbrazil@cuahsi.org, who will make the decision regarding user documentation. -->

- [ ] Passing Jenkins Build

<!-- Our Jenkins Instance is set up to automatically test every commit from a pull request. Every test must pass, including PEP8 code formatting tests. -->

- [ ] Peer Code review and approval

<!-- This is the process by which a peer developer on the HydroShare team will read the changeset, provide feedback, and ultimately give a formal approval to the code before it passes PR status. -->

### Positive Test Case
1. [Enter positive test case here]



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/hydroshare-pull-requests.yml b/.github/workflows/hydroshare-pull-requests.yml
new file mode 100644
index 0000000000..634ef2a5de
--- /dev/null
+++ b/.github/workflows/hydroshare-pull-requests.yml
@@ -0,0 +1,60 @@
+name: Test HydroShare Pull Requests
+
+on:
+  push:
+    branches: [ develop ]
+  pull_request:
+    branches: 
+      - '**'
+  workflow_dispatch:
+
+jobs:
+  hydroshare-pull-request-tests:
+
+    runs-on: ubuntu-latest
+    #env:
+    #  COVERAGE_TOTAL: 10
+
+    steps:
+    - uses: actions/checkout@v4
+
+    - name: Run flake8
+      run: |
+        python -m pip install flake8
+        ./run-pylint-jenkins
+
+    - name: Install Docker Compose
+      run: |
+        sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+        sudo chmod +x /usr/local/bin/docker-compose
+
+    - name: Setup HydroShare
+      run: |
+        printf "%s\n" c | ./local-dev-first-start-only.sh
+
+    - name: Start HydroShare
+      run: |
+        docker-compose -f local-dev.yml up -d
+        sleep 60
+
+    - name: Run HydroShare Tests
+      run: |
+        docker exec hydroshare ./run-tests-jenkins
+    
+    - name: Publish Test Results
+      uses: EnricoMi/publish-unit-test-result-action/linux@v2
+      if: always()
+      with:
+        files: |
+          nosetests.xml
+          pytests.xml
+    
+    # archive the test results
+    - name: Archive Test Results
+      uses: actions/upload-artifact@v4
+      with:
+        name: test-results
+        path: |
+          nosetests.xml
+          pytests.xml
+          coverage.xml
\ No newline at end of file
diff --git a/Dockerfile-data-irods b/Dockerfile-data-irods
index 33695792ba..9d3dacb64d 100644
--- a/Dockerfile-data-irods
+++ b/Dockerfile-data-irods
@@ -3,7 +3,7 @@ FROM mjstealey/irods-provider-postgres:4.2.4
 RUN useradd -m -p hsuserproxy -s /bin/bash hsuserproxy
 RUN useradd -m -p rods -s /bin/bash rods
 RUN mkdir -p /home/hsuserproxy/.irods
-COPY ./conf_irods/users.local.org/irods_environment.json /root/.irods/irods_environment.json
+
 COPY ./irods/delete_user.sh /home/hsuserproxy/delete_user.sh
 COPY ./irods/create_user.sh /home/hsuserproxy/create_user.sh
 
diff --git a/config/hydroshare-config.template b/config/hydroshare-config.template
index 78aae7bec2..98b8995ce5 100644
--- a/config/hydroshare-config.template
+++ b/config/hydroshare-config.template
@@ -17,9 +17,4 @@ SSL_CERT_DIR: /home/hydro/hydroshare/nginx/cert-files
 SSL_CERT_FILE: hydrodev-vb.example.org.cert
 SSL_KEY_FILE: hydrodev-vb.example.org.key
 
-### Irods Userzone Keys
-HOST_PK_FOR_HS_USER_ZONE: /opt/.ssh/id_ed25519_hs
-# Following should match the value set in local_settings.py
-PRIVATE_KEY_FILE_FOR_HS_USER_ZONE: /hydroshare/.ssh/id_ed25519_hs
-
 ### REQUIRES BLANK LINE AFTER LAST VARIABLE DEFINITION ###
diff --git a/django_irods/storage.py b/django_irods/storage.py
index cc835cb4d5..b80394c76f 100644
--- a/django_irods/storage.py
+++ b/django_irods/storage.py
@@ -28,13 +28,9 @@
 @deconstructible
 class IrodsStorage(Storage):
     def __init__(self, option=None):
-        if option == "federated":
-            # resource should be saved in federated zone
-            self.set_fed_zone_session()
-        else:
-            self.session = GLOBAL_SESSION
-            self.environment = GLOBAL_ENVIRONMENT
-            icommands.ACTIVE_SESSION = self.session
+        self.session = GLOBAL_SESSION
+        self.environment = GLOBAL_ENVIRONMENT
+        icommands.ACTIVE_SESSION = self.session
 
     @property
     def getUniqueTmpPath(self):
@@ -100,20 +96,6 @@ def set_user_session(
         self.session.run("iinit", None, self.environment.auth)
         icommands.ACTIVE_SESSION = self.session
 
-    # Set iRODS session to wwwHydroProxy for irods_storage input object for iRODS federated
-    # zone direct file operations
-    def set_fed_zone_session(self):
-        if settings.REMOTE_USE_IRODS:
-            self.set_user_session(
-                username=settings.IRODS_USERNAME,
-                password=settings.IRODS_AUTH,
-                host=settings.IRODS_HOST,
-                port=settings.IRODS_PORT,
-                def_res=settings.HS_IRODS_USER_ZONE_DEF_RES,
-                zone=settings.IRODS_ZONE,
-                sess_id="federated_session",
-            )
-
     def delete_user_session(self):
         if self.session != GLOBAL_SESSION and self.session.session_file_exists():
             self.session.delete_environment()
diff --git a/hs_core/hydroshare/hs_bagit.py b/hs_core/hydroshare/hs_bagit.py
index 02ee5191cc..98ed77132b 100755
--- a/hs_core/hydroshare/hs_bagit.py
+++ b/hs_core/hydroshare/hs_bagit.py
@@ -79,17 +79,11 @@ def create_bagit_files_by_irods(res, istorage):
     # check to see if bagit readme.txt file exists or not
     bagit_readme_file = res.get_irods_path('readme.txt')
     is_bagit_readme_exist = istorage.exists(bagit_readme_file)
-    if irods_bagit_input_path.startswith(resource_id):
-        # resource is in data zone, need to append the full path for iRODS bagit rule execution
-        irods_dest_prefix = "/" + settings.IRODS_ZONE + "/home/" + settings.IRODS_USERNAME
-        irods_bagit_input_path = os.path.join(irods_dest_prefix, resource_id)
-        bagit_input_resource = "*DESTRESC='{def_res}'".format(
-            def_res=settings.IRODS_DEFAULT_RESOURCE)
-    else:
-        # this will need to be changed with the default resource in whatever federated zone the
-        # resource is stored in when we have such use cases to support
-        bagit_input_resource = "*DESTRESC='{def_res}'".format(
-            def_res=settings.HS_IRODS_USER_ZONE_DEF_RES)
+    # resource is in data zone, need to append the full path for iRODS bagit rule execution
+    irods_dest_prefix = "/" + settings.IRODS_ZONE + "/home/" + settings.IRODS_USERNAME
+    irods_bagit_input_path = os.path.join(irods_dest_prefix, resource_id)
+    bagit_input_resource = "*DESTRESC='{def_res}'".format(
+        def_res=settings.IRODS_DEFAULT_RESOURCE)
 
     bagit_input_path = "*BAGITDATA='{path}'".format(path=irods_bagit_input_path)
 
diff --git a/hs_core/hydroshare/hs_requests.py b/hs_core/hydroshare/hs_requests.py
index fabd590810..c752e6431e 100644
--- a/hs_core/hydroshare/hs_requests.py
+++ b/hs_core/hydroshare/hs_requests.py
@@ -2,7 +2,7 @@
 
 """
 This bypasses firewalls to allow Nginx to be called from within HydroShare
-via its docker container address. Otherwise, Nginx cannot be reliably queried
+via its docker container address. Otherwise, HS cannot be reliably queried
 from within a docker container because it is advertised in DNS as an address
 on the other side of any firewall.
 
@@ -14,17 +14,14 @@
 from django.conf import settings
 
 
-def get_nginx_ip():
+def get_local_ip():
     """
-    Read the Nginx IP address from a local file, where it is stored during
-    every nginx restart. This address is dynamic and changes on every restart.
+    Returns the local IP address.
+
+    Returns:
+        str: The local IP address as a string.
     """
-    ip = None
-    with open('tmp/nginx_ip', 'r') as fd:
-        for line in fd:
-            ip = line
-            break
-    return ip.strip()
+    return '127.0.0.1'
 
 
 def localize_url(url):
@@ -40,7 +37,7 @@ def localize_url(url):
     if parsed[1] == getattr(settings, 'PROD_FQDN_OR_IP', 'www.hydroshare.org') or \
        parsed[1] == getattr(settings, 'FQDN_OR_IP', 'www.hydroshare.org'):
         # insert local IP instead of fake target
-        parsed2 = urllib.parse.ParseResult(parsed[0], get_nginx_ip(), parsed[2],
+        parsed2 = urllib.parse.ParseResult(parsed[0], get_local_ip(), parsed[2],
                                            parsed[3], parsed[4], parsed[5])
         return parsed2.geturl()
     else:
diff --git a/hs_core/hydroshare/resource.py b/hs_core/hydroshare/resource.py
index 558285910f..e4e273523d 100755
--- a/hs_core/hydroshare/resource.py
+++ b/hs_core/hydroshare/resource.py
@@ -23,8 +23,6 @@
 from hs_access_control.models import ResourceAccess, UserResourcePrivilege, PrivilegeCodes
 from hs_labels.models import ResourceLabels
 from theme.models import UserQuota
-from django_irods.icommands import SessionException
-from django_irods.storage import IrodsStorage
 from hs_core.enums import CrossRefSubmissionStatus
 
 FILE_SIZE_LIMIT = 1 * (1024 ** 3)
@@ -37,22 +35,13 @@
 
 def get_quota_usage(username, raise_on_error=True):
     """
-    Query iRODS AVU to get quota usage for a user reported in iRODS quota microservices
+    Query to get quota usage
     :param username: the user name to get quota usage for.
     :param raise_on_error: if True, raise ValidationError if quota usage cannot be retrieved from iRODS
-    :return: the quota usage from iRODS data zone and user zone; raise ValidationError
-    if quota usage cannot be retrieved from iRODS
+    :return: the quota usage from iRODS data zone; raise ValidationError if quota usage cannot be retrieved
     """
     uqDataZoneSize = get_data_zone_usage(username, raise_on_error=raise_on_error)
-
-    uqUserZoneSize = get_user_zone_usage(username)
-    if uqUserZoneSize < 0:
-        uqUserZoneSize = 0
-        err_msg = f'no quota size AVU for user {username} in userzone'
-        logger.error(err_msg)
-        if raise_on_error:
-            raise ValidationError(err_msg)
-    return uqUserZoneSize, uqDataZoneSize
+    return uqDataZoneSize
 
 
 def get_data_zone_usage(username, raise_on_error=True, include_published=False):
@@ -87,48 +76,9 @@ def get_data_zone_usage(username, raise_on_error=True, include_published=False):
     return uqDataZoneSize if uqDataZoneSize is not None else 0
 
 
-def get_user_zone_usage(username):
-    """
-    Get the quota size for a user in the iRODS user zone.
-
-    Args:
-        username (str): The username of the user.
-
-    Returns:
-        float: The quota size for the user in the iRODS user zone. If the user does not have resources
-        in the user zone, the function returns -1.
-
-    Raises:
-        SessionException: If there is an error retrieving the quota size.
-
-    """
-    attname = username + '-usage'
-    istorage = IrodsStorage()
-    try:
-        uz_bagit_path = os.path.join('/', settings.HS_USER_IRODS_ZONE, 'home',
-                                     settings.HS_IRODS_PROXY_USER_IN_USER_ZONE,
-                                     settings.IRODS_BAGIT_PATH)
-        uqUserZoneSize = istorage.getAVU(uz_bagit_path, attname)
-        if uqUserZoneSize is None:
-            # user may not have resources in user zone, so corresponding quota size AVU may not
-            # exist for this user
-            uqUserZoneSize = -1
-        else:
-            uqUserZoneSize = float(uqUserZoneSize)
-    except SessionException:
-        # user may not have resources in user zone, so corresponding quota size AVU may not exist
-        # for this user
-        uqUserZoneSize = -1
-    return uqUserZoneSize
-
-
 def update_quota_usage(username, notify_user=False):
     """
-    update quota usage by checking iRODS AVU to get the updated quota usage for the user. Note iRODS micro-service
-    quota update only happens on HydroShare iRODS data zone and user zone independently, so the aggregation of usage
-    in both zones need to be accounted for in this function to update Django DB as an aggregated usage for hydroshare
-    internal zone.
-    This function is called by the IRODS quota micro-service to update quota usage for a user in Django DB.
+    This function is called to update quota usage for a user in Django DB.
     :param
     username: the name of the user that needs to update quota usage for.
     : param notify_user: if True, send email notification to user if the quota is exceeded.
@@ -143,12 +93,9 @@ def update_quota_usage(username, notify_user=False):
         logger.error(err_msg)
         raise ValidationError(err_msg)
 
-    uz = get_user_zone_usage(username)
-
     original_quota_data = uq.get_quota_data()
     qmsg = original_quota_data["qmsg"]
     user = User.objects.get(username=username)
-    uq.set_userzone_used_value(uz)
     uq.save()
 
     if original_quota_data["enforce_quota"]:
@@ -173,7 +120,6 @@ def update_quota_usage(username, notify_user=False):
             # this avoids sending multiple notifications when files are changed but the status does not change
             if notify_user and (original_quota_data["status"] != updated_quota_data["status"]):
                 tasks.send_user_quota_notification.apply_async((user.pk))
-        uq.check_if_userzone_quota_enforcement_is_bypassed(original_quota_data, updated_quota_data)
 
 
 def res_has_web_reference(res):
@@ -294,12 +240,6 @@ def update_resource_file(pk, filename, f):
                 # TODO: should use add_file_to_resource
                 rf.resource_file = File(f) if not isinstance(f, UploadedFile) else f
                 rf.save()
-            if rf.fed_resource_file:
-                # TODO: should use delete_resource_file
-                rf.fed_resource_file.delete()
-                # TODO: should use add_file_to_resource
-                rf.fed_resource_file = File(f) if not isinstance(f, UploadedFile) else f
-                rf.save()
             return rf
     raise ObjectDoesNotExist(filename)
 
diff --git a/hs_core/hydroshare/utils.py b/hs_core/hydroshare/utils.py
index 88754287d8..3dfbd25c07 100644
--- a/hs_core/hydroshare/utils.py
+++ b/hs_core/hydroshare/utils.py
@@ -177,77 +177,6 @@ def community_from_id(community):
     return tgt
 
 
-def get_user_zone_status_info(user):
-    """
-    This function should be called to determine whether user zone functionality should be
-    enabled or not on the web site front end
-    Args:
-        user: the requesting user
-    Returns:
-        enable_user_zone boolean indicating whether user zone functionality should be enabled or
-        not on the web site front end
-    """
-    if user is None:
-        return None
-    if not hasattr(user, 'userprofile') or user.userprofile is None:
-        return None
-
-    enable_user_zone = user.userprofile.create_irods_user_account and settings.REMOTE_USE_IRODS
-    return enable_user_zone
-
-
-def is_federated(homepath):
-    """
-    Check if the selected file via the iRODS browser is from a federated zone or not
-    Args:
-        homepath: the logical iRODS file name with full logical path, e.g., selected from
-                  iRODS browser
-
-    Returns:
-    True is the selected file indicated by homepath is from a federated zone, False if otherwise
-    """
-    homepath = homepath.strip()
-    homepath_list = homepath.split('/')
-    # homepath is an iRODS logical path in the format of
-    # /irods_zone/home/irods_account_username/collection_relative_path, so homepath_list[1]
-    # is the irods_zone which we can use to form the fed_proxy_path to check whether
-    # fed_proxy_path exists to hold hydroshare resources in a federated zone
-    if homepath_list[1]:
-        fed_proxy_path = os.path.join(homepath_list[1], 'home',
-                                      settings.HS_IRODS_PROXY_USER_IN_USER_ZONE)
-        fed_proxy_path = '/' + fed_proxy_path
-    else:
-        # the test path input is invalid, return False meaning it is not federated
-        return False
-    if settings.REMOTE_USE_IRODS:
-        irods_storage = IrodsStorage('federated')
-    else:
-        irods_storage = IrodsStorage()
-
-    # if the iRODS proxy user in hydroshare zone can list homepath and the federation zone proxy
-    # user path, it is federated; otherwise, it is not federated
-    return irods_storage.exists(homepath) and irods_storage.exists(fed_proxy_path)
-
-
-def get_federated_zone_home_path(filepath):
-    """
-    Args:
-        filepath: the iRODS data object file path that included zone name in the format of
-        /zone_name/home/user_name/file_path
-
-    Returns:
-        the zone name extracted from filepath
-    """
-    if filepath and filepath.startswith('/'):
-        split_path_strs = filepath.split('/')
-        # the Zone name should follow the first slash
-        zone = split_path_strs[1]
-        return '/{zone}/home/{local_proxy_user}'.format(
-            zone=zone, local_proxy_user=settings.HS_IRODS_PROXY_USER_IN_USER_ZONE)
-    else:
-        return ''
-
-
 # TODO: replace with a cache facility that has automatic cleanup
 # TODO: pass a list rather than a string to allow commas in filenames.
 def get_fed_zone_files(irods_fnames):
@@ -444,12 +373,7 @@ def copy_file_to_target_resource(scr_file, save_to_db=True):
         tgt_storage_path = src_storage_path.replace(src_res.short_id, tgt_res.short_id)
         kwargs['content_object'] = tgt_res
         kwargs['file_folder'] = scr_file.file_folder
-        if tgt_res.is_federated:
-            kwargs['resource_file'] = None
-            kwargs['fed_resource_file'] = tgt_storage_path
-        else:
-            kwargs['resource_file'] = tgt_storage_path
-            kwargs['fed_resource_file'] = None
+        kwargs['resource_file'] = tgt_storage_path
 
         if save_to_db:
             return ResourceFile.objects.create(**kwargs)
diff --git a/hs_core/irods.py b/hs_core/irods.py
index edb51e9240..1aeb5434f4 100644
--- a/hs_core/irods.py
+++ b/hs_core/irods.py
@@ -177,22 +177,16 @@ def list_ticket(self, ticket_id):
                     value = line[1]
                     if key == 'collection name' or key == 'data collection':
                         output['full_path'] = value
-                        if self.is_federated:
-                            if __debug__:
-                                assert (value.startswith(self.resource_federation_path))
-                            output['long_path'] = value[len(self.resource_federation_path):]
-                            output['home_path'] = self.resource_federation_path
+                        location = value.find(self.short_id)
+                        if __debug__:
+                            assert (location >= 0)
+                        if location == 0:
+                            output['long_path'] = value
+                            output['home_path'] = self.irods_home_path
                         else:
-                            location = value.find(self.short_id)
-                            if __debug__:
-                                assert (location >= 0)
-                            if location == 0:
-                                output['long_path'] = value
-                                output['home_path'] = self.irods_home_path
-                            else:
-                                output['long_path'] = value[location:]
-                                # omit trailing slash
-                                output['home_path'] = value[:(location - 1)]
+                            output['long_path'] = value[location:]
+                            # omit trailing slash
+                            output['home_path'] = value[:(location - 1)]
 
                         if __debug__:
                             assert (output['long_path'].startswith(self.short_id))
diff --git a/hs_core/management/commands/check_django_metadata.py b/hs_core/management/commands/check_django_metadata.py
index e9dff1d795..b430eb2201 100644
--- a/hs_core/management/commands/check_django_metadata.py
+++ b/hs_core/management/commands/check_django_metadata.py
@@ -14,7 +14,6 @@
 * Optional argument --log instead logs output to system log.
 """
 
-from django.conf import settings
 from django.core.management.base import BaseCommand
 from hs_core.models import BaseResource
 
@@ -32,47 +31,37 @@ def check_django_metadata(self, stop_on_error=False,
     errors = []
     ecount = 0
 
-    # skip federated resources if not configured to handle these
-    if self.is_federated and not settings.REMOTE_USE_IRODS:
-        msg = "check_django_metadata: skipping check of federated resource {} in unfederated mode"\
-            .format(self.short_id)
+    # flag non-existent resources in iRODS
+    if not istorage.exists(self.root_path):
+        msg = "root path {} does not exist in iRODS".format(self.root_path)
+        ecount += 1
+        if echo_errors:
+            print(msg)
+        if log_errors:
+            logger.error(msg)
+        if return_errors:
+            errors.append(msg)
+
+    # basic check: metadata exists
+    if self.metadata is None:
+        msg = "metadata for {} does not exist".format(self.short_id)
+        ecount += 1
         if echo_errors:
             print(msg)
         if log_errors:
-            logger.info(msg)
+            logger.error(msg)
+        if return_errors:
+            errors.append(msg)
 
-    # flag non-existent resources in iRODS
-    else:
-        if not istorage.exists(self.root_path):
-            msg = "root path {} does not exist in iRODS".format(self.root_path)
-            ecount += 1
-            if echo_errors:
-                print(msg)
-            if log_errors:
-                logger.error(msg)
-            if return_errors:
-                errors.append(msg)
-
-        # basic check: metadata exists
-        if self.metadata is None:
-            msg = "metadata for {} does not exist".format(self.short_id)
-            ecount += 1
-            if echo_errors:
-                print(msg)
-            if log_errors:
-                logger.error(msg)
-            if return_errors:
-                errors.append(msg)
-
-        elif self.metadata.title is None:
-            msg = "{} has no title".format(self.short_id)
-            ecount += 1
-            if echo_errors:
-                print(msg)
-            if log_errors:
-                logger.error(msg)
-            if return_errors:
-                errors.append(msg)
+    elif self.metadata.title is None:
+        msg = "{} has no title".format(self.short_id)
+        ecount += 1
+        if echo_errors:
+            print(msg)
+        if log_errors:
+            logger.error(msg)
+        if return_errors:
+            errors.append(msg)
 
     return errors, ecount
 
diff --git a/hs_core/management/commands/fix_reftimeseries_json_data.py b/hs_core/management/commands/fix_reftimeseries_json_data.py
index ff3969f26d..1ecc0a4be6 100644
--- a/hs_core/management/commands/fix_reftimeseries_json_data.py
+++ b/hs_core/management/commands/fix_reftimeseries_json_data.py
@@ -13,10 +13,7 @@ def handle(self, *args, **options):
         for aggr in RefTimeseriesLogicalFile.objects.all().iterator():
             res_json_file = aggr.files.all().first()
             if res_json_file:
-                if res_json_file.resource_file:
-                    json_file_content = res_json_file.resource_file.read()
-                else:
-                    json_file_content = res_json_file.fed_resource_file.read()
+                json_file_content = res_json_file.resource_file.read()
                 aggr.metadata.json_file_content = json_file_content.decode()
                 aggr.metadata.save()
                 msg = "Saved json file data to aggregation metadata field for aggregation:"
diff --git a/hs_core/management/commands/migrate_resources_from_userzone_to_datazone.py b/hs_core/management/commands/migrate_resources_from_userzone_to_datazone.py
deleted file mode 100644
index 622a4fb9dd..0000000000
--- a/hs_core/management/commands/migrate_resources_from_userzone_to_datazone.py
+++ /dev/null
@@ -1,88 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from hs_core.models import BaseResource
-
-from django_irods.storage import IrodsStorage
-from django_irods.icommands import SessionException
-
-
-class Command(BaseCommand):
-    help = "Migrate all resources in iRODS user zone to iRODS data zone"
-
-    def handle(self, *args, **options):
-        resource_counter = 0
-        storage = IrodsStorage()
-        avu_list = ['bag_modified', 'metadata_dirty', 'isPublic', 'resourceType']
-        for resource in BaseResource.objects.all():
-            if resource.storage_type == 'user':
-                # resource is in user zone, so migrate it to data zone
-                # copy files from iRODS user zone to data zone
-                try:
-                    src_coll = resource.root_path
-                    tgt_coll = resource.short_id
-
-                    if storage.exists(tgt_coll):
-                        storage.delete(tgt_coll)
-                    storage.copyFiles(src_coll, tgt_coll)
-                    # copy AVU over for the resource collection from iRODS user zone to data zone
-
-                    for avu_name in avu_list:
-                        value = storage.getAVU(src_coll, avu_name)
-                        # bag_modified AVU needs to be set to true for the new resource so the bag
-                        # can be regenerated in the data zone
-                        if avu_name == 'bag_modified':
-                            storage.setAVU(tgt_coll, avu_name, 'true')
-                        # everything else gets copied literally
-                        else:
-                            storage.setAVU(tgt_coll, avu_name, value)
-
-                    # Just to be on the safe side, it is better not to delete resources from user
-                    # zone after it is migrated over to data zone in case there are issues with
-                    # migration. A simple irm iRODS command can be issued to delete all resource
-                    # collections afterwards if all works well after some time. Commenting the
-                    # deletion statement below rather than deleting it to serve as a reminder
-                    # that additional cleanup to delete all resource collections in user zone
-                    # is needed after we can confirm migration is successfully.
-                    # delete the original resource from user zone
-                    # storage.delete(src_coll)
-
-                    path_migrated = False
-                    for res_file in resource.files.all():
-                        if res_file.resource_file.name:
-                            print('The resource_file field should be empty for resource {} but '
-                                  'have the value of {}'.format(resource.short_id,
-                                                                res_file.resource_file.name))
-                            break
-                        file_path = res_file.fed_resource_file.name
-                        if not file_path:
-                            print('The fed_resource_file field should not be empty for '
-                                  'resource {}'.format(resource.short_id))
-                            break
-                        elif file_path.startswith(resource.resource_federation_path):
-                            file_path = file_path[len(resource.resource_federation_path) + 1:]
-                            res_file.resource_file.name = file_path
-                            res_file.fed_resource_file.name = ''
-                            res_file.save()
-                            path_migrated = True
-                        else:
-                            res_file.resource_file.name = file_path
-                            res_file.fed_resource_file.name = ''
-                            res_file.save()
-                            path_migrated = True
-                            print(('fed_resource_file field does not contain absolute federation '
-                                   'path which is an exception but can work after migration. '
-                                   'file_path is {}'.format(file_path)))
-                    if path_migrated or resource.files.count() == 0:
-                        # update resource federation path to point resource to data zone
-                        resource.resource_federation_path = ''
-                        resource.save()
-                        print("Resource {} has been moved from user zone to data zone "
-                              "successfully".format(resource.short_id))
-                        resource_counter += 1
-                    else:
-                        continue
-                except SessionException as ex:
-                    print("Resource {} failed to move: {}".format(resource.short_id, ex.stderr))
-
-        print("{} resources have been moved from user zone to data zone successfully".format(
-            resource_counter))
diff --git a/hs_core/management/utils.py b/hs_core/management/utils.py
index 78ad53687d..90d4bee270 100755
--- a/hs_core/management/utils.py
+++ b/hs_core/management/utils.py
@@ -293,17 +293,8 @@ def check_irods_files(resource, stop_on_error=False, log_errors=True,
     dangling_in_django = 0
     missing_in_django = 0
 
-    # skip federated resources if not configured to handle these
-    if resource.is_federated and not settings.REMOTE_USE_IRODS:
-        msg = "check_irods_files: skipping check of federated resource {} in unfederated mode"\
-            .format(resource.short_id)
-        if echo_errors:
-            print(msg)
-        if log_errors:
-            logger.info(msg)
-
     # skip resources that do not exist in iRODS
-    elif not istorage.exists(resource.root_path):
+    if not istorage.exists(resource.root_path):
         msg = "root path {} does not exist in iRODS".format(resource.root_path)
         ecount += 1
         if echo_errors:
@@ -540,47 +531,36 @@ def ingest_irods_files(resource,
     istorage = resource.get_irods_storage()
     errors = []
     ecount = 0
+    # flag non-existent resources in iRODS
+    if not istorage.exists(resource.root_path):
+        msg = "root path {} does not exist in iRODS".format(resource.root_path)
+        ecount += 1
+        if echo_errors:
+            print(msg)
+        if log_errors:
+            logger.error(msg)
+        if return_errors:
+            errors.append(msg)
 
-    # skip federated resources if not configured to handle these
-    if resource.is_federated and not settings.REMOTE_USE_IRODS:
-        msg = "ingest_irods_files: skipping ingest of federated resource {} in unfederated mode"\
-            .format(resource.short_id)
+    # flag non-existent file paths in iRODS
+    elif not istorage.exists(resource.file_path):
+        msg = "file path {} does not exist in iRODS".format(resource.file_path)
+        ecount += 1
         if echo_errors:
             print(msg)
         if log_errors:
-            logger.info(msg)
+            logger.error(msg)
+        if return_errors:
+            errors.append(msg)
 
     else:
-        # flag non-existent resources in iRODS
-        if not istorage.exists(resource.root_path):
-            msg = "root path {} does not exist in iRODS".format(resource.root_path)
-            ecount += 1
-            if echo_errors:
-                print(msg)
-            if log_errors:
-                logger.error(msg)
-            if return_errors:
-                errors.append(msg)
-
-        # flag non-existent file paths in iRODS
-        elif not istorage.exists(resource.file_path):
-            msg = "file path {} does not exist in iRODS".format(resource.file_path)
-            ecount += 1
-            if echo_errors:
-                print(msg)
-            if log_errors:
-                logger.error(msg)
-            if return_errors:
-                errors.append(msg)
-
-        else:
-            return __ingest_irods_directory(resource,
-                                            resource.file_path,
-                                            logger,
-                                            stop_on_error=False,
-                                            echo_errors=True,
-                                            log_errors=False,
-                                            return_errors=False)
+        return __ingest_irods_directory(resource,
+                                        resource.file_path,
+                                        logger,
+                                        stop_on_error=False,
+                                        echo_errors=True,
+                                        log_errors=False,
+                                        return_errors=False)
 
     return errors, ecount
 
@@ -829,12 +809,6 @@ def test(self):
             print("{} does not exist in Django".format(self.short_id))
             return
 
-        # skip federated resources if not configured to handle these
-        if self.resource.is_federated and not settings.REMOTE_USE_IRODS:
-            msg = "check_resource: skipping check of federated resource {} in unfederated mode"\
-                .format(self.resource.short_id)
-            print(msg)
-
         istorage = self.resource.get_irods_storage()
 
         if not istorage.exists(self.resource.root_path):
diff --git a/hs_core/migrations/0083_auto_20240702_2038.py b/hs_core/migrations/0083_auto_20240702_2038.py
new file mode 100644
index 0000000000..5129ea0f7b
--- /dev/null
+++ b/hs_core/migrations/0083_auto_20240702_2038.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.25 on 2024-07-02 20:38
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('hs_core', '0082_merge_20240509_1646'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='baseresource',
+            name='resource_federation_path',
+        ),
+        migrations.AlterIndexTogether(
+            name='resourcefile',
+            index_together={('object_id', 'resource_file')},
+        ),
+        migrations.RemoveField(
+            model_name='resourcefile',
+            name='fed_resource_file',
+        ),
+    ]
diff --git a/hs_core/models.py b/hs_core/models.py
index 2a03d197b6..13e9c67821 100755
--- a/hs_core/models.py
+++ b/hs_core/models.py
@@ -2454,10 +2454,7 @@ def get_irods_path(self, path, prepend_short_id=True):
         else:
             full_path = path
 
-        if self.is_federated:
-            return os.path.join(self.resource_federation_path, full_path)
-        else:
-            return full_path
+        return full_path
 
     def set_quota_holder(self, setter, new_holder):
         """Set quota holder of the resource to new_holder who must be an owner.
@@ -2828,14 +2825,9 @@ def readme_file(self):
 
         file_path_md = os.path.join(self.file_path, 'readme.md')
         file_path_txt = os.path.join(self.file_path, 'readme.txt')
-        if self.is_federated:
-            readme_res_file = self.files.filter(fed_resource_file__iexact=file_path_md).first()
-            if readme_res_file is None:
-                readme_res_file = self.files.filter(fed_resource_file__iexact=file_path_txt).first()
-        else:
-            readme_res_file = self.files.filter(resource_file__iexact=file_path_md).first()
-            if readme_res_file is None:
-                readme_res_file = self.files.filter(resource_file__iexact=file_path_txt).first()
+        readme_res_file = self.files.filter(resource_file__iexact=file_path_md).first()
+        if readme_res_file is None:
+            readme_res_file = self.files.filter(resource_file__iexact=file_path_txt).first()
 
         return readme_res_file
 
@@ -2934,16 +2926,7 @@ def supports_delete_folder_on_zip(self, original_folder):
 
     @property
     def storage_type(self):
-        if not self.is_federated:
-            return 'local'
-        userpath = '/' + os.path.join(
-            getattr(settings, 'HS_USER_IRODS_ZONE', 'hydroshareuserZone'),
-            'home',
-            getattr(settings, 'HS_IRODS_PROXY_USER_IN_USER_ZONE', 'localHydroProxy'))
-        if self.resource_federation_path == userpath:
-            return 'user'
-        else:
-            return 'federated'
+        return 'local'
 
     def is_folder(self, folder_path):
         """Determine whether a given path (relative to resource root, including /data/contents/)
@@ -2981,7 +2964,6 @@ def get_path(instance, filename, folder=''):
     :param folder: can override folder for ResourceFile instance
 
     The filename is only a single name. This routine converts it to an absolute
-    path that can be federated or local.  The instance points to the Resource record,
     which contains the federation path. The folder in the instance will be used unless
     overridden.
 
@@ -2996,14 +2978,13 @@ def get_path(instance, filename, folder=''):
 
 # TODO: make this an instance method of BaseResource.
 def get_resource_file_path(resource, filename, folder=''):
-    """Determine storage path for a FileField based upon whether resource is federated.
+    """Determine storage path for a FileField
 
     :param resource: resource containing the file.
     :param filename: name of file without folder.
     :param folder: folder of file
 
     The filename is only a single name. This routine converts it to an absolute
-    path that can be federated or local. The resource contains information on how
     to do this.
 
     """
@@ -3069,7 +3050,6 @@ class ResourceFile(ResourceFileIRODSMixin):
     """
     class Meta:
         index_together = [['object_id', 'resource_file'],
-                          ['object_id', 'fed_resource_file'],
                           ]
     # A ResourceFile is a sub-object of a resource, which can have several types.
     object_id = models.PositiveIntegerField()
@@ -3083,15 +3063,6 @@ class Meta:
     # This pair of FileFields deals with the fact that there are two kinds of storage
     resource_file = models.FileField(upload_to=get_path, max_length=4096, unique=True,
                                      storage=IrodsStorage())
-    # TODO: ideally we would like a unique constraint on fed_resource_file
-    # due to https://code.djangoproject.com/ticket/10244 we can't set null=True and unique=True
-    fed_resource_file = models.FileField(upload_to=get_path, max_length=4096,
-                                         null=True, blank=True, storage=FedStorage())
-
-    # DEPRECATED: utilize resfile.set_storage_path(path) and resfile.storage_path.
-    # fed_resource_file_name_or_path = models.CharField(max_length=255, null=True, blank=True)
-    # DEPRECATED: use native size routine
-    # fed_resource_file_size = models.CharField(max_length=15, null=True, blank=True)
 
     # we are using GenericForeignKey to allow resource file to be associated with any
     # HydroShare defined LogicalFile types (e.g., GeoRasterFile, NetCdfFile etc)
@@ -3112,11 +3083,7 @@ class Meta:
     filesize_cache_updated = models.DateTimeField(null=True)
 
     def __str__(self):
-        """Return resource filename or federated resource filename for string representation."""
-        if self.resource.resource_federation_path:
-            return self.fed_resource_file.name
-        else:
-            return self.resource_file.name
+        return self.resource_file.name
 
     @classmethod
     def banned_symbols(cls):
@@ -3176,12 +3143,7 @@ def create(cls, resource, file, folder='', source=None):
             if not ResourceFile.is_filename_valid(filename):
                 raise SuspiciousFileOperation("Filename is not compliant with Hydroshare requirements")
 
-            if resource.is_federated:
-                kwargs['resource_file'] = None
-                kwargs['fed_resource_file'] = file
-            else:
-                kwargs['resource_file'] = file
-                kwargs['fed_resource_file'] = None
+            kwargs['resource_file'] = file
 
         else:  # if file is not an open file, then it's a basename (string)
             if file is None and source is not None:
@@ -3210,12 +3172,7 @@ def create(cls, resource, file, folder='', source=None):
                     "ResourceFile.create: exactly one of source or file must be specified")
 
             # we've copied or moved if necessary; now set the paths
-            if resource.is_federated:
-                kwargs['resource_file'] = None
-                kwargs['fed_resource_file'] = target
-            else:
-                kwargs['resource_file'] = target
-                kwargs['fed_resource_file'] = None
+            kwargs['resource_file'] = target
 
         # Actually create the file record
         # when file is a File, the file is copied to storage in this step
@@ -3235,8 +3192,6 @@ def delete(self, delete_logical_file=False):
             if delete_logical_file and self.logical_file is not None:
                 # deleting logical file metadata deletes the logical file as well
                 self.logical_file.metadata.delete()
-            if self.fed_resource_file:
-                self.fed_resource_file.delete()
             if self.resource_file:
                 self.resource_file.delete()
         super(ResourceFile, self).delete()
@@ -3271,10 +3226,7 @@ def calculate_modified_time(self, resource=None, save=True):
         if resource is None:
             resource = self.resource
 
-        if resource.resource_federation_path:
-            file_path = self.fed_resource_file.name
-        else:
-            file_path = self.resource_file.name
+        file_path = self.resource_file.name
 
         try:
             self._modified_time = self.resource_file.storage.get_modified_time(file_path)
@@ -3302,10 +3254,7 @@ def calculate_checksum(self, resource=None, save=True):
         if resource is None:
             resource = self.resource
 
-        if resource.resource_federation_path:
-            file_path = self.fed_resource_file.name
-        else:
-            file_path = self.resource_file.name
+        file_path = self.resource_file.name
 
         try:
             self._checksum = self.resource_file.storage.checksum(file_path, force_compute=False)
@@ -3319,25 +3268,12 @@ def calculate_checksum(self, resource=None, save=True):
     # TODO: write unit test
     @property
     def exists(self):
-        """Check existence of files for both federated and non-federated."""
         istorage = self.resource.get_irods_storage()
-        if self.resource.is_federated:
-            if __debug__:
-                assert self.resource_file.name is None or \
-                    self.resource_file.name == ''
-            return istorage.exists(self.fed_resource_file.name)
-        else:
-            if __debug__:
-                assert self.fed_resource_file.name is None or \
-                    self.fed_resource_file.name == ''
-            return istorage.exists(self.resource_file.name)
+        return istorage.exists(self.resource_file.name)
 
     # TODO: write unit test
     def read(self):
-        if self.resource.is_federated:
-            return self.fed_resource_file.read()
-        else:
-            return self.resource_file.read()
+        return self.resource_file.read()
 
     @property
     def storage_path(self):
@@ -3345,7 +3281,6 @@ def storage_path(self):
 
         This is a valid input to IrodsStorage for manipulating the file.
         The output depends upon whether the IrodsStorage instance is running
-        in federated mode.
 
         """
         # instance.content_object can be stale after changes.
@@ -3358,44 +3293,20 @@ def get_storage_path(self, resource):
         Note: This is the preferred way to get the storage path for a file when we are trying to find
         the storage path for more than one file in a resource.
         """
-        if resource.is_federated:  # false if None or empty
-            if __debug__:
-                assert self.resource_file.name is None or \
-                    self.resource_file.name == ''
-            return self.fed_resource_file.name
-        else:
-            if __debug__:
-                assert self.fed_resource_file.name is None or \
-                    self.fed_resource_file.name == ''
-            return self.resource_file.name
+        return self.resource_file.name
 
     def calculate_size(self, resource=None, save=True):
         """Reads the file size and saves to the DB"""
         if resource is None:
             resource = self.resource
 
-        if resource.resource_federation_path:
-            if __debug__:
-                assert self.resource_file.name is None or \
-                    self.resource_file.name == ''
-            try:
-                self._size = self.fed_resource_file.size
-                self.filesize_cache_updated = now()
-            except (SessionException, ValidationError):
-                logger = logging.getLogger(__name__)
-                logger.warning("file {} not found in iRODS".format(self.storage_path))
-                self._size = 0
-        else:
-            if __debug__:
-                assert self.fed_resource_file.name is None or \
-                    self.fed_resource_file.name == ''
-            try:
-                self._size = self.resource_file.size
-                self.filesize_cache_updated = now()
-            except (SessionException, ValidationError):
-                logger = logging.getLogger(__name__)
-                logger.warning("file {} not found in iRODS".format(self.storage_path))
-                self._size = 0
+        try:
+            self._size = self.resource_file.size
+            self.filesize_cache_updated = now()
+        except (SessionException, ValidationError):
+            logger = logging.getLogger(__name__)
+            logger.warning("file {} not found in iRODS".format(self.storage_path))
+            self._size = 0
         if save:
             self.save(update_fields=["_size", "filesize_cache_updated"])
 
@@ -3426,7 +3337,6 @@ def set_storage_path(self, path, test_exists=True):
 
         Path can be absolute or relative.
 
-            * absolute paths contain full irods path to local or federated object.
             * relative paths start with anything else and can start with optional folder
 
         :raises ValidationError: if the pathname is inconsistent with resource configuration.
@@ -3445,18 +3355,8 @@ def set_storage_path(self, path, test_exists=True):
         folder, base = self.path_is_acceptable(path, test_exists=test_exists)
         self.file_folder = folder
 
-        # self.content_object can be stale after changes. Re-fetch based upon key
-        # bypass type system; it is not relevant
-        resource = self.resource
-
         # switch FileFields based upon federation path
-        if resource.is_federated:
-            # uses file_folder; must come after that setting.
-            self.fed_resource_file = get_path(self, base)
-            self.resource_file = None
-        else:
-            self.fed_resource_file = None
-            self.resource_file = get_path(self, base)
+        self.resource_file = get_path(self, base)
         self.save()
 
     @property
@@ -3485,10 +3385,7 @@ def get_short_path(self, resource):
         Note: This is the preferred way to get the short path for a file when we are trying to find short path
         for more than one file in a resource.
         """
-        if resource.is_federated:
-            folder, base = self.path_is_acceptable(self.fed_resource_file.name, test_exists=False)
-        else:
-            folder, base = self.path_is_acceptable(self.resource_file.name, test_exists=False)
+        folder, base = self.path_is_acceptable(self.resource_file.name, test_exists=False)
         if folder is not None:
             return os.path.join(folder, base)
         else:
@@ -3503,12 +3400,8 @@ def set_short_path(self, path):
         """
         folder, base = os.path.split(path)
         self.file_folder = folder  # must precede call to get_path
-        if self.resource.is_federated:
-            self.resource_file = None
-            self.fed_resource_file = get_path(self, base)
-        else:
-            self.resource_file = get_path(self, base)
-            self.fed_resource_file = None
+
+        self.resource_file = get_path(self, base)
         self.save()
 
     def parse(self):
@@ -3542,20 +3435,7 @@ def resource_path_is_acceptable(cls, resource, path, test_exists=True):
             storage = resource.get_irods_storage()
         locpath = os.path.join(resource.short_id, "data", "contents") + "/"
         relpath = path
-        fedpath = resource.resource_federation_path
-        if fedpath and relpath.startswith(fedpath + '/'):
-            if test_exists and not storage.exists(path):
-                raise ValidationError("Federated path does not exist in irods")
-            plen = len(fedpath + '/')
-            relpath = relpath[plen:]  # omit fed path
-
-            # strip resource id from path
-            if relpath.startswith(locpath):
-                plen = len(locpath)
-                relpath = relpath[plen:]  # omit local path
-            else:
-                raise ValidationError("Malformed federated resource path")
-        elif path.startswith(locpath):
+        if path.startswith(locpath):
             # strip optional local path prefix
             if test_exists and not storage.exists(path):
                 raise ValidationError("Local path ({}) does not exist in irods".format(path))
@@ -3669,10 +3549,7 @@ def validate_new_path(cls, new_path):
     def get(cls, resource, file, folder=''):
         """Get a ResourceFile record via its short path."""
         resource_file_path = get_resource_file_path(resource, file, folder)
-        if resource.resource_federation_path:
-            f = ResourceFile.objects.filter(object_id=resource.id, fed_resource_file=resource_file_path).first()
-        else:
-            f = ResourceFile.objects.filter(object_id=resource.id, resource_file=resource_file_path).first()
+        f = ResourceFile.objects.filter(object_id=resource.id, resource_file=resource_file_path).first()
         if f:
             return f
         else:
@@ -3700,14 +3577,9 @@ def list_folder(cls, resource, folder, sub_folders=True):
             # append trailing slash to match only this folder
             if not folder.endswith("/"):
                 folder += "/"
-            if resource.is_federated:
-                return ResourceFile.objects.filter(
-                    object_id=resource.id,
-                    fed_resource_file__startswith=folder)
-            else:
-                return ResourceFile.objects.filter(
-                    object_id=resource.id,
-                    resource_file__startswith=folder)
+            return ResourceFile.objects.filter(
+                object_id=resource.id,
+                resource_file__startswith=folder)
         else:
             return ResourceFile.objects.filter(
                 object_id=resource.id,
@@ -3818,7 +3690,6 @@ def url(self):
     @property
     def public_path(self):
         """ return the public path (unqualified iRODS path) for a resource.
-            This corresponds to the iRODS path if the resource isn't federated.
         """
         return os.path.join(self.resource.short_id, 'data', 'contents', self.short_path)
 
@@ -3828,10 +3699,7 @@ def irods_path(self):
             This consists of the resource id, /data/contents/, and the file path.
         """
 
-        if self.resource.is_federated:
-            return os.path.join(self.resource.resource_federation_path, self.public_path)
-        else:
-            return self.public_path
+        return self.public_path
 
 
 class PublicResourceManager(models.Manager):
@@ -3863,13 +3731,6 @@ class BaseResource(Page, AbstractResource):
     # means the resource is not locked
     locked_time = models.DateTimeField(null=True, blank=True)
 
-    # this resource_federation_path is added to record where a HydroShare resource is
-    # stored. The default is empty string meaning the resource is stored in HydroShare
-    # zone. If a resource is stored in a fedearated zone, the field should store the
-    # federated root path in the format of /federated_zone/home/localHydroProxy
-    # TODO: change to null=True, default=None to simplify logic elsewhere
-    resource_federation_path = models.CharField(max_length=100, blank=True, default='')
-
     objects = PublishedManager()
     public_resources = PublicResourceManager()
     discoverable_resources = DiscoverableResourceManager()
@@ -3903,16 +3764,7 @@ def can_view(self, request):
 
     def get_irods_storage(self):
         """Return either IrodsStorage or FedStorage."""
-        if self.resource_federation_path:
-            return FedStorage()
-        else:
-            return IrodsStorage()
-
-    @property
-    def is_federated(self):
-        """Return existence of resource_federation_path."""
-        return self.resource_federation_path is not None and \
-            self.resource_federation_path != ''
+        return IrodsStorage()
 
     # Paths relative to the resource
     @property
@@ -3922,10 +3774,7 @@ def root_path(self):
         Note that this folder doesn't directly contain the resource files;
         They are contained in ./data/contents/* instead.
         """
-        if self.is_federated:
-            return os.path.join(self.resource_federation_path, self.short_id)
-        else:
-            return self.short_id
+        return self.short_id
 
     @property
     def file_path(self):
@@ -3959,11 +3808,7 @@ def bag_path(self):
         """
         bagit_path = getattr(settings, 'IRODS_BAGIT_PATH', 'bags')
         bagit_postfix = getattr(settings, 'IRODS_BAGIT_POSTFIX', 'zip')
-        if self.is_federated:
-            return os.path.join(self.resource_federation_path, bagit_path,
-                                self.short_id + '.' + bagit_postfix)
-        else:
-            return os.path.join(bagit_path, self.short_id + '.' + bagit_postfix)
+        return os.path.join(bagit_path, self.short_id + '.' + bagit_postfix)
 
     @property
     def bag_url(self):
diff --git a/hs_core/tasks.py b/hs_core/tasks.py
index 28e5f2f841..032a51d5b4 100755
--- a/hs_core/tasks.py
+++ b/hs_core/tasks.py
@@ -145,18 +145,6 @@ def nightly_zips_cleanup():
     istorage = IrodsStorage()
     if istorage.exists(zips_daily_date):
         istorage.delete(zips_daily_date)
-    federated_prefixes = BaseResource.objects.order_by().values_list('resource_federation_path').distinct()
-
-    for p in federated_prefixes:
-        prefix = p[0]  # strip tuple
-        if prefix != "":
-            zips_daily_date = "{prefix}/zips/{daily_date}"\
-                .format(prefix=prefix, daily_date=date_folder)
-            if __debug__:
-                logger.debug("cleaning up {}".format(zips_daily_date))
-            istorage = IrodsStorage("federated")
-            if istorage.exists(zips_daily_date):
-                istorage.delete(zips_daily_date)
 
 
 @celery_app.task(ignore_result=True, base=HydroshareTask)
diff --git a/hs_core/templates/pages/baseresource.html b/hs_core/templates/pages/baseresource.html
index 7ba17475fb..e926085bde 100644
--- a/hs_core/templates/pages/baseresource.html
+++ b/hs_core/templates/pages/baseresource.html
@@ -940,22 +940,20 @@ <h4 class="modal-title">Confirm files deletion</h4>
 
                     <div class="files" id="previews"></div>
                     {% block download_bag %}
-                        {% if user_zone_account_exist %}
-                            <div>
-                                <div id='rep-alert-success' class="alert alert-success alert-dismissible"
-                                     role="alert" style="display:none">
-                                    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                                        <span aria-hidden="true">&times;</span></button>
-                                    <span id="rep-status-success"></span>
-                                </div>
-                                <div id='rep-alert-error' class="alert alert-danger alert-dismissible" role="alert"
-                                     style="display:none">
-                                    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-                                        <span aria-hidden="true">&times;</span></button>
-                                    <span id="rep-status-error"></span>
-                                </div>
+                        <div>
+                            <div id='rep-alert-success' class="alert alert-success alert-dismissible"
+                                    role="alert" style="display:none">
+                                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span></button>
+                                <span id="rep-status-success"></span>
                             </div>
-                        {% endif %}
+                            <div id='rep-alert-error' class="alert alert-danger alert-dismissible" role="alert"
+                                    style="display:none">
+                                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span></button>
+                                <span id="rep-status-error"></span>
+                            </div>
+                        </div>
                     {% endblock %} {#  download_bag #}
 
                     {# ======= Readme ======= #}
diff --git a/hs_core/views/__init__.py b/hs_core/views/__init__.py
index 8b9e6e883c..75484d8a47 100755
--- a/hs_core/views/__init__.py
+++ b/hs_core/views/__init__.py
@@ -1641,8 +1641,6 @@ def render(self, name, value, attrs=None, renderer=None):
 
 @processor_for(BaseResource)
 def add_generic_context(request, page):
-    user = request.user
-    user_zone_account_exist = hydroshare.utils.get_user_zone_status_info(user)
 
     class AddUserForm(forms.Form):
         user = forms.ModelChoiceField(
@@ -1692,7 +1690,6 @@ class AddGroupForm(forms.Form):
         # Reuse the same class AddGroupForm() leads to duplicated IDs.
         "add_view_group_form": AddGroupForm(),
         "add_edit_group_form": AddGroupForm(),
-        "user_zone_account_exist": user_zone_account_exist,
     }
 
 
diff --git a/hs_core/views/resource_folder_hierarchy.py b/hs_core/views/resource_folder_hierarchy.py
index cac5ea69e5..ddd49dc1c3 100755
--- a/hs_core/views/resource_folder_hierarchy.py
+++ b/hs_core/views/resource_folder_hierarchy.py
@@ -135,17 +135,11 @@ def data_store_structure(request):
                      'folder_aggregation_appkey': folder_aggregation_appkey,
                      })
 
-    is_federated = resource.is_federated
     for index, fname in enumerate(store[1]):  # files
         f_store_path = os.path.join(store_path, fname)
         file_in_irods = resource.get_irods_path(f_store_path)
-
-        if is_federated:
-            res_file = ResourceFile.objects.filter(object_id=resource.id,
-                                                   fed_resource_file=file_in_irods).first()
-        else:
-            res_file = ResourceFile.objects.filter(object_id=resource.id,
-                                                   resource_file=file_in_irods).first()
+        res_file = ResourceFile.objects.filter(object_id=resource.id,
+                                               resource_file=file_in_irods).first()
 
         if not res_file:
             # skip metadata files
diff --git a/hs_core/views/utils.py b/hs_core/views/utils.py
index 60ee7af230..3edfba9057 100755
--- a/hs_core/views/utils.py
+++ b/hs_core/views/utils.py
@@ -333,15 +333,9 @@ def run_ssh_command(host, uname, exec_cmd, pwd=None, private_key_file=None):
 # when private netCDF resources are made public so that all links of data services
 # provided by Hyrax service are instantaneously available on demand
 def run_script_to_update_hyrax_input_files(shortkey):
-    pwd = None
-    pk = None
-    if hasattr(settings, 'LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE'):
-        pwd = settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE
-    if hasattr(settings, 'PRIVATE_KEY_FILE_FOR_HS_USER_ZONE'):
-        pk = settings.PRIVATE_KEY_FILE_FOR_HS_USER_ZONE
+    pwd = settings.HYRAX_SSH_PROXY_USER_PWD,
     run_ssh_command(host=settings.HYRAX_SSH_HOST, uname=settings.HYRAX_SSH_PROXY_USER,
                     pwd=pwd,
-                    private_key_file=pk,
                     exec_cmd=settings.HYRAX_SCRIPT_RUN_COMMAND + ' ' + shortkey)
 
 
@@ -934,7 +928,6 @@ def rename_irods_file_or_folder_in_django(resource, src_name, tgt_name):
     except ObjectDoesNotExist:
         # src_name and tgt_name are folder names
         res_file_objs = ResourceFile.list_folder(resource=resource, folder=src_name)
-        resource_is_federated = resource.is_federated
         batch_size = settings.BULK_UPDATE_CREATE_BATCH_SIZE
         is_target_folder_aggregation = False
         if composite_file_move:
@@ -959,17 +952,10 @@ def rename_irods_file_or_folder_in_django(resource, src_name, tgt_name):
             new_path = src_path.replace(src_name, tgt_name, 1)
             folder, _ = fobj.path_is_acceptable(new_path, test_exists=False)
             fobj.file_folder = folder
-            if resource_is_federated:
-                fobj.fed_resource_file = new_path
-            else:
-                fobj.resource_file = new_path
+            fobj.resource_file = new_path
 
         if res_file_objs:
-            if resource_is_federated:
-                ResourceFile.objects.bulk_update(res_file_objs, ['file_folder', 'fed_resource_file'],
-                                                 batch_size=batch_size)
-            else:
-                ResourceFile.objects.bulk_update(res_file_objs, ['file_folder', 'resource_file'], batch_size=batch_size)
+            ResourceFile.objects.bulk_update(res_file_objs, ['file_folder', 'resource_file'], batch_size=batch_size)
 
             if is_target_folder_aggregation and composite_file_move:
                 res_file_objs = ResourceFile.list_folder(resource=resource, folder=tgt_name)
diff --git a/hs_file_types/models/base_model_program_instance.py b/hs_file_types/models/base_model_program_instance.py
index 455d631612..ad1b7b8bd3 100644
--- a/hs_file_types/models/base_model_program_instance.py
+++ b/hs_file_types/models/base_model_program_instance.py
@@ -494,8 +494,6 @@ def can_set_folder_to_aggregation(cls, resource, dir_path, aggregations=None):
 
         # check the first parent folder that represents an aggregation
         irods_path = dir_path
-        if resource.is_federated:
-            irods_path = os.path.join(resource.resource_federation_path, irods_path)
 
         # get the parent folder path
         path = os.path.dirname(dir_path)
diff --git a/hs_file_types/models/fileset.py b/hs_file_types/models/fileset.py
index 2dcdd93323..4c62a55d50 100644
--- a/hs_file_types/models/fileset.py
+++ b/hs_file_types/models/fileset.py
@@ -126,8 +126,6 @@ def can_set_folder_to_aggregation(cls, resource, dir_path, aggregations=None):
             path = os.path.dirname(path)
 
         irods_path = dir_path
-        if resource.is_federated:
-            irods_path = os.path.join(resource.resource_federation_path, irods_path)
 
         files_in_path = ResourceFile.list_folder(resource, folder=irods_path, sub_folders=True)
         # if there are any files in the dir_path, we can set the folder to fileset aggregation
diff --git a/hs_file_types/models/reftimeseries.py b/hs_file_types/models/reftimeseries.py
index 22fa533a4b..9c8c572dab 100755
--- a/hs_file_types/models/reftimeseries.py
+++ b/hs_file_types/models/reftimeseries.py
@@ -840,10 +840,7 @@ def _extract_metadata(resource, logical_file):
 
 
 def _validate_json_file(res_json_file):
-    if res_json_file.resource_file:
-        json_file_content = res_json_file.resource_file.read()
-    else:
-        json_file_content = res_json_file.fed_resource_file.read()
+    json_file_content = res_json_file.resource_file.read()
     try:
         json_data = json.loads(json_file_content)
     except: # noqa
diff --git a/hydroshare/local_settings.cutover b/hydroshare/local_settings.cutover
index 9b58a24b33..1520c542fd 100644
--- a/hydroshare/local_settings.cutover
+++ b/hydroshare/local_settings.cutover
@@ -135,21 +135,6 @@ HYRAX_SSH_PROXY_USER = ''
 HYRAX_SSH_PROXY_USER_PWD = ''
 HYRAX_SCRIPT_RUN_COMMAND = ''
 
-# hsuserproxy system user configuration used to create hydroshare iRODS users on-demand
-HS_USER_ZONE_HOST = ''
-LINUX_ADMIN_USER_FOR_HS_USER_ZONE = ''
-LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE = ''
-PRIVATE_KEY_FILE_FOR_HS_USER_ZONE = ''
-LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD = ''
-LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD = ''
-
-# the HydroShare iRODS proxy user in the federated user zone
-HS_IRODS_PROXY_USER_IN_USER_ZONE = 'localTestHydroProxy'
-# HydroShare iRODS default storage resource in the federated user zone
-HS_IRODS_USER_ZONE_DEF_RES = 'hydroshareLocalResc'
-# HydroShare iRODS federated user zone name
-HS_USER_IRODS_ZONE = 'hydroshareuserZone'
-
 # Email configuration
 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 # EMAIL_HOST_USER = ''
diff --git a/hydroshare/local_settings.template b/hydroshare/local_settings.template
index 22f9ac0844..37f584a96c 100644
--- a/hydroshare/local_settings.template
+++ b/hydroshare/local_settings.template
@@ -91,8 +91,6 @@ DATABASES = {
 }
 POSTGIS_VERSION = (2, 1, 1)
 
-SHARED_NEG_KEY = 'hydroshareZonehydroshareuserZone'
-
 # Native Docker iRODS developer local
 USE_IRODS = True
 IRODS_ROOT = '/tmp'
@@ -135,22 +133,6 @@ HYRAX_SSH_PROXY_USER = ''
 HYRAX_SSH_PROXY_USER_PWD = ''
 HYRAX_SCRIPT_RUN_COMMAND = ''
 
-# hsuserproxy system user configuration used to create hydroshare iRODS users on-demand
-HS_USER_ZONE_HOST = 'users.local.org'
-LINUX_ADMIN_USER_FOR_HS_USER_ZONE = 'hsuserproxy'
-LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE = 'hsuserproxy'
-PRIVATE_KEY_FILE_FOR_HS_USER_ZONE = '/hydroshare/.ssh/id_ed25519_hs'
-LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD = '/home/hsuserproxy/create_user.sh'
-LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD = '/home/hsuserproxy/delete_user.sh'
-LINUX_ADMIN_USER_SET_QUOTA_IN_USER_ZONE_CMD = '/home/hsuserproxy/set_userzone_quota.sh'
-
-# HydroShare iRODS proxy user in the federated user zone
-HS_IRODS_PROXY_USER_IN_USER_ZONE = 'localHydroProxy'
-# HydroShare iRODS default storage resource in the federated user zone
-HS_IRODS_USER_ZONE_DEF_RES = 'hydroshareLocalResc'
-# HydroShare iRODS federated user zone name
-HS_USER_IRODS_ZONE = 'hydroshareuserZone'
-
 # Email configuration
 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 # EMAIL_HOST_USER = ''
diff --git a/hydroshare/settings.py b/hydroshare/settings.py
index 704ba70821..dbdc55b9bc 100755
--- a/hydroshare/settings.py
+++ b/hydroshare/settings.py
@@ -349,7 +349,7 @@ def _(s): return s # noqa
 # )
 # STATICFILES_STORAGE = 'hydroshare.storage.Static'
 # THUMBNAIL_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
-# DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
+# DEFAULT_FILE_STORAGE = 'hydroshare.storage.MediaeGoogleCloudStorage'
 # # the media is served from the root of the bucket
 # MEDIA_URL = f'https://storage.googleapis.com/{GS_BUCKET_NAME}/'
 # # the static files are served from a static/ dir in the bucket
diff --git a/hydroshare/storage.py b/hydroshare/storage.py
index b08ede9cc0..8298ad03e2 100644
--- a/hydroshare/storage.py
+++ b/hydroshare/storage.py
@@ -42,5 +42,24 @@ def path(self, name):
         return name
 
 
+class MediaGoogleCloudStorage(GoogleCloudStorage):
+    '''
+    Google Cloud Storage backend for Django static files. Implements methods the
+    mezzanine admin interface requires.
+    '''
+
+    def isfile(self, name):
+        return self.exists(name)
+
+    def isdir(self, name):
+        if not name:  # Empty name is a directory
+            return True
+
+        return self.isfile(name)
+
+    def makedirs(self, name):
+        pass
+
+
 def Static():
     return ManifestGoogleCloudStorage(location='static')
diff --git a/hydroshare/urls.py b/hydroshare/urls.py
index 472b316b9b..b6413a3a2f 100755
--- a/hydroshare/urls.py
+++ b/hydroshare/urls.py
@@ -100,16 +100,6 @@
         name="confirm_reset_password",
     ),
     url(r"^deactivate_account/$", theme.deactivate_user, name="deactivate_account"),
-    url(
-        r"^delete_irods_account/$",
-        theme.delete_irods_account,
-        name="delete_irods_account",
-    ),
-    url(
-        r"^create_irods_account/$",
-        theme.create_irods_account,
-        name="create_irods_account",
-    ),
     url(r"^landingPage/$", theme.landingPage, name="landing_page"),
     url(r"^home/$", theme.dashboard, name="dashboard"),
     url(r"^$", theme.home_router, name="home_router"),
diff --git a/irods/README.md b/irods/README.md
index a4397940d5..745d20cad0 100644
--- a/irods/README.md
+++ b/irods/README.md
@@ -1,7 +1,6 @@
 ## Using local federated iRODS
 
-The scripts herein are a one-way street that update the configuration of HydroShare to use a locally deployed federated pair 
-of iCAT v.4.1.8 servers in Docker.
+The scripts herein are a one-way street that update the configuration of HydroShare to use a locally deployed iCAT server in Docker.
 
 Effected files:
 -	modified:   hsctl
@@ -12,25 +11,17 @@ Effected files:
 
 Generate a `local_settings.py` file by copying `hydroshare/local_settings.template` to `hydroshare/local_settings.py`
 
-From within the `irods` directory, run the script named **use-local-irods.sh**
+From within the `irods` directory, run the script named **partial_build.sh**
 
 **NOTE:** This script requires that package `jq` be installed on the host from which the script is being run. If it's not present it can be installed by invoking `sudo apt-get install jq` in Ubuntu (or similar) environments. 
 
 ```bash
 $ cd irods
-$ ./use-local-irods.sh
+$ ./partial_build.sh
 ```
 
-  - Optionally the user can choose to persist the iRODS vault and datbase to their local filesystem so that these files would be available even if the containers are destroyed and recreated.
-
-  ```bash
-  $ cd irods
-  $ ./use-local-irods.sh --persist
-  ```
-  The `--persist` flag will create two new directories as `/home/${USER}/icat1` and `/home/${USER}/icat2` where iRODS vault and database files will be persisted. These directories will remain in place until the user manually destroys them.
-
-The script will run, deploy and federate two iCAT servers, and modify the three aforementioned files to be configured to use the 
-newly created iRODS Docker servers. Once the script has completed, return back the the main `hydroshare` directory and run
+The script will run and deploy an iCAT server, and modify the three aforementioned files to be configured to use the 
+newly created iRODS Docker server. Once the script has completed, return back the the main `hydroshare` directory and run
 the **hsctl** script as you normally would.
 
 ```bash
@@ -58,7 +49,6 @@ CONTAINER ID        IMAGE                               COMMAND
 82aefa42b4f6        makuk66/docker-solr:4.10.4          "sh -c '/bin/bash /op"   17 minutes ago      Up 17 minutes       0.0.0.0:32786->8983/tcp                                                               solr
 e67b799315ab        hydroshare/hs_postgres:9.4.7         "/docker-entrypoint.s"   17 minutes ago      Up 17 minutes       5432/tcp                                                                              postgis
 52c67ebb6b57        rabbitmq:3.5                        "/docker-entrypoint.s"   17 minutes ago      Up 17 minutes       4369/tcp, 5671-5672/tcp, 25672/tcp                                                    rabbitmq
-0b1a6c66a585        mjstealey/docker-irods-icat:4.1.8   "/irods-docker-entryp"   22 minutes ago      Up 22 minutes       1248/tcp, 5432/tcp, 20000-20199/tcp, 0.0.0.0:32785->22/tcp, 0.0.0.0:32784->1247/tcp   users.local.org
 a4d976bcdeb7        mjstealey/docker-irods-icat:4.1.8   "/irods-docker-entryp"   22 minutes ago      Up 22 minutes       1248/tcp, 5432/tcp, 20000-20199/tcp, 0.0.0.0:32783->1247/tcp                          data.local.org
 ```
 
@@ -66,11 +56,11 @@ a4d976bcdeb7        mjstealey/docker-irods-icat:4.1.8   "/irods-docker-entryp"
 
 It can be useful to retain the system state after all containers have been stopped or a system running HydroShare with local iRODS has been shutdown.
 
-Assuming that the user has initially started with the `--persist` option, a normal initial deployment would look like this.
+A normal initial deployment would look like this.
 
 ```bash
 $ cd irods
-$ ./use-local-irods.sh --persist
+$ ./partial_build.sh
 $ cd ../
 $ ./hsctl rebuild --db
 ```
@@ -83,7 +73,7 @@ To bring all containers back up, and to have the state of the local iRODS contai
 
 ```bash
 $ cd irods
-$ ./use-local-irods.sh --persist
+$ ./partial_build.sh
 $ cd ../
 $ ./hsctl start
 ```
diff --git a/irods/config-hsctl-compose.sh b/irods/config-hsctl-compose.sh
index 3b396c469c..64dde10085 100755
--- a/irods/config-hsctl-compose.sh
+++ b/irods/config-hsctl-compose.sh
@@ -5,8 +5,6 @@ source env-files/use-local-irods.env
 # set extra_hosts in docker-compose-local-irods.template
 IRODS_DATA_HOSTNAME=$IRODS_HOST
 IRODS_DATA_IP=$(docker exec ${IRODS_HOST} /sbin/ip -f inet -4 -o addr | grep eth | cut -d '/' -f 1 | rev | cut -d ' ' -f 1 | rev)
-IRODS_USER_HOSTNAME=$HS_USER_ZONE_HOST
-IRODS_USER_IP=$(docker exec ${HS_USER_ZONE_HOST} /sbin/ip -f inet -4 -o addr | grep eth | cut -d '/' -f 1 | rev | cut -d ' ' -f 1 | rev)
 
 echo "CONFIGURE: docker-compose-local-irods.template"
 echo "$ sed -i s/\<IRODS_DATA_HOSTNAME\>/${IRODS_DATA_HOSTNAME}/ ../scripts/templates/docker-compose-local-irods.template"
@@ -15,12 +13,6 @@ sed -i "s/\<IRODS_DATA_HOSTNAME\>/"${IRODS_DATA_HOSTNAME}"/" ../scripts/template
 echo "$ sed -i s/\<IRODS_DATA_IP\>/${IRODS_DATA_IP}/ ../scripts/templates/docker-compose-local-irods.template"
 sed -i "s/\<IRODS_DATA_IP\>/"${IRODS_DATA_IP}"/" ../scripts/templates/docker-compose-local-irods.template
 
-echo "$ sed -i s/\<IRODS_USER_HOSTNAME\>/${IRODS_USER_HOSTNAME}/ ../scripts/templates/docker-compose-local-irods.template"
-sed -i "s/\<IRODS_USER_HOSTNAME\>/"${IRODS_USER_HOSTNAME}"/" ../scripts/templates/docker-compose-local-irods.template
-
-echo "$ sed -i s/\<IRODS_USER_IP\>/${IRODS_USER_IP}/ ../scripts/templates/docker-compose-local-irods.template"
-sed -i "s/\<IRODS_USER_IP\>/"${IRODS_USER_IP}"/" ../scripts/templates/docker-compose-local-irods.template
-
 # update hsctl to use-local-irods
 USE_LOCAL_IRODS=true
 echo "CONFIGURE: hsctl"
diff --git a/irods/create_user.sh b/irods/create_user.sh
deleted file mode 100755
index 58f6a15fe2..0000000000
--- a/irods/create_user.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env bash
-
-# HydroShare user zone control script to create iRODS users on-demand corresponding to HydroShare users
-# AUthor: Hong Yi <hongyi@renci.org>
-
-HS_WWW_IRODS_PROXY_USER=wwwHydroProxy
-HS_WWW_IRODS_ZONE=hydroshareZone
-HS_USER_IRODS_ZONE=hydroshareuserZone
-
-echo "  - iadmin mkuser $1 rodsuser"
-iadmin mkuser $1 rodsuser
-echo "  - iadmin moduser $1 password $2"
-iadmin moduser $1 password $2
-echo "  - ichmod -rM own ${HS_WWW_IRODS_PROXY_USER}#${HS_WWW_IRODS_ZONE} /${HS_USER_IRODS_ZONE}/home"
-ichmod -rM own ${HS_WWW_IRODS_PROXY_USER}#${HS_WWW_IRODS_ZONE} /${HS_USER_IRODS_ZONE}/home
-echo "  - ichmod -rM inherit /${HS_USER_IRODS_ZONE}/home/$1"
-ichmod -rM inherit /${HS_USER_IRODS_ZONE}/home/$1
\ No newline at end of file
diff --git a/irods/delete_user.sh b/irods/delete_user.sh
deleted file mode 100755
index 474d826814..0000000000
--- a/irods/delete_user.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-
-# HydroShare user zone control script to delete a specific iRODS users on-demand corresponding to HydroShare users
-# AUthor: Hong Yi <hongyi@renci.org>
-# change permission so that hsuserproxy can delete all collections on behalf of the user before deleting the user
-ichmod -rM own hsuserproxy /hydroshareuserZone/home/$1
-ils /hydroshareuserZone/home/$1 | rev | cut -d '/' -f 1 | rev | sed '1d' > resource_list
-while read line; do irm -rf /hydroshareuserZone/home/$1/$line; done < <(cat resource_list)
-
-iadmin rmuser $1
-
-exit 0;
\ No newline at end of file
diff --git a/irods/env-files/use-local-irods.env b/irods/env-files/use-local-irods.env
index 892c138b2d..d5e3ae9605 100644
--- a/irods/env-files/use-local-irods.env
+++ b/irods/env-files/use-local-irods.env
@@ -1,7 +1,5 @@
 #!/usr/bin/env bash
 
-SHARED_NEG_KEY=hydroshareZonehydroshareuserZone
-
 # Local resource iRODS configuration
 USE_IRODS=True
 IRODS_ROOT='/tmp'
@@ -18,19 +16,4 @@ IRODS_AUTH='wwwHydroProxy'
 IRODS_GLOBAL_SESSION=True
 
 # Remote user zone iRODS configuration
-REMOTE_USE_IRODS=True
-
-# hsuserproxy system user configuration used to create hydroshare iRODS users on-demand
-HS_USER_ZONE_HOST='users.local.org'
-LINUX_ADMIN_USER_FOR_HS_USER_ZONE='hsuserproxy'
-LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE='hsuserproxy'
-PUBLIC_KEY_FILE_FOR_HS_USER_ZONE='/home/hsuserproxy/.ssh/authorized_keys'
-PRIVATE_KEY_FILE_FOR_HS_USER_ZONE='/hydroshare/.ssh/id_ed25519_hs'
-LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD='/home/hsuserproxy/create_user.sh'
-LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD='/home/hsuserproxy/delete_user.sh'
-
-# HydroShare iRODS proxy user in the federated user zone
-HS_IRODS_PROXY_USER_IN_USER_ZONE='localHydroProxy'
-
-HS_IRODS_USER_ZONE_DEF_RES='hydroshareLocalResc'
-HS_USER_IRODS_ZONE='hydroshareuserZone'
\ No newline at end of file
+REMOTE_USE_IRODS=True
\ No newline at end of file
diff --git a/irods/partial_build.sh b/irods/partial_build.sh
index 7f604912d2..e2c991cfbc 100755
--- a/irods/partial_build.sh
+++ b/irods/partial_build.sh
@@ -3,25 +3,6 @@
 source env-files/use-local-irods.env
 
 RUN_ON_DATA="docker exec --interactive ${IRODS_HOST} sh -C"
-RUN_ON_USER="docker exec --interactive ${HS_USER_ZONE_HOST} sh -C"
-
-echo "------------------------------------------------------------"
-echo "INFO: add irods_environment.json files for ${HS_USER_ZONE_HOST}"
-
-echo "Create the ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}.json file"
-echo "  - jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}.json"
-jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}.json
-docker cp env-files/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}.json ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.irods/irods_environment.json
-
-echo "Create the rods@${HS_USER_ZONE_HOST}.json file"
-echo "  - jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "rods" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/rods@${HS_USER_ZONE_HOST}.json"
-jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "rods" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/rods@${HS_USER_ZONE_HOST}.json
-
-echo "Copy the ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}.json file to ${HS_USER_ZONE_HOST}"
-docker exec ${HS_USER_ZONE_HOST} mkdir -p /root/.irods
-docker exec ${HS_USER_ZONE_HOST} mkdir -p /home/rods/.irods
-docker cp env-files/rods@${HS_USER_ZONE_HOST}.json ${HS_USER_ZONE_HOST}:/root/.irods/irods_environment.json
-docker cp env-files/rods@${HS_USER_ZONE_HOST}.json ${HS_USER_ZONE_HOST}:/home/rods/.irods/irods_environment.json
 
 echo "------------------------------------------------------------"
 echo "INFO: add irods_environment.json files for ${IRODS_HOST}"
@@ -45,21 +26,6 @@ docker cp env-files/rods@${IRODS_HOST}.json ${IRODS_HOST}:/root/.irods/irods_env
 docker cp env-files/rods@${IRODS_HOST}.json ${IRODS_HOST}:/home/rods/.irods/irods_environment.json
 
 echo "echo rods | iinit" | $RUN_ON_DATA
-echo "echo rods | iinit" | $RUN_ON_USER
-
-echo "INFO: Federate ${IRODS_ZONE} with ${HS_USER_IRODS_ZONE}"
-
-echo "------------------------------------------------------------"
-echo "INFO: federation configuration for ${IRODS_HOST} - modify /etc/irods/server_config.json"
-docker exec ${IRODS_HOST} sh -c "jq '.federation[0].catalog_provider_hosts[0]=\"${HS_USER_ZONE_HOST}\" | .federation[0].zone_name=\"${HS_USER_IRODS_ZONE}\" | .federation[0].zone_key=\"${HS_USER_IRODS_ZONE}_KEY\" | .federation[0].negotiation_key=\"${SHARED_NEG_KEY}\"' /etc/irods/server_config.json > /tmp/tmp.json"
-docker exec ${IRODS_HOST} sh -c "cat /tmp/tmp.json | jq '.' > /etc/irods/server_config.json && chown irods:irods /etc/irods/server_config.json && rm -f /tmp/tmp.json"
-docker exec ${IRODS_HOST} sh -c "cat /etc/irods/server_config.json | jq '.federation'"
-
-echo "------------------------------------------------------------"
-echo "INFO: federation configuration for ${HS_USER_ZONE_HOST}"
-docker exec ${HS_USER_ZONE_HOST} sh -c "jq '.federation[0].catalog_provider_hosts[0]=\"${IRODS_HOST}\" | .federation[0].zone_name=\"${IRODS_ZONE}\" | .federation[0].zone_key=\"${IRODS_ZONE}_KEY\" | .federation[0].negotiation_key=\"${SHARED_NEG_KEY}\"' /etc/irods/server_config.json > /tmp/tmp.json"
-docker exec ${HS_USER_ZONE_HOST} sh -c "cat /tmp/tmp.json | jq '.' > /etc/irods/server_config.json && chown irods:irods /etc/irods/server_config.json && rm -f /tmp/tmp.json"
-docker exec ${HS_USER_ZONE_HOST} sh -c "cat /etc/irods/server_config.json | jq '.federation'"
 
 echo "------------------------------------------------------------"
 echo "INFO: make resource ${IRODS_DEFAULT_RESOURCE} in ${IRODS_ZONE}"
@@ -73,61 +39,4 @@ echo " - iadmin mkuser $IRODS_USERNAME rodsuser"
 echo "iadmin moduser $IRODS_USERNAME password $IRODS_AUTH" | $RUN_ON_DATA
 echo " - iadmin moduser $IRODS_USERNAME password $IRODS_AUTH" 
 
-echo "------------------------------------------------------------"
-echo "INFO: make ${HS_IRODS_PROXY_USER_IN_USER_ZONE} and ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} in ${HS_USER_ZONE_HOST}"
-echo "iadmin mkuser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} rodsuser" | $RUN_ON_USER
-echo " - iadmin mkuser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} rodsuser"
-echo "iadmin moduser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} password ${HS_AUTH}" | $RUN_ON_USER
-echo " - iadmin moduser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} password ${HS_AUTH}"
-
-echo "------------------------------------------------------------"
-echo "INFO: create and configure rodsadmin"
-echo "iadmin mkuser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} rodsadmin" | $RUN_ON_USER
-echo " - iadmin mkuser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} rodsadmin"
-echo "iadmin moduser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} password ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}" | $RUN_ON_USER
-echo " - iadmin moduser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} password ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}"
-
-echo "------------------------------------------------------------"
-echo "INFO: make resource ${HS_IRODS_USER_ZONE_DEF_RES} is able to access in ${HS_USER_ZONE_HOST}"
-echo "iadmin mkresc ${HS_IRODS_USER_ZONE_DEF_RES} unixfilesystem ${HS_USER_ZONE_HOST}:/var/lib/irods/iRODS/Vault" | $RUN_ON_USER
-echo " - iadmin mkresc ${HS_IRODS_USER_ZONE_DEF_RES} unixfilesystem ${HS_USER_ZONE_HOST}:/var/lib/irods/iRODS/Vault"
-
-# Tips for piping strings to docker exec container terminal https://gist.github.com/ElijahLynn/72cb111c7caf32a73d6f#file-pipe_to_docker_examples
-echo "iadmin mkzone ${HS_USER_IRODS_ZONE} remote ${HS_USER_ZONE_HOST}:1247" | $RUN_ON_DATA
-echo " - iadmin mkzone ${HS_USER_IRODS_ZONE} remote ${HS_USER_ZONE_HOST}:1247"
-sleep 1s
-echo "iadmin mkzone ${IRODS_ZONE} remote ${IRODS_HOST}:1247" | $RUN_ON_USER
-echo " - iadmin mkzone ${IRODS_ZONE} remote ${IRODS_HOST}:1247"
-sleep 1s
-
-echo "------------------------------------------------------------"
-echo "INFO: init the ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} in ${HS_USER_ZONE_HOST}"
-docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c "export IRODS_HOST=${HS_USER_ZONE_HOST} && export IRODS_PORT=${IRODS_PORT} && export IRODS_USER_NAME=${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} && export IRODS_PASSWORD=${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE} && iinit ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}"
-
-docker exec ${IRODS_HOST} chown -R ${IRODS_USERNAME}:${IRODS_USERNAME} /home/${IRODS_USERNAME}/
-docker exec ${HS_USER_ZONE_HOST} chown -R ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}:${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/
-
-echo "------------------------------------------------------------"
-echo "INFO: give ${IRODS_USERNAME} own rights over ${HS_USER_IRODS_ZONE}/home"
-echo "iadmin mkuser "${IRODS_USERNAME}"#"${IRODS_ZONE}" rodsuser" | $RUN_ON_USER
-echo " - iadmin mkuser "${IRODS_USERNAME}"#"${IRODS_ZONE}" rodsuser" 
-echo "iadmin mkuser rods#"${IRODS_ZONE}" rodsuser" | $RUN_ON_USER
-echo " - iadmin mkuser rods#"${IRODS_ZONE}" rodsuser"
-
-echo "------------------------------------------------------------"
-echo "INFO: update permissions"
-echo "ichmod -r -M own "${IRODS_USERNAME}"#"${IRODS_ZONE}" /${HS_USER_IRODS_ZONE}/home" | $RUN_ON_USER
-echo " - ichmod -r -M own "${IRODS_USERNAME}"#"${IRODS_ZONE}" /${HS_USER_IRODS_ZONE}/home" 
-echo "ichmod -r -M own rods#"${IRODS_ZONE}" /${HS_USER_IRODS_ZONE}/home/${HS_IRODS_PROXY_USER_IN_USER_ZONE}" | $RUN_ON_USER
-echo " - ichmod -r -M own rods#"${IRODS_ZONE}" /${HS_USER_IRODS_ZONE}/home/${HS_IRODS_PROXY_USER_IN_USER_ZONE}"
-
-echo "------------------------------------------------------------"
-echo "INFO: edit permissions in /home"
-echo "ichmod -r -M own "${HS_IRODS_PROXY_USER_IN_USER_ZONE}" /${HS_USER_IRODS_ZONE}/home" | $RUN_ON_USER
-echo " - ichmod -r -M own "${HS_IRODS_PROXY_USER_IN_USER_ZONE}" /${HS_USER_IRODS_ZONE}/home"
-
-echo "------------------------------------------------------------"
-echo "INFO: set ${HS_USER_IRODS_ZONE}/home to inherit"
-echo "ichmod -r -M inherit /"${HS_USER_IRODS_ZONE}"/home" | $RUN_ON_USER
-echo " - ichmod -r -M inherit /"${HS_USER_IRODS_ZONE}"/home"
-echo
+docker exec ${IRODS_HOST} chown -R ${IRODS_USERNAME}:${IRODS_USERNAME} /home/${IRODS_USERNAME}/
\ No newline at end of file
diff --git a/irods/set_userzone_quota.sh b/irods/set_userzone_quota.sh
deleted file mode 100755
index 5d9ca86a18..0000000000
--- a/irods/set_userzone_quota.sh
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-# HydroShare user zone control script to set quota for iRODS users on-demand
-# Usage: set_userzone_quota.sh <username> <quota>
-# Example: set_userzone_quota.sh johndoe 1000000000
-# Quota is set in bytes
-
-# This script currently not in use. Rather than enforcing quotas in the UserZone at the iRODS level, at this time, userZone enforcement is accomplished with a hybrid approach--the iRODS server itself does not enforce quotas.
-# Instead, DEFAULT_SUPPORT_EMAIL is notified when a user, having exceeded their quota allotment in HS, continues to put files into their UserZone.
-
-# Some potential approaches for implementation in the future (not limited to the following) include:
-# 1. iRods policy / rules
-#  a. Set a metadata AVU in the iRods userzone that will represent the remaining quota for the user
-#  b. Implement a rule triggered by the pep_api_data_obj_put_pre PEP that will pull in the AVU set in #1 and abort the put operation if the user has exceeded their quota
-
-# 2. iRods quota system
-#  a. use the iRods quota system in the userZone (suq, sgq, etc.) to set the quota for the user
-#     for example this could look something like:
-
-HS_USER_IRODS_ZONE=hydroshareuserZone
-# https://docs.irods.org/4.2.11/icommands/administrator/#suq
-suq $1#${HS_USER_IRODS_ZONE} 'total' $2
-
-# suq functionality removed in 4.3 so would require sgq
-# https://docs.irods.org/4.2.11/icommands/administrator/#sgq
-exit 0;
\ No newline at end of file
diff --git a/irods/use-local-irods.sh b/irods/use-local-irods.sh
deleted file mode 100755
index 5f79535be0..0000000000
--- a/irods/use-local-irods.sh
+++ /dev/null
@@ -1,256 +0,0 @@
-#!/usr/bin/env bash
-
-# use-local-irods.sh
-# Federated local iRODS for HydroShare in Docker
-# Author: Michael Stealey <michael.j.stealey@gmail.com>
-
-# TODO: do we still need to federate irods zones? Likely can remove this...
-# Pull in environment variables to use for federated local iRODS
-source env-files/use-local-irods.env
-ADD_IRODS_TO_GROUP=$(id -g ${USER})
-ADD_POSTGRES_TO_GROUP=$(id -g ${USER})
-
-# Remove ${IRODS_HOST} and ${HS_USER_ZONE_HOST} containers if the already exist
-ISICAT1=$(docker ps -a | rev | cut -d ' ' -f 1 | rev | grep ${IRODS_HOST})
-ISICAT2=$(docker ps -a | rev | cut -d ' ' -f 1 | rev | grep ${HS_USER_ZONE_HOST})
-
-if [[ "${ISICAT1}" == "${IRODS_HOST}" ]]; then
-    echo "REMOVE: ${IRODS_HOST}"
-    echo "  - docker stop ${IRODS_HOST} && docker rm -fv ${IRODS_HOST}"
-    docker stop ${IRODS_HOST} && docker rm -fv ${IRODS_HOST}
-fi
-if [[ "${ISICAT2}" == "${HS_USER_ZONE_HOST}" ]]; then
-    echo "REMOVE: ${HS_USER_ZONE_HOST}"
-    echo "  - docker stop ${HS_USER_ZONE_HOST} && docker rm -fv ${HS_USER_ZONE_HOST}"
-    docker stop ${HS_USER_ZONE_HOST} && docker rm -fv ${HS_USER_ZONE_HOST}
-fi
-
-cd ..
-echo " - rm -rf .ssh"
-rm -rf .ssh
-echo " - mkdir .ssh"
-mkdir .ssh
-echo " - ssh-keygen -t ed25519 -f .ssh/id_ed25519_hs -N ''"
-ssh-keygen -t ed25519 -f .ssh/id_ed25519_hs -N '' -C "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}"
-cd irods
-
-# create irods_environment.json file for rods user
-echo "INFO: create irods_environment.json file for rods user"
-echo "  - jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/rods@${HS_USER_ZONE_HOST}.json"
-jq -n --arg h "${HS_USER_ZONE_HOST}" --argjson p ${IRODS_PORT} --arg z "${HS_USER_IRODS_ZONE}" --arg n "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}" '{"irods_host": $h, "irods_port": $p, "irods_zone_name": $z, "irods_user_name": $n}' > env-files/rods@${HS_USER_ZONE_HOST}.json
-# Build the irods userzone image
-echo "BUILD: hydroshare/hs-irods:4.2.6-buster"
-cd ..
-echo " - docker build -t hs-irods-users -f Dockerfile-users-irods . "
-docker build -t hs-irods-users -f Dockerfile-users-irods .
-cd irods
-
-if [[ "${1}" == "--persist" ]]; then
-    # mkdir for ${IRODS_HOST} and ${HS_USER_ZONE_HOST} persistence files
-    if [[ ! -d /home/${USER}/icat1 ]]; then
-        mkdir -p /home/${USER}/icat1/vault
-        mkdir -p /home/${USER}/icat1/pgdata
-    fi
-    if [[ ! -d /home/${USER}/icat2 ]]; then
-        mkdir -p /home/${USER}/icat2/vault
-        mkdir -p /home/${USER}/icat2/pgdata
-    fi
-    # Create ${IRODS_HOST} container from irods v.4.2.6
-    echo "CREATE: ${IRODS_HOST} container"
-    docker run -d --name ${IRODS_HOST} \
-        -v /home/${USER}/icat1/vault:/var/lib/irods/iRODS/Vault \
-        -v /home/${USER}/icat1/pgdata:/var/lib/postgresql/data \
-        -e ADD_IRODS_TO_GROUP=${ADD_IRODS_TO_GROUP} \
-        -e ADD_POSTGRES_TO_GROUP=${ADD_POSTGRES_TO_GROUP} \
-        -e IRODS_ZONE_NAME=${IRODS_ZONE} \
-        -e IRODS_SERVER_ZONE_KEY=${IRODS_ZONE}_KEY \
-        -e IRODS_DATABASE_SERVER_HOSTNAME=${IRODS_HOST} \
-        -p ${IRODS_PORT} \
-        --hostname ${IRODS_HOST} \
-        hydroshare/hs-irods:4.2.6-buster
-
-    # Create ${HS_USER_ZONE_HOST} container from irods v.4.2.6
-    echo "CREATE: ${HS_USER_ZONE_HOST} container"
-    docker run -d --name ${HS_USER_ZONE_HOST} \
-        -v /home/${USER}/icat2/vault:/var/lib/irods/iRODS/Vault \
-        -v /home/${USER}/icat2/pgdata:/var/lib/postgresql/data \
-        -v $(pwd)/../.ssh/id_ed25519_hs.pub:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/id_ed25519_hs.pub \
-        -e ADD_IRODS_TO_GROUP=${ADD_IRODS_TO_GROUP} \
-        -e ADD_POSTGRES_TO_GROUP=${ADD_POSTGRES_TO_GROUP} \
-        -e IRODS_ZONE_NAME=${HS_USER_IRODS_ZONE} \
-        -e IRODS_SERVER_ZONE_KEY=${HS_USER_IRODS_ZONE}_KEY \
-        -e IRODS_DATABASE_SERVER_HOSTNAME=${HS_USER_ZONE_HOST} \
-        -p 1247 \
-        -p 22 \
-        --hostname ${HS_USER_ZONE_HOST} \
-        hs-irods-users:latest
-else
-    # Create ${IRODS_HOST} container from irods v.4.2.6
-    echo "CREATE: ${IRODS_HOST} container"
-    docker run -d --name ${IRODS_HOST} \
-        -e IRODS_ZONE_NAME=${IRODS_ZONE} \
-        -e IRODS_SERVER_ZONE_KEY=${IRODS_ZONE}_KEY \
-        -e IRODS_DATABASE_SERVER_HOSTNAME=${IRODS_HOST} \
-        -p ${IRODS_PORT} \
-        --hostname ${IRODS_HOST} \
-        hydroshare/hs-irods:4.2.6-buster
-
-    # Create ${HS_USER_ZONE_HOST} container from irods v.4.2.6
-    echo "CREATE: ${HS_USER_ZONE_HOST} container"
-    docker run -d --name ${HS_USER_ZONE_HOST} \
-        -e IRODS_ZONE_NAME=${HS_USER_IRODS_ZONE} \
-        -e IRODS_SERVER_ZONE_KEY=${HS_USER_IRODS_ZONE}_KEY \
-        -e IRODS_DATABASE_SERVER_HOSTNAME=${HS_USER_ZONE_HOST} \
-        -p 1247 \
-        -p 22 \
-        --hostname ${HS_USER_ZONE_HOST} \
-        hs-irods-users:latest
-    echo "Create .ssh directory on ${HS_USER_ZONE_HOST}"
-    echo "docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c \"mkdir -p /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh\""
-    docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c "mkdir -p /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh"
-    echo "Docker cp ../.ssh/id_ed25519_hs.pub ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/id_ed25519_hs.pub"
-    docker cp ../.ssh/id_ed25519_hs.pub ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/id_ed25519_hs.pub
-    echo "docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c \"cat /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/id_ed25519_hs.pub >> /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/authorized_keys\""
-    docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c "cat /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/id_ed25519_hs.pub >> /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.ssh/authorized_keys"
-fi
-
-# wait for ${IRODS_HOST} and ${HS_USER_ZONE_HOST} to finish standing up
-echo "INFO: allow data.local.org and users.local.org to stand up and be configured"
-for pc in $(seq 20 -1 1); do
-    echo -ne "$pc ...\033[0K\r" && sleep 1;
-done
-
-# Install iproute2 and jq on ${IRODS_HOST}
-echo "INFO: running apt-get update on ${IRODS_HOST}"
-docker exec ${IRODS_HOST} sh -c "apt-get update"
-echo "[root@${IRODS_HOST}]$ apt-get install -y iproute2 jq"
-docker exec ${IRODS_HOST} sh -c "apt-get install -y iproute2 jq"
-
-# Install OpenSSH iproute2 and jq on ${HS_USER_ZONE_HOST}
-echo "INFO: running apt-get update on ${HS_USER_ZONE_HOST}"
-docker exec ${HS_USER_ZONE_HOST} sh -c "apt-get update"
-echo "[root@${HS_USER_ZONE_HOST}]$ apt-get install -y iproute2 jq"
-docker exec ${HS_USER_ZONE_HOST} sh -c "apt-get install -y iproute2 jq"
-
-echo "INFO: Install OpenSSH on ${HS_USER_ZONE_HOST}"
-echo "[root@${HS_USER_ZONE_HOST}]$ apt-get update"
-docker exec ${HS_USER_ZONE_HOST} sh -c "apt-get update"
-echo "[root@${HS_USER_ZONE_HOST}]$ apt-get install -y openssh-client openssh-server && mkdir /var/run/sshd && sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config && sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd && /etc/init.d/ssh restart"
-docker exec ${HS_USER_ZONE_HOST} sh -c "apt-get install -y openssh-client openssh-server && mkdir /var/run/sshd && sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config && sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd && /etc/init.d/ssh restart"
-
-# Create Linux user ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} on ${HS_USER_ZONE_HOST}
-echo "INFO: Create Linux user ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} on ${HS_USER_ZONE_HOST}"
-echo "[root@${HS_USER_ZONE_HOST}]$ useradd -m -p ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE} -s /bin/bash ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}"
-docker exec ${HS_USER_ZONE_HOST} sh -c "useradd -m -p ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE} -s /bin/bash ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}"
-echo "[root@${HS_USER_ZONE_HOST}]$ cp create_user.sh delete_user.sh /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}"
-docker cp create_user.sh ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}
-docker cp delete_user.sh ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}
-docker exec ${HS_USER_ZONE_HOST} chown -R ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}:${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}
-docker exec ${HS_USER_ZONE_HOST} sh -c "echo "${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}":"${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}" | chpasswd"
-
-# Make ${IRODS_HOST} and ${HS_USER_ZONE_HOST} aware of each other via /etc/hosts
-echo "INFO: update /etc/hosts"
-ICAT1IP=$(docker exec ${IRODS_HOST} /sbin/ip -f inet -4 -o addr | grep eth | cut -d '/' -f 1 | rev | cut -d ' ' -f 1 | rev)
-ICAT2IP=$(docker exec ${HS_USER_ZONE_HOST} /sbin/ip -f inet -4 -o addr | grep eth | cut -d '/' -f 1 | rev | cut -d ' ' -f 1 | rev)
-echo "[root@${IRODS_HOST}]$ echo \"'${ICAT2IP}' ${HS_USER_ZONE_HOST}\" >> /etc/hosts"
-docker exec ${IRODS_HOST} sh -c 'echo "'${ICAT2IP}' '${HS_USER_ZONE_HOST}'" >> /etc/hosts'
-echo "[root@${HS_USER_ZONE_HOST}]$ echo \"'${ICAT1IP}' ${IRODS_HOST}\" >> /etc/hosts"
-docker exec ${HS_USER_ZONE_HOST} sh -c 'echo "'${ICAT1IP}' '${IRODS_HOST}'" >> /etc/hosts'
-
-# Generate .env files for rods@${IRODS_HOST} and rods@${HS_USER_ZONE_HOST}
-printf "IRODS_HOST=${ICAT1IP}\nIRODS_PORT=${IRODS_PORT}\nIRODS_USER_NAME=rods\nIRODS_ZONE_NAME=${IRODS_ZONE}\nIRODS_PASSWORD=rods" > env-files/rods@${IRODS_HOST}.env
-printf "IRODS_HOST=${ICAT2IP}\nIRODS_PORT=1247\nIRODS_USER_NAME=rods\nIRODS_ZONE_NAME=${HS_USER_IRODS_ZONE}\nIRODS_PASSWORD=rods" > env-files/rods@${HS_USER_ZONE_HOST}.env
-
-# Federate ${IRODS_ZONE} with ${HS_USER_IRODS_ZONE}
-echo "INFO: make remote zone for each"
-echo "[rods@${IRODS_HOST}]$ iadmin mkzone ${HS_USER_IRODS_ZONE} remote ${ICAT2IP}:1247"
-sleep 1s
-docker run --rm --env-file env-files/rods@${IRODS_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    iadmin mkzone ${HS_USER_IRODS_ZONE} remote ${ICAT2IP}:1247
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin mkzone ${IRODS_ZONE} remote ${ICAT1IP}:${IRODS_PORT}"
-sleep 1s
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    iadmin mkzone ${IRODS_ZONE} remote ${ICAT1IP}:${IRODS_PORT}
-
-# modify /etc/irods/server_config.json
-echo "INFO: federation configuration for ${IRODS_HOST}"
-docker exec ${IRODS_HOST} sh -c "jq '.federation[0].icat_host=\"${HS_USER_ZONE_HOST}\" | .federation[0].zone_name=\"${HS_USER_IRODS_ZONE}\" | .federation[0].zone_key=\"${HS_USER_IRODS_ZONE}_KEY\" | .federation[0].negotiation_key=\"${SHARED_NEG_KEY}\"' /etc/irods/server_config.json > /tmp/tmp.json"
-docker exec ${IRODS_HOST} sh -c "cat /tmp/tmp.json | jq '.' > /etc/irods/server_config.json && chown irods:irods /etc/irods/server_config.json && rm -f /tmp/tmp.json"
-docker exec ${IRODS_HOST} sh -c "cat /etc/irods/server_config.json | jq '.federation'"
-
-echo "INFO: federation configuration for ${HS_USER_ZONE_HOST}"
-docker exec ${HS_USER_ZONE_HOST} sh -c "jq '.federation[0].icat_host=\"${IRODS_HOST}\" | .federation[0].zone_name=\"${IRODS_ZONE}\" | .federation[0].zone_key=\"${IRODS_ZONE}_KEY\" | .federation[0].negotiation_key=\"${SHARED_NEG_KEY}\"' /etc/irods/server_config.json > /tmp/tmp.json"
-docker exec ${HS_USER_ZONE_HOST} sh -c "cat /tmp/tmp.json | jq '.' > /etc/irods/server_config.json && chown irods:irods /etc/irods/server_config.json && rm -f /tmp/tmp.json"
-docker exec ${HS_USER_ZONE_HOST} sh -c "cat /etc/irods/server_config.json | jq '.federation'"
-
-# make resource ${IRODS_DEFAULT_RESOURCE} in ${IRODS_ZONE}
-echo "[rods@${IRODS_HOST}]$ iadmin mkresc ${IRODS_DEFAULT_RESOURCE} unixfilesystem ${IRODS_HOST}:/var/lib/irods/iRODS/Vault"
-docker run --rm --env-file env-files/rods@${IRODS_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkresc ${IRODS_DEFAULT_RESOURCE} unixfilesystem ${IRODS_HOST}:/var/lib/irods/iRODS/Vault"
-
-# make user ${IRODS_USERNAME} in ${IRODS_ZONE}
-echo "[rods@${IRODS_HOST}]$ iadmin mkuser ${IRODS_USERNAME} rodsuser"
-echo "[rods@${IRODS_HOST}]$ iadmin moduser ${IRODS_USERNAME} password ${IRODS_AUTH}"
-docker run --rm --env-file env-files/rods@${IRODS_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkuser ${IRODS_USERNAME} rodsuser && iadmin moduser ${IRODS_USERNAME} password ${IRODS_AUTH}"
-
-# make ${HS_IRODS_PROXY_USER_IN_USER_ZONE} and ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} in ${HS_USER_ZONE_HOST}
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin mkuser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} rodsuser"
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin moduser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} password ${IRODS_AUTH}"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkuser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} rodsuser && iadmin moduser ${HS_IRODS_PROXY_USER_IN_USER_ZONE} password ${IRODS_AUTH}"
-
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin mkuser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} rodsadmin"
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin moduser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} password ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkuser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} rodsadmin && iadmin moduser ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} password ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}"
-
-# make resource ${HS_IRODS_USER_ZONE_DEF_RES} in ${HS_USER_ZONE_HOST}
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin mkresc ${HS_IRODS_USER_ZONE_DEF_RES} unixfilesystem ${HS_USER_ZONE_HOST}:/var/lib/irods/iRODS/Vault"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkresc ${HS_IRODS_USER_ZONE_DEF_RES} unixfilesystem ${HS_USER_ZONE_HOST}:/var/lib/irods/iRODS/Vault"
-
-# iint the ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} in ${HS_USER_ZONE_HOST}
-echo "[${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}@${HS_USER_ZONE_HOST}]$ iinit"
-docker exec -u ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} ${HS_USER_ZONE_HOST} sh -c "export IRODS_HOST=${ICAT2IP} && export IRODS_PORT=${IRODS_PORT} && export IRODS_USER_NAME=${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} && export IRODS_PASSWORD=${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE} && iinit ${LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE}"
-
-# add irods_environment.json file for rods user
-docker cp env-files/rods@${HS_USER_ZONE_HOST}.json ${HS_USER_ZONE_HOST}:/home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.irods/irods_environment.json
-docker exec ${HS_USER_ZONE_HOST} chown ${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}:${LINUX_ADMIN_USER_FOR_HS_USER_ZONE} /home/${LINUX_ADMIN_USER_FOR_HS_USER_ZONE}/.irods/irods_environment.json
-
-# give ${IRODS_USERNAME} own rights over ${HS_USER_IRODS_ZONE}/home
-echo "[rods@${HS_USER_ZONE_HOST}]$ iadmin mkuser ${IRODS_USERNAME}#${IRODS_ZONE} rodsuser"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "iadmin mkuser "${IRODS_USERNAME}"#"${IRODS_ZONE}" rodsuser"
-
-echo "[rods@${HS_USER_ZONE_HOST}]$ ichmod -r -M own ${IRODS_USERNAME}#${IRODS_ZONE} /${HS_USER_IRODS_ZONE}/home"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "ichmod -r -M own "${IRODS_USERNAME}"#"${IRODS_ZONE}" /${HS_USER_IRODS_ZONE}/home"
-
-# give ${HS_IRODS_PROXY_USER_IN_USER_ZONE} own rights over ${HS_USER_IRODS_ZONE}/home
-echo "[rods@${HS_USER_ZONE_HOST}]$ ichmod -r -M own ${HS_IRODS_PROXY_USER_IN_USER_ZONE} /${HS_USER_IRODS_ZONE}/home"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "ichmod -r -M own "${HS_IRODS_PROXY_USER_IN_USER_ZONE}" /${HS_USER_IRODS_ZONE}/home"
-
-# set ${HS_USER_IRODS_ZONE}/home to inherit
-echo "[rods@${HS_USER_ZONE_HOST}]$ ichmod -r -M inherit /${HS_USER_IRODS_ZONE}/home"
-docker run --rm --env-file env-files/rods@${HS_USER_ZONE_HOST}.env \
-    mjstealey/irods-icommands:4.2.2 \
-    sh -c "ichmod -r -M inherit /"${HS_USER_IRODS_ZONE}"/home"
-
-# configure local_settings.py
-./config-local-settings.sh
-# configure hsctl and docker-compose-local-irods.template
-./config-hsctl-compose.sh
-
-exit 0;
diff --git a/irods_browser_app/views.py b/irods_browser_app/views.py
index 1a148cb5a7..9d12d828c9 100644
--- a/irods_browser_app/views.py
+++ b/irods_browser_app/views.py
@@ -123,24 +123,18 @@ def upload_add(request):
         return JsonResponse({'error': "Invalid file type: {ext}".format(ext=ext)},
                             status=status.HTTP_400_BAD_REQUEST)
     else:
-        homepath = irods_fnames_list[0]
-        # TODO: this should happen whether resource is federated or not
-        irods_federated = utils.is_federated(homepath)
-        if irods_federated:
-            source_names = irods_fnames.split(',')
-        else:
-            user = request.POST.get('irods_username')
-            password = request.POST.get("irods_password")
-            port = request.POST.get("irods_port")
-            host = request.POST.get("irods_host")
-            zone = request.POST.get("irods_zone")
-            try:
-                upload_from_irods(username=user, password=password, host=host, port=port,
-                                  zone=zone, irods_fnames=irods_fnames, res_files=res_files)
-            except SessionException as ex:
-                return JsonResponse(
-                    {"error": ex.stderr}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
-                )
+        user = request.POST.get('irods_username')
+        password = request.POST.get("irods_password")
+        port = request.POST.get("irods_port")
+        host = request.POST.get("irods_host")
+        zone = request.POST.get("irods_zone")
+        try:
+            upload_from_irods(username=user, password=password, host=host, port=port,
+                              zone=zone, irods_fnames=irods_fnames, res_files=res_files)
+        except SessionException as ex:
+            return JsonResponse(
+                {"error": ex.stderr}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
+            )
 
     try:
         utils.resource_file_add_pre_process(resource=resource, files=res_files, user=request.user,
diff --git a/local-dev-first-start-only.sh b/local-dev-first-start-only.sh
index 40d0203a1c..325e13e335 100755
--- a/local-dev-first-start-only.sh
+++ b/local-dev-first-start-only.sh
@@ -169,7 +169,7 @@ else
 fi
 
 DOCKER_COMPOSER_YAML_FILE='local-dev.yml'
-HYDROSHARE_CONTAINERS=(nginx hydroshare defaultworker data.local.org rabbitmq solr postgis users.local.org)
+HYDROSHARE_CONTAINERS=(nginx hydroshare defaultworker data.local.org rabbitmq solr postgis)
 HYDROSHARE_VOLUMES=(hydroshare_idata_iconf_vol hydroshare_idata_pgres_vol hydroshare_idata_vault_vol hydroshare_iuser_iconf_vol hydroshare_iuser_pgres_vol hydroshare_iuser_vault_vol hydroshare_postgis_data_vol hydroshare_rabbitmq_data_vol hydroshare_share_vol hydroshare_solr_data_vol hydroshare_temp_vol)
 HYDROSHARE_IMAGES=(hydroshare_nginx hydroshare_defaultworker hydroshare_hydroshare solr hydroshare/hs-irods hydroshare/hs_docker_base hydroshare/hs_postgres rabbitmq)
 
@@ -278,14 +278,6 @@ mkdir -p log/nginx 2>/dev/null
 
 find . -name '*.hydro-bk' -exec rm -f {} \; 2>/dev/null
 
-echo "Creating .ssh directory and generating ssh key"
-echo " - rm -rf .ssh"
-rm -rf .ssh
-echo " - mkdir .ssh"
-mkdir .ssh
-echo " - ssh-keygen -t ed25519 -f .ssh/id_ed25519_hs -N ''"
-ssh-keygen -t ed25519 -f .ssh/id_ed25519_hs -N ''
-
 echo
 echo '########################################################################################################################'
 echo " Starting system"
@@ -309,10 +301,6 @@ do
   if [ "$DATA" != "" ]; then
     COUNT=$(($COUNT + 1))
   fi
-  USER=`docker $DOCKER_PARAM logs users.local.org 2>/dev/null | grep 'iRODS is installed and running'`
-  if [ "$USER" != "" ]; then
-    COUNT=$(($COUNT + 1))
-  fi
   SECOND=$(($SECOND + 1))
   echo -ne "$SECOND ...\033[0K\r" && sleep 1;
 done
diff --git a/local-dev.yml b/local-dev.yml
index efcbb2871a..ef31d01de5 100755
--- a/local-dev.yml
+++ b/local-dev.yml
@@ -4,8 +4,6 @@ services:
     image: hydroshare/hs-irods:4.2.6-buster
     container_name: data.local.org
     hostname: data.local.org
-    links:
-      - users.local.org:users.local.org
     ports:
       - "1247:1247"
     volumes:
@@ -14,7 +12,6 @@ services:
       - "idata_pgres_vol:/var/lib/postgresql/data"
       - "idata_home_vol:/home"
     environment:
-      SHARED_NEG_KEY: 'hydroshareZonehydroshareuserZone'
       USE_IRODS: 'True'
       IRODS_ROOT: '/tmp'
       IRODS_ICOMMANDS_PATH: '/usr/bin'
@@ -28,15 +25,6 @@ services:
       IRODS_AUTH: 'wwwHydroProxy'
       IRODS_GLOBAL_SESSION: 'True'
       REMOTE_USE_IRODS: 'True'
-      HS_USER_ZONE_HOST: 'users.local.org'
-      LINUX_ADMIN_USER_FOR_HS_USER_ZONE: 'hsuserproxy'
-      LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE: 'hsuserproxy'
-      PUBLIC_KEY_FILE_FOR_HS_USER_ZONE: '/home/hsuserproxy/.ssh/authorized_keys'
-      LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD: '/home/hsuserproxy/create_user.sh'
-      LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD: '/home/hsuserproxy/delete_user.sh'
-      HS_IRODS_PROXY_USER_IN_USER_ZONE: 'localHydroProxy'
-      HS_IRODS_USER_ZONE_DEF_RES: 'hydroshareLocalResc'
-      HS_USER_IRODS_ZONE: 'hydroshareuserZone'
       IRODS_ZONE_NAME: 'hydroshareZone'
       IRODS_SERVER_ZONE_KEY: 'hydroshareZone_KEY'
       IRODS_DATABASE_SERVER_HOSTNAME: 'data.local.org'
@@ -44,51 +32,6 @@ services:
       IRODS_PASSWORD: 'rods'
     stdin_open: true
     tty: true
-  users.local.org:
-    build:
-      context: .
-      dockerfile: Dockerfile-users-irods
-    container_name: users.local.org
-    hostname: users.local.org
-    volumes:
-      - "iuser_iconf_vol:/etc/irods"
-      - "iuser_vault_vol:/var/lib/irods"
-      - "iuser_pgres_vol:/var/lib/postgresql/data"
-      - "iuser_home_vol:/home/hsuserproxy"
-      - "./.ssh/id_ed25519_hs.pub:/home/hsuserproxy/.ssh/authorized_keys"
-    environment:
-      SHARED_NEG_KEY: 'hydroshareZonehydroshareuserZone'
-      USE_IRODS: 'True'
-      IRODS_ROOT: '/tmp'
-      IRODS_ICOMMANDS_PATH: '/usr/bin'
-      IRODS_HOST: 'users.local.org'
-      IRODS_PORT: '1247'
-      IRODS_DEFAULT_RESOURCE: 'hydroshareReplResc'
-      IRODS_SINGLE_RESC_FOR_QUERY: 'hydroshareReplResc'
-      IRODS_HOME_COLLECTION: '/hydroshareZone/home/wwwHydroProxy'
-      IRODS_ZONE: 'hydroshareZone'
-      IRODS_USERNAME: 'wwwHydroProxy'
-      IRODS_AUTH: 'wwwHydroProxy'
-      IRODS_GLOBAL_SESSION: 'True'
-      REMOTE_USE_IRODS: 'True'
-      HS_USER_ZONE_HOST: 'users.local.org'
-      LINUX_ADMIN_USER_FOR_HS_USER_ZONE: 'hsuserproxy'
-      LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE: 'hsuserproxy'
-      PUBLIC_KEY_FILE_FOR_HS_USER_ZONE: '/home/hsuserproxy/.ssh/authorized_keys'
-      LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD: '/home/hsuserproxy/create_user.sh'
-      LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD: '/home/hsuserproxy/delete_user.sh'
-      HS_IRODS_PROXY_USER_IN_USER_ZONE: 'localHydroProxy'
-      HS_IRODS_USER_ZONE_DEF_RES: 'hydroshareLocalResc'
-      HS_USER_IRODS_ZONE: 'hydroshareuserZone'
-      IRODS_ZONE_NAME: 'hydroshareuserZone'
-      IRODS_SERVER_ZONE_KEY: 'hydroshareuserZone_KEY'
-      IRODS_DATABASE_SERVER_HOSTNAME: 'users.local.org'
-      IRODS_USER_NAME: 'rods'
-      IRODS_PASSWORD: 'rods'
-    ports:
-      - "22:22"
-    stdin_open: true
-    tty: true
   postgis:
     image: postgis/postgis:15-3.3
     environment:
@@ -176,13 +119,11 @@ services:
       - solr:solr
       - rabbitmq:rabbitmq
       - data.local.org:data.local.org
-      - users.local.org:users.local.org
     depends_on:
       - postgis
       - solr
       - rabbitmq
       - data.local.org
-      - users.local.org
     stdin_open: true
     tty: true
     command: /bin/bash init-hydroshare
@@ -215,14 +156,12 @@ services:
       - postgis:postgis
       - rabbitmq:rabbitmq
       - data.local.org:data.local.org
-      - users.local.org:users.local.org
     depends_on:
       - hydroshare
       - postgis
       - solr
       - rabbitmq
       - data.local.org
-      - users.local.org
     stdin_open: true
     tty: true
     command: /bin/bash init-defaultworker
diff --git a/quota-info.md b/quota-info.md
index 4befb059d5..8164d2f5ad 100644
--- a/quota-info.md
+++ b/quota-info.md
@@ -6,14 +6,14 @@ The HS quota system relies on cooperation between iRods storage zones (two at th
 
 ### Django Quota System
 
-The quota system relies on resource sizes (for files in the dataZone) and irods AVUs (for files in the userZone).
+The quota system relies on resource sizes.
 Resource sizes are calculated as a sum of cached ResourceFile sizes.
 
 Django signals are used to update the stored UserQuota values in the database.
 
 The configuration for the quota system within django is done via the admin panel. The [QuotaMessage](theme/models.py) model contains the configurable messages as well as default grace period and soft/hard limits.
 
-Quota is enforced at all points of ingestion as well as during file processes that can result in more storage being used (for instance unzipping). Enforcement for the iRods UserZone would necessarily be accomplished at the iRods level. An example stubbing-out of how this could be accomplished can be found in [set_userzone_quota.sh](irods/set_userzone_quota.sh). However, ideally such an implementation would rely either on the quota system embedded in iRods, or perhaps using policy/rule enforcement with more microservices specific to the UserZone.
+Quota is enforced at all points of ingestion as well as during file processes that can result in more storage being used (for instance unzipping).
 
 Users can request additional quota allocation from within their profile. Such a request notifies **DEFAULT_SUPPORT_EMAIL** and the request can be approved/denied from within the sent email or within the HydroShare amdin panel.
 
diff --git a/scripts/templates/docker-compose-local-irods.template b/scripts/templates/docker-compose-local-irods.template
index e0aa92be50..3838526b50 100755
--- a/scripts/templates/docker-compose-local-irods.template
+++ b/scripts/templates/docker-compose-local-irods.template
@@ -54,7 +54,6 @@ hydroshare:
     - solr:solr
   extra_hosts:
     - "IRODS_DATA_HOSTNAME:IRODS_DATA_IP"
-    - "IRODS_USER_HOSTNAME:IRODS_USER_IP"
   command: /bin/bash init-hydroshare
 defaultworker:
   build: .
@@ -76,5 +75,4 @@ defaultworker:
     - rabbitmq:rabbitmq
   extra_hosts:
     - "IRODS_DATA_HOSTNAME:IRODS_DATA_IP"
-    - "IRODS_USER_HOSTNAME:IRODS_USER_IP"
   command: /bin/bash init-defaultworker
diff --git a/scripts/templates/docker-compose.template b/scripts/templates/docker-compose.template
index d619b60bdf..b4397b97e1 100755
--- a/scripts/templates/docker-compose.template
+++ b/scripts/templates/docker-compose.template
@@ -37,7 +37,6 @@ hydroshare:
   volumes:
     # hydroshare repository
     - "HS_PATH:/hydroshare"
-    - "${HOST_PK_FOR_HS_USER_ZONE:-/opt/.ssh/id_ed25519_hs}:${PRIVATE_KEY_FILE_FOR_HS_USER_ZONE:-/hydroshare/.ssh/id_ed25519_hs}
     # pycharm debugging
     # - "/home/hydro/pycharm-debug:/pycharm-debug"
     # log files
diff --git a/slow-start.sh b/slow-start.sh
index 4924b62365..de743c4543 100755
--- a/slow-start.sh
+++ b/slow-start.sh
@@ -29,10 +29,6 @@ do
   if [ "$DATA" != "" ]; then
     COUNT=$(($COUNT + 1))
   fi
-  USER=`docker $DOCKER_PARAM logs users.local.org 2>/dev/null | grep 'Success'`
-  if [ "$USER" != "" ]; then
-    COUNT=$(($COUNT + 1))
-  fi
   SECOND=$(($SECOND + 1))
   echo -ne "$SECOND ...\033[0K\r" && sleep 1;
 done
diff --git a/theme/admin.py b/theme/admin.py
index fbbda1dfb6..873a0ca76c 100755
--- a/theme/admin.py
+++ b/theme/admin.py
@@ -21,7 +21,7 @@ class UserQuotaForm(forms.ModelForm):
     class Meta:
         model = UserQuota
         fields = ['allocated_value',]
-        readonly_fields = ['zone', 'unit', 'user_zone_value', 'data_zone_value',]
+        readonly_fields = ['zone', 'unit', 'data_zone_value',]
 
     def save(self, *args, **kwargs):
         instance = super(UserQuotaForm, self).save(commit=False)
@@ -53,10 +53,10 @@ def get_form(self, request, obj=None, **kwargs):
 class QuotaAdmin(admin.ModelAdmin):
     model = UserQuota
 
-    list_display = ('user', 'allocated_value', 'used_value', 'user_zone_value', 'data_zone_value', 'unit', 'zone')
+    list_display = ('user', 'allocated_value', 'used_value', 'data_zone_value', 'unit', 'zone')
     list_filter = ('zone',)
 
-    readonly_fields = ('user', 'user_zone_value', 'data_zone_value',)
+    readonly_fields = ('user', 'data_zone_value',)
     search_fields = ('user__username',)
 
     def get_form(self, request, obj=None, **kwargs):
diff --git a/theme/forms.py b/theme/forms.py
index 5ff329982a..133b7841a7 100644
--- a/theme/forms.py
+++ b/theme/forms.py
@@ -326,7 +326,7 @@ def __init__(self, *args, **kwargs):
 
     class Meta:
         model = UserProfile
-        exclude = ['user', 'public', 'create_irods_user_account']
+        exclude = ['user', 'public']
 
     def clean_organization(self):
         data = self.cleaned_data['organization']
diff --git a/theme/management/commands/dump_user_quota.py b/theme/management/commands/dump_user_quota.py
index aab917e0bd..18c169f4f6 100755
--- a/theme/management/commands/dump_user_quota.py
+++ b/theme/management/commands/dump_user_quota.py
@@ -1,7 +1,4 @@
-import os
 import pandas as pd
-from django_irods.storage import IrodsStorage
-from django.conf import settings
 from hs_tools_resource.utils import convert_size
 from theme.models import UserQuota
 from django.core.management.base import BaseCommand
@@ -38,16 +35,13 @@ def handle(self, *args, **options):
             'User name',
             'User email',
             'Allocated quota value',
-            'Used quota value (uz+dz)',
+            'Used quota value (dz)',
             'Remaining quota value',
-            'Django UserQuota model UserZone value',
             'Django UserQuota model DataZone value',
             'Quota unit',
             'Grace period ends',
             'DataZone Bagit AVU Size (bytes)',
-            'UserZone Bagit AVU Size (bytes)',
             'DataZone Bagit AVU Converted Size',
-            'UserZone Bagit AVU Converted Size',
         ]
         if expand:
             fields.append('Total size of held resources (quota holder)')
@@ -55,12 +49,6 @@ def handle(self, *args, **options):
 
         user_quotas = UserQuota.objects.all()
         user_quotas.filter(user__is_active=True).filter(user__is_superuser=False)
-        istorage = IrodsStorage()
-        uz_bagit_path = os.path.join(
-            '/', settings.HS_USER_IRODS_ZONE, 'home',
-            settings.HS_IRODS_PROXY_USER_IN_USER_ZONE,
-            settings.IRODS_BAGIT_PATH
-        )
         data = []
         errors = []
         for uq in user_quotas:
@@ -69,10 +57,9 @@ def handle(self, *args, **options):
             allocated = uq.allocated_value
             if exceeded and used < allocated:
                 continue
-            uz_bytes = None
             dz_bytes = None
             try:
-                uz_bytes, dz_bytes = get_quota_usage(user.username)
+                dz_bytes = get_quota_usage(user.username)
             except ValidationError as e:
                 message = f"Error getting quota usage for {user.username}, {user.id}: {e}"
                 print(message)
@@ -80,11 +67,6 @@ def handle(self, *args, **options):
             if dz_bytes is None:
                 dz_bytes = 0
             dz = convert_size(int(dz_bytes))
-            uz_bytes = istorage.getAVU(uz_bagit_path,
-                                       f'{user.username}-usage')
-            if uz_bytes is None:
-                uz_bytes = 0
-            uz = convert_size(int(uz_bytes))
             values = [
                 user.id,
                 user.username,
@@ -92,14 +74,11 @@ def handle(self, *args, **options):
                 allocated,
                 used,
                 uq.remaining,
-                uq.user_zone_value,
                 uq.data_zone_value,
                 uq.unit,
                 uq.grace_period_ends,
                 dz_bytes,
-                uz_bytes,
                 dz,
-                uz
             ]
             if expand:
                 try:
diff --git a/theme/management/commands/report_quota_inconsistency.py b/theme/management/commands/report_quota_inconsistency.py
index cc64ad3c23..3409e46a63 100644
--- a/theme/management/commands/report_quota_inconsistency.py
+++ b/theme/management/commands/report_quota_inconsistency.py
@@ -20,11 +20,10 @@ def handle(self, *args, **options):
         for uq in UserQuota.objects.filter(
                 user__is_active=True).filter(user__is_superuser=False):
             used_value = 0.0
-            uz = 0.0
             dz = 0.0
             try:
-                uz, dz = get_quota_usage(uq.user.username)
-                used_value = uz + dz
+                dz = get_quota_usage(uq.user.username)
+                used_value = dz
             except ValidationError:
                 pass
             used_value = convert_file_size_to_unit(used_value, "gb")
@@ -32,12 +31,9 @@ def handle(self, *args, **options):
                 # report inconsistency
                 report_dict = {
                     'user': uq.user.username,
-                    'django total': uq.used_value,
-                    'django user zone': uq.user_zone_value,
-                    'django data zone': uq.data_zone_value,
-                    'irods total': used_value,
-                    'irods user zone': uz,
-                    'irods data zone': dz, }
+                    'django': uq.used_value,
+                    'irods': used_value,
+                }
                 quota_report_list.append(report_dict)
                 print('quota incosistency: {} reported in django vs {} reported in iRODS for user {}'.format(
                     uq.used_value, used_value, uq.user.username), flush=True)
diff --git a/theme/management/commands/report_resource_size_vs_quota_inconsistency.py b/theme/management/commands/report_resource_size_vs_quota_inconsistency.py
deleted file mode 100644
index 829a67c32f..0000000000
--- a/theme/management/commands/report_resource_size_vs_quota_inconsistency.py
+++ /dev/null
@@ -1,222 +0,0 @@
-
-import csv
-import math
-import time
-from django.contrib.auth.models import User
-from django.core.management.base import BaseCommand
-from django.core.exceptions import ValidationError
-from django.conf import settings
-from django_irods.icommands import SessionException
-from django_irods.storage import IrodsStorage
-from django.utils.timezone import now
-
-from hs_core.hydroshare import convert_file_size_to_unit
-from hs_core.hydroshare import current_site_url
-from hs_core.models import ResourceFile
-from theme.models import UserQuota
-
-current_site = current_site_url()
-_BATCH_SIZE = settings.BULK_UPDATE_CREATE_BATCH_SIZE
-
-
-def get_dz_quota_usage_from_irods(username):
-    """
-    Query iRODS AVU to get quota usage for a user reported in iRODS quota microservices
-    :param username: the user name to get quota usage for.
-    :return: the quota usage from iRODS data zone
-    :raises: ValidationError if quota usage cannot be retrieved from iRODS
-    """
-    attname = username + '-usage'
-    istorage = IrodsStorage()
-    # get quota size for user in iRODS data zone by retrieving AVU set on irods bagit path
-    # collection
-    try:
-        uqDataZoneSize = istorage.getAVU(settings.IRODS_BAGIT_PATH, attname)
-        if uqDataZoneSize is None:
-            # user may not have resources in data zone, so corresponding quota size AVU may not
-            # exist for this user
-            uqDataZoneSize = -1
-        else:
-            uqDataZoneSize = float(uqDataZoneSize)
-    except SessionException:
-        # user may not have resources in data zone, so corresponding quota size AVU may not exist
-        # for this user
-        uqDataZoneSize = -1
-
-    if uqDataZoneSize < 0:
-        err_msg = 'no quota size AVU in data zone and user zone for user {}'.format(username)
-        print(err_msg)
-        raise ValidationError(err_msg)
-    else:
-        used_val = uqDataZoneSize
-    return used_val
-
-
-class Command(BaseCommand):
-    help = "Output potential quota inconsistencies between iRODS and Django resource aggregate filesize for all users"
-
-    def add_arguments(self, parser):
-        parser.add_argument('output_file_name_with_path', type=str, help='output file name with path')
-        parser.add_argument('--update', action='store_true', help='fix inconsistencies by recalculating in django')
-        parser.add_argument('--reset', action='store_true', help='reset resource file size in django when inconsistent')
-        parser.add_argument('--no_compare', action='store_true', help='reset/update filesizes without comparing quotas')
-        parser.add_argument('--uid', type=str, help='filter to just a single user by uid')
-        parser.add_argument('--min_quota_use_avu', type=int,
-                            help='filter to just users with AVU quota use above this size in GB')
-        parser.add_argument('--min_quota_django_model', type=int,
-                            help='filter to django UserQuota use above this size in GB')
-        parser.add_argument('--desc', action='store_true', help='order by descending quota use in iRODS Datazone')
-        parser.add_argument('--rel_tol', help='relative tolerance for comparing quota sizes, default is 0.01')
-
-    def handle(self, *args, **options):
-        uid = options['uid'] if options['uid'] else None
-        update = options['update'] if options['update'] else False
-        desc = options['desc'] if options['desc'] else False
-        reset = options['reset'] if options['reset'] else False
-        no_compare = options['no_compare'] if options['no_compare'] else False
-        min_quota_use_avu = int(options['min_quota_use_avu']) if options['min_quota_use_avu'] else 0
-        min_quota_django_model = int(options['min_quota_django_model']) if options['min_quota_django_model'] else 0
-        rel_tol = float(options['rel_tol']) if options['rel_tol'] else 0.01
-
-        if no_compare:
-            if not update and not reset:
-                print('Error: --no_compare option must be used with --update or --reset option')
-                return
-            if options['rel_tol']:
-                print('Warning: --rel_tol option is ignored when --no_compare is used')
-                print('Update or reset will be done even if comparison is within the tolerance.')
-            print('Using --no_compare option. Will reset/update file sizes even when no incosistency is detected.')
-
-        if min_quota_use_avu and min_quota_django_model:
-            print('Using --min_quota_use_avu and --min_quota_django_model')
-            if min_quota_use_avu != min_quota_django_model:
-                print('Warning: min_quota_use_avu and min_quota_django_model are different')
-            print('First quotas will be filtered by django model. Then users will be filtered by iRODS AVU.')
-
-        if update and reset:
-            print('Cannot use both --update and --reset options together')
-            return
-        uqs = UserQuota.objects.filter(user__is_active=True) \
-            .filter(user__is_superuser=False)
-        if uid:
-            try:
-                user = User.objects.get(id=uid, is_active=True, is_superuser=False)
-            except User.DoesNotExist:
-                print(f'Active user with id {uid} not found')
-            uqs = uqs.filter(user=user)
-        if min_quota_django_model > 0:
-            # TODO: once the quota system is moved from irods to django, used_value can no longer be filtered on
-            # It will be a property, so this will need to be updated
-            uqs = uqs.filter(used_value__gt=min_quota_django_model)
-        if desc:
-            uqs = uqs.order_by('-used_value')
-        num_uqs = uqs.count()
-        print(f"Number of user quotas to check: {num_uqs}")
-        counter = 1
-        csvfile = open(options['output_file_name_with_path'], 'w')
-        w = csv.writer(csvfile)
-        fields = [
-            'User',
-            'Starting summed resource sizes in Django',
-            'Updated summed resource sizes in Django',
-            'Quota reported in iRODS Datazone'
-        ]
-        w.writerow(fields)
-        for uq in uqs:
-            start_time = time.time()
-            user = uq.user
-            print("\n" + "*" * 80)
-            print(f'{counter}/{num_uqs}: Checking quota for user {user.username}, {current_site}/user/{user.id}/')
-            used_value_irods_dz = 0.0
-            try:
-                used_value_irods_dz = get_dz_quota_usage_from_irods(user.username)
-            except ValidationError:
-                # purposely ignore the error and continue to check for inconsistencies
-                # assumes that the user may not have any resources in the data zone
-                pass
-            used_value_irods_dz = convert_file_size_to_unit(used_value_irods_dz, "gb")
-            if used_value_irods_dz < min_quota_use_avu:
-                print(f"Quota usage in iRODS Datazone AVU is below {min_quota_use_avu} GB. Skipping.")
-                continue
-            # sum the resources sizes for all resources that the user is the quota holder for
-            owned_resources = user.uaccess.owned_resources
-            # filter resources above size limit
-            held_resources = []
-            total_size = 0
-            for res in owned_resources:
-                if res.get_quota_holder() == user:
-                    held_resources.append(res)
-                    res_size = res.size
-                    print(f"Resource {res.short_id} is held by {user.username}, size: {res_size} bytes")
-                    total_size += res_size
-            converted_total_size_django = convert_file_size_to_unit(int(total_size), 'gb')
-            print(f"Quota usage in iRODS Datazone from AVU: {used_value_irods_dz} GB")
-            print(f"Aggregate size of resources in Django: {converted_total_size_django} GB")
-            is_close = math.isclose(used_value_irods_dz, converted_total_size_django, rel_tol=rel_tol)
-            if not is_close or no_compare:
-                django_updated = ''
-                print('{} reported in django vs {} reported in iRODS for user {}'.format(
-                    converted_total_size_django, used_value_irods_dz, user.username), flush=True)
-
-                if update:
-                    print("Attempting to fix any inconsistency by updating file sizes in django")
-                    res_files = []
-                    updated_size = 0
-                    for res in held_resources:
-                        res_files = res.files.exclude(_size=0)
-                        num_files = res_files.count()
-                        print(f"Total files in resource {res.short_id}: {num_files}")
-                        if num_files == 0:
-                            print(f"Resource {res.short_id} has no files.")
-                            continue
-                        print(f'{current_site}/resource/{res.short_id}: currently {res.size} bytes')
-                        file_counter = 0
-                        # exclude files with size 0 as they don't exist in iRODS
-                        print("Updating files:")
-                        for res_file in res_files.iterator():
-                            # this is an expensive operation (3 irods calls per file) - about 1 min for 100 files
-                            # size, checksum and modified time are obtained from irods and assigned to
-                            # relevant fields of the resource file object
-                            res_file.calculate_size(resource=res, save=False)
-                            file_counter += 1
-                            print(f"{file_counter}/{num_files}")
-                            if res_file._size <= 0:
-                                print(f"File {res_file.short_path} was not found in iRODS.")
-
-                        ResourceFile.objects.bulk_update(res_files, '_size', batch_size=_BATCH_SIZE)
-                        print(f"Updated {file_counter} files for resource {res.short_id}")
-                        res.refresh_from_db()
-                        print(f'{current_site}/resource/{res.short_id}: now {res.size} bytes')
-                        # keep track of the updated size so that we can compare again
-                        updated_size += res.size
-                    converted_updated_size_django = convert_file_size_to_unit(int(updated_size), 'gb')
-                    django_updated = converted_updated_size_django
-                    if not math.isclose(used_value_irods_dz, converted_updated_size_django, rel_tol=rel_tol):
-                        print("Even after updating, an inconsistency remains!")
-                        print(f"Quota usage in iRODS Datazone AVU: {used_value_irods_dz} GB")
-                        print(f"Updated Total size of resources in Django: {converted_updated_size_django} GB")
-
-                elif reset:
-                    print("Resetting file size cache in django")
-                    for res in held_resources:
-                        print(f'Resetting all filesizes in {current_site}/resource/{res.short_id}')
-                        res.files.update(_size=-1)
-                        # set the updated date to now so that nightly celery task can update the size
-                        res.updated = now().isoformat()
-                        res_modified_date = res.metadata.dates.all().filter(type='modified').first()
-                        if res_modified_date:
-                            res.metadata.update_element('date', res_modified_date.id)
-                        else:
-                            res.metadata.create_element('date', type='modified', start_date=res.updated)
-                    django_updated = 'reset'
-                else:
-                    print('No action taken. Use --update or --reset to fix inconsistencies')
-                w.writerow([uq.user.username, converted_total_size_django, django_updated, used_value_irods_dz])
-            else:
-                print(f"Quota deemed consistent for user {user.username}, using {rel_tol} relative tolerance")
-            counter += 1
-            # Print the amount of time spent for this user
-            time_spent = time.time() - start_time
-            print(f"Time spent for user {user.username}: {time_spent} seconds")
-        csvfile.close()
-        print("Done")
diff --git a/theme/management/commands/reset_quota.py b/theme/management/commands/reset_quota.py
deleted file mode 100644
index d10d39d8a7..0000000000
--- a/theme/management/commands/reset_quota.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from django.core.management.base import BaseCommand
-from django_irods.storage import IrodsStorage
-from django.conf import settings
-
-
-class Command(BaseCommand):
-    help = "Reset userzone quota by forcing quota iRODS microservices to recalculate quota for all users."
-
-    def handle(self, *args, **options):
-        istorage = IrodsStorage()
-        # reset quota for user zone
-        user_root_path = '/{}/home/{}'.format(settings.HS_USER_IRODS_ZONE, settings.HS_IRODS_PROXY_USER_IN_USER_ZONE)
-        istorage.setAVU(user_root_path, 'resetQuotaDir', 1)
diff --git a/theme/management/commands/update_used_storage.py b/theme/management/commands/update_used_storage.py
deleted file mode 100644
index 51c2d91731..0000000000
--- a/theme/management/commands/update_used_storage.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from django.contrib.auth.models import User
-from django.core.management.base import BaseCommand
-from django.core.exceptions import ValidationError
-
-from theme.models import UserQuota
-from hs_core.hydroshare.resource import update_quota_usage
-from hs_core.hydroshare import current_site_url
-
-
-class Command(BaseCommand):
-    help = "Update used storage space in UserQuota table for all users in HydroShare by reading " \
-           "userZone quota usage AVUs for users which are updated by iRODS quota update micro-services." \
-           "This command also reads dataZone usage which is calculated upon request." \
-           "This management command needs to be run initially to update Django DB quota usage " \
-           "or whenever iRODS quota update micro-services are reset and Django DB needs to be " \
-           "brought in sync with iRODS quota AVUs"
-
-    def handle(self, *args, **options):
-        errors = {}
-        users = User.objects.filter(is_active=True, is_superuser=False)
-        count = users.count()
-        for i, u in enumerate(users, start=0):
-            i += 1
-            if UserQuota.objects.filter(user=u).exists():
-                count_string = f"{i}/{count}:"
-                profile = f"{current_site_url()}/user/{u.id}"
-                try:
-                    update_quota_usage(u.username, notify_user=False)
-                    print(f"{count_string}Success updating quota in Django for {u.username}: {profile}")
-                except ValidationError as e:
-                    print(f"{count_string}{profile} Error updating quota:{e.message}")
-                    errors[profile] = e.message
-        print("Completed updating quotas.")
-        if errors:
-            for profile, message in errors:
-                print(f"Error encountered while updating quota for {profile}: {message}")
diff --git a/theme/migrations/0036_auto_20240702_1847.py b/theme/migrations/0036_auto_20240702_1847.py
new file mode 100644
index 0000000000..a781b1661f
--- /dev/null
+++ b/theme/migrations/0036_auto_20240702_1847.py
@@ -0,0 +1,22 @@
+# Generated by Django 3.2.25 on 2024-07-02 18:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('theme', '0035_remove_userquota_data_zone_value'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='userquota',
+            name='user_zone_value',
+        ),
+        migrations.AlterField(
+            model_name='userprofile',
+            name='create_irods_user_account',
+            field=models.BooleanField(default=False, help_text='Has an iRODS user account been created in HydroShare user iRODS space for staging large files (>2GB) using iRODS clients such as Cyberduck (https://cyberduck.io/) and icommands (https://docs.irods.org/master/icommands/user/).Uncheck after requesting your iRODS user account be deleted. Note that deletion of your iRODS user account deletes all of your files under this account as well.'),
+        ),
+    ]
diff --git a/theme/migrations/0037_remove_userprofile_create_irods_user_account.py b/theme/migrations/0037_remove_userprofile_create_irods_user_account.py
new file mode 100644
index 0000000000..8524c9a4fb
--- /dev/null
+++ b/theme/migrations/0037_remove_userprofile_create_irods_user_account.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.2.25 on 2024-07-02 18:59
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('theme', '0036_auto_20240702_1847'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='userprofile',
+            name='create_irods_user_account',
+        ),
+    ]
diff --git a/theme/models.py b/theme/models.py
index 3f79ffd2dd..d6f5bcfc49 100755
--- a/theme/models.py
+++ b/theme/models.py
@@ -241,7 +241,7 @@ class QuotaMessage(models.Model):
 class UserQuota(models.Model):
     # ForeignKey relationship makes it possible to associate multiple UserQuota models to
     # a User with each UserQuota model defining quota for a set of iRODS zones. By default,
-    # the UserQuota model instance defines quota in hydroshareZone and hydroshareuserZone,
+    # the UserQuota model instance defines quota in hydroshareZone,
     # categorized as hydroshare in zone field in UserQuota model, however,
     # another UserQuota model instance could be defined in a third-party federated zone as needed.
     user = models.ForeignKey(
@@ -254,7 +254,6 @@ class UserQuota(models.Model):
     )
 
     allocated_value = models.FloatField(default=20)
-    user_zone_value = models.FloatField(default=0)
     unit = models.CharField(max_length=10, default="GB")
     zone = models.CharField(max_length=100, default="hydroshare")
     # grace_period_ends to be quota-enforced. Default is None meaning the user is below
@@ -280,7 +279,10 @@ def data_zone_value(self):
 
     @property
     def used_percent(self):
-        return self.used_value * 100.0 / self.allocated_value
+        try:
+            return self.used_value * 100.0 / self.allocated_value
+        except ZeroDivisionError:
+            return float('inf')
 
     @property
     def remaining(self):
@@ -289,8 +291,8 @@ def remaining(self):
 
     @property
     def used_value(self):
-        uz, dz = self.get_used_value_by_zone(refresh=False)
-        return uz + dz
+        dz = self.get_used_value_by_zone(refresh=False)
+        return dz
 
     def get_used_value_by_zone(self, refresh=False):
         """
@@ -300,27 +302,15 @@ def get_used_value_by_zone(self, refresh=False):
             refresh (bool): If True, refreshes the quota usage before returning the values.
 
         Returns:
-            tuple: A tuple containing the used value for the user zone and the data zone.
+            used value in the dataZone
         """
         from hs_core.hydroshare.resource import get_quota_usage
 
         if refresh:
-            uz, dz = get_quota_usage(self.user.username, False)
-            self.set_userzone_used_value(uz)
+            dz = get_quota_usage(self.user.username, False)
         else:
-            uz = self.user_zone_value
             dz = self.data_zone_value
-        return uz, dz
-
-    def set_userzone_used_value(self, uz_size):
-        """
-        set used values in self.unit with pass in size in bytes.
-        :param uz_size: pass in size in bytes unit from userZone
-        :return:
-        """
-        from hs_core.hydroshare.utils import convert_file_size_to_unit
-        self.user_zone_value = convert_file_size_to_unit(uz_size, self.unit)
-        self.save()
+        return dz
 
     def add_to_used_value(self, size):
         """
@@ -348,45 +338,6 @@ def reset_grace_period(self):
         self.grace_period_ends = None
         self.save()
 
-    def check_if_userzone_quota_enforcement_is_bypassed(self, original_quota_data, updated_quota_data=None):
-        """
-        Check if a user is bypassing quota enforcement in userZone by continuing to put resources
-        Notifies admin if user is continuing to upload while over quota
-        :param user: the user to check
-        :param original_quota_data: the original quota data
-        :param updated_quota_data: the updated quota data
-        :return:
-        """
-        from hs_core import tasks
-        user = self.user
-
-        if updated_quota_data is None:
-            updated_quota_data = self.get_quota_data()
-
-        uz = updated_quota_data["uz"]
-        dz = updated_quota_data["dz"]
-        updated_quota_status = updated_quota_data["status"]
-        original_quota_status = original_quota_data["status"]
-        if original_quota_status == QuotaStatus.ENFORCEMENT and updated_quota_status == QuotaStatus.ENFORCEMENT:
-            original_userzone_usage = original_quota_data["uz"]
-            original_datazone_usage = original_quota_data["dz"]
-            message = ""
-            if uz > original_userzone_usage:
-                # userZone quota usage has increased
-                message += f"""UserZone quota usage has increased from {original_userzone_usage} to {uz}.
-                It is possible for the user to continue putting resources into the userZone
-                because quota is not enforced in userZone. You are being notified to manually check this usage
-                and determine what action is appropriate. It is possible to remove the user account from the irods
-                userzone if necessary."""
-            if dz > original_datazone_usage:
-                # dataZone quota usage has increased
-                message += f"""DataZone quota usage has increased from {original_datazone_usage} to {dz}.
-                This user has exceeded quota and limitations should have been enforced. It should not have been possible
-                for the user to continue putting resources into the dataZone. This case requires manual intervention.
-                """
-            if message:
-                tasks.notify_increased_usage_during_quota_enforcement.apply_async((user.pk, message))
-
     def get_quota_data(self):
         """
         get user quota data for display on user profile page
@@ -405,10 +356,8 @@ def get_quota_data(self):
         grace = self.grace_period_ends
         allocated = self.allocated_value
         unit = self.unit
-        uz, dz = self.get_used_value_by_zone(refresh=False)
-        used = uz + dz
-        uzp = uz * 100.0 / allocated
-        dzp = dz * 100.0 / allocated
+        used = self.get_used_value_by_zone(refresh=False)
+        dzp = used * 100.0 / allocated
         percent = used * 100.0 / allocated
         remaining = allocated - used
 
@@ -430,9 +379,7 @@ def get_quota_data(self):
         uq_data = {"used": used,
                    "allocated": allocated,
                    "unit": unit,
-                   "uz": uz,
-                   "dz": dz,
-                   "uz_percent": uzp if uzp < 100 else 100,
+                   "dz": used,
                    "dz_percent": dzp if dzp < 100 else 100,
                    "percent": percent if percent < 100 else 100,
                    "remaining": 0 if remaining < 0 else remaining,
@@ -640,15 +587,6 @@ class UserProfile(models.Model):
     state = models.CharField(max_length=1024, null=True, blank=True)
     country = models.CharField(max_length=1024, null=True, blank=True)
 
-    create_irods_user_account = models.BooleanField(
-        default=False,
-        help_text="Check to create an iRODS user account in HydroShare user "
-        "iRODS space for staging large files (>2GB) using iRODS clients such as Cyberduck "
-        "(https://cyberduck.io/) and icommands (https://docs.irods.org/master/icommands/user/)."
-        "Uncheck to delete your iRODS user account. Note that deletion of your iRODS user "
-        "account deletes all of your files under this account as well.",
-    )
-
     # to store one or more external identifier (Google Scholar, ResearchGate, ORCID etc)
     # each identifier is stored as a key/value pair {name:link}
     identifiers = HStoreField(default=dict, null=True, blank=True)
@@ -666,18 +604,6 @@ def profile_is_missing(self):
             missing.append("User Type")
         return missing
 
-    @property
-    def get_user_zone_status_info(self):
-        """
-        This function should be called to determine whether user zone functionality should be
-        enabled or not on the web site front end
-        Returns:
-            enable_user_zone boolean indicating whether user zone functionality should be enabled or
-            not on the web site front end
-        """
-        enable_user_zone = self.create_irods_user_account and settings.REMOTE_USE_IRODS
-        return enable_user_zone
-
 
 def force_unique_emails(sender, instance, **kwargs):
     if instance:
diff --git a/theme/static/js/profile.js b/theme/static/js/profile.js
index f0e01793a3..e9cf2950b1 100755
--- a/theme/static/js/profile.js
+++ b/theme/static/js/profile.js
@@ -172,64 +172,10 @@ function irods_status_info(alert_type, status, title) {
 }
 
 function create_irods_account() {
-    var url = $("#url-create-irods-account").val();
-
-    $.ajax({
-        url: url,
-        type: "POST",
-        data: {
-            password: $('#id_irods_password').val()
-        },
-        success: function(json) {
-            if(json.success) {
-                $('#create-irods-account-dialog').modal('hide');
-                var irodsContainer = $("#irods-account-container");
-                irodsContainer.empty();
-                irodsContainer.append(irods_account_link("#delete-irods-account-dialog", "Delete your iRODS user account"));
-                irodsContainer.append(irods_status_info('alert-success', json.success, 'Success'));
-            }
-            if(json.error) {
-                $('#create-irods-account-dialog').modal('hide');
-                var irodsContainer = $("#irods-account-container");
-                irodsContainer.append(irods_status_info('alert-danger', json.error, 'Failure'));
-            }
-        },
-        error: function(xhr, errmsg, err) {
-            $('#create-irods-account-dialog').modal('hide');
-            var irodsContainer = $("#irods-account-container");
-            const errorText = JSON.parse(xhr.responseText).error
-            errorText && irodsContainer.append(irods_status_info('alert-danger', errorText , 'Failure'));
-        }
-    });
-    return false;
-}
-
-function delete_irods_account() {
-    var url = $("#url-delete-irods-account").val();
-    $.ajax({
-        url: url,
-        type: "POST",
-        data: {},
-        success: function(json) {
-            var irodsContainer = $("#irods-account-container");
-            if(json.success) {
-                irodsContainer.empty();
-                irodsContainer.append(irods_account_link("#create-irods-account-dialog", "Create your iRODS user account"));
-                irodsContainer.append(irods_status_info('alert-success', json.success, 'Success'));
-            }
-            if(json.error) {
-                irodsContainer.append(irods_status_info('alert-warning', json.error, 'Failure'));
-            }
-            $('#delete-irods-account-dialog').modal('hide');
-        },
-        error: function(xhr, errmsg, err) {
-            $('#delete-irods-account-dialog').modal('hide');
-            var irodsContainer = $("#irods-account-container");
-            const errorText = JSON.parse(xhr.responseText).error
-            errorText && irodsContainer.append(irods_status_info('alert-warning', errorText, 'Failure'));
-        }
-    });
-    return false;
+    let email = 'help@cuahsi.org';
+    let subject = 'Request for new iRODS account';
+    let emailBody = 'I would like to request an iRODS account.';
+    document.location = "mailto:"+email+"?subject="+subject+"&body="+emailBody;
 }
 
 function getUrlVars()
@@ -351,16 +297,9 @@ $(document).ready(function () {
     // Multiple orgs are a string delimited by ";" --wrap them so we can style them
     $("#organization").splitAndWrapWithClass(";", "organization-divider");
     
-    $("#btn-create-irods-account").click(create_irods_account);
-    $("#btn-delete-irods-account").click(delete_irods_account);
-
-    // Only enable Confirm button when input password is longer than 8 characters
-    $("#id_irods_password").keyup(function () {
-        var pwdlen = $("input#id_irods_password").val().length;
-        if (pwdlen >= 8)
-            $('#btn-create-irods-account').removeAttr('disabled');
-        else
-            $('#btn-create-irods-account').attr('disabled', 'disabled');
+    $("#btn-request-irods-account").click(() => {
+        $('#request-irods-account-dialog').modal('hide');
+        create_irods_account();
     });
 
     // File name preview for Add CV
diff --git a/theme/templates/accounts/profile.html b/theme/templates/accounts/profile.html
index c8f35f4f4b..b13583fb4e 100755
--- a/theme/templates/accounts/profile.html
+++ b/theme/templates/accounts/profile.html
@@ -10,8 +10,6 @@
 {% block main %}
     <div class="container">
         {% with profile_user.userprofile as profile %}
-        <input type="hidden" id="url-create-irods-account" value="{% url 'create_irods_account' %}">
-        <input type="hidden" id="url-delete-irods-account" value="{% url 'delete_irods_account' %}">
         {% if profile_user.id == user.id or user.is_superuser %}
         <form id="profile-form" action="{% url 'update_profile' profile_user.id %}" class="account-form" method="post"
               enctype="multipart/form-data" onsubmit="return validateForm()">
@@ -89,13 +87,7 @@
                                     <hr>
                                     {# Account creation/deletion in iRODS user zone #}
                                     <div id="irods-account-container">
-                                        {% if profile.get_user_zone_status_info %}
-                                            {# an iRODS account already exists #}
-                                            <a data-toggle="modal" data-target="#delete-irods-account-dialog">Delete your iRODS user account</a>
-                                        {% else %}
-                                            {# an iRODS account does not exist #}
-                                            <a data-toggle="modal" data-target="#create-irods-account-dialog">Create your iRODS user account</a>
-                                        {% endif %}
+                                        <a data-toggle="modal" data-target="#request-irods-account-dialog">Request an iRODS user account</a>
                                     </div>
                                 </div>
                             </div>
@@ -386,12 +378,6 @@ <h3 class="panel-title">
                                                                 title="{{ qd.dz_percent|floatformat:"1" }}% used by dataZone">
                                                                 <span class="sr-only">{{ qd.dz_percent|floatformat:"1" }}% of quota is used by dataZone</span>
                                                             </div>
-                                                            <div class="progress-bar progress-divider"></div>
-                                                            <div class="progress-bar progress-bar-striped active {%if qd.percent < 70%}progress-bar-success{%elif qd.percent < 90%}progress-bar-warning{%else%}progress-bar-danger{%endif%}"
-                                                                style="width: {{ qd.uz_percent }}%" 
-                                                                title="{{ qd.uz_percent|floatformat:"1" }}% used by userZone">
-                                                                <span class="sr-only">{{ qd.uz_percent|floatformat:"1" }}% of quota used by userZone</span>
-                                                            </div>
                                                         </div>
                                                         <span>{{ qd.remaining|floatformat:"1" }} {{ qd.unit }} free of {{ qd.allocated }} {{ qd.unit }}</span>
                                                     {% endif %}
@@ -483,14 +469,6 @@ <h3 class="panel-title">
                                                     </div>
                                                 </div>
                                             {% endif %}
-                                            {% if profile_user.id == user.id or user.is_superuser %}
-                                                <br>
-                                                {% if profile.get_user_zone_status_info %}
-                                                    <div class="alert alert-success">You have created an associated iRODS account (username: <strong>{{ profile_user.username }}</strong>) in HydroShare user zone (<strong>users.hydroshare.org</strong>). </div>
-                                                {% else %}
-                                                    <div class="alert alert-warning">You have not created an associated iRODS account in HydroShare user zone (<strong>users.hydroshare.org</strong>). </div>
-                                                {% endif %}
-                                            {% endif %}
                                         </div>
 
                                         {% if profile_user.id == user.id or user.is_superuser %}
@@ -888,75 +866,36 @@ <h4 class="modal-title" id="deleteCV">Delete CV</h4>
                 </div>
             </div>
 
-            <!-- Delete iRODS User Account Modal -->
-            <div class="modal fade" id="delete-irods-account-dialog" tabindex="-1" role="dialog"
-                 aria-labelledby="Create iRODS user account">
-                <div class="modal-dialog" role="document">
+            <!-- Request iRODS User Account Modal -->
+            <div class="modal fade" id="request-irods-account-dialog" tabindex="-1" role="dialog"
+                 aria-labelledby="myModalLabel" aria-hidden="true">
+                <div class="modal-dialog modal-sm">
                     <div class="modal-content">
                         <div class="modal-header">
                             <button type="button" class="close" data-dismiss="modal"
                                     aria-label="Close"><span aria-hidden="true">&times;</span>
                             </button>
-                            <h4 class="modal-title" id="myModalLabel">Delete your iRODS user account</h4>
+                            <h4 class="modal-title" id="myModalLabel">Request an iRODS user account</h4>
                         </div>
                         <div class="modal-body">
+                            {% csrf_token %}
                             <div class="row">
-                                <div class="col-sm-2 col-xs-12 text-center">
-                                    <span id="icon-warning" class="glyphicon glyphicon-warning-sign"></span>
-                                </div>
                                 <div class="col-sm-10 col-xs-12">
-                                    <div><strong>WARNING</strong>: This will <strong>delete</strong> your iRODS user account in the
-                                        HydroShare user zone as well as all data you have under that account. You cannot recover
-                                        your data after this action.
+                                    <div>You can request that a user account be created in the HydroShare user zone.
+                                        This account will allow you to access your data using iRODS commands and clients.
+                                        You will be able to access your data in the user zone at <strong>users.hydroshare.org</strong>.
+                                        Note that your request must be sent from the email associated with your HydroShare account.
                                     </div>
                                 </div>
                             </div>
                         </div>
                         <div class="modal-footer">
                             <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
-                            <button id="btn-delete-irods-account" class="btn btn-danger">Confirm</button>
+                            <button id="btn-request-irods-account" class="btn btn-primary" type="button">Email Request</button>
                         </div>
                     </div>
                 </div>
             </div>
-
-            <!-- Create iRODS User Account Modal -->
-            <div class="modal fade" id="create-irods-account-dialog" tabindex="-1" role="dialog"
-                 aria-labelledby="myModalLabel" aria-hidden="true">
-                <div class="modal-dialog modal-sm">
-                    <div class="modal-content">
-                        <form role="form" method="post">
-                            <div class="modal-header">
-                                <button type="button" class="close" data-dismiss="modal"
-                                        aria-label="Close"><span aria-hidden="true">&times;</span>
-                                </button>
-                                <h4 class="modal-title" id="myModalLabel">Create your iRODS user account</h4>
-                            </div>
-                            <div class="modal-body">
-                                {% csrf_token %}
-                                <div class="row">
-                                    <div class="col-sm-10 col-xs-12">
-                                        <div>A corresponding iRODS user account with the same username <strong>{{ profile_user.username }}</strong> will be created
-                                            in the HydroShare user zone when you confirm this action. You will have to input a password below for your
-                                            iRODS user account, which does not have to be the same as your current HydroShare password. <strong>The password
-                                            is required to be composed of at least 8 characters.</strong> Please keep your iRODS user account password in a
-                                            secure place and use it to access your account in the HydroShare user zone using iRODS clients such as icommands or Cyberduck.
-                                        </div>
-                                        <div class="form-group">
-                                            <label for="id_irods_password">Password: </label>
-                                            <input class="form-control" autocomplete="off" id="id_irods_password" name="irods_password" placeholder="Password" type="password">
-                                        </div>
-                                    </div>
-                                </div>
-                            </div>
-                            <div class="modal-footer">
-                                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
-                                <button id="btn-create-irods-account" class="btn btn-primary" disabled>Confirm</button>
-                            </div>
-                        </form>
-                    </div>
-                </div>
-            </div>
         </form>
         {% endif %}
         {% if profile_user.id == user.id or user.is_superuser %}
diff --git a/theme/templates/includes/navbar.html b/theme/templates/includes/navbar.html
index a1071f1806..e3ede55f6f 100644
--- a/theme/templates/includes/navbar.html
+++ b/theme/templates/includes/navbar.html
@@ -554,16 +554,6 @@ <h4 class="modal-title">Create a Resource</h4>
                         "delivered": "Delivered"
                     }
                 },
-                "resource copy to user zone": {
-                    title: "Resource copy to user zone",
-                    status: {
-                        "progress": "In progress...",
-                        "aborted": "Aborted",
-                        "completed": "Completed",
-                        "failed": "Failed",
-                        "delivered": "Delivered"
-                    }
-                },
                 "resource delete": {
                     title: "Resource delete",
                     status: {
@@ -796,11 +786,6 @@ <h4 class="modal-title">Create a Resource</h4>
                             vue.deliverTask(task);
                         }
                         break;
-                    case "resource copy to user zone":
-                        if (task.status === "completed") {
-                            vue.deliverTask(task);
-                        }
-                        break;
                     case "resource delete":
                         if (task.status === "completed" && task.payload) {
                             vue.deliverTask(task);
diff --git a/theme/views.py b/theme/views.py
index c6cf3cfb92..ea0ca5a7ea 100755
--- a/theme/views.py
+++ b/theme/views.py
@@ -50,7 +50,6 @@
 from hs_core.authentication import build_oidc_url
 from hs_core.hydroshare.utils import user_from_id
 from hs_core.models import Party
-from hs_core.views.utils import run_ssh_command
 from hs_dictionary.models import University, UncategorizedTerm
 from hs_tracking.models import Variable
 from theme.forms import RatingForm, UserProfileForm, UserForm
@@ -879,126 +878,3 @@ def deactivate_user(request):
     user.save()
     messages.success(request, "Your account has been successfully deactivated.")
     return HttpResponseRedirect("/accounts/logout/")
-
-
-@login_required
-def delete_irods_account(request):
-    if request.method == "POST":
-        user = request.user
-        try:
-            exec_cmd = "{0} {1}".format(
-                settings.LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD, user.username
-            )
-            pwd = None
-            pk = None
-            if hasattr(settings, 'LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE'):
-                pwd = settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE
-            if hasattr(settings, 'PRIVATE_KEY_FILE_FOR_HS_USER_ZONE'):
-                pk = settings.PRIVATE_KEY_FILE_FOR_HS_USER_ZONE
-            output = run_ssh_command(
-                host=settings.HS_USER_ZONE_HOST,
-                uname=settings.LINUX_ADMIN_USER_FOR_HS_USER_ZONE,
-                pwd=pwd,
-                private_key_file=pk,
-                exec_cmd=exec_cmd,
-            )
-            for out_str in output:
-                if "ERROR:" in out_str.upper():
-                    # there is an error from icommand run, report the error
-                    return JsonResponse(
-                        {
-                            "error": "iRODS server failed to delete this iRODS account {0}. "
-                            "If this issue persists, please notify help@cuahsi.org.".format(
-                                user.username
-                            )
-                        },
-                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
-                    )
-
-            user_profile = UserProfile.objects.filter(user=user).first()
-            user_profile.create_irods_user_account = False
-            user_profile.save()
-            return JsonResponse(
-                {
-                    "success": "iRODS account {0} is deleted successfully".format(
-                        user.username
-                    )
-                },
-                status=status.HTTP_200_OK,
-            )
-        except Exception as ex:
-            return JsonResponse(
-                {
-                    "error": str(ex)
-                    + " - iRODS server failed to delete this iRODS account. "
-                    "If this issue persists, please notify help@cuahsi.org."
-                },
-                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
-            )
-
-
-@login_required
-def create_irods_account(request):
-    if request.method == "POST":
-        try:
-            user = request.user
-            pwd = str(request.POST.get("password"))
-            exec_cmd = "{0} {1} {2}".format(
-                settings.LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD,
-                user.username,
-                pwd,
-            )
-            pwd = None
-            pk = None
-            if hasattr(settings, 'LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE'):
-                pwd = settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE
-            if hasattr(settings, 'PRIVATE_KEY_FILE_FOR_HS_USER_ZONE'):
-                pk = settings.PRIVATE_KEY_FILE_FOR_HS_USER_ZONE
-            output = run_ssh_command(
-                host=settings.HS_USER_ZONE_HOST,
-                uname=settings.LINUX_ADMIN_USER_FOR_HS_USER_ZONE,
-                pwd=pwd,
-                private_key_file=pk,
-                exec_cmd=exec_cmd,
-            )
-            for out_str in output:
-                if "bash:" in out_str or (
-                    "ERROR:" in out_str.upper()
-                    and "CATALOG_ALREADY_HAS_ITEM_BY_THAT_NAME" not in out_str.upper()
-                ):
-                    # there is an error from icommand run which is not about the fact
-                    # that the user already exists, report the error
-                    return JsonResponse(
-                        {
-                            "error": "iRODS server failed to create this iRODS account {0}. "
-                            "If this issue persists, please notify help@cuahsi.org. More details: {1}".format(
-                                user.username, out_str
-                            )
-                        },
-                        status=status.HTTP_500_INTERNAL_SERVER_ERROR,
-                    )
-
-            user_profile = UserProfile.objects.filter(user=user).first()
-            user_profile.create_irods_user_account = True
-            user_profile.save()
-            return JsonResponse(
-                {
-                    "success": "iRODS account {0} was created successfully".format(
-                        user.username
-                    )
-                },
-                status=status.HTTP_200_OK,
-            )
-        except Exception as ex:
-            return JsonResponse(
-                {
-                    "error": str(ex)
-                    + " - iRODS server failed to create this iRODS account. "
-                    "If this issue persists, please notify help@cuahsi.org."
-                },
-                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
-            )
-    else:
-        return JsonResponse(
-            {"error": "Not POST request"}, status=status.HTTP_400_BAD_REQUEST
-        )


Test patch:
diff --git a/hs_composite_resource/tests/test_composite_resource_user_zone.py b/hs_composite_resource/tests/test_composite_resource_user_zone.py
deleted file mode 100644
index 99836274fd..0000000000
--- a/hs_composite_resource/tests/test_composite_resource_user_zone.py
+++ /dev/null
@@ -1,116 +0,0 @@
-import os
-
-from django.test import TransactionTestCase
-from django.contrib.auth.models import Group
-from django.conf import settings
-
-from hs_core import hydroshare
-from hs_core.models import BaseResource
-from hs_core.hydroshare.utils import resource_file_add_process, resource_file_add_pre_process
-from hs_core.views.utils import create_folder
-
-from hs_core.testing import TestCaseCommonUtilities
-
-from hs_file_types.models import GenericLogicalFile
-
-
-class CompositeResourceTest(TestCaseCommonUtilities, TransactionTestCase):
-    def setUp(self):
-        super(CompositeResourceTest, self).setUp()
-        super(CompositeResourceTest, self).assert_federated_irods_available()
-
-        self.group, _ = Group.objects.get_or_create(name='Hydroshare Author')
-        self.user = hydroshare.create_account(
-            'user1@nowhere.com',
-            username='user1',
-            first_name='Creator_FirstName',
-            last_name='Creator_LastName',
-            superuser=False,
-            groups=[self.group]
-        )
-
-        super(CompositeResourceTest, self).create_irods_user_in_user_zone()
-
-        self.raster_file_name = 'small_logan.tif'
-        self.raster_file = 'hs_composite_resource/tests/data/{}'.format(self.raster_file_name)
-
-        # transfer this valid tif file to user zone space for testing
-        # only need to test that tif file stored in iRODS user zone space can be used to create a
-        # composite resource and the file gets set to GenericLogicalFile type
-        # Other relevant tests are adding a file to resource, deleting a file from resource
-        # and deleting composite resource stored in iRODS user zone
-        # Other detailed tests don't need to be retested for irods user zone space scenario since
-        # as long as the tif file in iRODS user zone space can be read with metadata extracted
-        # correctly, other functionalities are done with the same common functions regardless of
-        # where the tif file comes from, either from local disk or from a federated user zone
-        irods_target_path = '/' + settings.HS_USER_IRODS_ZONE + '/home/' + self.user.username + '/'
-        file_list_dict = {self.raster_file: irods_target_path + self.raster_file_name}
-        super(CompositeResourceTest, self).save_files_to_user_zone(file_list_dict)
-
-    def tearDown(self):
-        super(CompositeResourceTest, self).tearDown()
-        super(CompositeResourceTest, self).assert_federated_irods_available()
-        super(CompositeResourceTest, self).delete_irods_user_in_user_zone()
-
-    def test_file_add_to_composite_resource(self):
-        # only do federation testing when REMOTE_USE_IRODS is True and irods docker containers
-        # are set up properly
-        super(CompositeResourceTest, self).assert_federated_irods_available()
-
-        # test that when we add file to an existing composite resource, the added file
-        # automatically set to genericlogicalfile type
-        self.assertEqual(BaseResource.objects.count(), 0)
-
-        self.composite_resource = hydroshare.create_resource(
-            resource_type='CompositeResource',
-            owner=self.user,
-            title='Test Composite Resource With Files Added From Federated Zone',
-            auto_aggregate=False
-        )
-
-        # there should not be any GenericLogicalFile object at this point
-        self.assertEqual(GenericLogicalFile.objects.count(), 0)
-
-        # add a file to the resource
-        fed_test_file_full_path = '/{zone}/home/{username}/{fname}'.format(
-            zone=settings.HS_USER_IRODS_ZONE, username=self.user.username,
-            fname=self.raster_file_name)
-        res_upload_files = []
-        resource_file_add_pre_process(resource=self.composite_resource, files=res_upload_files,
-                                      source_names=[fed_test_file_full_path], user=self.user,
-                                      folder='')
-        resource_file_add_process(resource=self.composite_resource, files=res_upload_files,
-                                  source_names=[fed_test_file_full_path], user=self.user,
-                                  auto_aggregate=False)
-
-        # there should be one resource at this point
-        self.assertEqual(BaseResource.objects.count(), 1)
-        self.assertEqual(self.composite_resource.resource_type, "CompositeResource")
-        self.assertEqual(self.composite_resource.files.all().count(), 1)
-        res_file = self.composite_resource.files.first()
-        # create the generic aggregation (logical file)
-        GenericLogicalFile.set_file_type(self.composite_resource, self.user, res_file.id)
-        # check that the resource file is associated with GenericLogicalFile
-        res_file = self.composite_resource.files.first()
-        self.assertEqual(res_file.has_logical_file, True)
-        self.assertEqual(res_file.logical_file_type_name, "GenericLogicalFile")
-        # there should be 1 GenericLogicalFile object at this point
-        self.assertEqual(GenericLogicalFile.objects.count(), 1)
-
-        # test adding a file to a folder (Note the UI does not support uploading a iRODS file
-        # to a specific folder)
-
-        # create the folder
-        new_folder = "my-new-folder"
-        new_folder_path = os.path.join("data", "contents", new_folder)
-        create_folder(self.composite_resource.short_id, new_folder_path)
-        resource_file_add_pre_process(resource=self.composite_resource, files=res_upload_files,
-                                      source_names=[fed_test_file_full_path], user=self.user,
-                                      folder=new_folder)
-        resource_file_add_process(resource=self.composite_resource, files=res_upload_files,
-                                  source_names=[fed_test_file_full_path], user=self.user,
-                                  folder=new_folder, auto_aggregate=False)
-
-        self.assertEqual(self.composite_resource.files.all().count(), 2)
-
-        self.composite_resource.delete()
diff --git a/hs_core/testing.py b/hs_core/testing.py
index 88c7620f37..a8527e6abf 100644
--- a/hs_core/testing.py
+++ b/hs_core/testing.py
@@ -13,10 +13,7 @@
 from hs_core.hydroshare import add_file_to_resource, add_resource_files
 from hs_core.views.utils import create_folder, move_or_rename_file_or_folder, zip_folder, \
     unzip_file, remove_folder
-from hs_core.views.utils import run_ssh_command
 from hs_core.tasks import FileOverrideException
-from theme.models import UserProfile
-from django_irods.storage import IrodsStorage
 
 
 class MockIRODSTestCaseMixin(object):
@@ -49,82 +46,6 @@ def tearDown(self):
 class TestCaseCommonUtilities(object):
     """Enable common utilities for iRODS testing."""
 
-    def assert_federated_irods_available(self):
-        """assert federated iRODS is available before proceeding with federation-related tests."""
-        self.assertTrue(settings.REMOTE_USE_IRODS
-                        and settings.HS_USER_ZONE_HOST == 'users.local.org'
-                        and settings.IRODS_HOST == 'data.local.org',
-                        "irods docker containers are not set up properly for federation testing")
-        self.irods_fed_storage = IrodsStorage('federated')
-        self.irods_storage = IrodsStorage()
-
-    def create_irods_user_in_user_zone(self):
-        """Create corresponding irods account in user zone."""
-        try:
-            exec_cmd = "{0} {1} {2}".format(settings.LINUX_ADMIN_USER_CREATE_USER_IN_USER_ZONE_CMD,
-                                            self.user.username, self.user.username)
-            pwd = None
-            pk = None
-            if hasattr(settings, 'LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE'):
-                pwd = settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE
-            if hasattr(settings, 'PRIVATE_KEY_FILE_FOR_HS_USER_ZONE'):
-                pk = settings.PRIVATE_KEY_FILE_FOR_HS_USER_ZONE
-            output = run_ssh_command(host=settings.HS_USER_ZONE_HOST,
-                                     uname=settings.LINUX_ADMIN_USER_FOR_HS_USER_ZONE,
-                                     pwd=pwd,
-                                     private_key_file=pk,
-                                     exec_cmd=exec_cmd)
-            for out_str in output:
-                if 'ERROR:' in out_str.upper():
-                    # irods account failed to create
-                    self.fail(out_str)
-
-            user_profile = UserProfile.objects.filter(user=self.user).first()
-            user_profile.create_irods_user_account = True
-            user_profile.save()
-        except Exception as ex:
-            self.fail(str(ex))
-
-    def delete_irods_user_in_user_zone(self):
-        """Delete irods test user in user zone."""
-        try:
-            exec_cmd = "{0} {1}".format(settings.LINUX_ADMIN_USER_DELETE_USER_IN_USER_ZONE_CMD,
-                                        self.user.username)
-            pwd = None
-            pk = None
-            if hasattr(settings, 'LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE'):
-                pwd = settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE
-            if hasattr(settings, 'PRIVATE_KEY_FILE_FOR_HS_USER_ZONE'):
-                pk = settings.PRIVATE_KEY_FILE_FOR_HS_USER_ZONE
-            output = run_ssh_command(host=settings.HS_USER_ZONE_HOST,
-                                     uname=settings.LINUX_ADMIN_USER_FOR_HS_USER_ZONE,
-                                     pwd=pwd,
-                                     private_key_file=pk,
-                                     exec_cmd=exec_cmd)
-            if output:
-                for out_str in output:
-                    if 'ERROR:' in out_str.upper():
-                        # there is an error from icommand run, report the error
-                        self.fail(out_str)
-
-            user_profile = UserProfile.objects.filter(user=self.user).first()
-            user_profile.create_irods_user_account = False
-            user_profile.save()
-        except Exception as ex:
-            # there is an error from icommand run, report the error
-            self.fail(str(ex))
-
-    def save_files_to_user_zone(self, file_name_to_target_name_dict):
-        """Save a list of files to iRODS user zone.
-
-        :param file_name_to_target_name_dict: a dictionary in the form of {ori_file, target_file}
-        where ori_file is the file to be save to, and the target_file is the full path file name
-        in iRODS user zone to save ori_file to
-        :return:
-        """
-        for file_name, target_name in list(file_name_to_target_name_dict.items()):
-            self.irods_fed_storage.saveFile(file_name, target_name)
-
     def check_file_exist(self, irods_path):
         """Check whether the input irods_path exist in iRODS.
 
@@ -140,27 +61,6 @@ def delete_directory(self, irods_path):
         """
         self.irods_fed_storage.delete(irods_path)
 
-    def verify_user_quota_usage_avu_in_user_zone(self, attname, qsize):
-        '''
-        Have to use LINUX_ADMIN_USER_FOR_HS_USER_ZONE with rodsadmin role to get user type AVU
-        in user zone and verify its quota usage is set correctly
-        :param attname: quota usage attribute name set on iRODS proxy user in user zone
-        :param qsize: quota size (type string) to be verified to equal to the value set for attname.
-        '''
-        istorage = IrodsStorage()
-        istorage.set_user_session(username=settings.LINUX_ADMIN_USER_FOR_HS_USER_ZONE,
-                                  password=settings.LINUX_ADMIN_USER_PWD_FOR_HS_USER_ZONE,
-                                  host=settings.HS_USER_ZONE_HOST,
-                                  port=settings.IRODS_PORT,
-                                  zone=settings.HS_USER_IRODS_ZONE,
-                                  sess_id='user_proxy_session')
-
-        uz_bagit_path = os.path.join('/', settings.HS_USER_IRODS_ZONE, 'home',
-                                     settings.HS_IRODS_PROXY_USER_IN_USER_ZONE,
-                                     settings.IRODS_BAGIT_PATH)
-        get_qsize = istorage.getAVU(uz_bagit_path, attname)
-        self.assertEqual(qsize, get_qsize)
-
     def resource_file_oprs(self):
         """Test common iRODS file operations.
 
diff --git a/hs_core/tests/api/native/__init__.py b/hs_core/tests/api/native/__init__.py
index d328e98cff..d21b5f05c5 100755
--- a/hs_core/tests/api/native/__init__.py
+++ b/hs_core/tests/api/native/__init__.py
@@ -16,7 +16,6 @@
 from .test_get_resource_types import *
 from .test_group_from_id import *
 from .test_hstore_extra_metadata import *
-from .test_irods_user_zone_federation import *
 from .test_publish_resource import *
 from .test_resolve_doi import *
 from .test_resource_file_folder_operations import *
diff --git a/hs_core/tests/api/native/test_add_resource_files.py b/hs_core/tests/api/native/test_add_resource_files.py
index 9b45c774b5..aaa8af8323 100755
--- a/hs_core/tests/api/native/test_add_resource_files.py
+++ b/hs_core/tests/api/native/test_add_resource_files.py
@@ -100,8 +100,8 @@ def test_add_files_over_quota(self):
 
         uquota = self.user.quotas.first()
         # make user's quota over hard limit 125%
-        uquota.user_zone_value = uquota.allocated_value * 1.3
-        uquota.save()
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
 
         # add files should raise quota exception now that the quota holder is over hard limit
         # and quota enforce flag is set to True
diff --git a/hs_core/tests/api/native/test_change_quota_holder.py b/hs_core/tests/api/native/test_change_quota_holder.py
index f4d077516c..089576bfb5 100644
--- a/hs_core/tests/api/native/test_change_quota_holder.py
+++ b/hs_core/tests/api/native/test_change_quota_holder.py
@@ -63,8 +63,6 @@ def test_change_quota_holder(self):
         # test to make sure quota holder cannot be changed to an owner who is over-quota
         uquota = self.user1.quotas.first()
         # make user1's quota over hard limit 125%
-        uquota.user_zone_value = uquota.allocated_value * 1.3
-        uquota.save()
 
         if not QuotaMessage.objects.exists():
             QuotaMessage.objects.create()
@@ -72,6 +70,9 @@ def test_change_quota_holder(self):
         qmsg.enforce_quota = True
         qmsg.save()
 
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
+
         # QuotaException should be raised when attempting to change quota holder to user1 when
         # quota is enforced
         with self.assertRaises(QuotaException):
diff --git a/hs_core/tests/api/native/test_copy_resource.py b/hs_core/tests/api/native/test_copy_resource.py
index f2de5d0126..8b231d2d8d 100644
--- a/hs_core/tests/api/native/test_copy_resource.py
+++ b/hs_core/tests/api/native/test_copy_resource.py
@@ -348,8 +348,8 @@ def test_copy_resource_over_quota(self):
         qmsg.enforce_quota = True
         qmsg.save()
         uquota = self.owner.quotas.first()
-        uquota.user_zone_value = uquota.allocated_value * 1.3
-        uquota.save()
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
 
         with self.assertRaises(QuotaException):
             hydroshare.create_empty_resource(self.res.short_id,
diff --git a/hs_core/tests/api/native/test_create_resource.py b/hs_core/tests/api/native/test_create_resource.py
index 72ec1b4461..605786daf1 100755
--- a/hs_core/tests/api/native/test_create_resource.py
+++ b/hs_core/tests/api/native/test_create_resource.py
@@ -423,8 +423,8 @@ def test_create_resource_over_quota(self):
 
         uquota = self.user.quotas.first()
         # make user's quota over hard limit 125%
-        uquota.user_zone_value = uquota.allocated_value * 1.3
-        uquota.save()
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
 
         # create_resource should raise quota exception now that the creator user is over hard
         # limit and enforce quota flag is set to True
diff --git a/hs_core/tests/api/native/test_hs_requests.py b/hs_core/tests/api/native/test_hs_requests.py
index 510160500b..ed82fed819 100644
--- a/hs_core/tests/api/native/test_hs_requests.py
+++ b/hs_core/tests/api/native/test_hs_requests.py
@@ -9,21 +9,21 @@ class TestRewrite(TestCase):
     def setUp(self):
         self.prod_fqdn = getattr(settings, "PROD_FQDN_OR_IP", "www.hydroshare.org")
         self.fqdn = getattr(settings, "FQDN_OR_IP", "www.hydroshare.org")
-        self.nginx_ip = hs_requests.get_nginx_ip()
+        self.hs_ip = hs_requests.get_local_ip()
 
     def test_localize_outer(self):
         """ rewrite requests to outer host"""
         self.assertEqual(hs_requests.localize_url("https://{}/foo/bar/".format(self.fqdn)),
-                         "https://{}/foo/bar/".format(self.nginx_ip))
+                         "https://{}/foo/bar/".format(self.hs_ip))
         self.assertEqual(hs_requests.localize_url("http://{}/foo/bar/".format(self.fqdn)),
-                         "http://{}/foo/bar/".format(self.nginx_ip))
+                         "http://{}/foo/bar/".format(self.hs_ip))
 
     def test_localize_www(self):
         """ rewrite requests to production host"""
         self.assertEqual(hs_requests.localize_url("https://{}/foo/bar/".format(self.prod_fqdn)),
-                         "https://{}/foo/bar/".format(self.nginx_ip))
+                         "https://{}/foo/bar/".format(self.hs_ip))
         self.assertEqual(hs_requests.localize_url("http://{}/foo/bar/".format(self.prod_fqdn)),
-                         "http://{}/foo/bar/".format(self.nginx_ip))
+                         "http://{}/foo/bar/".format(self.hs_ip))
 
     def test_do_not_localize_others(self):
         """ don't rewrite other host addresses """
diff --git a/hs_core/tests/api/native/test_irods_user_zone_federation.py b/hs_core/tests/api/native/test_irods_user_zone_federation.py
deleted file mode 100644
index e4f76473b0..0000000000
--- a/hs_core/tests/api/native/test_irods_user_zone_federation.py
+++ /dev/null
@@ -1,121 +0,0 @@
-import os
-from django.test import TransactionTestCase
-
-from django.conf import settings
-from django.contrib.auth.models import Group
-
-from hs_core.models import BaseResource
-from hs_core import hydroshare
-from hs_core.testing import TestCaseCommonUtilities
-
-
-class TestUserZoneIRODSFederation(TestCaseCommonUtilities, TransactionTestCase):
-    """
-    This TestCase class tests all federation zone related functionalities for composite resource,
-    in particular, only file operation-related functionalities need to be tested, including
-    creating resources in the federated user zone, adding files to resources in the user zone,
-    creating a new version of a resource in the user zone, deleting resources in the user zone,
-    and folder-related operations to the resources in the user zone. Federation zone related
-    functionalities for specific resource types need to be tested in specific resource type unit
-    tests just to make sure metadata extraction works to extract metadata from files coming from
-    a federated user zone.
-    """
-
-    def setUp(self):
-        super(TestUserZoneIRODSFederation, self).setUp()
-        super(TestUserZoneIRODSFederation, self).assert_federated_irods_available()
-
-        self.hs_group, _ = Group.objects.get_or_create(name='Hydroshare Author')
-        # create a user
-        self.user = hydroshare.create_account(
-            'test_user@email.com',
-            username='testuser',
-            first_name='some_first_name',
-            last_name='some_last_name',
-            password='testuser',
-            superuser=False,
-            groups=[self.hs_group]
-        )
-
-        # create corresponding irods account in user zone
-        super(TestUserZoneIRODSFederation, self).create_irods_user_in_user_zone()
-
-        # create files
-        self.file_one = "file1.txt"
-        self.file_two = "file2.txt"
-
-        test_file = open(self.file_one, 'w')
-        test_file.write("Test text file in file1.txt")
-        test_file.close()
-        test_file = open(self.file_two, 'w')
-        test_file.write("Test text file in file2.txt")
-        test_file.close()
-
-        # transfer files to user zone space
-        irods_target_path = '/' + settings.HS_USER_IRODS_ZONE + '/home/testuser/'
-        file_list_dict = {}
-        file_list_dict[self.file_one] = irods_target_path + self.file_one
-        file_list_dict[self.file_two] = irods_target_path + self.file_two
-        super(TestUserZoneIRODSFederation, self).save_files_to_user_zone(file_list_dict)
-
-        # start with a clean slate of resources in case other tests didn't clean up properly
-        BaseResource.objects.all().delete()
-
-        self.res = hydroshare.resource.create_resource(
-            resource_type='CompositeResource',
-            owner=self.user,
-            title='My Test Resource in HydroShare Zone'
-        )
-
-    def tearDown(self):
-        super(TestUserZoneIRODSFederation, self).tearDown()
-        # no need for further cleanup if federation testing is not setup in the first place
-        super(TestUserZoneIRODSFederation, self).assert_federated_irods_available()
-
-        # delete irods test user in user zone
-        super(TestUserZoneIRODSFederation, self).delete_irods_user_in_user_zone()
-
-        os.remove(self.file_one)
-        os.remove(self.file_two)
-        BaseResource.objects.all().delete()
-
-    def test_resource_operations_in_user_zone(self):
-        super(TestUserZoneIRODSFederation, self).assert_federated_irods_available()
-
-        # test adding files from federated user zone to an empty resource
-        # created in hydroshare zone
-        res = self.res
-        self.assertEqual(BaseResource.objects.all().count(), 1,
-                         msg='Number of resources not equal to 1')
-        self.assertEqual(res.files.all().count(), 0,
-                         msg="Number of content files is not equal to 0")
-        fed_test_file1_full_path = '/{zone}/home/testuser/{fname}'.format(
-            zone=settings.HS_USER_IRODS_ZONE, fname=self.file_one)
-        fed_file2_full_path = '/{zone}/home/testuser/{fname}'.format(
-            zone=settings.HS_USER_IRODS_ZONE, fname=self.file_two)
-        hydroshare.add_resource_files(
-            res.short_id,
-            source_names=[fed_test_file1_full_path, fed_file2_full_path])
-        # test resource has two files
-        self.assertEqual(res.files.all().count(), 2,
-                         msg="Number of content files is not equal to 2")
-
-        user_path = '/{zone}/home/testuser/'.format(zone=settings.HS_USER_IRODS_ZONE)
-        file_list = []
-        for f in res.files.all():
-            file_list.append(os.path.basename(f.storage_path))
-        self.assertTrue(self.file_one in file_list,
-                        msg='file 1 has not been added in the resource in hydroshare zone')
-        self.assertTrue(self.file_two in file_list,
-                        msg='file 2 has not been added in the resource in hydroshare zone')
-        # test original file in user test zone still exist after adding it to the resource
-        self.assertTrue(self.irods_storage.exists(user_path + self.file_one))
-        self.assertTrue(self.irods_storage.exists(user_path + self.file_two))
-
-        # test resource deletion
-        hydroshare.resource.delete_resource(res.short_id)
-        self.assertEqual(BaseResource.objects.all().count(), 0,
-                         msg='Number of resources not equal to 0')
-        # test to make sure original files still exist after resource deletion
-        self.assertTrue(self.irods_storage.exists(user_path + self.file_one))
-        self.assertTrue(self.irods_storage.exists(user_path + self.file_two))
diff --git a/hs_core/tests/api/native/test_resource_file.py b/hs_core/tests/api/native/test_resource_file.py
index 343841418f..ca4cbe007a 100644
--- a/hs_core/tests/api/native/test_resource_file.py
+++ b/hs_core/tests/api/native/test_resource_file.py
@@ -7,7 +7,7 @@
 from hs_core import hydroshare
 from hs_core.testing import MockIRODSTestCaseMixin, TestCaseCommonUtilities
 
-from hs_core.models import ResourceFile, get_path
+from hs_core.models import ResourceFile
 
 
 class TestResourceFileAPI(MockIRODSTestCaseMixin,
@@ -166,156 +166,3 @@ def test_unfederated_folder_path_setting(self):
 
         # delete resources to clean up
         hydroshare.delete_resource(self.res.short_id)
-
-    def test_federated_root_path_logic(self):
-        """ a federated file path in the root folder has the proper state after state changes """
-        # resource should not have any files at this point
-        self.assertEqual(self.res.files.all().count(), 0,
-                         msg="resource file count didn't match")
-
-        # add one file to the resource
-        hydroshare.add_resource_files(self.res.short_id, self.test_file_1)
-
-        # resource should has only one file at this point
-        self.assertEqual(self.res.files.all().count(), 1,
-                         msg="resource file count didn't match")
-
-        # get the handle of the file created above
-        resfile = self.res.files.all()[0]
-
-        # cheat: set a fake federated path to test path logic
-        oldfedpath = self.res.resource_federation_path
-        oldpath = resfile.storage_path
-
-        # intentionally break path logic by setting an unused federation path
-        fedpath = "/myzone/home/myuser"
-        self.res.resource_federation_path = fedpath
-        self.res.save()
-        # must load changes into resfile from self.res before setting storage path
-        resfile.content_object.refresh_from_db()
-        resfile.set_storage_path('file1.txt', test_exists=False)
-
-        self.assertEqual(self.res.resource_federation_path, fedpath)
-        self.assertEqual(resfile.storage_path, get_path(resfile, 'file1.txt'))
-
-        # determine where that file should live; THIS IS FAKE
-        shortpath = os.path.join(fedpath, self.res.short_id, "data",
-                                 "contents", "file1.txt")
-
-        # intentionally break the resource file path
-        resfile.set_storage_path(shortpath, test_exists=False)
-        self.assertEqual(shortpath, resfile.storage_path)
-
-        self.assertEqual(resfile.file_folder, '')
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        self.assertTrue(resfile.path_is_acceptable(shortpath, test_exists=False))
-
-        otherpath = os.path.join(fedpath, self.res.short_id, "data", "contents", "file2.txt")
-        resfile.path_is_acceptable(otherpath, test_exists=False)
-
-        # non-existent files should raise error
-        # This won't work because federation path is fake
-        # with self.assertRaises(ValidationError):
-        #     resfile.path_is_acceptable(otherpath, test_exists=True)
-
-        # try setting to an unqualified name; should qualify it
-        resfile.set_storage_path("file1.txt", test_exists=False)
-        # should match computed path
-        self.assertEqual(resfile.file_folder, '')
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        # now try to change that path to what it is already
-        resfile.set_storage_path(shortpath, test_exists=False)
-        # should match computed path
-        self.assertEqual(resfile.file_folder, '')
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        # now try to change that path to a good path to a non-existent object
-        resfile.set_storage_path(otherpath, test_exists=False)
-
-        # conclusion: strip off federation path
-        self.res.resource_federation_path = oldfedpath
-        self.res.save()
-        # must load changes into resfile from self.res before setting storage path
-        resfile.content_object.refresh_from_db()
-        resfile.set_storage_path(oldpath, test_exists=False)
-
-        # delete resources to clean up
-        hydroshare.delete_resource(self.res.short_id)
-
-    def test_federated_folder_path_logic(self):
-        """ a federated file in a subfolder has the proper state after state changes """
-
-        # resource should not have any files at this point
-        self.assertEqual(self.res.files.all().count(), 0,
-                         msg="resource file count didn't match")
-
-        ResourceFile.create_folder(self.res, 'foo')
-
-        # add one file to the resource
-        hydroshare.add_resource_files(self.res.short_id, self.test_file_1, folder='foo')
-
-        # resource should has only one file at this point
-        self.assertEqual(self.res.files.all().count(), 1,
-                         msg="resource file count didn't match")
-
-        # get the handle of the file created above
-        resfile = self.res.files.all()[0]
-
-        self.assertEqual(resfile.resource_file.name, os.path.join(self.res.short_id,
-                                                                  "data", "contents",
-                                                                  "foo", "file1.txt"))
-        self.assertEqual(resfile.file_folder, "foo")
-
-        # cheat: set a fake federated path to test path logic
-        fedpath = "/myzone/home/myuser"
-        self.res.resource_federation_path = fedpath
-        self.res.save()
-        resfile.content_object.refresh_from_db()
-        resfile.set_storage_path('foo/file1.txt', test_exists=False)
-
-        # determine where that file should live
-        shortpath = os.path.join(fedpath, self.res.short_id, "data",
-                                 "contents", "foo", "file1.txt")
-
-        self.assertEqual(shortpath, resfile.storage_path)
-
-        # this should result in an exact path
-        resfile.set_storage_path(shortpath, test_exists=False)
-
-        self.assertEqual(resfile.file_folder, "foo")
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        self.assertTrue(resfile.path_is_acceptable(shortpath, test_exists=False))
-
-        # non-existent files should raise error
-        otherpath = os.path.join(fedpath, self.res.short_id, "data", "contents", "foo", "file2.txt")
-        resfile.path_is_acceptable(otherpath, test_exists=False)
-        # This won't work because federation path is fake.
-        # with self.assertRaises(ValidationError):
-        #     resfile.path_is_acceptable(otherpath, test_exists=True)
-
-        # try setting to an unqualified name; should qualify it
-        resfile.set_storage_path("foo/file1.txt", test_exists=False)
-        # should match computed path
-        self.assertEqual(resfile.file_folder, "foo")
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        # now try to change that path to what it is already
-        resfile.set_storage_path(shortpath, test_exists=False)
-        # should match computed path
-        self.assertEqual(resfile.file_folder, "foo")
-        self.assertEqual(resfile.storage_path, shortpath)
-
-        # now try to change that path to a good path to a non-existent object
-        resfile.set_storage_path(otherpath, test_exists=False)
-
-        # conclusion: unfederate the resource
-        self.res.resource_federation_path = ""
-        self.res.save()
-        resfile.content_object.refresh_from_db()
-        resfile.set_storage_path("foo/file1.txt", test_exists=False)
-
-        # delete resources to clean up
-        hydroshare.delete_resource(self.res.short_id)
diff --git a/hs_core/tests/api/rest/test_create_resource_version.py b/hs_core/tests/api/rest/test_create_resource_version.py
index 3662e5e861..745a95be1c 100644
--- a/hs_core/tests/api/rest/test_create_resource_version.py
+++ b/hs_core/tests/api/rest/test_create_resource_version.py
@@ -34,8 +34,8 @@ def test_create_resource_version_over_quota(self):
         qmsg.enforce_quota = True
         qmsg.save()
         uquota = self.user.quotas.first()
-        uquota.user_zone_value = uquota.allocated_value * 1.3
-        uquota.save()
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
 
         version_url = "/hsapi/resource/%s/version/" % self.pid
         response = self.client.post(version_url, {}, format='json')
diff --git a/hs_core/tests/api/rest/test_resource_science_meta.py b/hs_core/tests/api/rest/test_resource_science_meta.py
index 5845c2b196..83fff2eade 100755
--- a/hs_core/tests/api/rest/test_resource_science_meta.py
+++ b/hs_core/tests/api/rest/test_resource_science_meta.py
@@ -440,7 +440,7 @@ def test_put_web_app_resource_tool_metadata(self):
                 "sharing_status": ["Public", "Discoverable"]
             },
             "toolicon": {
-                "value": "https://www.hydroshare.org/static/static/img/logo-sm.png"
+                "value": "https://storage.googleapis.com/hydroshare-prod-static-media/static/img/logo-sm.png"
             },
             "apphomepageurl": {
                 "value": "https://mywebapp.com"
@@ -502,7 +502,7 @@ def test_put_web_app_resource_without_core_metadata(self):
                 "sharing_status": ["Public", "Discoverable"]
             },
             "toolicon": {
-                "value": "https://www.hydroshare.org/static/static/img/logo-sm.png"
+                "value": "https://storage.googleapis.com/hydroshare-prod-static-media/static/img/logo-sm.png"
             },
             "apphomepageurl": {
                 "value": "https://mywebapp.com"
diff --git a/hs_core/tests/api/rest/test_unzip.py b/hs_core/tests/api/rest/test_unzip.py
index b19893a168..9e04e06c1e 100644
--- a/hs_core/tests/api/rest/test_unzip.py
+++ b/hs_core/tests/api/rest/test_unzip.py
@@ -171,7 +171,8 @@ def test_unzip_over_quota(self):
         qmsg.enforce_quota = True
         qmsg.save()
         uquota = self.user.quotas.first()
-        uquota.user_zone_value = uquota.allocated_value * 1.3
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
         uquota.save()
 
         unzip_url = "/hsapi/resource/%s/functions/unzip/test.zip/" % self.pid
diff --git a/hs_core/tests/api/rest/test_zip_folder.py b/hs_core/tests/api/rest/test_zip_folder.py
index 22cdf957a4..7198ff7073 100644
--- a/hs_core/tests/api/rest/test_zip_folder.py
+++ b/hs_core/tests/api/rest/test_zip_folder.py
@@ -162,7 +162,8 @@ def test_zip_over_quota(self):
 
         uquota = self.user.quotas.first()
         # make user's quota over hard limit 125%
-        uquota.user_zone_value = uquota.allocated_value * 1.3
+        from hs_core.tests.utils.test_utils import set_quota_usage_over_hard_limit
+        set_quota_usage_over_hard_limit(uquota, qmsg)
         uquota.save()
 
         # zip should raise quota exception now that the quota holder is over hard limit
diff --git a/hs_core/tests/test_update_quota_usage.py b/hs_core/tests/test_update_quota_usage.py
index 5874b78f3e..9c433015d7 100644
--- a/hs_core/tests/test_update_quota_usage.py
+++ b/hs_core/tests/test_update_quota_usage.py
@@ -101,7 +101,6 @@ def test_adding_file_increase_quota(self):
         self.assertNotEqual(user_quota.data_zone_value, 0)
 
         # Assert that the used values have been updated correctly
-        self.assertEqual(user_quota.user_zone_value, 0)
         dz = self.convert_gb_to_bytes(user_quota.data_zone_value)
         self.assertAlmostEqual(dz, expected, places=5)
 
diff --git a/hs_core/tests/utils/test_utils.py b/hs_core/tests/utils/test_utils.py
index 6493175bf3..fef9e1e488 100644
--- a/hs_core/tests/utils/test_utils.py
+++ b/hs_core/tests/utils/test_utils.py
@@ -4,7 +4,7 @@
 import functools
 import traceback
 
-from hs_core.hydroshare.utils import encode_resource_url, decode_resource_url
+from hs_core.hydroshare.utils import encode_resource_url, decode_resource_url, convert_file_size_to_unit
 
 
 @pytest.mark.parametrize("decoded_url,encoded_url", [
@@ -45,3 +45,9 @@ def wrapper(*args, **kwargs):
                 pdb.post_mortem(info[2])
         return wrapper
     return decorator
+
+
+def set_quota_usage_over_hard_limit(uquota, qmsg):
+    used_in_units = convert_file_size_to_unit(uquota.used_value, uquota.unit, 'B')
+    uquota.allocated_value = used_in_units / (qmsg.hard_limit_percent + 5) * 100
+    uquota.save()
diff --git a/hs_tools_resource/tests/test_web_app.py b/hs_tools_resource/tests/test_web_app.py
index 7f0d88e4b4..3eedb66329 100755
--- a/hs_tools_resource/tests/test_web_app.py
+++ b/hs_tools_resource/tests/test_web_app.py
@@ -141,11 +141,12 @@ def test_web_app_res_specific_metadata(self):
 
         # no ToolIcon obj
         self.assertEqual(ToolIcon.objects.all().count(), 0)
+        logo_sm_url = 'https://storage.googleapis.com/hydroshare-prod-static-media/static/img/logo-sm.png'
 
         # create 1 ToolIcon obj with required params
         resource.create_metadata_element(self.resWebApp.short_id,
                                          'ToolIcon',
-                                         value='https://www.hydroshare.org/static/static/img/logo-sm.png')
+                                         value=logo_sm_url)
         self.assertEqual(ToolIcon.objects.all().count(), 1)
 
         # may not create additional instance of ToolIcon
@@ -153,16 +154,15 @@ def test_web_app_res_specific_metadata(self):
             resource. \
                 create_metadata_element(self.resWebApp.short_id,
                                         'ToolIcon',
-                                        value='https://www.hydroshare.org/static/static/img/logo-sm.png')
+                                        value=logo_sm_url)
         self.assertEqual(ToolIcon.objects.all().count(), 1)
 
         # update existing meta
         resource. \
             update_metadata_element(self.resWebApp.short_id, 'ToolIcon',
                                     element_id=ToolIcon.objects.first().id,
-                                    value='https://www.hydroshare.org/static/static/img/logo-sm.png')
-        self.assertEqual(ToolIcon.objects.first().value,
-                         'https://www.hydroshare.org/static/static/img/logo-sm.png')
+                                    value=logo_sm_url)
+        self.assertEqual(ToolIcon.objects.first().value, logo_sm_url)
 
         # delete ToolIcon obj
         resource.delete_metadata_element(self.resWebApp.short_id, 'ToolIcon',
@@ -300,12 +300,13 @@ def test_bulk_metadata_update(self):
 
         # create 1 ToolIcon obj with required params
         metadata.append(
-            {'toolicon': {'value': 'https://www.hydroshare.org/static/static/img/logo-sm.png'}})
+            {'toolicon':
+             {'value': 'https://storage.googleapis.com/hydroshare-prod-static-media/static/img/logo-sm.png'}})
         # do the bulk metadata update
         self.resWebApp.metadata.update(metadata, self.user)
         self.assertEqual(ToolIcon.objects.all().count(), 1)
         self.assertEqual(ToolIcon.objects.first().value,
-                         'https://www.hydroshare.org/static/static/img/logo-sm.png')
+                         'https://storage.googleapis.com/hydroshare-prod-static-media/static/img/logo-sm.png')
 
         # test creating AppHomePageURL element
         del metadata[:]
diff --git a/irods/test-fed-irods.sh b/irods/test-fed-irods.sh
deleted file mode 100755
index 2ab85cc1cc..0000000000
--- a/irods/test-fed-irods.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-# As the mechanism for testing local irods is quite involved,
-# I wrote this script to take me through the configuration steps.
-# -- Alva
-
-. ./turn-off-local-irods.sh 
-
-./use-local-irods.sh --persist
-(cd ..; ./hsctl rebuild --db)
-sleep 30
-(cd ..; ./hsctl managepy test)