Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-04-27 18:10:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-04-27 18:10:01 +0300
commit6d82b3a0c58f427e90bb8665cd13931128753a23 (patch)
treef89d05cab91e0b56d6fae7194c8b048d75314846 /app/assets/javascripts/clusters
parent863ba7d77355b305b06112b0c6c3cab3c09898b0 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/clusters')
-rw-r--r--app/assets/javascripts/clusters/agents/components/revoke_token_button.vue201
-rw-r--r--app/assets/javascripts/clusters/agents/components/token_table.vue11
-rw-r--r--app/assets/javascripts/clusters/agents/constants.js2
-rw-r--r--app/assets/javascripts/clusters/agents/graphql/cache_update.js22
-rw-r--r--app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql5
5 files changed, 241 insertions, 0 deletions
diff --git a/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue
new file mode 100644
index 00000000000..7d36cbb170d
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue
@@ -0,0 +1,201 @@
+<script>
+import {
+ GlButton,
+ GlModalDirective,
+ GlTooltip,
+ GlModal,
+ GlFormGroup,
+ GlFormInput,
+ GlSprintf,
+} from '@gitlab/ui';
+import { s__, __, sprintf } from '~/locale';
+import { REVOKE_TOKEN_MODAL_ID, TOKEN_STATUS_ACTIVE } from '../constants';
+import revokeAgentToken from '../graphql/mutations/revoke_token.mutation.graphql';
+import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql';
+import { removeTokenFromStore } from '../graphql/cache_update';
+
+export default {
+ components: {
+ GlButton,
+ GlTooltip,
+ GlModal,
+ GlFormGroup,
+ GlFormInput,
+ GlSprintf,
+ },
+ directives: {
+ GlModalDirective,
+ },
+ inject: ['agentName', 'projectPath', 'canAdminCluster'],
+ props: {
+ token: {
+ required: true,
+ type: Object,
+ validator: (value) => ['id', 'name'].every((prop) => value[prop]),
+ },
+ cursor: {
+ required: true,
+ type: Object,
+ },
+ },
+ i18n: {
+ revokeButton: s__('ClusterAgents|Revoke token'),
+ dropdownDisabledHint: s__(
+ 'ClusterAgents|Requires a Maintainer or greater role to perform this action',
+ ),
+ modalTitle: s__('ClusterAgents|Revoke access token?'),
+ modalBody: s__(
+ 'ClusterAgents|Are you sure you want to revoke this token? You cannot undo this action.',
+ ),
+ modalInputLabel: s__('ClusterAgents|To revoke the token, type %{name} to confirm:'),
+ modalCancel: __('Cancel'),
+ successMessage: s__('ClusterAgents|%{name} successfully revoked'),
+ defaultError: __('An error occurred. Please try again.'),
+ },
+ data() {
+ return {
+ loading: false,
+ error: null,
+ revokeConfirmText: null,
+ tokenName: null,
+ variables: {
+ agentName: this.agentName,
+ projectPath: this.projectPath,
+ tokenStatus: TOKEN_STATUS_ACTIVE,
+ ...this.cursor,
+ },
+ };
+ },
+ computed: {
+ revokeBtnDisabled() {
+ return this.loading || !this.canAdminCluster;
+ },
+ modalId() {
+ return sprintf(REVOKE_TOKEN_MODAL_ID, {
+ tokenName: this.token.name,
+ });
+ },
+ primaryModalProps() {
+ return {
+ text: this.$options.i18n.revokeButton,
+ attributes: [
+ { disabled: this.loading || this.disableModalSubmit, loading: this.loading },
+ { variant: 'danger' },
+ ],
+ };
+ },
+ cancelModalProps() {
+ return {
+ text: this.$options.i18n.modalCancel,
+ attributes: [],
+ };
+ },
+ disableModalSubmit() {
+ return this.revokeConfirmText !== this.token.name;
+ },
+ },
+ methods: {
+ async revokeToken() {
+ if (this.disableModalSubmit || this.loading) {
+ return;
+ }
+
+ this.loading = true;
+ this.error = null;
+ this.tokenName = this.token.name;
+
+ try {
+ const { errors } = await this.revokeTokenMutation();
+
+ if (errors.length) {
+ throw new Error(errors[0]);
+ }
+ } catch (error) {
+ this.error = error?.message || this.$options.i18n.defaultError;
+ } finally {
+ this.loading = false;
+ const successMessage = sprintf(this.$options.i18n.successMessage, {
+ name: this.tokenName,
+ });
+
+ this.$toast.show(this.error || successMessage);
+
+ this.hideModal();
+ }
+ },
+ revokeTokenMutation() {
+ return this.$apollo
+ .mutate({
+ mutation: revokeAgentToken,
+ variables: {
+ input: {
+ id: this.token.id,
+ },
+ },
+ update: (store) => {
+ removeTokenFromStore(store, this.token, getClusterAgentQuery, this.variables);
+ },
+ })
+
+ .then(({ data: { clusterAgentTokenRevoke } }) => {
+ return clusterAgentTokenRevoke;
+ });
+ },
+ resetModal() {
+ this.loading = false;
+ this.error = null;
+ this.revokeConfirmText = null;
+ },
+ hideModal() {
+ this.resetModal();
+ this.$refs.modal.hide();
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div ref="revokeToken" class="gl-display-inline-block">
+ <gl-button
+ v-gl-modal-directive="modalId"
+ icon="remove"
+ category="secondary"
+ variant="danger"
+ :disabled="revokeBtnDisabled"
+ :title="$options.i18n.revokeButton"
+ :aria-label="$options.i18n.revokeButton"
+ />
+
+ <gl-tooltip
+ v-if="!canAdminCluster"
+ :target="() => $refs.revokeToken"
+ :title="$options.i18n.dropdownDisabledHint"
+ />
+ </div>
+
+ <gl-modal
+ ref="modal"
+ :modal-id="modalId"
+ :title="$options.i18n.modalTitle"
+ :action-primary="primaryModalProps"
+ :action-cancel="cancelModalProps"
+ size="sm"
+ @primary="revokeToken"
+ @hide="hideModal"
+ >
+ <p>{{ $options.i18n.modalBody }}</p>
+
+ <gl-form-group>
+ <template #label>
+ <gl-sprintf :message="$options.i18n.modalInputLabel">
+ <template #name>
+ <code>{{ token.name }}</code>
+ </template>
+ </gl-sprintf>
+ </template>
+ <gl-form-input v-model="revokeConfirmText" @keydown.enter="revokeToken" />
+ </gl-form-group>
+ </gl-modal>
+ </div>
+</template>
diff --git a/app/assets/javascripts/clusters/agents/components/token_table.vue b/app/assets/javascripts/clusters/agents/components/token_table.vue
index fbb39c28d78..9e64c9da712 100644
--- a/app/assets/javascripts/clusters/agents/components/token_table.vue
+++ b/app/assets/javascripts/clusters/agents/components/token_table.vue
@@ -3,6 +3,7 @@ import { GlEmptyState, GlTable, GlTooltip, GlTruncate } from '@gitlab/ui';
import { s__ } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import CreateTokenButton from './create_token_button.vue';
+import RevokeTokenButton from './revoke_token_button.vue';
export default {
components: {
@@ -12,6 +13,7 @@ export default {
GlTruncate,
TimeAgoTooltip,
CreateTokenButton,
+ RevokeTokenButton,
},
i18n: {
createdBy: s__('ClusterAgents|Created by'),
@@ -66,6 +68,11 @@ export default {
label: this.$options.i18n.description,
tdAttr: { 'data-testid': 'agent-token-description' },
},
+ {
+ key: 'actions',
+ label: '',
+ tdAttr: { 'data-testid': 'agent-token-revoke' },
+ },
];
},
},
@@ -119,6 +126,10 @@ export default {
</gl-tooltip>
</div>
</template>
+
+ <template #cell(actions)="{ item }">
+ <revoke-token-button :token="item" :cluster-agent-id="clusterAgentId" :cursor="cursor" />
+ </template>
</gl-table>
</div>
diff --git a/app/assets/javascripts/clusters/agents/constants.js b/app/assets/javascripts/clusters/agents/constants.js
index 50d8f5e9e40..962fa243903 100644
--- a/app/assets/javascripts/clusters/agents/constants.js
+++ b/app/assets/javascripts/clusters/agents/constants.js
@@ -44,3 +44,5 @@ export const EVENT_ACTIONS_OPEN = 'open_modal';
export const EVENT_ACTIONS_CLICK = 'click_button';
export const TOKEN_NAME_LIMIT = 255;
+
+export const REVOKE_TOKEN_MODAL_ID = 'revoke-token-%{tokenName}';
diff --git a/app/assets/javascripts/clusters/agents/graphql/cache_update.js b/app/assets/javascripts/clusters/agents/graphql/cache_update.js
index 0219c4150eb..8db79c82708 100644
--- a/app/assets/javascripts/clusters/agents/graphql/cache_update.js
+++ b/app/assets/javascripts/clusters/agents/graphql/cache_update.js
@@ -22,3 +22,25 @@ export function addAgentTokenToStore(store, clusterAgentTokenCreate, query, vari
});
}
}
+
+export function removeTokenFromStore(store, revokeToken, query, variables) {
+ if (!hasErrors(revokeToken)) {
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ draftData.project.clusterAgent.tokens.nodes = draftData.project.clusterAgent.tokens.nodes.filter(
+ ({ id }) => id !== revokeToken.id,
+ );
+ draftData.project.clusterAgent.tokens.count -= 1;
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+ }
+}
diff --git a/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql b/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql
new file mode 100644
index 00000000000..6f1c6a66690
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/graphql/mutations/revoke_token.mutation.graphql
@@ -0,0 +1,5 @@
+mutation revokeAgentToken($input: ClusterAgentTokenRevokeInput!) {
+ clusterAgentTokenRevoke(input: $input) {
+ errors
+ }
+}