mirror of https://github.com/apache/cloudstack.git
fixup
This commit is contained in:
parent
d7c5e249b6
commit
d8cdddb540
|
|
@ -145,6 +145,10 @@ CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profiles` (
|
|||
CONSTRAINT `fk_kms_hsm_profiles__zone_id` FOREIGN KEY (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='HSM profiles for KMS providers';
|
||||
|
||||
-- Add default database HSM profile (disabled by default)
|
||||
INSERT INTO `cloud`.`kms_hsm_profiles` (`uuid`, `name`, `protocol`, `account_id`, `domain_id`, `enabled`, `system`, `created`)
|
||||
VALUES (UUID(), 'default', 'database', 1, 1, 0, 1, NOW());
|
||||
|
||||
-- KMS HSM Profile Details (Protocol-specific configuration)
|
||||
CREATE TABLE IF NOT EXISTS `cloud`.`kms_hsm_profile_details` (
|
||||
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
|
|
|
|||
|
|
@ -103,6 +103,18 @@ public interface KMSProvider extends Configurable, Adapter {
|
|||
*/
|
||||
void deleteKek(String kekId) throws KMSException;
|
||||
|
||||
/**
|
||||
* Validates the configuration details for this provider before saving an HSM
|
||||
* profile.
|
||||
* Implementations should override this to perform provider-specific validation.
|
||||
*
|
||||
* @param details the configuration details to validate
|
||||
* @throws KMSException if validation fails
|
||||
*/
|
||||
default void validateProfileConfig(java.util.Map<String, String> details) throws KMSException {
|
||||
// default no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a KEK exists and is accessible
|
||||
*
|
||||
|
|
|
|||
|
|
@ -296,30 +296,35 @@ public class PKCS11HSMProvider extends AdapterBase implements KMSProvider {
|
|||
/**
|
||||
* Validates HSM profile configuration for PKCS#11 provider.
|
||||
*
|
||||
* <p>Validates:
|
||||
* <p>
|
||||
* Validates:
|
||||
* <ul>
|
||||
* <li>{@code library}: Required, should point to PKCS#11 library</li>
|
||||
* <li>{@code slot} or {@code token_label}: At least one required</li>
|
||||
* <li>{@code pin}: Required for HSM authentication</li>
|
||||
* <li>{@code max_sessions}: Optional, must be positive integer if provided</li>
|
||||
* <li>{@code library}: Required, should point to PKCS#11 library</li>
|
||||
* <li>{@code slot}, {@code slot_list_index}, or {@code token_label}: At least
|
||||
* one required</li>
|
||||
* <li>{@code pin}: Required for HSM authentication</li>
|
||||
* <li>{@code max_sessions}: Optional, must be positive integer if provided</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param config Configuration map from HSM profile details
|
||||
* @throws KMSException with {@code INVALID_PARAMETER} if validation fails
|
||||
*/
|
||||
void validateProfileConfig(Map<String, String> config) throws KMSException {
|
||||
@Override
|
||||
public void validateProfileConfig(Map<String, String> config) throws KMSException {
|
||||
String libraryPath = config.get("library");
|
||||
if (StringUtils.isEmpty(libraryPath)) {
|
||||
if (StringUtils.isBlank(libraryPath)) {
|
||||
throw KMSException.invalidParameter("library is required for PKCS#11 HSM profile");
|
||||
}
|
||||
|
||||
String slot = config.get("slot");
|
||||
String slotListIndex = config.get("slot_list_index");
|
||||
String tokenLabel = config.get("token_label");
|
||||
if (StringUtils.isEmpty(slot) && StringUtils.isEmpty(tokenLabel)) {
|
||||
throw KMSException.invalidParameter("Either 'slot' or 'token_label' is required for PKCS#11 HSM profile");
|
||||
if (StringUtils.isAllBlank(slot, slotListIndex, tokenLabel)) {
|
||||
throw KMSException.invalidParameter(
|
||||
"One of 'slot', 'slot_list_index', or 'token_label' is required for PKCS#11 HSM profile");
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(slot)) {
|
||||
if (StringUtils.isNotBlank(slot)) {
|
||||
try {
|
||||
Integer.parseInt(slot);
|
||||
} catch (NumberFormatException e) {
|
||||
|
|
@ -327,6 +332,17 @@ public class PKCS11HSMProvider extends AdapterBase implements KMSProvider {
|
|||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(slotListIndex)) {
|
||||
try {
|
||||
int idx = Integer.parseInt(slotListIndex);
|
||||
if (idx < 0) {
|
||||
throw KMSException.invalidParameter("slot_list_index must be a non-negative integer");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw KMSException.invalidParameter("slot_list_index must be a valid integer: " + slotListIndex);
|
||||
}
|
||||
}
|
||||
|
||||
File libraryFile = new File(libraryPath);
|
||||
if (!libraryFile.exists() && !libraryFile.isAbsolute()) {
|
||||
// The HSM library might be in the system library path
|
||||
|
|
@ -334,31 +350,16 @@ public class PKCS11HSMProvider extends AdapterBase implements KMSProvider {
|
|||
libraryPath);
|
||||
}
|
||||
|
||||
parsePositiveInteger(config, "max_sessions", "max_sessions");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a positive integer from configuration.
|
||||
*
|
||||
* @param config Configuration map
|
||||
* @param key Configuration key
|
||||
* @param errorPrefix Prefix for error messages
|
||||
* @return Parsed integer value, or -1 if not provided
|
||||
* @throws KMSException if value is invalid or not positive
|
||||
*/
|
||||
private int parsePositiveInteger(Map<String, String> config, String key, String errorPrefix) throws KMSException {
|
||||
String value = config.get(key);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return -1; // Not provided
|
||||
}
|
||||
try {
|
||||
int parsed = Integer.parseInt(value);
|
||||
if (parsed <= 0) {
|
||||
throw KMSException.invalidParameter(errorPrefix + " must be greater than 0");
|
||||
String max_sessions = config.get("max_sessions");
|
||||
if (StringUtils.isNotBlank(max_sessions)) {
|
||||
try {
|
||||
int idx = Integer.parseInt(max_sessions);
|
||||
if (idx <= 0) {
|
||||
throw KMSException.invalidParameter("max_sessions must be greater than 0");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw KMSException.invalidParameter("max_sessions must be a valid integer: " + max_sessions);
|
||||
}
|
||||
return parsed;
|
||||
} catch (NumberFormatException e) {
|
||||
throw KMSException.invalidParameter(errorPrefix + " must be a valid integer: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +616,7 @@ public class PKCS11HSMProvider extends AdapterBase implements KMSProvider {
|
|||
*/
|
||||
private String buildSunPKCS11Config(Map<String, String> config, String nameSuffix) throws KMSException {
|
||||
String libraryPath = config.get("library");
|
||||
if (StringUtils.isEmpty(libraryPath)) {
|
||||
if (StringUtils.isBlank(libraryPath)) {
|
||||
throw KMSException.invalidParameter("library is required");
|
||||
}
|
||||
|
||||
|
|
@ -627,14 +628,17 @@ public class PKCS11HSMProvider extends AdapterBase implements KMSProvider {
|
|||
configBuilder.append("library=").append(libraryPath).append("\n");
|
||||
|
||||
String tokenLabel = config.get("token_label");
|
||||
String slotListIndex = config.get("slot_list_index");
|
||||
String slot = config.get("slot");
|
||||
|
||||
if (!StringUtils.isEmpty(tokenLabel)) {
|
||||
if (StringUtils.isNotBlank(tokenLabel)) {
|
||||
configBuilder.append("tokenLabel=").append(tokenLabel).append("\n");
|
||||
} else if (!StringUtils.isEmpty(slot)) {
|
||||
} else if (StringUtils.isNotBlank(slotListIndex)) {
|
||||
configBuilder.append("slotListIndex=").append(slotListIndex).append("\n");
|
||||
} else if (StringUtils.isNotBlank(slot)) {
|
||||
configBuilder.append("slot=").append(slot).append("\n");
|
||||
} else {
|
||||
throw KMSException.invalidParameter("Either 'slot' or 'token_label' is required");
|
||||
throw KMSException.invalidParameter("One of 'slot', 'slot_list_index', or 'token_label' is required");
|
||||
}
|
||||
|
||||
return configBuilder.toString();
|
||||
|
|
|
|||
|
|
@ -969,12 +969,16 @@ public class KMSManagerImpl extends ManagerBase implements KMSManager, Pluggable
|
|||
throw new InvalidParameterValueException("Protocol cannot be empty");
|
||||
}
|
||||
|
||||
KMSProvider provider;
|
||||
try {
|
||||
getKMSProvider(protocol);
|
||||
provider = getKMSProvider(protocol);
|
||||
} catch (CloudRuntimeException e) {
|
||||
throw new InvalidParameterValueException("No provider found for protocol: " + protocol);
|
||||
}
|
||||
|
||||
Map<String, String> details = cmd.getDetails() != null ? cmd.getDetails() : new HashMap<>();
|
||||
provider.validateProfileConfig(details);
|
||||
|
||||
boolean isSystem = cmd.isSystem();
|
||||
if (isSystem && !accountManager.isRootAdmin(caller.getId())) {
|
||||
throw new PermissionDeniedException("Only root admins can create system HSM profiles");
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ export default {
|
|||
name: 'kms',
|
||||
title: 'label.kms',
|
||||
icon: 'hdd-outlined',
|
||||
show: (record, store) => {
|
||||
return ['Admin'].includes(store.getters.userInfo.roletype) || store.getters.features.hashsmprofiles
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'kmskey',
|
||||
|
|
@ -157,6 +160,7 @@ export default {
|
|||
title: 'label.hsm.profile',
|
||||
icon: 'safety-outlined',
|
||||
permission: ['listHSMProfiles'],
|
||||
show: (record, route, user) => { return ['Admin'].includes(user.roletype) },
|
||||
resourceType: 'HSMProfile',
|
||||
columns: () => {
|
||||
const fields = ['name', 'enabled']
|
||||
|
|
|
|||
|
|
@ -480,6 +480,16 @@ const user = {
|
|||
commit('SET_CLOUDIAN', cloudian)
|
||||
}).catch(ignored => {
|
||||
})
|
||||
|
||||
if ('listHSMProfiles' in store.getters.apis) {
|
||||
getAPI('listHSMProfiles', { listall: true }).then(response => {
|
||||
const hasHsmProfiles = (response.listhsmprofilesresponse.count > 0)
|
||||
const features = Object.assign({}, store.getters.features)
|
||||
features.hashsmprofiles = hasHsmProfiles
|
||||
commit('SET_FEATURES', features)
|
||||
}).catch(ignored => {
|
||||
})
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2030,9 +2030,14 @@ export default {
|
|||
domainid: this.owner.domainid,
|
||||
projectid: this.owner.projectid
|
||||
}).then(response => {
|
||||
this.options.kmsKeys = response.listkmskeysresponse.kmskey || []
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
const kmskeyMap = response.listkmskeysresponse.kmskey || []
|
||||
if (kmskeyMap.length > 0) {
|
||||
this.options.kmsKeys = kmskeyMap
|
||||
} else {
|
||||
this.options.kmsKeys = null
|
||||
}
|
||||
}).catch(() => {
|
||||
this.options.kmsKeys = null
|
||||
}).finally(() => {
|
||||
this.loading.kmsKeys = false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -131,6 +131,9 @@ export default {
|
|||
return this.rootDiskSelected?.iscustomizediops || false
|
||||
},
|
||||
showKmsKeySelector () {
|
||||
if (this.kmsKeys === null) {
|
||||
return false
|
||||
}
|
||||
const isRootDisk = this.inputDecorator === 'rootdisksize'
|
||||
const isDataDisk = this.inputDecorator === 'size'
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@
|
|||
:placeholder="apiParams.maxiops.description"/>
|
||||
</a-form-item>
|
||||
</span>
|
||||
<span v-if="diskOfferingSupportsEncryption">
|
||||
<span v-if="diskOfferingSupportsEncryption && kmsKeys !== null">
|
||||
<a-form-item ref="kmskeyid" name="kmskeyid">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.kms.key')" :tooltip="apiParams.kmskeyid.description"/>
|
||||
|
|
@ -529,9 +529,14 @@ export default {
|
|||
purpose: 'volume'
|
||||
}
|
||||
getAPI('listKMSKeys', params).then(response => {
|
||||
this.kmsKeys = response.listkmskeysresponse.kmskey || []
|
||||
}).catch(error => {
|
||||
this.$notifyError(error)
|
||||
const kmskeyMap = response.listkmskeysresponse.kmskey || []
|
||||
if (kmskeyMap.length > 0) {
|
||||
this.kmsKeys = kmskeyMap
|
||||
} else {
|
||||
this.kmsKeys = null
|
||||
}
|
||||
}).catch(() => {
|
||||
this.kmsKeys = null
|
||||
}).finally(() => {
|
||||
this.loadingKmsKeys = false
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue