-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Added recursive fetch of child domains for listUsageRecords API call #4717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0da721a
e66d92b
7a70230
9270df4
5d43c1c
71c7c71
19fd612
a45fcee
83c9b0a
80860ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ | |
| 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 Pair<List<? extends Usage>, Integer> getUsageRecords(ListUsageRecordsCmd | |
| } | ||
| } | ||
|
|
||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Spaceman1984 is this method call required for domain admin ? |
||
|
|
||
| Date startDate = cmd.getStartDate(); | ||
| Date endDate = cmd.getEndDate(); | ||
| if (startDate.after(endDate)) { | ||
|
|
@@ -234,22 +254,27 @@ public Pair<List<? extends Usage>, Integer> getUsageRecords(ListUsageRecordsCmd | |
|
|
||
| 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); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Simplify, reuse existing DomainDao::findAllChildren
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rhtyd Using findAllChildren here would not pass the top domain. |
||
| 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 Pair<List<? extends Usage>, Integer> getUsageRecords(ListUsageRecordsCmd | |
| 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; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.