This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch 4.15
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.15 by this push:
new 99a9063 server: Added recursive fetch of child domains for
listUsageRecords API call (#4717)
99a9063 is described below
commit 99a9063cf40e12c585ad61699aa931280f2ec4c0
Author: Spaceman1984 <[email protected]>
AuthorDate: Sat Apr 10 09:45:29 2021 +0200
server: Added recursive fetch of child domains for listUsageRecords API
call (#4717)
When calling the listUasageRecords API records per domain are fetched
recursively. This is not the case if you specify a domain id.
This PR adds a new parameter to enable fetching records recursively
(isRecursive) when passing the domain id.
---
.../command/admin/usage/ListUsageRecordsCmd.java | 9 ++
.../java/com/cloud/usage/UsageServiceImpl.java | 105 +++++++++++++++++----
2 files changed, 94 insertions(+), 20 deletions(-)
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java
index 748b9d7..91a38b0 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/usage/ListUsageRecordsCmd.java
@@ -85,6 +85,11 @@ public class ListUsageRecordsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.OLD_FORMAT, type = CommandType.BOOLEAN,
description = "Flag to enable description rendered in old format which uses
internal database IDs instead of UUIDs. False by default.")
private Boolean oldFormat;
+ @Parameter(name = ApiConstants.IS_RECURSIVE, type = CommandType.BOOLEAN,
+ description = "Specify if usage records should be fetched
recursively per domain. If an account id is passed, records will be limited to
that account.",
+ since = "4.15")
+ private Boolean recursive = false;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -153,6 +158,10 @@ public class ListUsageRecordsCmd extends BaseListCmd {
return oldFormat != null && oldFormat;
}
+ public Boolean isRecursive() {
+ return recursive;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
index 2d8d937..253daad 100644
--- a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
+++ b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java
@@ -26,6 +26,7 @@ import java.util.TimeZone;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import com.cloud.domain.Domain;
import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd;
import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd;
import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd;
@@ -200,22 +201,41 @@ public class UsageServiceImpl extends ManagerBase
implements UsageService, Manag
}
}
- boolean isAdmin = false;
- boolean isDomainAdmin = false;
+ boolean ignoreAccountId = false;
+ boolean isDomainAdmin = _accountService.isDomainAdmin(caller.getId());
+ boolean isNormalUser = _accountService.isNormalUser(caller.getId());
//If accountId couldn't be found using accountName and domainId, get
it from userContext
if (accountId == null) {
accountId = caller.getId();
//List records for all the accounts if the caller account is of
type admin.
//If account_id or account_name is explicitly mentioned, list
records for the specified account only even if the caller is of type admin
- if (_accountService.isRootAdmin(caller.getId())) {
- isAdmin = true;
- } else if (_accountService.isDomainAdmin(caller.getId())) {
- isDomainAdmin = true;
- }
+ ignoreAccountId = _accountService.isRootAdmin(caller.getId());
s_logger.debug("Account details not available. Using userContext
accountId: " + accountId);
}
+ // Check if a domain admin is allowed to access the requested domain id
+ if (isDomainAdmin) {
+ if (domainId != null) {
+ Account callerAccount =
_accountService.getAccount(caller.getId());
+ Domain domain = _domainDao.findById(domainId);
+ _accountService.checkAccess(callerAccount, domain);
+ } else {
+ // Domain admins can only access their own domain's usage
records.
+ // Set the domain if not specified.
+ domainId = caller.getDomainId();
+ }
+
+ if (cmd.getAccountId() != null) {
+ // Check if a domain admin is allowed to access the requested
account info.
+ checkDomainAdminAccountAccess(accountId, domainId);
+ }
+ }
+
+ // By default users do not have access to this API.
+ // Adding checks here in case someone changes the default access.
+ checkUserAccess(cmd, accountId, caller, isNormalUser);
+
Date startDate = cmd.getStartDate();
Date endDate = cmd.getEndDate();
if (startDate.after(endDate)) {
@@ -234,22 +254,27 @@ public class UsageServiceImpl extends ManagerBase
implements UsageService, Manag
SearchCriteria<UsageVO> sc = _usageDao.createSearchCriteria();
- if (accountId != -1 && accountId != Account.ACCOUNT_ID_SYSTEM &&
!isAdmin && !isDomainAdmin) {
- sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
- }
-
- if (isDomainAdmin) {
- SearchCriteria<DomainVO> sdc = _domainDao.createSearchCriteria();
- sdc.addOr("path", SearchCriteria.Op.LIKE,
_domainDao.findById(caller.getDomainId()).getPath() + "%");
- List<DomainVO> domains = _domainDao.search(sdc, null);
- List<Long> domainIds = new ArrayList<Long>();
- for (DomainVO domain : domains)
- domainIds.add(domain.getId());
- sc.addAnd("domainId", SearchCriteria.Op.IN, domainIds.toArray());
+ if (accountId != -1 && accountId != Account.ACCOUNT_ID_SYSTEM &&
!ignoreAccountId) {
+ // account exists and either domain on user role
+ // If not recursive and the account belongs to the user/domain
admin, or the account was passed in, filter
+ if ((accountId == caller.getId() && !cmd.isRecursive()) ||
cmd.getAccountId() != null){
+ sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId);
+ }
}
if (domainId != null) {
- sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
+ if (cmd.isRecursive()) {
+ SearchCriteria<DomainVO> sdc =
_domainDao.createSearchCriteria();
+ sdc.addOr("path", SearchCriteria.Op.LIKE,
_domainDao.findById(domainId).getPath() + "%");
+ List<DomainVO> domains = _domainDao.search(sdc, null);
+ List<Long> domainIds = new ArrayList<Long>();
+ for (DomainVO domain : domains) {
+ domainIds.add(domain.getId());
+ }
+ sc.addAnd("domainId", SearchCriteria.Op.IN,
domainIds.toArray());
+ } else {
+ sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId);
+ }
}
if (usageType != null) {
@@ -372,6 +397,46 @@ public class UsageServiceImpl extends ManagerBase
implements UsageService, Manag
return new Pair<List<? extends Usage>, Integer>(usageRecords.first(),
usageRecords.second());
}
+ private void checkUserAccess(ListUsageRecordsCmd cmd, Long accountId,
Account caller, boolean isNormalUser) {
+ if (isNormalUser) {
+ // A user can only access their own account records
+ if (caller.getId() != accountId) {
+ throw new PermissionDeniedException("Users are only allowed to
list usage records for their own account.");
+ }
+ // Users cannot get recursive records
+ if (cmd.isRecursive()) {
+ throw new PermissionDeniedException("Users are not allowed to
list usage records recursively.");
+ }
+ // Users cannot get domain records
+ if (cmd.getDomainId() != null) {
+ throw new PermissionDeniedException("Users are not allowed to
list usage records for a domain");
+ }
+ }
+ }
+
+ private void checkDomainAdminAccountAccess(Long accountId, Long domainId) {
+ Account account = _accountService.getAccount(accountId);
+ boolean matchFound = false;
+
+ if (account.getDomainId() == domainId) {
+ matchFound = true;
+ } else {
+
+ // Check if the account is in a child domain of this domain admin.
+ List<DomainVO> childDomains =
_domainDao.findAllChildren(_domainDao.findById(domainId).getPath(), domainId);
+
+ for (DomainVO domainVO : childDomains) {
+ if (account.getDomainId() == domainVO.getId()) {
+ matchFound = true;
+ break;
+ }
+ }
+ }
+ if (!matchFound) {
+ throw new PermissionDeniedException("Domain admins may only
retrieve usage records for accounts in their own domain and child domains.");
+ }
+ }
+
@Override
public TimeZone getUsageTimezone() {
return _usageTimezone;