Fix VM and volume metrics listing regressions (#12284)

diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVolumesUsageHistoryCmd.java b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVolumesUsageHistoryCmd.java
index 4e9191a..e5d6a24 100644
--- a/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVolumesUsageHistoryCmd.java
+++ b/plugins/metrics/src/main/java/org/apache/cloudstack/api/ListVolumesUsageHistoryCmd.java
@@ -21,7 +21,6 @@
 
 import org.apache.cloudstack.acl.RoleType;
 import org.apache.cloudstack.api.response.ListResponse;
-import org.apache.cloudstack.api.response.SystemVmResponse;
 import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.response.VolumeMetricsStatsResponse;
 
@@ -37,7 +36,7 @@
     @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "the ID of the volume.")
     private Long id;
 
-    @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType= SystemVmResponse.class, description="the IDs of the volumes, mutually exclusive with id.")
+    @Parameter(name = ApiConstants.IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = VolumeResponse.class, description = "the IDs of the volumes, mutually exclusive with id.")
     private List<Long> ids;
 
     @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the volume (a substring match is made against the parameter value returning the data for all matching Volumes).")
diff --git a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
index c305d93..7778513 100644
--- a/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
+++ b/plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java
@@ -245,24 +245,48 @@
     }
 
     /**
+     * Outputs the parameters that should be used for access control in the query of a resource to
+     * {@code permittedAccounts} and {@code domainIdRecursiveListProject}.
+     * @param isIdProvided indicates whether any ID was provided to the command
+     */
+    private void buildBaseACLSearchParametersForMetrics(boolean isIdProvided, List<Long> permittedAccounts, Ternary<Long, Boolean,
+            Project.ListProjectResourcesCriteria> domainIdRecursiveListProject) {
+        Account caller = CallContext.current().getCallingAccount();
+        Account.Type callerType = caller.getType();
+
+        boolean recursive = AccountTypesWithRecursiveUsageAccess.contains(callerType);
+        domainIdRecursiveListProject.second(recursive);
+
+        // If no ID was provided, then the listing will skip project resources (null); otherwise, project resources should
+        // be listed as well (any long allows this)
+        Long id = isIdProvided ? 1L : null;
+
+        // Allow users to also list metrics of resources owned by projects they belong to (-1L), and admins to list all
+        // metrics belonging to their domains recursively (null)
+        Long projectId = isIdProvided && callerType == Account.Type.NORMAL ? -1L : null;
+
+        accountMgr.buildACLSearchParameters(caller, id, null, projectId, permittedAccounts, domainIdRecursiveListProject, true, false);
+    }
+
+    /**
      * Searches VMs based on {@code ListVMsUsageHistoryCmd} parameters.
      *
      * @param cmd the {@link ListVMsUsageHistoryCmd} specifying the parameters.
      * @return the list of VMs.
      */
     protected Pair<List<UserVmVO>, Integer> searchForUserVmsInternal(ListVMsUsageHistoryCmd cmd) {
-        final Long id = cmd.getId();
-        Account caller = CallContext.current().getCallingAccount();
+        List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
+
+        boolean isIdProvided = CollectionUtils.isNotEmpty(ids);
         List<Long> permittedAccounts = new ArrayList<>();
-        boolean recursive = AccountTypesWithRecursiveUsageAccess.contains(caller.getType());
-        Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, recursive, null);
-        accountMgr.buildACLSearchParameters(caller, id, null, null, permittedAccounts, domainIdRecursiveListProject, true, false);
+        Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, null, null);
+        buildBaseACLSearchParametersForMetrics(isIdProvided, permittedAccounts, domainIdRecursiveListProject);
+
         Long domainId = domainIdRecursiveListProject.first();
         Boolean isRecursive = domainIdRecursiveListProject.second();
         Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
 
         Filter searchFilter = new Filter(UserVmVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
-        List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
         String name = cmd.getName();
         String keyword = cmd.getKeyword();
 
@@ -357,18 +381,18 @@
      * @return the list of VMs.
      */
     protected Pair<List<VolumeVO>, Integer> searchForVolumesInternal(ListVolumesUsageHistoryCmd cmd) {
-        final Long id = cmd.getId();
-        Account caller = CallContext.current().getCallingAccount();
+        List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
+
+        boolean isIdProvided = CollectionUtils.isNotEmpty(ids);
         List<Long> permittedAccounts = new ArrayList<>();
-        boolean recursive = AccountTypesWithRecursiveUsageAccess.contains(caller.getType());
-        Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, recursive, null);
-        accountMgr.buildACLSearchParameters(caller, id, null, null, permittedAccounts, domainIdRecursiveListProject, true, false);
+        Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<>(null, null, null);
+        buildBaseACLSearchParametersForMetrics(isIdProvided, permittedAccounts, domainIdRecursiveListProject);
+
         Long domainId = domainIdRecursiveListProject.first();
         Boolean isRecursive = domainIdRecursiveListProject.second();
         Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
 
         Filter searchFilter = new Filter(VolumeVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal());
-        List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
         String name = cmd.getName();
         String keyword = cmd.getKeyword();