diff --git a/.gitignore b/.gitignore index 30cb231e83f..93fb0b1144b 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ eslint-report.html /.gitlab_kas_secret /webpack-report/ /crystalball/ +/deprecations/ /knapsack/ /rspec_flaky/ /locale/**/LC_MESSAGES diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 14b07dd4a2a..3962be8d84d 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -21,6 +21,7 @@ RUBY_GC_MALLOC_LIMIT: 67108864 RUBY_GC_MALLOC_LIMIT_MAX: 134217728 CRYSTALBALL: "true" + RECORD_DEPRECATIONS: "true" needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets"] script: - *base-script @@ -31,6 +32,7 @@ paths: - coverage/ - crystalball/ + - deprecations/ - knapsack/ - rspec_flaky/ - rspec_profiling/ diff --git a/.gitlab/issue_templates/Feature Flag Roll Out.md b/.gitlab/issue_templates/Feature Flag Roll Out.md index 584574bcbfe..8fda3297c38 100644 --- a/.gitlab/issue_templates/Feature Flag Roll Out.md +++ b/.gitlab/issue_templates/Feature Flag Roll Out.md @@ -36,8 +36,7 @@ If applicable, any groups/projects that are happy to have this feature turned on - [ ] Ensure that documentation has been updated - [ ] Enable on GitLab.com for individual groups/projects listed above and verify behaviour (`/chatops run feature set --project=gitlab-org/gitlab feature_name true`) - [ ] Coordinate a time to enable the flag with the SRE oncall and release managers - - In `#production` by pinging `@sre-oncall` - - In `#g_delivery` by pinging `@release-managers` + - In `#production` mention `@sre-oncall` and `@release-managers`. Once an SRE on call and Release Manager on call confirm, you can proceed with the rollout - [ ] Announce on the issue an estimated time this will be enabled on GitLab.com - [ ] Enable on GitLab.com by running chatops command in `#production` (`/chatops run feature set feature_name true`) - [ ] Cross post chatops Slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel diff --git a/.rubocop.yml b/.rubocop.yml index 34d6fe5e434..125b2db5cf8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -47,6 +47,10 @@ Cop/StaticTranslationDefinition: - 'spec/**/*' - 'ee/spec/**/*' +Lint/LastKeywordArgument: + Enabled: true + Safe: false + # This cop checks whether some constant value isn't a # mutable literal (e.g. array or hash). Style/MutableConstant: diff --git a/Gemfile b/Gemfile index 82a09e2f7eb..63a13c50373 100644 --- a/Gemfile +++ b/Gemfile @@ -351,6 +351,7 @@ group :development do end group :development, :test do + gem 'deprecation_toolkit', '~> 1.5.1', require: false gem 'bullet', '~> 6.1.0' gem 'pry-byebug', '~> 3.9.0', platform: :mri gem 'pry-rails', '~> 0.3.9' diff --git a/Gemfile.lock b/Gemfile.lock index 807c24c0d31..f5d7c409c0c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -224,6 +224,8 @@ GEM declarative-option (0.1.0) default_value_for (3.3.0) activerecord (>= 3.2.0, < 6.1) + deprecation_toolkit (1.5.1) + activesupport (>= 4.2) derailed_benchmarks (1.7.0) benchmark-ips (~> 2) get_process_mem (~> 0) @@ -1302,6 +1304,7 @@ DEPENDENCIES database_cleaner (~> 1.7.0) deckar01-task_list (= 2.3.1) default_value_for (~> 3.3.0) + deprecation_toolkit (~> 1.5.1) derailed_benchmarks device_detector devise (~> 4.7.2) diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue similarity index 95% rename from app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue rename to app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue index 3656fc4d7ec..bf2874b6cc7 100644 --- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue +++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue @@ -216,8 +216,12 @@ export default { return { name: this.currentIntegration?.name || '', active: this.currentIntegration?.active || false, - token: this.currentIntegration?.token || this.selectedIntegrationType.token, - url: this.currentIntegration?.url || this.selectedIntegrationType.url, + token: + this.currentIntegration?.token || + (this.selectedIntegrationType !== this.generic ? this.selectedIntegrationType.token : ''), + url: + this.currentIntegration?.url || + (this.selectedIntegrationType !== this.generic ? this.selectedIntegrationType.url : ''), apiUrl: this.currentIntegration?.apiUrl || '', }; }, @@ -246,8 +250,20 @@ export default { canEditPayload() { return this.hasSamplePayload && !this.resetSamplePayloadConfirmed; }, + isResetAuthKeyDisabled() { + return !this.active && !this.integrationForm.token !== ''; + }, isPayloadEditDisabled() { - return !this.active || this.canEditPayload; + return this.glFeatures.multipleHttpIntegrationsCustomMapping + ? !this.active || this.canEditPayload + : !this.active; + }, + isSubmitTestPayloadDisabled() { + return ( + !this.active || + Boolean(this.integrationTestPayload.error) || + this.integrationTestPayload.json === '' + ); }, }, watch: { @@ -257,7 +273,7 @@ export default { } this.selectedIntegration = val.type; this.active = val.active; - if (val.type === typeSet.http) this.getIntegrationMapping(val.id); + if (val.type === typeSet.http && this.showMappingBuilder) this.getIntegrationMapping(val.id); return this.integrationTypeSelect(); }, }, @@ -297,14 +313,8 @@ export default { }); }, submitWithTestPayload() { - return service - .updateTestAlert(this.testAlertPayload) - .then(() => { - this.submit(); - }) - .catch(() => { - this.$emit('test-payload-failure'); - }); + this.$emit('set-test-alert-payload', this.testAlertPayload); + this.submit(); }, submit() { // TODO: Will be removed in 13.7 as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/273657 @@ -323,6 +333,7 @@ export default { return this.$emit('update-integration', integrationPayload); } + this.reset(); return this.$emit('create-new-integration', integrationPayload); }, reset() { @@ -539,7 +550,7 @@ export default { - + {{ $options.i18n.integrationFormSteps.step3.reset }} { this.isUpdating = false; + this.testAlertPayload = null; }); }, resetToken({ type, variables }) { @@ -194,7 +220,13 @@ export default { const integration = httpIntegrationResetToken?.integration || prometheusIntegrationResetToken?.integration; - this.currentIntegration = integration; + + this.$apollo.mutate({ + mutation: updateCurrentIntergrationMutation, + variables: { + ...integration, + }, + }); return createFlash({ message: this.$options.i18n.changesSaved, @@ -262,8 +294,21 @@ export default { variables: {}, }); }, - testPayloadFailure() { - createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR }); + setTestAlertPayload(payload) { + this.testAlertPayload = payload; + }, + validateAlertPayload(payload) { + return service + .updateTestAlert(payload ?? this.testAlertPayload) + .then(() => { + return createFlash({ + message: this.$options.i18n.alertSent, + type: FLASH_TYPES.SUCCESS, + }); + }) + .catch(() => { + createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR }); + }); }, }, }; @@ -297,7 +342,7 @@ export default { @edit-integration="editIntegration" @delete-integration="deleteIntegration" /> - diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 11c5369f726..ddba6589474 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -38,6 +38,8 @@ module Types feature_flag: :user_group_counts field :status, Types::UserStatusType, null: true, description: 'User status' + field :location, ::GraphQL::STRING_TYPE, null: true, + description: 'The location of the user.' field :project_memberships, Types::ProjectMemberType.connection_type, null: true, description: 'Project memberships of the user', method: :project_members diff --git a/app/models/terraform/state.rb b/app/models/terraform/state.rb index e6f295ee97a..19700587f09 100644 --- a/app/models/terraform/state.rb +++ b/app/models/terraform/state.rb @@ -3,7 +3,6 @@ module Terraform class State < ApplicationRecord include UsageStatistics - include FileStoreMounter include IgnorableColumns # These columns are being removed since geo replication falls to the versioned state # Tracking in https://gitlab.com/gitlab-org/gitlab/-/issues/258262 @@ -36,18 +35,8 @@ module Terraform default_value_for(:uuid, allows_nil: false) { SecureRandom.hex(UUID_LENGTH / 2) } - mount_file_store_uploader StateUploader - - def file_store - super || StateUploader.default_store - end - def latest_file - if versioning_enabled? - latest_version&.file - else - latest_version&.file || file - end + latest_version&.file end def locked? @@ -55,13 +44,14 @@ module Terraform end def update_file!(data, version:, build:) + # This check is required to maintain backwards compatibility with + # states that were created prior to versioning being supported. + # This can be removed in 14.0 when support for these states is dropped. + # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960 if versioning_enabled? create_new_version!(data: data, version: version, build: build) - elsif latest_version.present? - migrate_legacy_version!(data: data, version: version, build: build) else - self.file = data - save! + migrate_legacy_version!(data: data, version: version, build: build) end end diff --git a/app/models/terraform/state_version.rb b/app/models/terraform/state_version.rb index cc5d94b8e09..19d708616fc 100644 --- a/app/models/terraform/state_version.rb +++ b/app/models/terraform/state_version.rb @@ -10,9 +10,9 @@ module Terraform scope :ordered_by_version_desc, -> { order(version: :desc) } - default_value_for(:file_store) { VersionedStateUploader.default_store } + default_value_for(:file_store) { StateUploader.default_store } - mount_file_store_uploader VersionedStateUploader + mount_file_store_uploader StateUploader delegate :project_id, :uuid, to: :terraform_state, allow_nil: true diff --git a/app/models/user.rb b/app/models/user.rb index be64e057d59..22e62bc5f1e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -587,11 +587,13 @@ class User < ApplicationRecord sanitized_order_sql = Arel.sql(sanitize_sql_array([order, query: query])) - where( - fuzzy_arel_match(:name, query, lower_exact_match: true) - .or(fuzzy_arel_match(:username, query, lower_exact_match: true)) - .or(arel_table[:email].eq(query)) - ).reorder(sanitized_order_sql, :name) + search_query = if Feature.enabled?(:user_search_secondary_email) + search_with_secondary_emails(query) + else + search_without_secondary_emails(query) + end + + search_query.reorder(sanitized_order_sql, :name) end # Limits the result set to users _not_ in the given query/list of IDs. @@ -606,6 +608,18 @@ class User < ApplicationRecord reorder(:name) end + def search_without_secondary_emails(query) + return none if query.blank? + + query = query.downcase + + where( + fuzzy_arel_match(:name, query, lower_exact_match: true) + .or(fuzzy_arel_match(:username, query, lower_exact_match: true)) + .or(arel_table[:email].eq(query)) + ) + end + # searches user by given pattern # it compares name, email, username fields and user's secondary emails with given pattern # This method uses ILIKE on PostgreSQL. @@ -616,15 +630,16 @@ class User < ApplicationRecord query = query.downcase email_table = Email.arel_table - matched_by_emails_user_ids = email_table + matched_by_email_user_id = email_table .project(email_table[:user_id]) .where(email_table[:email].eq(query)) + .take(1) # at most 1 record as there is a unique constraint where( fuzzy_arel_match(:name, query) .or(fuzzy_arel_match(:username, query)) .or(arel_table[:email].eq(query)) - .or(arel_table[:id].in(matched_by_emails_user_ids)) + .or(arel_table[:id].eq(matched_by_email_user_id)) ) end diff --git a/app/services/users/approve_service.rb b/app/services/users/approve_service.rb index 27668e9430e..debd1e8cd17 100644 --- a/app/services/users/approve_service.rb +++ b/app/services/users/approve_service.rb @@ -7,8 +7,9 @@ module Users end def execute(user) - return error(_('You are not allowed to approve a user')) unless allowed? - return error(_('The user you are trying to approve is not pending an approval')) unless approval_required?(user) + return error(_('You are not allowed to approve a user'), :forbidden) unless allowed? + return error(_('The user you are trying to approve is not pending an approval'), :conflict) if user.active? + return error(_('The user you are trying to approve is not pending an approval'), :conflict) unless approval_required?(user) if user.activate # Resends confirmation email if the user isn't confirmed yet. @@ -18,9 +19,9 @@ module Users DeviseMailer.user_admin_approval(user).deliver_later after_approve_hook(user) - success + success(message: 'Success', http_status: :created) else - error(user.errors.full_messages.uniq.join('. ')) + error(user.errors.full_messages.uniq.join('. '), :unprocessable_entity) end end diff --git a/app/uploaders/terraform/state_uploader.rb b/app/uploaders/terraform/state_uploader.rb index 2306313fc82..d80725cb051 100644 --- a/app/uploaders/terraform/state_uploader.rb +++ b/app/uploaders/terraform/state_uploader.rb @@ -6,17 +6,33 @@ module Terraform storage_options Gitlab.config.terraform_state - delegate :project_id, to: :model + delegate :terraform_state, :project_id, to: :model # Use Lockbox to encrypt/decrypt the stored file (registers CarrierWave callbacks) encrypt(key: :key) def filename - "#{model.uuid}.tfstate" + # This check is required to maintain backwards compatibility with + # states that were created prior to versioning being supported. + # This can be removed in 14.0 when support for these states is dropped. + # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960 + if terraform_state.versioning_enabled? + "#{model.version}.tfstate" + else + "#{model.uuid}.tfstate" + end end def store_dir - project_id.to_s + # This check is required to maintain backwards compatibility with + # states that were created prior to versioning being supported. + # This can be removed in 14.0 when support for these states is dropped. + # See https://gitlab.com/gitlab-org/gitlab/-/issues/258960 + if terraform_state.versioning_enabled? + Gitlab::HashedPath.new(model.uuid, root_hash: project_id) + else + project_id.to_s + end end def key diff --git a/app/uploaders/terraform/versioned_state_uploader.rb b/app/uploaders/terraform/versioned_state_uploader.rb deleted file mode 100644 index e50ab6c7dc6..00000000000 --- a/app/uploaders/terraform/versioned_state_uploader.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Terraform - class VersionedStateUploader < StateUploader - delegate :terraform_state, to: :model - - def filename - if terraform_state.versioning_enabled? - "#{model.version}.tfstate" - else - "#{model.uuid}.tfstate" - end - end - - def store_dir - if terraform_state.versioning_enabled? - Gitlab::HashedPath.new(model.uuid, root_hash: project_id) - else - project_id.to_s - end - end - end -end diff --git a/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml b/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml new file mode 100644 index 00000000000..ac580b87b55 --- /dev/null +++ b/changelogs/unreleased/241267-add-partitioned-audit-events-indexes.yml @@ -0,0 +1,5 @@ +--- +title: Add secondary indexes to partitioned audit_events +merge_request: 48270 +author: +type: added diff --git a/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml b/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml new file mode 100644 index 00000000000..424a600e298 --- /dev/null +++ b/changelogs/unreleased/263107-user-admin-approval-approve-user-via-api.yml @@ -0,0 +1,5 @@ +--- +title: Add API endoint for Administrators to approve pending users +merge_request: 47564 +author: +type: added diff --git a/changelogs/unreleased/ajk-graphql-user-location.yml b/changelogs/unreleased/ajk-graphql-user-location.yml new file mode 100644 index 00000000000..95c8660860c --- /dev/null +++ b/changelogs/unreleased/ajk-graphql-user-location.yml @@ -0,0 +1,5 @@ +--- +title: Add User.location field to GraphQL API +merge_request: 48059 +author: +type: changed diff --git a/changelogs/unreleased/alert-settings-form-json-bug.yml b/changelogs/unreleased/alert-settings-form-json-bug.yml new file mode 100644 index 00000000000..9bb46fe640b --- /dev/null +++ b/changelogs/unreleased/alert-settings-form-json-bug.yml @@ -0,0 +1,6 @@ +--- +title: Update alert setting form to handle JSON payload submit when mapping builder + is not enabled +merge_request: 48231 +author: +type: fixed diff --git a/changelogs/unreleased/cat-user-search-secondary-emails.yml b/changelogs/unreleased/cat-user-search-secondary-emails.yml new file mode 100644 index 00000000000..9a88b1b0031 --- /dev/null +++ b/changelogs/unreleased/cat-user-search-secondary-emails.yml @@ -0,0 +1,5 @@ +--- +title: Allow secondary emails in user search +merge_request: 47587 +author: +type: added diff --git a/config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml b/config/feature_flags/development/user_search_secondary_email.yml similarity index 54% rename from config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml rename to config/feature_flags/development/user_search_secondary_email.yml index a3a66295896..65e0fd8ce97 100644 --- a/config/feature_flags/development/ci_variable_expansion_in_rules_changes.yml +++ b/config/feature_flags/development/user_search_secondary_email.yml @@ -1,7 +1,8 @@ --- -name: ci_variable_expansion_in_rules_changes -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45037 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267192 +name: user_search_secondary_email +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47587 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/282137 +milestone: '13.7' type: development -group: group::pipeline authoring -default_enabled: true +group: group::access +default_enabled: false diff --git a/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb b/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb new file mode 100644 index 00000000000..d8b2833474b --- /dev/null +++ b/db/migrate/20201112215028_add_partitioned_audit_event_indexes.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class AddPartitionedAuditEventIndexes < ActiveRecord::Migration[6.0] + include Gitlab::Database::PartitioningMigrationHelpers + + DOWNTIME = false + + CREATED_AT_AUTHOR_ID_INDEX_NAME = 'analytics_index_audit_events_part_on_created_at_and_author_id' + ENTITY_ID_DESC_INDEX_NAME = 'idx_audit_events_part_on_entity_id_desc_author_id_created_at' + + disable_ddl_transaction! + + def up + add_concurrent_partitioned_index :audit_events_part_5fc467ac26, + [:created_at, :author_id], + name: CREATED_AT_AUTHOR_ID_INDEX_NAME + + add_concurrent_partitioned_index :audit_events_part_5fc467ac26, + [:entity_id, :entity_type, :id, :author_id, :created_at], + order: { id: :desc }, + name: ENTITY_ID_DESC_INDEX_NAME + end + + def down + remove_concurrent_partitioned_index_by_name :audit_events_part_5fc467ac26, ENTITY_ID_DESC_INDEX_NAME + + remove_concurrent_partitioned_index_by_name :audit_events_part_5fc467ac26, CREATED_AT_AUTHOR_ID_INDEX_NAME + end +end diff --git a/db/schema_migrations/20201112215028 b/db/schema_migrations/20201112215028 new file mode 100644 index 00000000000..07cedc7a146 --- /dev/null +++ b/db/schema_migrations/20201112215028 @@ -0,0 +1 @@ +d8d774e788eeaaecbda3cb7c5530926e74843d844bfad27b6a6e65bf5f89ac8a \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 0cd8e6f5993..b10088d7ea9 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -20038,6 +20038,8 @@ CREATE INDEX active_billable_users ON users USING btree (id) WHERE (((state)::te CREATE INDEX analytics_index_audit_events_on_created_at_and_author_id ON audit_events USING btree (created_at, author_id); +CREATE INDEX analytics_index_audit_events_part_on_created_at_and_author_id ON ONLY audit_events_part_5fc467ac26 USING btree (created_at, author_id); + CREATE INDEX analytics_index_events_on_created_at_and_author_id ON events USING btree (created_at, author_id); CREATE INDEX analytics_repository_languages_on_project_id ON analytics_language_trend_repository_languages USING btree (project_id); @@ -20082,6 +20084,8 @@ CREATE INDEX finding_links_on_vulnerability_occurrence_id ON vulnerability_findi CREATE INDEX idx_audit_events_on_entity_id_desc_author_id_created_at ON audit_events USING btree (entity_id, entity_type, id DESC, author_id, created_at); +CREATE INDEX idx_audit_events_part_on_entity_id_desc_author_id_created_at ON ONLY audit_events_part_5fc467ac26 USING btree (entity_id, entity_type, id DESC, author_id, created_at); + CREATE INDEX idx_ci_pipelines_artifacts_locked ON ci_pipelines USING btree (ci_ref_id, id) WHERE (locked = 1); CREATE INDEX idx_container_exp_policies_on_project_id_next_run_at_enabled ON container_expiration_policies USING btree (project_id, next_run_at, enabled); diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 4ce05199d49..60ee98703aa 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -23030,6 +23030,11 @@ type User { """ id: ID! + """ + The location of the user. + """ + location: String + """ Human-readable name of the user """ diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index fa5ae0d8dcb..c1615dd9436 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -66794,6 +66794,20 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "location", + "description": "The location of the user.", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "name", "description": "Human-readable name of the user", diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 4ee61debade..e4250701f82 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -3450,6 +3450,7 @@ Autogenerated return type of UpdateSnippet. | `groupCount` | Int | Group count for the user. Available only when feature flag `user_group_counts` is enabled | | `groupMemberships` | GroupMemberConnection | Group memberships of the user | | `id` | ID! | ID of the user | +| `location` | String | The location of the user. | | `name` | String! | Human-readable name of the user | | `projectMemberships` | ProjectMemberConnection | Project memberships of the user | | `snippets` | SnippetConnection | Snippets authored by the user | diff --git a/doc/api/users.md b/doc/api/users.md index 0d837924507..9b9005754d2 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -1275,8 +1275,8 @@ Parameters: Returns: - `201 OK` on success. -- `404 User Not Found` if user cannot be found. -- `403 Forbidden` when trying to activate a user blocked by admin or by LDAP synchronization. +- `404 User Not Found` if the user cannot be found. +- `403 Forbidden` if the user cannot be activated because they are blocked by an administrator or by LDAP synchronization. ### Get user contribution events @@ -1337,6 +1337,44 @@ Example response: ] ``` +## Approve user + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263107) in GitLab 13.7. + +Approves the specified user. Available only for administrators. + +```plaintext +POST /users/:id/approve +``` + +Parameters: + +- `id` (required) - ID of specified user + +```shell +curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/users/42/approve" +``` + +Returns: + +- `201 OK` on success. +- `404 User Not Found` if user cannot be found. +- `403 Forbidden` if the user cannot be approved because they are blocked by an administrator or by LDAP synchronization. + +Example Responses: + +```json +{ "message": "Success" } +``` + +```json +{ "message": "404 User Not Found" } +``` + +```json +{ "message": "The user you are trying to approve is not pending an approval" } +``` + ## Get an impersonation token of a user > Requires admin permissions. diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 77684576154..5063d8da6e5 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1342,14 +1342,7 @@ if there is no `if:` statement that limits the job to branch or merge request pi ##### Variables in `rules:changes` > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34272) in GitLab 13.6. -> - It was [deployed behind a feature flag](../../user/feature_flags.md), disabled by default. -> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/267192) in GitLab 13.6. -> - It's enabled on GitLab.com. -> - It's recommended for production use. -> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-variables-support-in-ruleschanges). **(CORE ONLY)** - -CAUTION: **Warning:** -This feature might not be available to you. Check the **version history** note above for details. +> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/267192) in GitLab 13.7. Environment variables can be used in `rules:changes` expressions to determine when to add jobs to a pipeline: @@ -1368,25 +1361,6 @@ The `$` character can be used for both variables and paths. For example, if the `$DOCKERFILES_DIR` variable exists, its value is used. If it does not exist, the `$` is interpreted as being part of a path. -###### Enable or disable variables support in `rules:changes` **(CORE ONLY)** - -Variables support in `rules:changes` is under development, but is ready for production use. -It is deployed behind a feature flag that is **enabled by default**. -[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can opt to disable it. - -To enable it: - -```ruby -Feature.enable(:ci_variable_expansion_in_rules_changes) -``` - -To disable it: - -```ruby -Feature.disable(:ci_variable_expansion_in_rules_changes) -``` - #### `rules:exists` > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/24021) in GitLab 12.4. diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 32b7f18c840..a10557d2db8 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -257,19 +257,22 @@ Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:back #### Excluding specific directories from the backup -You can choose what should be exempt from the backup by adding the environment -variable `SKIP`. The available options are: +You can exclude specific directories from the backup by adding the environment variable `SKIP`, whose values are a comma-separated list of the following options: - `db` (database) - `uploads` (attachments) -- `repositories` (Git repositories data) - `builds` (CI job output logs) - `artifacts` (CI job artifacts) - `lfs` (LFS objects) - `registry` (Container Registry images) - `pages` (Pages content) +- `repositories` (Git repositories data) -Use a comma to specify several options at the same time: +All wikis will be backed up as part of the `repositories` group. Non-existent wikis will be skipped during a backup. + +NOTE: **Note:** +When [backing up and restoring Helm Charts](https://docs.gitlab.com/charts/architecture/backup-restore.html), there is an additional option `packages`, which refers to any packages managed by the GitLab [package registry](../user/packages/package_registry/index.md). +For more information see [command line arguments](https://docs.gitlab.com/charts/architecture/backup-restore.html#command-line-arguments). All wikis are backed up as part of the `repositories` group. Non-existent wikis are skipped during a backup. diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md index 5eba0fa44ba..153753ea0c0 100644 --- a/doc/user/application_security/secret_detection/index.md +++ b/doc/user/application_security/secret_detection/index.md @@ -283,3 +283,25 @@ Support for custom certificate authorities was introduced in the following versi ### Getting warning message `gl-secret-detection-report.json: no matching files` For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload). + +### Error: `Couldn't run the gitleaks command: exit status 2` + +This error is usually caused by the `GIT_DEPTH` value of 50 that is set for all [projects by default](../../../ci/pipelines/settings.md#git-shallow-clone). + +For example, if a pipeline is triggered from a Merge Request containing 60 commits while the `GIT_DEPTH` is set to 50, the Secret Detection job will fail as the clone will not have been deep enough to contain all of the relevant commits. + +You can confirm this to be the cause of the error by implementing a [logging level](../../application_security/secret_detection/index.md#logging-level) of `debug`. Once implemented, the logs should look similar to the following example, wherein an "object not found" error can be seen: + +```plaintext +ERRO[2020-11-18T18:05:52Z] object not found +[ERRO] [secrets] [2020-11-18T18:05:52Z] ▶ Couldn't run the gitleaks command: exit status 2 +[ERRO] [secrets] [2020-11-18T18:05:52Z] ▶ Gitleaks analysis failed: exit status 2 +``` + +If this is the case, we can resolve the issue by setting the [`GIT_DEPTH` variable](../../../ci/runners/README.md#shallow-cloning) to a higher value. In order to apply this only to the Secret Detection job, the following can be added to your `.gitlab-ci.yml`: + +```yaml +secret_detection: + variables: + GIT_DEPTH: 100 +``` diff --git a/lib/api/users.rb b/lib/api/users.rb index 501ed629c7e..8b9b82877f7 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -534,6 +534,24 @@ module API user.activate end + + desc 'Approve a pending user. Available only for admins.' + params do + requires :id, type: Integer, desc: 'The ID of the user' + end + post ':id/approve', feature_category: :authentication_and_authorization do + user = User.find_by(id: params[:id]) + not_found!('User') unless can?(current_user, :read_user, user) + + result = ::Users::ApproveService.new(current_user).execute(user) + + if result[:success] + result + else + render_api_error!(result[:message], result[:http_status]) + end + end + # rubocop: enable CodeReuse/ActiveRecord desc 'Deactivate an active user. Available only for admins.' params do diff --git a/lib/gitlab/ci/build/rules/rule/clause/changes.rb b/lib/gitlab/ci/build/rules/rule/clause/changes.rb index cbecce57163..9c2f6eea1dd 100644 --- a/lib/gitlab/ci/build/rules/rule/clause/changes.rb +++ b/lib/gitlab/ci/build/rules/rule/clause/changes.rb @@ -11,7 +11,7 @@ module Gitlab def satisfied_by?(pipeline, context) return true if pipeline.modified_paths.nil? - expanded_globs = expand_globs(pipeline, context) + expanded_globs = expand_globs(context) pipeline.modified_paths.any? do |path| expanded_globs.any? do |glob| File.fnmatch?(glob, path, File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB) @@ -19,8 +19,7 @@ module Gitlab end end - def expand_globs(pipeline, context) - return @globs unless ::Feature.enabled?(:ci_variable_expansion_in_rules_changes, pipeline.project, default_enabled: true) + def expand_globs(context) return @globs unless context @globs.map do |glob| diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml index 385959389de..e5b40e5f49a 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml @@ -1,5 +1,5 @@ .auto-deploy: - image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0-beta.2" + image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v2.0.0" dependencies: [] review: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2a9eda83746..6e84c95b3ae 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2733,6 +2733,9 @@ msgstr "" msgid "AlertsIntegrations|The integration token could not be reset. Please try again." msgstr "" +msgid "AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list." +msgstr "" + msgid "AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone." msgstr "" diff --git a/rubocop/cop/lint/last_keyword_argument.rb b/rubocop/cop/lint/last_keyword_argument.rb new file mode 100644 index 00000000000..d8f8d03b552 --- /dev/null +++ b/rubocop/cop/lint/last_keyword_argument.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Lint + # This cop only works if there are files from deprecation_toolkit. You can + # generate these files by: + # + # 1. Running specs with RECORD_DEPRECATIONS=1 + # 1. Downloading the complete set of deprecations/ files from a CI + # pipeline (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47720) + class LastKeywordArgument < Cop + MSG = 'Using the last argument as keyword parameters is deprecated'.freeze + + DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__) + KEYWORD_DEPRECATION_STR = 'maybe ** should be added to the call' + + def on_send(node) + arg = node.arguments.last + return unless arg + + return unless known_match?(processed_source.file_path, node.first_line, node.method_name.to_s) + + return if arg.children.first.respond_to?(:kwsplat_type?) && arg.children.first&.kwsplat_type? + + # parser thinks `a: :b, c: :d` is hash type, it's actually kwargs + return if arg.hash_type? && !arg.source.match(/\A{/) + + add_offense(arg) + end + + def autocorrect(arg) + lambda do |corrector| + if arg.hash_type? + kwarg = arg.source.sub(/\A{\s*/, '').sub(/\s*}\z/, '') + corrector.replace(arg, kwarg) + elsif arg.splat_type? + corrector.insert_before(arg, '*') + else + corrector.insert_before(arg, '**') + end + end + end + + private + + def known_match?(file_path, line_number, method_name) + file_path_from_root = file_path.sub(File.expand_path('../../..', __dir__), '') + + self.class.keyword_warnings.any? do |warning| + warning.include?("#{file_path_from_root}:#{line_number}") && warning.include?("called method `#{method_name}'") + end + end + + def self.keyword_warnings + @keyword_warnings ||= keywords_list + end + + def self.keywords_list + hash = Dir.glob(DEPRECATIONS_GLOB).each_with_object({}) do |file, hash| + hash.merge!(YAML.safe_load(File.read(file))) + end + + hash.values.flatten.select { |str| str.include?(KEYWORD_DEPRECATION_STR) }.uniq + end + end + end + end +end diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb new file mode 100644 index 00000000000..bc90f67f0db --- /dev/null +++ b/spec/deprecation_toolkit_env.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +if ENV.key?('RECORD_DEPRECATIONS') + require 'deprecation_toolkit' + require 'deprecation_toolkit/rspec' + DeprecationToolkit::Configuration.test_runner = :rspec + DeprecationToolkit::Configuration.deprecation_path = 'deprecations' + DeprecationToolkit::Configuration.behavior = DeprecationToolkit::Behaviors::Record + + # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7.2 + Warning[:deprecated] = true + + kwargs_warnings = [ + # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 + %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} + ] + DeprecationToolkit::Configuration.warnings_treated_as_deprecation = kwargs_warnings +end diff --git a/spec/factories/terraform/state.rb b/spec/factories/terraform/state.rb index c54a8aedbc6..fb63c845073 100644 --- a/spec/factories/terraform/state.rb +++ b/spec/factories/terraform/state.rb @@ -6,11 +6,6 @@ FactoryBot.define do sequence(:name) { |n| "state-#{n}" } - trait :with_file do - versioning_enabled { false } - file { fixture_file_upload('spec/fixtures/terraform/terraform.tfstate', 'application/json') } - end - trait :locked do sequence(:lock_xid) { |n| "lock-#{n}" } locked_at { Time.current } @@ -22,8 +17,5 @@ FactoryBot.define do create(:terraform_state_version, terraform_state: state) end end - - # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/235108 - factory :legacy_terraform_state, parent: :terraform_state, traits: [:with_file] end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 1b430009ab5..50656d14eb7 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -35,6 +35,10 @@ FactoryBot.define do user_type { :alert_bot } end + trait :deactivated do + after(:build) { |user, _| user.deactivate! } + end + trait :project_bot do user_type { :project_bot } end diff --git a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap similarity index 97% rename from spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap rename to spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap index 98c79b406f2..a1ced8910b3 100644 --- a/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_new_spec.js.snap +++ b/spec/frontend/alerts_settings/__snapshots__/alerts_settings_form_spec.js.snap @@ -87,7 +87,7 @@ exports[`AlertsSettingsFormNew with default values renders the initial template