Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -60,22 +59,4 @@ public void execute() {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -43,29 +42,30 @@
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) {
Expand Down Expand Up @@ -188,25 +188,4 @@ public void execute() {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3399,6 +3399,7 @@ private void removeApiKeyPairIfExpired(ApiKeyPair apiKeyPair) {
}
}

@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) {
Expand Down
2 changes: 0 additions & 2 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 1 addition & 3 deletions ui/public/locales/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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...",
Expand Down
27 changes: 5 additions & 22 deletions ui/src/components/view/ApiKeyPairsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@winterhazel, should we return the plain error here? Or should we return a more generic message, similar to the error handling performed for the registerUserKeys command?

})
}))
} 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 => {
Expand Down
18 changes: 5 additions & 13 deletions ui/src/views/iam/GenerateApiKeyPair.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -187,7 +180,6 @@ export default {
})
}).finally(() => {
this.loading = false
this.closeModal()
})
})
},
Expand Down
Loading