Skip to content

Commit 0223c6f

Browse files
author
Sina Kashipazha
committed
Added configuration and Integration test to restrict public template access.
1 parent a1be9b0 commit 0223c6f

5 files changed

Lines changed: 691 additions & 28 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,16 @@
4242
public interface TemplateManager {
4343
static final String AllowPublicUserTemplatesCK = "allow.public.user.templates";
4444
static final String TemplatePreloaderPoolSizeCK = "template.preloader.pool.size";
45+
static final String RestrictPublicTemplateAccessToDomainCK = "restrict.public.template.access.to.domain";
4546

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

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

52-
53+
static final ConfigKey<Boolean> RestrictPublicTemplateAccessToDomain = new ConfigKey<>("Advanced", Boolean.class, RestrictPublicTemplateAccessToDomainCK, "false",
54+
"If true, the public template wouldn't share between domains", true, ConfigKey.Scope.Global);
5355

5456
/**
5557
* Prepares a template for vm creation for a certain storage pool.

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;
@@ -166,6 +167,16 @@ public boolean checkAccess(Account caller, ControlledEntity entity, AccessType a
166167
throw new PermissionDeniedException("Domain Admin and regular users can modify only their own Public templates");
167168
}
168169
}
170+
} 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
171+
if (caller.getDomainId() != owner.getDomainId() && !_domainDao.isChildDomain(owner.getDomainId(), caller.getDomainId())) {
172+
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL || caller.getType() == Account.ACCOUNT_TYPE_PROJECT) {
173+
throw new PermissionDeniedException(caller + "is not allowed to access the template " + template);
174+
} else if (caller.getType() == Account.ACCOUNT_TYPE_DOMAIN_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) {
175+
if (!_domainDao.isChildDomain(caller.getDomainId(), owner.getDomainId())) {
176+
throw new PermissionDeniedException(caller + "is not allowed to access the template " + template);
177+
}
178+
}
179+
}
169180
}
170181
}
171182

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

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
import com.cloud.storage.dao.VMTemplateDao;
219219
import com.cloud.tags.ResourceTagVO;
220220
import com.cloud.tags.dao.ResourceTagDao;
221+
import com.cloud.template.TemplateManager;
221222
import com.cloud.template.VirtualMachineTemplate.State;
222223
import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
223224
import com.cloud.user.Account;
@@ -3355,7 +3356,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(ListTempl
33553356
Long parentTemplateId = cmd.getParentTemplateId();
33563357

33573358
boolean listAll = false;
3358-
if (templateFilter != null && templateFilter == TemplateFilter.all) {
3359+
if (TemplateFilter.all == templateFilter) {
33593360
if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL) {
33603361
throw new InvalidParameterValueException("Filter " + TemplateFilter.all + " can be specified by admin only");
33613362
}
@@ -3365,21 +3366,48 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(ListTempl
33653366
List<Long> permittedAccountIds = new ArrayList<Long>();
33663367
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
33673368
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false);
3369+
Long domainId = domainIdRecursiveListProject.first();
33683370
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
33693371
List<Account> permittedAccounts = new ArrayList<Account>();
33703372
for (Long accountId : permittedAccountIds) {
33713373
permittedAccounts.add(_accountMgr.getAccount(accountId));
33723374
}
33733375

3374-
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured));
3376+
boolean showDomr = ((templateFilter != TemplateFilter.selfexecutable) && (templateFilter != TemplateFilter.featured) && _accountMgr.isRootAdmin(caller.getId()));
33753377
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
33763378

33773379
return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType,
3378-
showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique());
3380+
showDomr, cmd.listInReadyState(), permittedAccounts, caller, domainId, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique());
3381+
}
3382+
3383+
private DomainVO getDomainOf(TemplateFilter templateFilter, Account account ) {
3384+
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community || templateFilter == TemplateFilter.executable);
3385+
3386+
// get all parent domain ID's all the way till root domain
3387+
//if template filter is featured, or community, all child domains should be included in search
3388+
if (publicTemplates)
3389+
return _domainDao.findById(Domain.ROOT_DOMAIN);
3390+
else
3391+
return _domainDao.findById(account.getDomainId());
3392+
}
3393+
3394+
private ArrayList<Long> getChildDomainIds(boolean isAdmin, TemplateFilter templateFilter, DomainVO domainTreeNode ){
3395+
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community || templateFilter == TemplateFilter.executable);
3396+
ArrayList<Long> domainIds = new ArrayList<>();
3397+
3398+
// get all child domain ID's
3399+
if (isAdmin || publicTemplates) {
3400+
List<DomainVO> allChildDomains = _domainDao.findAllChildren(domainTreeNode.getPath(), domainTreeNode.getId());
3401+
for (DomainVO childDomain : allChildDomains) {
3402+
domainIds.add(childDomain.getId());
3403+
}
3404+
}
3405+
3406+
return domainIds;
33793407
}
33803408

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

33853413
// check if zone is configured, if not, just return empty list
@@ -3407,6 +3435,8 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long temp
34073435
}
34083436
SearchCriteria<TemplateJoinVO> sc = sb.create();
34093437

3438+
boolean restrictPublicTemplatesToDomain = TemplateManager.RestrictPublicTemplateAccessToDomain.value();
3439+
34103440
// verify templateId parameter and specially handle it
34113441
if (templateId != null) {
34123442
template = _templateDao.findByIdIncludingRemoved(templateId); // Done for backward compatibility - Bug-5221
@@ -3434,6 +3464,8 @@ private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long temp
34343464
// if template is not public, perform permission check here
34353465
else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
34363466
_accountMgr.checkAccess(caller, null, false, template);
3467+
} else if (template.isPublicTemplate()) {
3468+
_accountMgr.checkAccess(caller, null, false, template);
34373469
}
34383470

34393471
// if templateId is specified, then we will just use the id to
@@ -3444,6 +3476,8 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
34443476
DomainVO domain = null;
34453477
if (!permittedAccounts.isEmpty()) {
34463478
domain = _domainDao.findById(permittedAccounts.get(0).getDomainId());
3479+
} else if (restrictPublicTemplatesToDomain && domainId != null) {
3480+
domain = _domainDao.findById(domainId);
34473481
} else {
34483482
domain = _domainDao.findById(Domain.ROOT_DOMAIN);
34493483
}
@@ -3468,31 +3502,34 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
34683502
if (!permittedAccounts.isEmpty()) {
34693503
for (Account account : permittedAccounts) {
34703504
permittedAccountIds.add(account.getId());
3471-
boolean publicTemplates = (templateFilter == TemplateFilter.featured || templateFilter == TemplateFilter.community);
34723505

3473-
// get all parent domain ID's all the way till root domain
3474-
DomainVO domainTreeNode = null;
3475-
//if template filter is featured, or community, all child domains should be included in search
3476-
if (publicTemplates) {
3477-
domainTreeNode = _domainDao.findById(Domain.ROOT_DOMAIN);
3506+
DomainVO domainTreeNode = domain;
34783507

3479-
} else {
3480-
domainTreeNode = _domainDao.findById(account.getDomainId());
3508+
if(! restrictPublicTemplatesToDomain) {
3509+
domainTreeNode = getDomainOf(templateFilter, account);
34813510
}
34823511
relatedDomainIds.add(domainTreeNode.getId());
34833512
while (domainTreeNode.getParent() != null) {
34843513
domainTreeNode = _domainDao.findById(domainTreeNode.getParent());
34853514
relatedDomainIds.add(domainTreeNode.getId());
34863515
}
34873516

3488-
// get all child domain ID's
3489-
if (_accountMgr.isAdmin(account.getId()) || publicTemplates) {
3490-
List<DomainVO> allChildDomains = _domainDao.findAllChildren(domainTreeNode.getPath(), domainTreeNode.getId());
3491-
for (DomainVO childDomain : allChildDomains) {
3492-
relatedDomainIds.add(childDomain.getId());
3493-
}
3517+
boolean isAdmin = _accountMgr.isAdmin(account.getId());
3518+
if(! restrictPublicTemplatesToDomain) {
3519+
relatedDomainIds.addAll(getChildDomainIds(isAdmin, templateFilter, domainTreeNode));
3520+
} else if (isAdmin) {
3521+
relatedDomainIds.addAll(getChildDomainIds(isAdmin, templateFilter, domain));
34943522
}
34953523
}
3524+
} else if (restrictPublicTemplatesToDomain && domainId != null) {
3525+
// templatefilter=all or domainid is passed, called by root admin/domain admin
3526+
DomainVO domainTreeNode = domain;
3527+
relatedDomainIds.add(domainTreeNode.getId());
3528+
while (domainTreeNode.getParent() != null) {
3529+
domainTreeNode = _domainDao.findById(domainTreeNode.getParent());
3530+
relatedDomainIds.add(domainTreeNode.getId());
3531+
}
3532+
relatedDomainIds.addAll(getChildDomainIds(true, templateFilter, domain));
34963533
}
34973534

34983535
// control different template filters
@@ -3517,18 +3554,38 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
35173554
// only show templates shared by others
35183555
sc.addAnd("sharedAccountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
35193556
} else if (templateFilter == TemplateFilter.executable) {
3557+
SearchCriteria<TemplateJoinVO> scc2 = _templateJoinDao.createSearchCriteria();
3558+
scc2.addAnd("publicTemplate", SearchCriteria.Op.EQ, true);
3559+
if (restrictPublicTemplatesToDomain) {
3560+
SearchCriteria<TemplateJoinVO> scc3 = _templateJoinDao.createSearchCriteria();
3561+
scc3.addOr("domainId", SearchCriteria.Op.IN, relatedDomainIds.toArray());
3562+
scc3.addOr("domainId", SearchCriteria.Op.NULL);
3563+
scc2.addAnd("domainId", SearchCriteria.Op.SC, scc3);
3564+
}
35203565
SearchCriteria<TemplateJoinVO> scc = _templateJoinDao.createSearchCriteria();
3521-
scc.addOr("publicTemplate", SearchCriteria.Op.EQ, true);
3566+
scc.addOr("publicTemplate", SearchCriteria.Op.SC, scc2);
35223567
if (!permittedAccounts.isEmpty()) {
35233568
scc.addOr("accountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
35243569
}
35253570
sc.addAnd("publicTemplate", SearchCriteria.Op.SC, scc);
3526-
} else if (templateFilter == TemplateFilter.all && caller.getType() != Account.ACCOUNT_TYPE_ADMIN) {
3571+
} else if (templateFilter == TemplateFilter.all && (caller.getType() != Account.ACCOUNT_TYPE_ADMIN || domainId != null)) {
3572+
SearchCriteria<TemplateJoinVO> scc2 = _templateJoinDao.createSearchCriteria();
3573+
scc2.addAnd("publicTemplate", SearchCriteria.Op.EQ, true);
3574+
if (restrictPublicTemplatesToDomain) {
3575+
SearchCriteria<TemplateJoinVO> scc3 = _templateJoinDao.createSearchCriteria();
3576+
scc3.addOr("domainId", SearchCriteria.Op.IN, relatedDomainIds.toArray());
3577+
scc3.addOr("domainId", SearchCriteria.Op.NULL);
3578+
scc2.addAnd("domainId", SearchCriteria.Op.SC, scc3);
3579+
}
35273580
SearchCriteria<TemplateJoinVO> scc = _templateJoinDao.createSearchCriteria();
3528-
scc.addOr("publicTemplate", SearchCriteria.Op.EQ, true);
3581+
scc.addOr("publicTemplate", SearchCriteria.Op.SC, scc2);
35293582

35303583
if (listProjectResourcesCriteria == ListProjectResourcesCriteria.SkipProjectResources) {
3531-
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(caller.getDomainId()).getPath() + "%");
3584+
if (domainId != null) {
3585+
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(domainId).getPath() + "%");
3586+
} else {
3587+
scc.addOr("domainPath", SearchCriteria.Op.LIKE, _domainDao.findById(caller.getDomainId()).getPath() + "%");
3588+
}
35323589
} else {
35333590
if (!permittedAccounts.isEmpty()) {
35343591
scc.addOr("accountId", SearchCriteria.Op.IN, permittedAccountIds.toArray());
@@ -3539,13 +3596,13 @@ else if (!template.isPublicTemplate() && caller.getType() != Account.ACCOUNT_TYP
35393596
}
35403597
}
35413598

3542-
return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr,
3599+
return templateChecks(isIso, hypers, tags, name, keyword, hyperType, onlyReady, bootable, zoneId, showDomr, domainId,
35433600
showRemovedTmpl, parentTemplateId, showUnique, searchFilter, sc);
35443601

35453602
}
35463603

35473604
private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<HypervisorType> hypers, Map<String, String> tags, String name, String keyword,
3548-
HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr,
3605+
HypervisorType hyperType, boolean onlyReady, Boolean bootable, Long zoneId, boolean showDomr, Long domainId,
35493606
boolean showRemovedTmpl, Long parentTemplateId, Boolean showUnique,
35503607
Filter searchFilter, SearchCriteria<TemplateJoinVO> sc) {
35513608
if (!isIso) {
@@ -3606,7 +3663,7 @@ private Pair<List<TemplateJoinVO>, Integer> templateChecks(boolean isIso, List<H
36063663
sc.addAnd("state", SearchCriteria.Op.SC, readySc);
36073664
}
36083665

3609-
if (!showDomr) {
3666+
if (!showDomr || domainId != null) {
36103667
// excluding system template
36113668
sc.addAnd("templateType", SearchCriteria.Op.NEQ, Storage.TemplateType.SYSTEM);
36123669
}
@@ -3707,6 +3764,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForIsosInternal(ListIsosCmd cm
37073764
List<Long> permittedAccountIds = new ArrayList<Long>();
37083765
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(cmd.getDomainId(), cmd.isRecursive(), null);
37093766
_accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccountIds, domainIdRecursiveListProject, listAll, false);
3767+
Long domainId = domainIdRecursiveListProject.first();
37103768
ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
37113769
List<Account> permittedAccounts = new ArrayList<Account>();
37123770
for (Long accountId : permittedAccountIds) {
@@ -3716,7 +3774,7 @@ private Pair<List<TemplateJoinVO>, Integer> searchForIsosInternal(ListIsosCmd cm
37163774
HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor());
37173775

37183776
return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(),
3719-
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique());
3777+
hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, domainId, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique());
37203778
}
37213779

37223780
@Override

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

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

22442244
@Override
22452245
public ConfigKey<?>[] getConfigKeys() {
2246-
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize};
2246+
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize, RestrictPublicTemplateAccessToDomain};
22472247
}
22482248

22492249
public List<TemplateAdapter> getTemplateAdapters() {

0 commit comments

Comments
 (0)