api: introduce domainid and account parameter in createTemplate API (#8210)

Introduces domainid and account as optional parameter for createTemplate API. It will allow admin to create templates for specified domain belonging to specific account.
This commit is contained in:
Manoj Kumar 2023-11-16 17:39:38 +05:30 committed by GitHub
parent cc45bffdbd
commit 0735b91037
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 51 deletions

View File

@ -21,18 +21,6 @@ import java.util.List;
import java.util.Map;
import org.apache.cloudstack.acl.SecurityChecker;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.GuestOSResponse;
import org.apache.cloudstack.api.response.SnapshotResponse;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
@ -41,13 +29,23 @@ import org.apache.cloudstack.api.BaseAsyncCreateCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.GuestOSResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
import org.apache.cloudstack.api.response.SnapshotResponse;
import org.apache.cloudstack.api.response.TemplateResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.api.response.VolumeResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.projects.Project;
import com.cloud.storage.Snapshot;
import com.cloud.storage.Volume;
import com.cloud.template.VirtualMachineTemplate;
@ -139,6 +137,19 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
@Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the zone for the template. Can be specified with snapshot only", since = "4.19.0")
private Long zoneId;
@Parameter(name = ApiConstants.DOMAIN_ID,
type = CommandType.UUID,
entityType = DomainResponse.class,
description = "an optional domainId. If the account parameter is used, domainId must also be used.",
since = "4.19.0")
private Long domainId;
@Parameter(name = ApiConstants.ACCOUNT,
type = CommandType.STRING,
description = "an optional accountName. Must be used with domainId.",
since = "4.19.0")
private String accountName;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@ -217,6 +228,14 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
return zoneId;
}
public Long getDomainId() {
return domainId;
}
public String getAccountName() {
return accountName;
}
// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
@ -232,47 +251,12 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
@Override
public long getEntityOwnerId() {
Long volumeId = getVolumeId();
Long snapshotId = getSnapshotId();
Account callingAccount = CallContext.current().getCallingAccount();
if (volumeId != null) {
Volume volume = _entityMgr.findById(Volume.class, volumeId);
if (volume != null) {
_accountService.checkAccess(callingAccount, SecurityChecker.AccessType.UseEntry, false, volume);
} else {
throw new InvalidParameterValueException("Unable to find volume by id=" + volumeId);
}
} else {
Snapshot snapshot = _entityMgr.findById(Snapshot.class, snapshotId);
if (snapshot != null) {
_accountService.checkAccess(callingAccount, SecurityChecker.AccessType.UseEntry, false, snapshot);
} else {
throw new InvalidParameterValueException("Unable to find snapshot by id=" + snapshotId);
}
}
if(projectId != null){
final Project project = _projectService.getProject(projectId);
if (project != null) {
if (project.getState() == Project.State.Active) {
Account projectAccount= _accountService.getAccount(project.getProjectAccountId());
_accountService.checkAccess(callingAccount, SecurityChecker.AccessType.UseEntry, false, projectAccount);
return project.getProjectAccountId();
} else {
final PermissionDeniedException ex =
new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() +
" as it's no longer active");
ex.addProxyObject(project.getUuid(), "projectId");
throw ex;
}
} else {
throw new InvalidParameterValueException("Unable to find project by id");
}
}
return callingAccount.getId();
ensureAccessCheck(callingAccount);
return findAccountIdToUse(callingAccount);
}
@Override
public String getEventType() {
return EventTypes.EVENT_TEMPLATE_CREATE;
@ -330,4 +314,47 @@ public class CreateTemplateCmd extends BaseAsyncCreateCmd implements UserCmd {
}
}
/***
* Performs access check on volume and snapshot for given account
* @param account
*/
private void ensureAccessCheck(Account account) {
if (volumeId != null) {
Volume volume = _entityMgr.findById(Volume.class, volumeId);
if (volume != null) {
_accountService.checkAccess(account, SecurityChecker.AccessType.UseEntry, false, volume);
} else {
throw new InvalidParameterValueException("Unable to find volume by id=" + volumeId);
}
} else {
Snapshot snapshot = _entityMgr.findById(Snapshot.class, snapshotId);
if (snapshot != null) {
_accountService.checkAccess(account, SecurityChecker.AccessType.UseEntry, false, snapshot);
} else {
throw new InvalidParameterValueException("Unable to find snapshot by id=" + snapshotId);
}
}
}
/***
* Find accountId based on accountName and domainId or projectId
* if not found, return callingAccountId for further use
* @param callingAccount
* @return accountId
*/
private Long findAccountIdToUse(Account callingAccount) {
Long accountIdToUse = null;
try {
accountIdToUse = _accountService.finalyzeAccountId(accountName, domainId, projectId, true);
} catch (InvalidParameterValueException | PermissionDeniedException ex) {
if (s_logger.isDebugEnabled()) {
s_logger.debug(String.format("An exception occurred while finalizing account id with accountName, domainId and projectId" +
"using callingAccountId=%s", callingAccount.getUuid()), ex);
}
s_logger.warn("Unable to find accountId associated with accountName=" + accountName + " and domainId="
+ domainId + " or projectId=" + projectId + ", using callingAccountId=" + callingAccount.getUuid());
}
return accountIdToUse != null ? accountIdToUse : callingAccount.getAccountId();
}
}

View File

@ -29,4 +29,20 @@ public class CreateTemplateCmdTest {
ReflectionTestUtils.setField(cmd, "zoneId", id);
Assert.assertEquals(id, cmd.getZoneId());
}
@Test
public void testDomainId() {
final CreateTemplateCmd cmd = new CreateTemplateCmd();
Long id = 2L;
ReflectionTestUtils.setField(cmd, "domainId", id);
Assert.assertEquals(id, cmd.getDomainId());
}
@Test
public void testGetAccountName() {
final CreateTemplateCmd cmd = new CreateTemplateCmd();
String accountName = "user1";
ReflectionTestUtils.setField(cmd, "accountName", accountName);
Assert.assertEquals(accountName, cmd.getAccountName());
}
}