Skip to content

Commit dbfc7f2

Browse files
committed
Merge branch '4.19'
2 parents 046870e + 0602f46 commit dbfc7f2

File tree

32 files changed

+947
-181
lines changed

32 files changed

+947
-181
lines changed

api/src/main/java/com/cloud/user/AccountService.java

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ User createUser(String userName, String password, String firstName, String lastN
116116

117117
void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException;
118118

119+
void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource);
120+
119121
Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly);
120122

121123
/**

engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java

+42-23
Original file line numberDiff line numberDiff line change
@@ -1958,25 +1958,26 @@ protected MigrationOptions createFullCloneMigrationOptions(VolumeInfo srcVolumeI
19581958
* - Full clones (no backing file): Take snapshot of the VM prior disk creation
19591959
* Return this information
19601960
*/
1961-
protected void setVolumeMigrationOptions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo,
1962-
VirtualMachineTO vmTO, Host srcHost, StoragePoolVO destStoragePool) {
1963-
if (!destStoragePool.isManaged()) {
1964-
String srcVolumeBackingFile = getVolumeBackingFile(srcVolumeInfo);
1965-
1966-
String srcPoolUuid = srcVolumeInfo.getDataStore().getUuid();
1967-
StoragePoolVO srcPool = _storagePoolDao.findById(srcVolumeInfo.getPoolId());
1968-
Storage.StoragePoolType srcPoolType = srcPool.getPoolType();
1969-
1970-
MigrationOptions migrationOptions;
1971-
if (StringUtils.isNotBlank(srcVolumeBackingFile)) {
1972-
migrationOptions = createLinkedCloneMigrationOptions(srcVolumeInfo, destVolumeInfo,
1973-
srcVolumeBackingFile, srcPoolUuid, srcPoolType);
1974-
} else {
1975-
migrationOptions = createFullCloneMigrationOptions(srcVolumeInfo, vmTO, srcHost, srcPoolUuid, srcPoolType);
1976-
}
1977-
migrationOptions.setTimeout(StorageManager.KvmStorageOnlineMigrationWait.value());
1978-
destVolumeInfo.setMigrationOptions(migrationOptions);
1961+
protected void setVolumeMigrationOptions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo, VirtualMachineTO vmTO, Host srcHost, StoragePoolVO destStoragePool,
1962+
MigrationOptions.Type migrationType) {
1963+
if (destStoragePool.isManaged()) {
1964+
return;
19791965
}
1966+
1967+
String srcVolumeBackingFile = getVolumeBackingFile(srcVolumeInfo);
1968+
1969+
String srcPoolUuid = srcVolumeInfo.getDataStore().getUuid();
1970+
StoragePoolVO srcPool = _storagePoolDao.findById(srcVolumeInfo.getPoolId());
1971+
Storage.StoragePoolType srcPoolType = srcPool.getPoolType();
1972+
1973+
MigrationOptions migrationOptions;
1974+
if (MigrationOptions.Type.LinkedClone.equals(migrationType)) {
1975+
migrationOptions = createLinkedCloneMigrationOptions(srcVolumeInfo, destVolumeInfo, srcVolumeBackingFile, srcPoolUuid, srcPoolType);
1976+
} else {
1977+
migrationOptions = createFullCloneMigrationOptions(srcVolumeInfo, vmTO, srcHost, srcPoolUuid, srcPoolType);
1978+
}
1979+
migrationOptions.setTimeout(StorageManager.KvmStorageOnlineMigrationWait.value());
1980+
destVolumeInfo.setMigrationOptions(migrationOptions);
19801981
}
19811982

19821983
/**
@@ -2007,6 +2008,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20072008
Map<VolumeInfo, VolumeInfo> srcVolumeInfoToDestVolumeInfo = new HashMap<>();
20082009

20092010
boolean managedStorageDestination = false;
2011+
boolean migrateNonSharedInc = false;
20102012
for (Map.Entry<VolumeInfo, DataStore> entry : volumeDataStoreMap.entrySet()) {
20112013
VolumeInfo srcVolumeInfo = entry.getKey();
20122014
DataStore destDataStore = entry.getValue();
@@ -2034,6 +2036,9 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20342036
logger.debug(String.format("Skipping copy template from source storage pool [%s] to target storage pool [%s] before migration due to volume [%s] does not have a template.", sourceStoragePool.getId(), destStoragePool.getId(), srcVolumeInfo.getId()));
20352037
}
20362038

2039+
MigrationOptions.Type migrationType = decideMigrationTypeAndCopyTemplateIfNeeded(destHost, vmInstance, srcVolumeInfo, sourceStoragePool, destStoragePool, destDataStore);
2040+
migrateNonSharedInc = migrateNonSharedInc || MigrationOptions.Type.LinkedClone.equals(migrationType);
2041+
20372042
VolumeVO destVolume = duplicateVolumeOnAnotherStorage(srcVolume, destStoragePool);
20382043
VolumeInfo destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
20392044

@@ -2044,7 +2049,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20442049
// move the volume from Ready to Migrating
20452050
destVolumeInfo.processEvent(Event.MigrationRequested);
20462051

2047-
setVolumeMigrationOptions(srcVolumeInfo, destVolumeInfo, vmTO, srcHost, destStoragePool);
2052+
setVolumeMigrationOptions(srcVolumeInfo, destVolumeInfo, vmTO, srcHost, destStoragePool, migrationType);
20482053

20492054
// create a volume on the destination storage
20502055
destDataStore.getDriver().createAsync(destDataStore, destVolumeInfo, null);
@@ -2059,7 +2064,7 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
20592064

20602065
_volumeDao.update(destVolume.getId(), destVolume);
20612066

2062-
postVolumeCreationActions(srcVolumeInfo, destVolumeInfo, vmTO, srcHost);
2067+
postVolumeCreationActions(srcVolumeInfo, destVolumeInfo);
20632068

20642069
destVolumeInfo = _volumeDataFactory.getVolume(destVolume.getId(), destDataStore);
20652070

@@ -2110,8 +2115,6 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
21102115
VMInstanceVO vm = _vmDao.findById(vmTO.getId());
21112116
boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
21122117

2113-
boolean migrateNonSharedInc = isSourceAndDestinationPoolTypeOfNfs(volumeDataStoreMap);
2114-
21152118
MigrateCommand migrateCommand = new MigrateCommand(vmTO.getName(), destHost.getPrivateIpAddress(), isWindows, vmTO, true);
21162119
migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value());
21172120
migrateCommand.setMigrateStorage(migrateStorage);
@@ -2161,6 +2164,22 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
21612164
}
21622165
}
21632166

2167+
private MigrationOptions.Type decideMigrationTypeAndCopyTemplateIfNeeded(Host destHost, VMInstanceVO vmInstance, VolumeInfo srcVolumeInfo, StoragePoolVO sourceStoragePool, StoragePoolVO destStoragePool, DataStore destDataStore) {
2168+
VMTemplateVO vmTemplate = _vmTemplateDao.findById(vmInstance.getTemplateId());
2169+
String srcVolumeBackingFile = getVolumeBackingFile(srcVolumeInfo);
2170+
if (StringUtils.isNotBlank(srcVolumeBackingFile) && supportStoragePoolType(destStoragePool.getPoolType(), StoragePoolType.Filesystem) &&
2171+
srcVolumeInfo.getTemplateId() != null &&
2172+
Objects.nonNull(vmTemplate) &&
2173+
!Arrays.asList(KVM_VM_IMPORT_DEFAULT_TEMPLATE_NAME, VM_IMPORT_DEFAULT_TEMPLATE_NAME).contains(vmTemplate.getName())) {
2174+
LOGGER.debug(String.format("Copying template [%s] of volume [%s] from source storage pool [%s] to target storage pool [%s].", srcVolumeInfo.getTemplateId(), srcVolumeInfo.getId(), sourceStoragePool.getId(), destStoragePool.getId()));
2175+
copyTemplateToTargetFilesystemStorageIfNeeded(srcVolumeInfo, sourceStoragePool, destDataStore, destStoragePool, destHost);
2176+
return MigrationOptions.Type.LinkedClone;
2177+
}
2178+
LOGGER.debug(String.format("Skipping copy template from source storage pool [%s] to target storage pool [%s] before migration due to volume [%s] does not have a " +
2179+
"template or we are doing full clone migration.", sourceStoragePool.getId(), destStoragePool.getId(), srcVolumeInfo.getId()));
2180+
return MigrationOptions.Type.FullClone;
2181+
}
2182+
21642183
protected String formatMigrationElementsAsJsonToDisplayOnLog(String objectName, Object object, Object from, Object to){
21652184
return String.format("{%s: \"%s\", from: \"%s\", to:\"%s\"}", objectName, object, from, to);
21662185
}
@@ -2422,7 +2441,7 @@ protected void updateCopiedTemplateReference(VolumeInfo srcVolumeInfo, VolumeInf
24222441
/**
24232442
* Handle post destination volume creation actions depending on the migrating volume type: full clone or linked clone
24242443
*/
2425-
protected void postVolumeCreationActions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo, VirtualMachineTO vmTO, Host srcHost) {
2444+
protected void postVolumeCreationActions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo) {
24262445
MigrationOptions migrationOptions = destVolumeInfo.getMigrationOptions();
24272446
if (migrationOptions != null) {
24282447
if (migrationOptions.getType() == MigrationOptions.Type.LinkedClone && migrationOptions.isCopySrcTemplate()) {

plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaBalanceCmd.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
import javax.inject.Inject;
2323

24+
import com.cloud.user.Account;
25+
26+
import org.apache.cloudstack.api.ACL;
2427
import org.apache.cloudstack.api.APICommand;
2528
import org.apache.cloudstack.api.ApiConstants;
2629
import org.apache.cloudstack.api.BaseCmd;
@@ -40,6 +43,7 @@ public class QuotaBalanceCmd extends BaseCmd {
4043
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Account Id for which statement needs to be generated")
4144
private String accountName;
4245

46+
@ACL
4347
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "If domain Id is given and the caller is domain admin then the statement is generated for domain.")
4448
private Long domainId;
4549

@@ -51,6 +55,7 @@ public class QuotaBalanceCmd extends BaseCmd {
5155
ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
5256
private Date startDate;
5357

58+
@ACL
5459
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List usage records for the specified account")
5560
private Long accountId;
5661

@@ -104,7 +109,14 @@ public void setStartDate(Date startDate) {
104109

105110
@Override
106111
public long getEntityOwnerId() {
107-
return _accountService.getActiveAccountByName(accountName, domainId).getAccountId();
112+
if (accountId != null) {
113+
return accountId;
114+
}
115+
Account account = _accountService.getActiveAccountByName(accountName, domainId);
116+
if (account != null) {
117+
return account.getAccountId();
118+
}
119+
return Account.ACCOUNT_ID_SYSTEM;
108120
}
109121

110122
@Override

plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaCreditsCmd.java

+6
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.cloud.user.Account;
2020

21+
import org.apache.cloudstack.api.ACL;
2122
import org.apache.cloudstack.api.APICommand;
2223
import org.apache.cloudstack.api.ApiConstants;
2324
import org.apache.cloudstack.api.ApiErrorCode;
@@ -46,6 +47,7 @@ public class QuotaCreditsCmd extends BaseCmd {
4647
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Account Id for which quota credits need to be added")
4748
private String accountName;
4849

50+
@ACL
4951
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "Domain for which quota credits need to be added")
5052
private Long domainId;
5153

@@ -130,6 +132,10 @@ public void execute() {
130132

131133
@Override
132134
public long getEntityOwnerId() {
135+
Account account = _accountService.getActiveAccountByName(accountName, domainId);
136+
if (account != null) {
137+
return account.getAccountId();
138+
}
133139
return Account.ACCOUNT_ID_SYSTEM;
134140
}
135141

plugins/database/quota/src/main/java/org/apache/cloudstack/api/command/QuotaStatementCmd.java

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import javax.inject.Inject;
2323

24+
import org.apache.cloudstack.api.ACL;
2425
import org.apache.cloudstack.api.APICommand;
2526
import org.apache.cloudstack.api.ApiConstants;
2627
import org.apache.cloudstack.api.BaseCmd;
@@ -42,6 +43,7 @@ public class QuotaStatementCmd extends BaseCmd {
4243
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Optional, Account Id for which statement needs to be generated")
4344
private String accountName;
4445

46+
@ACL
4547
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "Optional, If domain Id is given and the caller is domain admin then the statement is generated for domain.")
4648
private Long domainId;
4749

@@ -56,6 +58,7 @@ public class QuotaStatementCmd extends BaseCmd {
5658
@Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER, description = "List quota usage records for the specified usage type")
5759
private Integer usageType;
5860

61+
@ACL
5962
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List usage records for the specified account")
6063
private Long accountId;
6164

@@ -112,6 +115,9 @@ public void setStartDate(Date startDate) {
112115

113116
@Override
114117
public long getEntityOwnerId() {
118+
if (accountId != null) {
119+
return accountId;
120+
}
115121
Account activeAccountByName = _accountService.getActiveAccountByName(accountName, domainId);
116122
if (activeAccountByName != null) {
117123
return activeAccountByName.getAccountId();

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/MigrateKVMAsync.java

+2
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ public Domain call() throws LibvirtException {
123123
if (migrateNonSharedInc) {
124124
flags |= VIR_MIGRATE_PERSIST_DEST;
125125
flags |= VIR_MIGRATE_NON_SHARED_INC;
126+
logger.debug("Setting VIR_MIGRATE_NON_SHARED_INC for linked clone migration.");
126127
} else {
127128
flags |= VIR_MIGRATE_NON_SHARED_DISK;
129+
logger.debug("Setting VIR_MIGRATE_NON_SHARED_DISK for full clone migration.");
128130
}
129131
}
130132

plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/MockAccountManager.java

+5
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ public void checkAccess(Account account, AccessType accessType, boolean sameOwne
454454
// TODO Auto-generated method stub
455455
}
456456

457+
@Override
458+
public void validateAccountHasAccessToResource(Account account, AccessType accessType, Object resource) {
459+
// TODO Auto-generated method stub
460+
}
461+
457462
@Override
458463
public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) {
459464
// TODO Auto-generated method stub

plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.apache.cloudstack.saml.SAML2AuthManager;
4848
import org.apache.cloudstack.saml.SAMLUtils;
4949

50+
import com.cloud.api.ApiServer;
5051
import com.cloud.api.response.ApiResponseSerializer;
5152
import com.cloud.domain.Domain;
5253
import com.cloud.domain.dao.DomainDao;
@@ -59,6 +60,8 @@
5960
import com.cloud.user.dao.UserDao;
6061
import com.cloud.utils.HttpUtils;
6162

63+
import org.apache.commons.lang3.EnumUtils;
64+
6265
@APICommand(name = "listAndSwitchSamlAccount", description = "Lists and switches to other SAML accounts owned by the SAML user", responseObject = SuccessResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
6366
public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthenticator {
6467

@@ -102,7 +105,9 @@ public String authenticate(final String command, final Map<String, Object[]> par
102105
params, responseType));
103106
}
104107

105-
if (!HttpUtils.validateSessionKey(session, params, req.getCookies(), ApiConstants.SESSIONKEY)) {
108+
HttpUtils.ApiSessionKeyCheckOption sessionKeyCheckOption = EnumUtils.getEnumIgnoreCase(HttpUtils.ApiSessionKeyCheckOption.class,
109+
ApiServer.ApiSessionKeyCheckLocations.value(), HttpUtils.ApiSessionKeyCheckOption.CookieAndParameter);
110+
if (!HttpUtils.validateSessionKey(session, params, req.getCookies(), ApiConstants.SESSIONKEY, sessionKeyCheckOption)) {
106111
throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, _apiServer.getSerializedApiError(ApiErrorCode.UNAUTHORIZED.getHttpCode(),
107112
"Unauthorized session, please re-login",
108113
params, responseType));

plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManager.java

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableSe
7373
ConfigKey<Boolean> SAMLCheckSignature = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.check.signature", "true",
7474
"When enabled (default and recommended), SAML2 signature checks are enforced and lack of signature in the SAML SSO response will cause login exception. Disabling this is not advisable but provided for backward compatibility for users who are able to accept the risks.", false);
7575

76+
ConfigKey<String> SAMLUserSessionKeyPathAttribute = new ConfigKey<String>("Advanced", String.class, "saml2.user.sessionkey.path", "",
77+
"The Path attribute of sessionkey cookie when SAML users have logged in. If not set, it will be set to the path of SAML redirection URL (saml2.redirect.url).", true);
78+
7679
SAMLProviderMetadata getSPMetadata();
7780
SAMLProviderMetadata getIdPMetadata(String entityId);
7881
Collection<SAMLProviderMetadata> getAllIdPMetadata();

plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ public ConfigKey<?>[] getConfigKeys() {
540540
SAMLServiceProviderSingleSignOnURL, SAMLServiceProviderSingleLogOutURL,
541541
SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
542542
SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
543-
SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature};
543+
SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature,
544+
SAMLUserSessionKeyPathAttribute};
544545
}
545546
}

plugins/user-authenticators/saml2/src/main/java/org/apache/cloudstack/saml/SAMLUtils.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import java.io.StringWriter;
2626
import java.io.UnsupportedEncodingException;
2727
import java.math.BigInteger;
28+
import java.net.URI;
29+
import java.net.URISyntaxException;
2830
import java.net.URLEncoder;
2931
import java.nio.charset.Charset;
3032
import java.security.InvalidKeyException;
@@ -102,7 +104,9 @@
102104
import org.w3c.dom.Element;
103105
import org.xml.sax.SAXException;
104106

107+
import com.cloud.api.ApiServlet;
105108
import com.cloud.utils.HttpUtils;
109+
import com.cloud.utils.exception.CloudRuntimeException;
106110

107111
public class SAMLUtils {
108112
protected static Logger LOGGER = LogManager.getLogger(SAMLUtils.class);
@@ -297,7 +301,26 @@ public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, fi
297301
resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
298302
}
299303
resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
300-
resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api", ApiConstants.SESSIONKEY, loginResponse.getSessionKey()));
304+
305+
String redirectUrl = SAML2AuthManager.SAMLCloudStackRedirectionUrl.value();
306+
String path = SAML2AuthManager.SAMLUserSessionKeyPathAttribute.value();
307+
String domain = null;
308+
try {
309+
URI redirectUri = new URI(redirectUrl);
310+
domain = redirectUri.getHost();
311+
if (StringUtils.isBlank(path)) {
312+
path = redirectUri.getPath();
313+
}
314+
if (StringUtils.isBlank(path)) {
315+
path = "/";
316+
}
317+
} catch (URISyntaxException ex) {
318+
throw new CloudRuntimeException("Invalid URI: " + redirectUrl);
319+
}
320+
String sameSite = ApiServlet.getApiSessionKeySameSite();
321+
String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite);
322+
s_logger.debug("Adding sessionkey cookie to response: " + sessionKeyCookie);
323+
resp.addHeader("SET-COOKIE", sessionKeyCookie);
301324
}
302325

303326
/**

0 commit comments

Comments
 (0)