diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index a2a02d2fc90ca1f835d8c714b84ac5621ee42b42..8f7db09e72bcb5007b58df8bdcf4219b8a3259fe 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -61,8 +61,8 @@ "id_tokens": { "$ref": "#/definitions/id_tokens" }, - "identity_provider": { - "$ref": "#/definitions/identity_provider" + "identity": { + "$ref": "#/definitions/identity" }, "retry": { "$ref": "#/definitions/retry" @@ -706,9 +706,9 @@ } } }, - "identity_provider": { + "identity": { "type": "string", - "markdownDescription": "Sets an identity provider (experimental), allowing automatic authentication with the external provider. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#identity_provider).", + "markdownDescription": "Sets a workload identity (experimental), allowing automatic authentication with the external system. [Learn More](https://docs.gitlab.com/ee/ci/yaml/#identity).", "enum": [ "google_cloud" ] @@ -1642,8 +1642,8 @@ "id_tokens": { "$ref": "#/definitions/id_tokens" }, - "identity_provider": { - "$ref": "#/definitions/identity_provider" + "identity": { + "$ref": "#/definitions/identity" }, "secrets": { "$ref": "#/definitions/secrets" diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 41a3ee3e1c8ab5055aa91156e4b1ac01bd4d9841..ccc1c3e6c855c3b34072b5eaa9f6d87e7a4c2579 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -16,8 +16,7 @@ module AuthenticatesWithTwoFactor # # Returns nil def prompt_for_two_factor(user) - # Set @user for Devise views - @user = user # rubocop:disable Gitlab/ModuleWithInstanceVariables + @user = user # rubocop:disable Gitlab/ModuleWithInstanceVariables -- Set @user for Devise views return handle_locked_user(user) unless user.can?(:log_in) diff --git a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb index 045ccf1e5b880856f277d826d031ef13796246e0..0f36e78dab330acbf785d84762212bc7abd44344 100644 --- a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb +++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @@ -8,6 +8,8 @@ module AuthenticatesWithTwoFactorForAdminMode end def admin_mode_prompt_for_two_factor(user) + @user = user # rubocop:disable Gitlab/ModuleWithInstanceVariables -- Set @user for Admin views + return handle_locked_user(user) unless user.can?(:log_in) session[:otp_user_id] = user.id diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index c828c156d50fa4680e0ac9e514c7c36eabe6004c..aa01a459b0f73abdcd77efb21c177ecd0be60861 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -49,15 +49,8 @@ def external_author def before_create(issue) issue.check_for_spam(user: current_user, action: :create) if perform_spam_check - # current_user (defined in BaseService) is not available within run_after_commit block - user = current_user assign_description_from_template(issue) - issue.run_after_commit do - NewIssueWorker.perform_async(issue.id, user.id, issue.class.to_s) - Issues::PlacementWorker.perform_async(nil, issue.project_id) - # issue.namespace_id can point to either a project through project namespace or a group. - Onboarding::IssueCreatedWorker.perform_async(issue.namespace_id) - end + after_commit_tasks(current_user, issue) end # Add new items to Issues::AfterCreateService if they can be performed in Sidekiq @@ -160,6 +153,15 @@ def assign_description_from_template(issue) issue.description = default_template.content if default_template.present? end + + def after_commit_tasks(user, issue) + issue.run_after_commit do + NewIssueWorker.perform_async(issue.id, user.id, issue.class.to_s) + Issues::PlacementWorker.perform_async(nil, issue.project_id) + # issue.namespace_id can point to either a project through project namespace or a group. + Onboarding::IssueCreatedWorker.perform_async(issue.namespace_id) + end + end end end diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb index e1e6063c8ac1d3768b175929206332fcd949ded1..c0d855369b274cd5c971b3c12061fe1311dc2f9f 100644 --- a/app/services/work_items/create_service.rb +++ b/app/services/work_items/create_service.rb @@ -16,8 +16,8 @@ def initialize(container:, perform_spam_check: true, current_user: nil, params: @widget_params = widget_params end - def execute - result = super + def execute(skip_system_notes: false) + result = skip_system_notes? ? super(skip_system_notes: true) : super return result if result.error? work_item = result[:issue] @@ -86,6 +86,10 @@ def authorization_action def payload(work_item) { work_item: work_item } end + + def skip_system_notes? + false + end end end diff --git a/app/views/admin/sessions/_two_factor_otp.html.haml b/app/views/admin/sessions/_two_factor_otp.html.haml deleted file mode 100644 index 5978290c66cb89d758cfdef7bb584ca3482a2ff1..0000000000000000000000000000000000000000 --- a/app/views/admin/sessions/_two_factor_otp.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -= form_tag(admin_session_path, { method: :post, class: "edit_user gl-show-field-errors js-2fa-form #{'hidden' if current_user.two_factor_webauthn_enabled?}" }) do - .form-group - = label_tag :user_otp_attempt, _('Enter verification code') - = text_field_tag 'user[otp_attempt]', nil, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', inputmode: 'numeric', title: _('This field is required.') - %p.form-text.text-muted.hint - = _("Enter the code from your two-factor authenticator app. If you've lost your device, you can enter one of your recovery codes.") - - .submit-container - = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true) do - = _("Verify code") diff --git a/app/views/admin/sessions/two_factor.html.haml b/app/views/admin/sessions/two_factor.html.haml index 802470be71fc75f1065854386be39cc82a07960b..37a33a11a70159ea99b28ab96879d5a84f5f6c60 100644 --- a/app/views/admin/sessions/two_factor.html.haml +++ b/app/views/admin/sessions/two_factor.html.haml @@ -5,7 +5,4 @@ .col-md-5 .login-page .login-box.gl-p-5 - - if current_user.two_factor_enabled? - = render 'admin/sessions/two_factor_otp' - - if current_user.two_factor_webauthn_enabled? - = render 'authentication/authenticate', render_remember_me: false, target_path: admin_session_path + = render 'devise/shared/totp_recovery_code_or_webauthn', admin_mode: true diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index c39e6230f69352d7923a7efc6c933f90ae7ad3cb..d305560b097b50dcbe25d008ecaa4e357f19be9e 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -1,18 +1,2 @@ .login-box.gl-p-5 - - if @user.two_factor_enabled? - = gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: "gl-show-field-errors js-2fa-form #{'hidden' if @user.two_factor_webauthn_enabled?}", aria: { live: 'assertive' }}) do |f| - .form-group - = f.label :otp_attempt, _('Enter verification code') - = f.text_field :otp_attempt, class: 'form-control gl-form-input', required: true, autofocus: true, autocomplete: 'off', inputmode: 'numeric', title: _('This field is required.'), data: { testid: 'two-fa-code-field' } - %p.form-text.text-muted.hint - = _("Enter the code from your two-factor authenticator app. If you've lost your device, you can enter one of your recovery codes.") - - - if remember_me_enabled? - - resource_params = params[resource_name].presence || params - = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) - - = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, button_options: { data: { testid: 'verify-code-button' } }) do - = _("Verify code") - - - if @user.two_factor_webauthn_enabled? - = render "authentication/authenticate", params: params, resource: resource, resource_name: resource_name, render_remember_me: true, target_path: new_user_session_path + = render 'devise/shared/totp_recovery_code_or_webauthn', admin_mode: false diff --git a/app/views/devise/shared/_totp_recovery_code_or_webauthn.html.haml b/app/views/devise/shared/_totp_recovery_code_or_webauthn.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..52c0ff0d436a960f00a49317876adb70bcc83c21 --- /dev/null +++ b/app/views/devise/shared/_totp_recovery_code_or_webauthn.html.haml @@ -0,0 +1,20 @@ +- target_path = admin_mode ? admin_session_path : user_session_path +- render_remember_me = admin_mode ? false : remember_me_enabled? + += gitlab_ui_form_for(:user, url: target_path, method: :post, html: { class: "gl-show-field-errors js-2fa-form #{'hidden' if @user.two_factor_webauthn_enabled?}", aria: { live: 'assertive' }}) do |f| + .form-group + = f.label :otp_attempt, _('Enter verification code') + -# Note: we use inputmode="numeric", because TOTP. However, recovery codes are alphanumeric. + = f.text_field :otp_attempt, class: 'form-control gl-form-input', required: true, autofocus: true, autocomplete: 'off', inputmode: 'numeric', title: _('This field is required.'), data: { testid: 'two-fa-code-field' } + %p.form-text.text-muted.hint + = _("Enter the code from your two-factor authenticator app. If you've lost your device, you can enter one of your recovery codes.") + + - if render_remember_me + - resource_params = params[:user].presence || params + = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) + + = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, block: true, button_options: { data: { testid: 'verify-code-button' } }) do + = _("Verify code") + +- if @user.two_factor_webauthn_enabled? + = render 'authentication/authenticate', target_path: target_path, render_remember_me: render_remember_me diff --git a/doc/architecture/blueprints/cells/application-deployment.md b/doc/architecture/blueprints/cells/application-deployment.md index e941940a7af0a964ee937dad8e0bde071fc206b6..8af7051f06d8132f9760fe2c517ed100f50bad45 100644 --- a/doc/architecture/blueprints/cells/application-deployment.md +++ b/doc/architecture/blueprints/cells/application-deployment.md @@ -42,13 +42,12 @@ User ==> R R ==> Primary R ==> Secondary -Primary --> Secondary : "DB selective replica" -Secondary --> Primary : "internal API" +Secondary --> Primary : "Internal API" @enduml ``` -As we can see from the diagram, users interact with the system through the router only. Cells communicate with the Primary Cell using internal API calls and have a local copy of all the database rows necessary to operate. +As we can see from the diagram, users interact with the system through the router only. Secondary Cells communicate with the Primary Cell using internal API and have a local copy of all the database rows necessary to operate. It is important to note that even if a Secondary Cell supports GitLab Geo out of the box, we will not be able to provide this feature to our users until the Router supports it. diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 973891f93d738534cefef68c56fb3942413c038d..c474c7bcaa8384454d1cd09328294ae612650f13 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -62,7 +62,7 @@ A GitLab CI/CD pipeline configuration includes: | [`dependencies`](#dependencies) | Restrict which artifacts are passed to a specific job by providing a list of jobs to fetch artifacts from. | | [`environment`](#environment) | Name of an environment to which the job deploys. | | [`extends`](#extends) | Configuration entries that this job inherits from. | - | [`identity_provider`](#identity_provider) | Authenticate with third party services. | + | [`identity`](#identity) | Authenticate with third party services using identity federation. | | [`image`](#image) | Use Docker images. | | [`inherit`](#inherit) | Select which global defaults all jobs inherit. | | [`interruptible`](#interruptible) | Defines if a job can be canceled when made redundant by a newer run. | @@ -2442,7 +2442,7 @@ job1: - [GitLab Runner configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) -### `identity_provider` +### `identity` DETAILS: **Status:** Experiment @@ -2453,17 +2453,17 @@ FLAG: On GitLab.com, this feature is not available. The feature is not ready for production use. -Use `identity_provider` to authenticate with third party services. +Use `identity` to authenticate with third party services using identity federation. **Keyword type**: Job keyword. You can use it only as part of a job or in the [`default:` section](#default). -**Possible inputs**: A provider identifier. Supported providers: `google_cloud` (Google Cloud). The Google Cloud +**Possible inputs**: An identifier. Supported providers: `google_cloud` (Google Cloud). -**Example of `identity_provider`**: +**Example of `identity`**: ```yaml -job_with_identity_provider: - identity_provider: google_cloud +job_with_workload_identity: + identity: google_cloud script: - gcloud compute instances list ``` diff --git a/doc/development/code_suggestions/index.md b/doc/development/code_suggestions/index.md index 5aded77191717c910a84b2c3fd29916a7adc9ff8..baed2c152b71783c2713cb76e67b5e7f913dad98 100644 --- a/doc/development/code_suggestions/index.md +++ b/doc/development/code_suggestions/index.md @@ -23,15 +23,19 @@ This should enable everyone to see locally any change in an IDE being sent to th 1. In VS Code navigate to the Extensions page and find "GitLab Workflow" in the list 1. Open the extension settings by clicking a small cog icon and select "Extension Settings" option 1. Check a "GitLab: Debug" checkbox. -1. Main Application +1. Main Application (GDK): + 1. Install the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/index.md#one-line-installation). 1. Enable Feature Flag ```code_suggestions_tokens_api``` - 1. In your terminal, navigate to a `gitlab` inside your `gitlab-development-kit` directory - 1. Run `bundle exec rails c` to start a Rails console - 1. Call `Feature.enable(:code_suggestions_tokens_api)` from the console + 1. In your terminal, navigate to your `gitlab-development-kit` > `gitlab` directory. + 1. Run `gdk rails console` or `bundle exec rails c` to start a Rails console. + 1. [Enable the Feature Flag](../../administration/feature_flags.md#enable-or-disable-the-feature) for the code suggestions tokens API by calling + `Feature.enable(:code_suggestions_tokens_api)` from the console. 1. Run the GDK with ```export AI_GATEWAY_URL=http://localhost:5052``` -1. [Setup AI Gateway](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist#how-to-run-the-server-locally) - 1. Build tree sitter libraries ```poetry run scripts/build-tree-sitter-lib.py``` - 1. Extra .env changes for all debugging insights +1. [Setup AI Gateway](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist): + 1. Complete the steps to [run the server locally](https://gitlab.com/gitlab-org/modelops/applied-ml/code-suggestions/ai-assist#how-to-run-the-server-locally). + - If running `asdf install` doesn't install the dependencies in ``.tool-versions``, you may need to run `asdf plugin add <name>` for each dependency first. + 1. Inside ``poetry shell``, build tree sitter libraries by running ```poetry run scripts/build-tree-sitter-lib.py``` + 1. Add the following variables to the `.env` file for all debugging insights: 1. `AIGW_LOGGING__LEVEL=DEBUG` 1. `AIGW_LOGGING__FORMAT_JSON=false` 1. `AIGW_LOGGING__TO_FILE=true` diff --git a/ee/app/models/ee/ci/build.rb b/ee/app/models/ee/ci/build.rb index 73fbb91ceb4453ae94159a783520f9e5f030467e..2d34f8750a971c353adf13af9b674402410c4ccd 100644 --- a/ee/app/models/ee/ci/build.rb +++ b/ee/app/models/ee/ci/build.rb @@ -85,7 +85,7 @@ def variables override :job_jwt_variables def job_jwt_variables - super.concat(identity_provider_variables) + super.concat(identity_variables) end def cost_factor_enabled? @@ -315,14 +315,14 @@ def hashicorp_vault_provider? variable_value('VAULT_SERVER_URL').present? end - def identity_provider_variables - return [] if options[:identity_provider].blank? + def identity_variables + return [] if options[:identity].blank? - case options[:identity_provider] + case options[:identity] when 'google_cloud' ::Gitlab::Ci::GoogleCloud::GenerateBuildEnvironmentVariablesService.new(self).execute else - raise ArgumentError, "Unknown identity_provider value: #{options[:identity_provider]}" + raise ArgumentError, "Unknown identity value: #{options[:identity]}" end end end diff --git a/ee/app/models/geo_node_status.rb b/ee/app/models/geo_node_status.rb index 2ea0919bf90d039a320746840a4a8294d01491c1..206fab45161c8973eb750c0b98cb00e3941cacd8 100644 --- a/ee/app/models/geo_node_status.rb +++ b/ee/app/models/geo_node_status.rb @@ -1,28 +1,8 @@ # frozen_string_literal: true class GeoNodeStatus < ApplicationRecord - include IgnorableColumns include ShaAttribute - ignore_columns( - %i[ - container_repositories_count - container_repositories_failed_count - container_repositories_registry_count - container_repositories_synced_count - job_artifacts_count - job_artifacts_failed_count - job_artifacts_synced_count - job_artifacts_synced_missing_on_primary_count - lfs_objects_count - lfs_objects_failed_count - lfs_objects_synced_count - lfs_objects_synced_missing_on_primary_count - ], - remove_with: '16.7', - remove_after: '2023-11-22' - ) - belongs_to :geo_node delegate :selective_sync_type, to: :geo_node diff --git a/ee/app/services/ee/work_items/create_service.rb b/ee/app/services/ee/work_items/create_service.rb index 23180bbaf1a23d7dd0f7653b75d97edaf531dce1..0aacc79069f65b3f8ea6d99fbb42c0268f4485e8 100644 --- a/ee/app/services/ee/work_items/create_service.rb +++ b/ee/app/services/ee/work_items/create_service.rb @@ -9,16 +9,33 @@ module CreateService override :iid_param_allowed? def iid_param_allowed? - # Used when creating a new epic with a synced work item - extra_params&.fetch(:synced_work_item, false) || super + sync_work_item? || super end override :filter_timestamp_params def filter_timestamp_params - return if extra_params&.fetch(:synced_work_item, false) + return if sync_work_item? super end + + override :skip_system_notes? + def skip_system_notes? + return true if sync_work_item? + + super + end + + override :after_commit_tasks + def after_commit_tasks(user, work_item) + return if sync_work_item? + + super + end + + def sync_work_item? + extra_params&.fetch(:synced_work_item, false) + end end end end diff --git a/ee/lib/ee/gitlab/ci/config/entry/job.rb b/ee/lib/ee/gitlab/ci/config/entry/job.rb index b7bc743ecab20a05db631b345a54c8fa82efc853..6dbb26ace856526e75431e429a442778431aa38e 100644 --- a/ee/lib/ee/gitlab/ci/config/entry/job.rb +++ b/ee/lib/ee/gitlab/ci/config/entry/job.rb @@ -9,7 +9,7 @@ module Job extend ActiveSupport::Concern extend ::Gitlab::Utils::Override - EE_ALLOWED_KEYS = %i[dast_configuration identity_provider secrets].freeze + EE_ALLOWED_KEYS = %i[dast_configuration identity secrets].freeze prepended do attributes :dast_configuration, :secrets @@ -18,8 +18,8 @@ module Job description: 'DAST configuration for this job', inherit: false - entry :identity_provider, ::Gitlab::Ci::Config::Entry::IdentityProvider, - description: 'Configured identity provider for this job.', + entry :identity, ::Gitlab::Ci::Config::Entry::Identity, + description: 'Configured workload identity for this job.', inherit: false entry :secrets, ::Gitlab::Config::Entry::ComposableHash, @@ -41,14 +41,14 @@ def allowed_keys def value super.merge({ dast_configuration: dast_configuration_value, - identity_provider: identity_provider_available? ? identity_provider_value : nil, + identity: identity_available? ? identity_value : nil, secrets: secrets_value }.compact) end private - def identity_provider_available? + def identity_available? ::Gitlab::Ci::YamlProcessor::FeatureFlags.enabled?(:ci_yaml_support_for_identity_provider, type: :beta) && ::Gitlab::Saas.feature_available?(:google_artifact_registry) end diff --git a/ee/lib/ee/gitlab/ci/yaml_processor/result.rb b/ee/lib/ee/gitlab/ci/yaml_processor/result.rb index fd354c612c9fd14232ee87d62113d17d021ab18e..c90dd5e9fa457d7a9d378bc1944426a99cc4dace 100644 --- a/ee/lib/ee/gitlab/ci/yaml_processor/result.rb +++ b/ee/lib/ee/gitlab/ci/yaml_processor/result.rb @@ -17,7 +17,7 @@ def build_attributes(name) { options: { dast_configuration: job[:dast_configuration], - identity_provider: job[:identity_provider] + identity: job[:identity] }.compact, secrets: job[:secrets] }.compact diff --git a/ee/lib/gitlab/ci/config/entry/identity_provider.rb b/ee/lib/gitlab/ci/config/entry/identity.rb similarity index 80% rename from ee/lib/gitlab/ci/config/entry/identity_provider.rb rename to ee/lib/gitlab/ci/config/entry/identity.rb index a6eb4bfc57a2a2d4329e5001ebc16bd0417277f2..f662c2e6d215e88c53e3a71f4d33e00367c709e1 100644 --- a/ee/lib/gitlab/ci/config/entry/identity_provider.rb +++ b/ee/lib/gitlab/ci/config/entry/identity.rb @@ -5,9 +5,9 @@ module Ci class Config module Entry ## - # Entry that represents identity provider settings. + # Entry that represents workload identity settings. # - class IdentityProvider < ::Gitlab::Config::Entry::Node + class Identity < ::Gitlab::Config::Entry::Node include ::Gitlab::Config::Entry::Validatable ALLOWED_IDENTITY_PROVIDERS = %w[google_cloud].freeze diff --git a/ee/spec/lib/ee/gitlab/ci/config/entry/job_spec.rb b/ee/spec/lib/ee/gitlab/ci/config/entry/job_spec.rb index f791dcabd6c59269d78797f426d5726cb22e318c..195faa208755772880ed2ffc53d386dbc745add0 100644 --- a/ee/spec/lib/ee/gitlab/ci/config/entry/job_spec.rb +++ b/ee/spec/lib/ee/gitlab/ci/config/entry/job_spec.rb @@ -9,7 +9,7 @@ context 'when filtering all the entry/node names' do subject { described_class.nodes.keys } - let(:result) { %i[dast_configuration identity_provider secrets] } + let(:result) { %i[dast_configuration identity secrets] } it { is_expected.to include(*result) } end @@ -76,10 +76,10 @@ it_behaves_like 'an invalid entry', 'secrets config should be a hash' end - context 'when entry has unknown identity provider' do - let(:config) { { script: 'rspec', identity_provider: 'unknown' } } + context 'when entry has unknown identity' do + let(:config) { { script: 'rspec', identity: 'unknown' } } - it_behaves_like 'an invalid entry', 'identity_provider config should be one of: google_cloud' + it_behaves_like 'an invalid entry', 'identity config should be one of: google_cloud' end end end @@ -147,13 +147,13 @@ end end - describe 'identity_provider', :aggregate_failures, feature_category: :secrets_management do + describe 'identity', :aggregate_failures, feature_category: :secrets_management do let(:feature_flag_enabled) { true } let(:saas_feature_enabled) { true } let(:config) do { script: 'rspec', - identity_provider: 'google_cloud' + identity: 'google_cloud' } end @@ -164,23 +164,23 @@ entry.compose! end - it 'includes identity provider-related values' do - expect(entry.value).to include(identity_provider: 'google_cloud') + it 'includes identity-related values' do + expect(entry.value).to include(identity: 'google_cloud') end context 'when ci_yaml_support_for_identity_provider FF is disabled' do let(:feature_flag_enabled) { false } it 'does not include identity provider-related values' do - expect(entry.value).not_to match(a_hash_including(identity_provider: anything)) + expect(entry.value).not_to match(a_hash_including(identity: anything)) end end context 'when feature is disabled' do let(:saas_feature_enabled) { false } - it 'does not include identity provider-related values' do - expect(entry.value).not_to match(a_hash_including(identity_provider: anything)) + it 'does not include identity-related values' do + expect(entry.value).not_to match(a_hash_including(identity: anything)) end end end diff --git a/ee/spec/lib/ee/gitlab/ci/yaml_processor/result_spec.rb b/ee/spec/lib/ee/gitlab/ci/yaml_processor/result_spec.rb index cd93559e5416d77cba9695aacaf297ae2f5e838b..845bc1431b770d5eaa8093239f3092ae25cf97a5 100644 --- a/ee/spec/lib/ee/gitlab/ci/yaml_processor/result_spec.rb +++ b/ee/spec/lib/ee/gitlab/ci/yaml_processor/result_spec.rb @@ -13,10 +13,10 @@ subject(:build) { result.builds.first } describe '#builds' do - context 'when a job has identity_provider', feature_category: :secrets_management do + context 'when a job has identity', feature_category: :secrets_management do let(:config_content) do YAML.dump( - test: { stage: 'test', script: 'echo', identity_provider: 'google_cloud' } + test: { stage: 'test', script: 'echo', identity: 'google_cloud' } ) end @@ -24,8 +24,8 @@ stub_saas_features(google_artifact_registry: true) end - it 'includes :identity_provider in :options' do - expect(build.dig(:options, :identity_provider)).to eq('google_cloud') + it 'includes :identity in :options' do + expect(build.dig(:options, :identity)).to eq('google_cloud') end end end diff --git a/ee/spec/lib/gitlab/ci/yaml_processor_spec.rb b/ee/spec/lib/gitlab/ci/yaml_processor_spec.rb index 35e8d8843c88452e8b1e88f8b5c6be7497acb7b5..890039d140d79d3ce136d1a53d350e2d1abe3c85 100644 --- a/ee/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/ee/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -331,12 +331,12 @@ end end - describe 'identity_provider', feature_category: :secrets_management do + describe 'identity', feature_category: :secrets_management do let(:config) do { build: { stage: 'build', script: 'test', - identity_provider: 'google_cloud' + identity: 'google_cloud' } } end @@ -345,10 +345,10 @@ stub_saas_features(google_artifact_registry: true) end - it 'includes identity provider-related values' do - identity_provider = result.builds.first.dig(:options, :identity_provider) + it 'includes identity-related values' do + identity = result.builds.first.dig(:options, :identity) - expect(identity_provider).to eq('google_cloud') + expect(identity).to eq('google_cloud') end end end diff --git a/ee/spec/models/ci/build_spec.rb b/ee/spec/models/ci/build_spec.rb index b114f48fdc8890fa6b7da0b3f51b8fc276ba6547..400d0e952aad32012a35fa20bfb550948b517902 100644 --- a/ee/spec/models/ci/build_spec.rb +++ b/ee/spec/models/ci/build_spec.rb @@ -986,12 +986,12 @@ end end - describe 'build identity_provider' do + describe 'build identity' do let_it_be(:user) { create(:user) } - let(:identity_provider) { 'google_cloud' } + let(:identity) { 'google_cloud' } let(:build) do - create(:ci_build, pipeline: pipeline, user: user, options: { identity_provider: identity_provider }) + create(:ci_build, pipeline: pipeline, user: user, options: { identity: identity }) end subject(:variables) { build.variables } @@ -1037,11 +1037,11 @@ }) end - context 'when identity_provider is unknown' do - let(:identity_provider) { 'unknown' } + context 'when identity is unknown' do + let(:identity) { 'unknown' } it 'raises an error' do - expect { subject }.to raise_error ArgumentError, "Unknown identity_provider value: #{identity_provider}" + expect { subject }.to raise_error ArgumentError, "Unknown identity value: #{identity}" end end end diff --git a/ee/spec/services/epics/create_service_spec.rb b/ee/spec/services/epics/create_service_spec.rb index 635edb04e7316ed961d89f87eab8057ea024f06a..f2fb9c73a8cae8d9a668627ce26f819d2dfd65f5 100644 --- a/ee/spec/services/epics/create_service_spec.rb +++ b/ee/spec/services/epics/create_service_spec.rb @@ -82,6 +82,35 @@ let(:epic) { Epic.last } end + it 'does not create work item metrics' do + expect { subject }.to change { Epic.count }.by(1) + .and(change { WorkItem.count }.by(1)) + .and(not_change { Issue::Metrics.count }) + end + + it 'does not duplicate system notes' do + expect { subject }.to change { Epic.count }.by(1).and(change { WorkItem.count }.by(1)) + + expect(Epic.last.notes.size).to eq(1) + expect(WorkItem.last.notes.size).to eq(0) + end + + it 'does not call run_after_commit for the work item' do + expect_next_instance_of(WorkItem) do |instance| + expect(instance).not_to receive(:run_after_commit) + end + + subject + end + + it 'does not call after commit workers for the work item' do + expect(NewIssueWorker).not_to receive(:perform_async) + expect(Issues::PlacementWorker).not_to receive(:perform_async) + expect(Onboarding::IssueCreatedWorker).not_to receive(:perform_async) + + subject + end + context 'when work item creation fails' do it 'does not create epic' do error_message = ['error 1', 'error 2'] diff --git a/ee/spec/services/work_items/create_service_spec.rb b/ee/spec/services/work_items/create_service_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f5ffcde98a100549c559aabb36f755644712404f --- /dev/null +++ b/ee/spec/services/work_items/create_service_spec.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::CreateService, feature_category: :team_planning do + let_it_be(:developer) { create(:user) } + let_it_be(:group) { create(:group).tap { |group| group.add_developer(developer) } } + let_it_be(:work_item_type) { create(:work_item_type, :epic, namespace: group) } + + let(:current_user) { developer } + let(:extra_params) { {} } + let(:widget_params) { {} } + let(:params) do + { + work_item_type: work_item_type, + title: 'Awesome work_item', + description: 'please fix', + confidential: true + } + end + + describe '#execute' do + let(:service) do + described_class.new( + container: group, + current_user: current_user, + params: params.merge(extra_params), + widget_params: widget_params + ) + end + + subject(:create_work_item) { service.execute } + + before do + stub_licensed_features(epics: true) + end + + context 'when params are valid' do + it 'created instance is a WorkItem' do + expect(Issuable::CommonSystemNotesService).to receive_message_chain(:new, :execute) + + work_item = create_work_item[:work_item] + + expect(work_item).to be_persisted + expect(work_item).to be_a(::WorkItem) + expect(work_item.title).to eq('Awesome work_item') + expect(work_item.description).to eq('please fix') + expect(work_item.confidential).to eq(true) + expect(work_item.work_item_type.base_type).to eq('epic') + end + + it 'calls NewIssueWorker with correct arguments' do + expect(NewIssueWorker).to receive(:perform_async) + .with(Integer, current_user.id, 'WorkItem') + + create_work_item + end + + context 'when synced_work_item is true' do + let(:extra_params) { { extra_params: { synced_work_item: true } } } + + it 'does not call system notes service' do + expect(Issuable::CommonSystemNotesService).not_to receive(:new) + + work_item = create_work_item[:work_item] + + expect(work_item).to be_persisted + expect(work_item).to be_a(::WorkItem) + end + + it 'does not call after commit workers' do + expect(NewIssueWorker).not_to receive(:perform_async) + expect(Issues::PlacementWorker).not_to receive(:perform_async) + expect(Onboarding::IssueCreatedWorker).not_to receive(:perform_async) + + create_work_item + end + end + end + end +end diff --git a/qa/qa/page/main/two_factor_auth.rb b/qa/qa/page/main/two_factor_auth.rb index 186027900cac703a112a66eddaabeaa499714dc4..2da0acf36aa89ce6415a246a6dadbc239d29766f 100644 --- a/qa/qa/page/main/two_factor_auth.rb +++ b/qa/qa/page/main/two_factor_auth.rb @@ -4,7 +4,7 @@ module QA module Page module Main class TwoFactorAuth < Page::Base - view 'app/views/devise/sessions/two_factor.html.haml' do + view 'app/views/devise/shared/_totp_recovery_code_or_webauthn.html.haml' do element 'verify-code-button' element 'two-fa-code-field' end diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/identity_provider.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/identity_provider.yml index 76836cc1cea8a4ce0660937e1dc0ef47877fc2df..4d20a63609f2f5b6c5022bb305c48a225dfbf663 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/identity_provider.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/identity_provider.yml @@ -1,5 +1,5 @@ -id_token_with_missing_identity_provider_value: - identity_provider: "" +id_token_with_missing_identity_value: + identity: "" -id_token_with_unknown_identity_provider_value: - identity_provider: unknown +id_token_with_unknown_identity_value: + identity: unknown diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/identity_provider.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/identity_provider.yml index 6a54422bc314807c03804f4eb84620906da33afa..a98a920cfa2f584f331952a8fe32ac85dd5bcb1f 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/identity_provider.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/identity_provider.yml @@ -1,2 +1,2 @@ -valid_identity_provider: - identity_provider: google_cloud +valid_workload_identity: + identity: google_cloud diff --git a/spec/views/admin/sessions/two_factor.html.haml_spec.rb b/spec/views/admin/sessions/two_factor.html.haml_spec.rb index 9ac9356b91a6db6f7eef720118e29d66ef6a9f22..334a31f8247d4b70a8df741b827e0a2d20e7f1e2 100644 --- a/spec/views/admin/sessions/two_factor.html.haml_spec.rb +++ b/spec/views/admin/sessions/two_factor.html.haml_spec.rb @@ -2,20 +2,9 @@ require 'spec_helper' -RSpec.describe 'admin/sessions/two_factor.html.haml' do +RSpec.describe 'admin/sessions/two_factor.html.haml', feature_category: :system_access do before do - allow(view).to receive(:current_user).and_return(user) - end - - context 'user has no two factor auth' do - let(:user) { create(:admin) } - - it 'shows tab' do - render - - expect(rendered).to have_no_field('user[otp_attempt]') - expect(rendered).to have_no_field('user[device_response]') - end + assign(:user, user) end context 'user has otp active' do