This is an automated email from the ASF dual-hosted git repository.
winterhazel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 2b78432edf3 Revert `registerUserKeys` back to a synchronous API
(#13896)
2b78432edf3 is described below
commit 2b78432edf3974ad24a1768d79e50aca81f46de7
Author: Fabricio Duarte <[email protected]>
AuthorDate: Wed Aug 19 16:41:49 2026 -0300
Revert `registerUserKeys` back to a synchronous API (#13896)
---
.../org/apache/cloudstack/api/BaseAsyncCmd.java | 1 -
.../api/command/admin/user/DeleteUserKeysCmd.java | 23 ++------------
.../command/admin/user/RegisterUserKeysCmd.java | 37 +++++-----------------
.../java/com/cloud/user/AccountManagerImpl.java | 1 +
ui/public/locales/en.json | 2 --
ui/public/locales/pt_BR.json | 4 +--
ui/src/components/view/ApiKeyPairsTab.vue | 27 +++-------------
ui/src/views/iam/GenerateApiKeyPair.vue | 18 +++--------
8 files changed, 22 insertions(+), 91 deletions(-)
diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java
b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java
index c67c5a023e0..6859b0a7f40 100644
--- a/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/BaseAsyncCmd.java
@@ -29,7 +29,6 @@ public abstract class BaseAsyncCmd extends BaseCmd {
public static final String migrationSyncObject = "migration";
public static final String snapshotHostSyncObject = "snapshothost";
public static final String gslbSyncObject = "globalserverloadbalancer";
- public static final String user = "user";
private Object job;
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java
index 6cf55514ba3..dec41530c48 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserKeysCmd.java
@@ -16,21 +16,20 @@
// under the License.
package org.apache.cloudstack.api.command.admin.user;
-import com.cloud.event.EventTypes;
import com.cloud.user.Account;
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ApiKeyPairResponse;
import org.apache.cloudstack.api.response.SuccessResponse;
@APICommand(name = "deleteUserKeys", description = "Deletes a keypair from a
user", responseObject = SuccessResponse.class,
since = "4.23.0", requestHasSensitiveInfo = false,
responseHasSensitiveInfo = false)
-public class DeleteUserKeysCmd extends BaseAsyncCmd {
+public class DeleteUserKeysCmd extends BaseCmd {
@ACL
@Parameter(name = ApiConstants.KEYPAIR_ID, type = CommandType.UUID,
entityType = ApiKeyPairResponse.class, required = true, description = "ID of
the keypair to be deleted.")
private Long id;
@@ -60,22 +59,4 @@ public class DeleteUserKeysCmd extends BaseAsyncCmd {
SuccessResponse response = new SuccessResponse(getCommandName());
this.setResponseObject(response);
}
-
- @Override
- public String getEventType() {
- return EventTypes.EVENT_DELETE_SECRET_API_KEY;
- }
-
- @Override
- public String getEventDescription() {
- ApiKeyPair keyPair = apiKeyPairService.findById(id);
- return String.format("Deleting API key pair with ID [%s]%s",
- keyPair == null ? id : keyPair.getUuid(),
- keyPair == null ? "." : String.format(" and name [%s].",
keyPair.getName()));
- }
-
- @Override
- public Long getSyncObjId() {
- return getId();
- }
}
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java
index 28c79517f4b..42c66470ac0 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/admin/user/RegisterUserKeysCmd.java
@@ -16,14 +16,13 @@
// under the License.
package org.apache.cloudstack.api.command.admin.user;
-import com.cloud.event.EventTypes;
import com.cloud.user.Account;
import com.cloud.user.User;
import org.apache.cloudstack.acl.Rule;
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
import org.apache.cloudstack.api.ApiCommandResourceType;
import org.apache.cloudstack.api.ApiErrorCode;
-import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.commons.lang3.StringUtils;
@@ -43,29 +42,30 @@ import java.util.Map;
responseObject = ApiKeyPairResponse.class,
description = "Registers an API key pair (API and secret keys) for
a user.",
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
-public class RegisterUserKeysCmd extends BaseAsyncCmd {
+public class RegisterUserKeysCmd extends BaseCmd {
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType =
UserResponse.class, required = true, description = "ID of the user.")
private Long id;
- @Parameter(name = ApiConstants.NAME, type = CommandType.STRING,
description = "API key pair name.")
+ @Parameter(name = ApiConstants.NAME, type = CommandType.STRING,
description = "API key pair name.", since = "4.23.0")
private String name;
- @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING,
description = "API key pair description.", length = 1024)
+ @Parameter(name = ApiConstants.DESCRIPTION, type = CommandType.STRING,
description = "API key pair description.", length = 1024,
+ since = "4.23.0")
private String description;
@Parameter(name = ApiConstants.START_DATE, type = CommandType.DATE,
description = "Start date of the API key pair. " +
- ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
+ ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS,
since = "4.23.0")
private Date startDate;
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE,
description = "Expiration date of the API key pair. " +
- ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS)
+ ApiConstants.PARAMETER_DESCRIPTION_END_DATE_POSSIBLE_FORMATS,
since = "4.23.0")
private Date endDate;
@Parameter(name = ApiConstants.RULES, type = CommandType.MAP, description
= "The rules of the API key pair. If no rules are informed, " +
"defaults to allowing all account permissions. Otherwise, only the
explicitly informed permissions for the key pair will be " +
"considered. Lower indexed rules take precedence over higher.
Thus, in the following example: " +
"\"rules[0].rule=deleteUserKeys rules[0].permission=deny
rules[1].rule=*UserKey* rules[1].permission=allow\", all rules matching " +
- "the expression \"*UserKeys*\" will be allowed, except for
\"deleteUserKeys\".")
+ "the expression \"*UserKeys*\" will be allowed, except for
\"deleteUserKeys\".", since = "4.23.0")
private Map rules;
public void setUserId(Long userId) {
@@ -188,25 +188,4 @@ public class RegisterUserKeysCmd extends BaseAsyncCmd {
response.setResponseName(getCommandName());
this.setResponseObject(response);
}
-
- @Override
- public String getEventType() {
- return EventTypes.EVENT_REGISTER_FOR_SECRET_API_KEY;
- }
-
- @Override
- public String getEventDescription() {
- String userUuid = getResourceUuid(ApiConstants.ID);
- return String.format("Registering API keypair for user [%s].",
userUuid == null ? id : userUuid);
- }
-
- @Override
- public String getSyncObjType() {
- return BaseAsyncCmd.user;
- }
-
- @Override
- public Long getSyncObjId() {
- return getUserId();
- }
}
diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
index e4d835f74b1..a0a08a3ce04 100644
--- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java
+++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java
@@ -3417,6 +3417,7 @@ public class AccountManagerImpl extends ManagerBase
implements AccountManager, M
}
}
+ @ActionEvent(eventType = EventTypes.EVENT_DELETE_SECRET_API_KEY,
eventDescription = "deleting API key pair")
public void deleteApiKey(DeleteUserKeysCmd cmd) {
ApiKeyPair keyPair = apiKeyPairService.findById(cmd.getId());
if (keyPair == null) {
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 775de26103a..f57460efa48 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1731,7 +1731,6 @@
"message.memory.usage.info.hypervisor.additionals": "The data shown may not
reflect the actual memory usage if the Instance does not have the additional
hypervisor tools installed",
"message.memory.usage.info.negative.value": "If the Instance's memory usage
cannot be obtained from the hypervisor, the lines for free memory in the raw
data graph and memory usage in the percentage graph will be disabled",
"message.migrate.volume.tooltip": "Volume can be migrated to any suitable
storage pool. Admin has to choose the appropriate disk offering to replace,
that supports the new storage pool",
-"message.register.keypair.failed": "Failed to register API key pair",
"label.migrate.with.storage": "Migrate with storage",
"label.migrating": "Migrating",
"label.migrating.data": "Migrating data",
@@ -2200,7 +2199,6 @@
"label.register.user.data": "Register User Data",
"label.register.cni.config": "Register CNI Configuration",
"label.register.user.data.details": "Enter the User Data in plain text or in
Base64 encoding. Up to 32KB of Base64 encoded User Data can be sent by default.
The setting vm.userdata.max.length can be used to increase the limit to upto
1MB.",
-"label.registering.keypair": "Registering API key pair for user \"{user}\"",
"label.reinstall.vm": "Reinstall Instance",
"label.reject": "Reject",
"label.related": "Related",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index b3eae6eb11c..8fba779b1d1 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -51,7 +51,7 @@
"label.action": "A\u00e7\u00e3o",
"label.action.attach.disk": "Anexar disco",
"label.action.attach.iso": "Anexar ISO",
-"label.action.bulk.delete.api.keys": "Apagar em massa as chaves de acesso
\u00e0 API.",
+"label.action.bulk.delete.api.keys": "Apagar em massa as chaves de acesso
\u00e0 API",
"label.action.bulk.delete.egress.firewall.rules": "Apagar em massa as regras
de sa\u00edda do firewall.",
"label.action.bulk.delete.firewall.rules": "Apagar em massa as regras do
firewall.",
"label.action.bulk.delete.ip.v6.firewall.rules": "Apagar em massa as regras de
firewall IPv6.",
@@ -1931,7 +1931,6 @@
"label.register.oauth": "Registrar OAuth",
"label.register.user.data": "Registrar dados de usu\u00e1rio",
"label.register.template": "Registrar template",
-"label.registering.keypair": "Registrando par de chaves de API para o
usu\u00e1rio \"{user}\"",
"label.reinstall.vm": "Reinstalar VM",
"label.reject": "Rejeitar",
"label.related": "Relacionado",
@@ -3445,7 +3444,6 @@
"message.read.accept.license.agreements": "Leia e aceite os termos dos
contratos de licen\u00e7a.",
"message.read.admin.guide.scaling.up": "Por favor leia a sess\u00e3o sobre
escalonamento din\u00e2mico no guia do administrador antes de escalonar.",
"message.recover.vm": "Por favor, confirme a recupera\u00e7\u00e3o desta VM.",
-"message.register.keypair.failed": "Falha ao registrar par de chave de API",
"message.reinstall.vm": "NOTA: proceda com cuidado. Isso far\u00e1 com que a
m\u00e1quina virtual seja re-instalada a partir do template. Todos os dados do
disco root ser\u00e3o perdidos. Se houver volumes de dados adicionais, eles
n\u00e3o ser\u00e3o alterados.",
"message.release.ip.failed": "Falha ao liberar IP",
"message.releasing.dedicated.cluster": "Liberando cluster dedicado...",
diff --git a/ui/src/components/view/ApiKeyPairsTab.vue
b/ui/src/components/view/ApiKeyPairsTab.vue
index 87feda59d99..5e426cba683 100644
--- a/ui/src/components/view/ApiKeyPairsTab.vue
+++ b/ui/src/components/view/ApiKeyPairsTab.vue
@@ -287,34 +287,17 @@ export default {
this.fetchLoading = true
try {
await Promise.all(keypairs.map(async keypair => {
- try {
- const jobId = await this.deleteKeyPair({
- keypairid: keypair.id
- })
- await this.$pollJob({
- jobId,
- action: {
- isFetchData: false
- },
- successMethod: () => {
- eventBus.emit('update-resource-state', { selectedItems:
this.selectedItems, resource: keypair.id, state: 'success' })
- },
- catchMethod: () => {
- eventBus.emit('update-resource-state', { selectedItems:
this.selectedItems, resource: keypair.id, state: 'failed' })
- }
- })
- } catch (e) {
+ await postAPI('deleteUserKeys', { keypairid: keypair.id
}).then(response => {
+ eventBus.emit('update-resource-state', { selectedItems:
this.selectedItems, resource: keypair.id, state: 'success' })
+ }).catch(error => {
eventBus.emit('update-resource-state', { selectedItems:
this.selectedItems, resource: keypair.id, state: 'failed' })
- }
+ this.$notifyError(error)
+ })
}))
} finally {
this.fetchLoading = false
}
},
- async deleteKeyPair (args) {
- const response = await postAPI('deleteUserKeys', args)
- return response.deleteuserkeysresponse.jobid
- },
bulkActionConfirmation () {
this.showConfirmationAction = true
this.selectedColumns = this.columns.filter(column => {
diff --git a/ui/src/views/iam/GenerateApiKeyPair.vue
b/ui/src/views/iam/GenerateApiKeyPair.vue
index bc0b2bb475e..9e0d307c455 100644
--- a/ui/src/views/iam/GenerateApiKeyPair.vue
+++ b/ui/src/views/iam/GenerateApiKeyPair.vue
@@ -166,19 +166,12 @@ export default {
const params = this.buildRequestParams()
this.loading = true
postAPI('registerUserKeys', params).then(response => {
- this.$pollJob({
- jobId: response.registeruserkeysresponse.jobid,
- successMessage: this.$t('message.success.register.user.keypair', {
user: this.resource.username }),
- successMethod: () => {
- this.fetchData()
- },
- errorMessage: this.$t('message.register.keypair.failed'),
- errorMethod: () => {
- this.fetchData()
- },
- loadingMessage: this.$t('label.registering.keypair', { user:
this.resource.username }),
- catchMessage: this.$t('error.fetching.async.job.result')
+ this.$notification.success({
+ message: this.$t('label.action.create.api.key'),
+ description: this.$t('message.success.register.user.keypair', {
user: this.resource.username })
})
+ this.fetchData()
+ this.closeModal()
}).catch(error => {
this.$notification.error({
message: this.$t('message.request.failed'),
@@ -187,7 +180,6 @@ export default {
})
}).finally(() => {
this.loading = false
- this.closeModal()
})
})
},