From 3a776ed005ad92628aa204bcbb4a1eb88b83dcd1 Mon Sep 17 00:00:00 2001 From: Mireya Andres Date: Tue, 21 Jan 2025 22:45:28 +0800 Subject: [PATCH 1/3] Remove autopopulated entries from job token allowlist This is developed under the `authentication_logs_migration_for_allowlist` feature flag. --- .../autopopulate_allowlist_modal.vue | 11 +- .../components/inbound_token_access.vue | 305 +++++++++++------- .../remove_autopopulated_entries_modal.vue | 72 +++++ .../components/token_access_table.vue | 12 + .../javascripts/token_access/constants.js | 2 + ...ove_autopopulated_entries.mutation.graphql | 6 + locale/gitlab.pot | 21 ++ .../autopopulate_allowlist_modal_spec.js | 21 +- .../token_access/inbound_token_access_spec.js | 154 ++++++++- spec/frontend/token_access/mock_data.js | 19 ++ ...remove_autopopulated_entries_modal_spec.js | 63 ++++ .../token_access/token_access_table_spec.js | 12 + 12 files changed, 567 insertions(+), 131 deletions(-) create mode 100644 app/assets/javascripts/token_access/components/remove_autopopulated_entries_modal.vue create mode 100644 app/assets/javascripts/token_access/graphql/mutations/remove_autopopulated_entries.mutation.graphql create mode 100644 spec/frontend/token_access/remove_autopopulated_entries_modal_spec.js diff --git a/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue b/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue index 31f86d1eea9d02..3b6df8c2e5e4ab 100644 --- a/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue +++ b/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue @@ -1,5 +1,6 @@ @@ -129,6 +133,7 @@ export default { @primary.prevent="autopopulateAllowlist" @secondary="hideModal" @canceled="hideModal" + @hidden="hideModal" > {{ errorMessage }} @@ -138,8 +143,6 @@ export default { {{ authLogExceedsLimitMessage }}

- -

diff --git a/app/assets/javascripts/token_access/components/inbound_token_access.vue b/app/assets/javascripts/token_access/components/inbound_token_access.vue index 014b0a1d55dc44..d11ac3bd8b69a0 100644 --- a/app/assets/javascripts/token_access/components/inbound_token_access.vue +++ b/app/assets/javascripts/token_access/components/inbound_token_access.vue @@ -3,6 +3,7 @@ import { GlAlert, GlButton, GlCollapsibleListbox, + GlDisclosureDropdown, GlIcon, GlLink, GlLoadingIcon, @@ -24,13 +25,16 @@ import inboundGetCIJobTokenScopeQuery from '../graphql/queries/inbound_get_ci_jo import inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_groups_and_projects_with_ci_job_token_scope.query.graphql'; import getCiJobTokenScopeAllowlistQuery from '../graphql/queries/get_ci_job_token_scope_allowlist.query.graphql'; import getAuthLogCountQuery from '../graphql/queries/get_auth_log_count.query.graphql'; +import removeAutopopulatedEntriesMutation from '../graphql/mutations/remove_autopopulated_entries.mutation.graphql'; import { JOB_TOKEN_FORM_ADD_GROUP_OR_PROJECT, JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG, + JOB_TOKEN_REMOVE_AUTOPOPULATED_ENTRIES_MODAL, } from '../constants'; import TokenAccessTable from './token_access_table.vue'; import NamespaceForm from './namespace_form.vue'; import AutopopulateAllowlistModal from './autopopulate_allowlist_modal.vue'; +import RemoveAutopopulatedEntriesModal from './remove_autopopulated_entries_modal.vue'; export default { i18n: { @@ -55,6 +59,7 @@ export default { 'CICD|Are you sure you want to remove %{namespace} from the job token allowlist?', ), removeNamespaceModalActionText: s__('CICD|Remove group or project'), + removeAutopopulatedEntries: s__('CICD|Remove only entries auto-added from authentication log'), }, inboundJobTokenScopeOptions: [ { @@ -81,11 +86,13 @@ export default { GlAlert, GlButton, GlCollapsibleListbox, + GlDisclosureDropdown, GlIcon, GlLink, GlLoadingIcon, GlSprintf, CrudComponent, + RemoveAutopopulatedEntriesModal, TokenAccessTable, GlFormRadioGroup, NamespaceForm, @@ -170,9 +177,11 @@ export default { data() { return { authLogCount: 0, + allowlistLoadingMessage: '', inboundJobTokenScopeEnabled: null, - isUpdating: false, + isUpdatingJobTokenScope: false, groupsAndProjectsWithAccess: { groups: [], projects: [] }, + autopopulationErrorMessage: null, projectName: '', namespaceToEdit: null, namespaceToRemove: null, @@ -183,6 +192,12 @@ export default { authLogExceedsLimit() { return this.projectCount + this.groupCount + this.authLogCount > this.projectAllowlistLimit; }, + isAllowlistLoading() { + return ( + this.$apollo.queries.groupsAndProjectsWithAccess.loading || + this.allowlistLoadingMessage.length > 0 + ); + }, isJobTokenPoliciesEnabled() { return this.glFeatures.addPoliciesToCiJobToken; }, @@ -198,6 +213,17 @@ export default { canAutopopulateAuthLog() { return this.glFeatures.authenticationLogsMigrationForAllowlist; }, + disclosureDropdownOptions() { + return [ + { + text: this.$options.i18n.removeAutopopulatedEntries, + variant: 'danger', + action: () => { + this.selectedAction = JOB_TOKEN_REMOVE_AUTOPOPULATED_ENTRIES_MODAL; + }, + }, + ]; + }, groupCount() { return this.groupsAndProjectsWithAccess.groups.length; }, @@ -210,14 +236,14 @@ export default { projectCountTooltip() { return n__('%d project has access', '%d projects have access', this.projectCount); }, - isAllowlistLoading() { - return this.$apollo.queries.groupsAndProjectsWithAccess.loading; - }, removeNamespaceModalTitle() { return sprintf(this.$options.i18n.removeNamespaceModalTitle, { namespace: this.namespaceToRemove?.fullPath, }); }, + showRemoveAutopopulatedEntriesModal() { + return this.selectedAction === JOB_TOKEN_REMOVE_AUTOPOPULATED_ENTRIES_MODAL; + }, showAutopopulateModal() { return this.selectedAction === JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG; }, @@ -238,7 +264,7 @@ export default { })); }, async updateCIJobTokenScope() { - this.isUpdating = true; + this.isUpdatingJobTokenScope = true; try { const { @@ -268,7 +294,7 @@ export default { this.inboundJobTokenScopeEnabled = !this.inboundJobTokenScopeEnabled; createAlert({ message: error.message }); } finally { - this.isUpdating = false; + this.isUpdatingJobTokenScope = false; } }, async removeItem() { @@ -291,6 +317,43 @@ export default { this.refetchGroupsAndProjects(); return Promise.resolve(); }, + async removeAutopopulatedEntries() { + this.hideSelectedAction(); + this.autopopulationErrorMessage = null; + this.allowlistLoadingMessage = s__( + 'CICD|Removing auto-added allowlist entries. Please wait while the action completes.', + ); + + try { + const { + data: { + ciJobTokenScopeClearAllowlistAutopopulations: { errors }, + }, + } = await this.$apollo.mutate({ + mutation: removeAutopopulatedEntriesMutation, + variables: { + projectPath: this.fullPath, + }, + }); + + if (errors.length) { + throw new Error(errors[0]); + } + + this.refetchAllowlist(); + this.$toast.show( + s__('CICD|Authentication log entries were successfully removed from the allowlist.'), + ); + } catch (error) { + this.autopopulationErrorMessage = + error?.message || + s__( + 'CICD|An error occurred while adding the authentication log entries. Please try again.', + ); + } finally { + this.allowlistLoadingMessage = ''; + } + }, refetchAllowlist() { this.$apollo.queries.groupsAndProjectsWithAccess.refetch(); this.hideSelectedAction(); @@ -328,117 +391,133 @@ export default { @hide="hideSelectedAction" @refetch-allowlist="refetchAllowlist" /> - -