Skip to content

Commit 5e2372f

Browse files
author
Sina Kashipazha
committed
Added configuration and Integration test to restrict public template access.
1 parent 6509f43 commit 5e2372f

File tree

5 files changed

+693
-27
lines changed

5 files changed

+693
-27
lines changed

engine/components-api/src/main/java/com/cloud/template/TemplateManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,21 @@
4141
public interface TemplateManager {
4242
static final String AllowPublicUserTemplatesCK = "allow.public.user.templates";
4343
static final String TemplatePreloaderPoolSizeCK = "template.preloader.pool.size";
44+
static final String RestrictPublicTemplateAccessToDomainCK = "restrict.public.template.access.to.domain";
4445

4546
static final ConfigKey<Boolean> AllowPublicUserTemplates = new ConfigKey<Boolean>("Advanced", Boolean.class, AllowPublicUserTemplatesCK, "true",
4647
"If false, users will not be able to create public templates.", true, ConfigKey.Scope.Account);
4748

4849
static final ConfigKey<Integer> TemplatePreloaderPoolSize = new ConfigKey<Integer>("Advanced", Integer.class, TemplatePreloaderPoolSizeCK, "8",
4950
"Size of the TemplateManager threadpool", false, ConfigKey.Scope.Global);
5051

52+
static final ConfigKey<Boolean> RestrictPublicTemplateAccessToDomain = new ConfigKey<>("Advanced", Boolean.class, RestrictPublicTemplateAccessToDomainCK, "false",
53+
"If true, the public template wouldn't share between domains", true, ConfigKey.Scope.Global);
54+
5155
static final String VMWARE_TOOLS_ISO = "vmware-tools.iso";
5256
static final String XS_TOOLS_ISO = "xs-tools.iso";
5357

58+
5459
/**
5560
* Prepares a template for vm creation for a certain storage pool.
5661
*

server/src/main/java/com/cloud/acl/DomainChecker.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import com.cloud.service.dao.ServiceOfferingDetailsDao;
5656
import com.cloud.storage.LaunchPermissionVO;
5757
import com.cloud.storage.dao.LaunchPermissionDao;
58+
import com.cloud.template.TemplateManager;
5859
import com.cloud.template.VirtualMachineTemplate;
5960
import com.cloud.user.Account;
6061
import com.cloud.user.AccountService;
@@ -167,6 +168,16 @@ public boolean checkAccess(Account caller, ControlledEntity entity, AccessType a
167168
throw new PermissionDeniedException("Domain Admin and regular users can modify only their own Public templates");
168169
}
169170
}
171+
} else if (TemplateManager.RestrictPublicTemplateAccessToDomain.value() && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { // public template can be used by other accounts in the same domain or in sub-domains, and domain admin of parent domains
172+
if (caller.getDomainId() != owner.getDomainId() && !_domainDao.isChildDomain(owner.getDomainId(), caller.getDomainId())) {
173+
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL || caller.getType() == Account.ACCOUNT_TYPE_PROJECT) {
174+
throw new PermissionDeniedException(caller + "is not allowed to access the template " + template);
175+
} else if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) {
176+
if (!_domainDao.isChildDomain(caller.getDomainId(), owner.getDomainId())) {
177+
throw new PermissionDeniedException(caller + "is not allowed to access the template " + template);
178+
}
179+
}
180+
}
170181
}
171182
}
172183

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@
221221
import com.cloud.storage.dao.VMTemplateDetailsDao;
222222
import com.cloud.tags.ResourceTagVO;
223223
import com.cloud.tags.dao.ResourceTagDao;
224+
import com.cloud.template.TemplateManager;
224225
import com.cloud.template.VirtualMachineTemplate.State;
225226
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
226227
import com.cloud.user.Account;
@@ -3406,7 +3407,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(ListTempl
34063407
Long parentTemplateId = cmd.getParentTemplateId();
34073408

34083409
boolean listAll = false;
3409-
if (templateFilter != null && templateFilter == TemplateFilter.all) {
3410+
if (TemplateFilter.all == templateFilter) {
34103411
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) {
34113412
throw new InvalidParameterValueException("Filter " + TemplateFilter.all + " can be specified by admin only");
34123413
}
@@ -3416,21 +3417,48 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(ListTempl
34163417
List<Long> permittedAccountIds = new ArrayList<Long>();
34173418
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
34183419
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false);
3420+
Long domainId = domainIdRecursiveListProject.first();
34193421
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
34203422
List<Account> permittedAccounts = new ArrayList<Account>();
34213423
for (Long accountId : permittedAccountIds) {
34223424
permittedAccounts.add(_accountMgr.getAccount(accountId));
34233425
}
34243426

3425-
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured));
3427+
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured) && _accountMgr.isRootAdmin(caller.getId()));
34263428
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
34273429

34283430
return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType,
3429-
showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique());
3431+
showDomr, cmd.listInReadyState(), permittedAccounts, caller, domainId, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique());
3432+
}
3433+
3434+
private DomainVO getDomainOf(TemplateFilter templateFilter, Account account ) {
3435+
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community || templateFilter == TemplateFilter.executable);
3436+
3437+
// get all parent domain ID's all the way till root domain
3438+
//if template filter is featured, or community, all child domains should be included in search
3439+
if (publicTemplates)
3440+
return _domainDao.findById(Domain.ROOT_DOMAIN);
3441+
else
3442+
return _domainDao.findById(account.getDomainId());
3443+
}
3444+
3445+
private ArrayList<Long> getChildDomainIds(boolean isAdmin, TemplateFilter templateFilter, DomainVO domainTreeNode ){
3446+
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community || templateFilter == TemplateFilter.executable);
3447+
ArrayList<Long> domainIds = new ArrayList<>();
3448+
3449+
// get all child domain ID's
3450+
if (isAdmin || publicTemplates) {
3451+
List<DomainVO> allChildDomains = _domainDao.findAllChildren(domainTreeNode.getPath(), domainTreeNode.getId());
3452+
for (DomainVO childDomain : allChildDomains) {
3453+
domainIds.add(childDomain.getId());
3454+
}
3455+
}
3456+
3457+
return domainIds;
34303458
}
34313459

34323460
private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize,
3433-
Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller,
3461+
Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List<Account> permittedAccounts, Account caller, Long domainId,
34343462
ListProjectResourcesCriteria listProjectResourcesCriteria, Map<String, String> tags, boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, Boolean showUnique) {
34353463

34363464
// check if zone is configured, if not, just return empty list
@@ -3458,6 +3486,8 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long temp
34583486
}
34593487
SearchCriteria<TemplateJoinVO> sc = sb.create();
34603488

3489+
boolean restrictPublicTemplatesToDomain = TemplateManager.RestrictPublicTemplateAccessToDomain.value();
3490+
34613491
// verify templateId parameter and specially handle it
34623492
if (templateId != null) {
34633493
template = _templateDao.findByIdIncludingRemoved(templateId); // Done for backward compatibility - Bug-5221
@@ -3485,6 +3515,8 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long temp
34853515
// if template is not public, perform permission check here
34863516
else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
34873517
_accountMgr.checkAccess(caller, null, false, template);
3518+
} else if (template.isPublicTemplate()) {
3519+
_accountMgr.checkAccess(caller, null, false, template);
34883520
}
34893521

34903522
// if templateId is specified, then we will just use the id to
@@ -3495,6 +3527,8 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
34953527
DomainVO domain = null;
34963528
if (!permittedAccounts.isEmpty()) {
34973529
domain = _domainDao.findById(permittedAccounts.get(0).getDomainId());
3530+
} else if (restrictPublicTemplatesToDomain && domainId != null) {
3531+
domain = _domainDao.findById(domainId);
34983532
} else {
34993533
domain = _domainDao.findById(Domain.ROOT_DOMAIN);
35003534
}
@@ -3519,31 +3553,34 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
35193553
if (!permittedAccounts.isEmpty()) {
35203554
for (Account account : permittedAccounts) {
35213555
permittedAccountIds.add(account.getId());
3522-
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community);
35233556

3524-
// get all parent domain ID's all the way till root domain
3525-
DomainVO domainTreeNode = null;
3526-
//if template filter is featured, or community, all child domains should be included in search
3527-
if (publicTemplates) {
3528-
domainTreeNode = _domainDao.findById(Domain.ROOT_DOMAIN);
3557+
DomainVO domainTreeNode = domain;
35293558

3530-
} else {
3531-
domainTreeNode = _domainDao.findById(account.getDomainId());
3559+
if(! restrictPublicTemplatesToDomain) {
3560+
domainTreeNode = getDomainOf(templateFilter, account);
35323561
}
35333562
relatedDomainIds.add(domainTreeNode.getId());
35343563
while (domainTreeNode.getParent() != null) {
35353564
domainTreeNode = _domainDao.findById(domainTreeNode.getParent());
35363565
relatedDomainIds.add(domainTreeNode.getId());
35373566
}
35383567

3539-
// get all child domain ID's
3540-
if (_accountMgr.isAdmin(account.getId()) || publicTemplates) {
3541-
List<DomainVO> allChildDomains = _domainDao.findAllChildren(domainTreeNode.getPath(), domainTreeNode.getId());
3542-
for (DomainVO childDomain : allChildDomains) {
3543-
relatedDomainIds.add(childDomain.getId());
3544-
}
3568+
boolean isAdmin = _accountMgr.isAdmin(account.getId());
3569+
if(! restrictPublicTemplatesToDomain) {
3570+
relatedDomainIds.addAll(getChildDomainIds(isAdmin, templateFilter, domainTreeNode));
3571+
} else if (isAdmin) {
3572+
relatedDomainIds.addAll(getChildDomainIds(isAdmin, templateFilter, domain));
35453573
}
35463574
}
3575+
} else if (restrictPublicTemplatesToDomain && domainId != null) {
3576+
// templatefilter=all or domainid is passed, called by root admin/domain admin
3577+
DomainVO domainTreeNode = domain;
3578+
relatedDomainIds.add(domainTreeNode.getId());
3579+
while (domainTreeNode.getParent() != null) {
3580+
domainTreeNode = _domainDao.findById(domainTreeNode.getParent());
3581+
relatedDomainIds.add(domainTreeNode.getId());
3582+
}
3583+
relatedDomainIds.addAll(getChildDomainIds(true, templateFilter, domain));
35473584
}
35483585

35493586
// control different template filters
@@ -3568,18 +3605,38 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
35683605
// only show templates shared by others
35693606
sc.addAnd("sharedAccountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
35703607
} else if (templateFilter == TemplateFilter.executable) {
3608+
SearchCriteria<TemplateJoinVO> scc2 = _templateJoinDao.createSearchCriteria();
3609+
scc2.addAnd("publicTemplate", SearchCriteria.Op.EQ, true);
3610+
if (restrictPublicTemplatesToDomain) {
3611+
SearchCriteria<TemplateJoinVO> scc3 = _templateJoinDao.createSearchCriteria();
3612+
scc3.addOr("domainId", SearchCriteria.Op.IN, relatedDomainIds.toArray());
3613+
scc3.addOr("domainId", SearchCriteria.Op.NULL);
3614+
scc2.addAnd("domainId", SearchCriteria.Op.SC, scc3);
3615+
}
35713616
SearchCriteria<TemplateJoinVO> scc = _templateJoinDao.createSearchCriteria();
3572-
scc.addOr("publicTemplate", SearchCriteria.Op.EQ, true);
3617+
scc.addOr("publicTemplate", SearchCriteria.Op.SC, scc2);
35733618
if (!permittedAccounts.isEmpty()) {
35743619
scc.addOr("accountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
35753620
}
35763621
sc.addAnd("publicTemplate", SearchCriteria.Op.SC, scc);
3577-
} else if (templateFilter == TemplateFilter.all && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
3622+
} else if (templateFilter == TemplateFilter.all && (caller.getType() != Account.ACCOUNT_TYPE_ADMIN || domainId != null)) {
3623+
SearchCriteria<TemplateJoinVO> scc2 = _templateJoinDao.createSearchCriteria();
3624+
scc2.addAnd("publicTemplate", SearchCriteria.Op.EQ, true);
3625+
if (restrictPublicTemplatesToDomain) {
3626+
SearchCriteria<TemplateJoinVO> scc3 = _templateJoinDao.createSearchCriteria();
3627+
scc3.addOr("domainId", SearchCriteria.Op.IN, relatedDomainIds.toArray());
3628+
scc3.addOr("domainId", SearchCriteria.Op.NULL);
3629+
scc2.addAnd("domainId", SearchCriteria.Op.SC, scc3);
3630+
}
35783631
SearchCriteria<TemplateJoinVO> scc = _templateJoinDao.createSearchCriteria();
3579-
scc.addOr("publicTemplate", SearchCriteria.Op.EQ, true);
3632+
scc.addOr("publicTemplate", SearchCriteria.Op.SC, scc2);
35803633

35813634
if (listProjectResourcesCriteria == ListProjectResourcesCriteria.SkipProjectResources) {
3582-
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(caller.getDomainId()).getPath() + "%");
3635+
if (domainId != null) {
3636+
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(domainId).getPath() + "%");
3637+
} else {
3638+
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(caller.getDomainId()).getPath() + "%");
3639+
}
35833640
} else {
35843641
if (!permittedAccounts.isEmpty()) {
35853642
scc.addOr("accountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
@@ -3590,13 +3647,13 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
35903647
}
35913648
}
35923649

3593-
return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr,
3650+
return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr, domainId,
35943651
showRemovedTmpl, parentTemplateId, showUnique, searchFilter, sc);
35953652

35963653
}
35973654

35983655
private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<HypervisorType> hypers, Map<String, String> tags, String name, String keyword,
3599-
HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr,
3656+
HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr, Long domainId,
36003657
boolean showRemovedTmpl, Long parentTemplateId, Boolean showUnique,
36013658
Filter searchFilter, SearchCriteria<TemplateJoinVO> sc) {
36023659
if (!isIso) {
@@ -3657,7 +3714,7 @@ private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<H
36573714
sc.addAnd("state", SearchCriteria.Op.SC, readySc);
36583715
}
36593716

3660-
if (!showDomr) {
3717+
if (!showDomr || domainId != null) {
36613718
// excluding system template
36623719
sc.addAnd("templateType", SearchCriteria.Op.NEQ, Storage.TemplateType.SYSTEM);
36633720
}
@@ -3758,6 +3815,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForIsosInternal(ListIsosCmd cm
37583815
List<Long> permittedAccountIds = new ArrayList<Long>();
37593816
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
37603817
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false);
3818+
Long domainId = domainIdRecursiveListProject.first();
37613819
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
37623820
List<Account> permittedAccounts = new ArrayList<Account>();
37633821
for (Long accountId : permittedAccountIds) {
@@ -3767,7 +3825,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForIsosInternal(ListIsosCmd cm
37673825
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
37683826

37693827
return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(),
3770-
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique());
3828+
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, domainId, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique());
37713829
}
37723830

37733831
@Override

server/src/main/java/com/cloud/template/TemplateManagerImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2227,7 +2227,7 @@ public String getConfigComponentName() {
22272227

22282228
@Override
22292229
public ConfigKey<?>[] getConfigKeys() {
2230-
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize};
2230+
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize, RestrictPublicTemplateAccessToDomain};
22312231
}
22322232

22332233
public List<TemplateAdapter> getTemplateAdapters() {

0 commit comments

Comments
 (0)