Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									faedfbb473
								
							
						
					
					
						commit
						e8a7b1cd3f
					
				|  | @ -1 +1 @@ | ||||||
| ac315ef254b265af5323387a1df1abb5d57daa95 | 3e9cc60933881ce8c33c379c72dad3907755aac2 | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ | ||||||
|   padding-right: $gutter_collapsed_width; |   padding-right: $gutter_collapsed_width; | ||||||
|   background: $white; |   background: $white; | ||||||
|   border-top: 1px solid $border-color; |   border-top: 1px solid $border-color; | ||||||
|   transition: padding $sidebar-transition-duration; |   transition: padding $gl-transition-duration-medium; | ||||||
| 
 | 
 | ||||||
|   .page-with-icon-sidebar & { |   .page-with-icon-sidebar & { | ||||||
|     padding-left: $contextual-sidebar-collapsed-width; |     padding-left: $contextual-sidebar-collapsed-width; | ||||||
|  |  | ||||||
|  | @ -204,7 +204,7 @@ | ||||||
| // | // | ||||||
| 
 | 
 | ||||||
| .page-with-contextual-sidebar { | .page-with-contextual-sidebar { | ||||||
|   transition: padding-left $sidebar-transition-duration; |   transition: padding-left $gl-transition-duration-medium; | ||||||
| 
 | 
 | ||||||
|   @include media-breakpoint-up(md) { |   @include media-breakpoint-up(md) { | ||||||
|     padding-left: $contextual-sidebar-collapsed-width; |     padding-left: $contextual-sidebar-collapsed-width; | ||||||
|  | @ -233,7 +233,7 @@ | ||||||
|   @include gl-fixed; |   @include gl-fixed; | ||||||
|   @include gl-bottom-0; |   @include gl-bottom-0; | ||||||
|   @include gl-left-0; |   @include gl-left-0; | ||||||
|   transition: width $sidebar-transition-duration, left $sidebar-transition-duration; |   transition: width $gl-transition-duration-medium, left $gl-transition-duration-medium; | ||||||
|   z-index: 600; |   z-index: 600; | ||||||
|   width: $contextual-sidebar-width; |   width: $contextual-sidebar-width; | ||||||
|   top: $header-height; |   top: $header-height; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 
 | 
 | ||||||
|   > a, |   > a, | ||||||
|   > button { |   > button { | ||||||
|     transition: padding $sidebar-transition-duration; |     transition: padding $gl-transition-duration-medium; | ||||||
|     font-weight: $gl-font-weight-bold; |     font-weight: $gl-font-weight-bold; | ||||||
|     display: flex; |     display: flex; | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  |  | ||||||
|  | @ -478,7 +478,7 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @mixin side-panel-toggle { | @mixin side-panel-toggle { | ||||||
|   transition: width $sidebar-transition-duration; |   transition: width $gl-transition-duration-medium; | ||||||
|   height: $toggle-sidebar-height; |   height: $toggle-sidebar-height; | ||||||
|   padding: 0 $gl-padding; |   padding: 0 $gl-padding; | ||||||
|   background-color: $gray-light; |   background-color: $gray-light; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .page-initialised  .content-wrapper { | .page-initialised  .content-wrapper { | ||||||
|   transition: padding $sidebar-transition-duration; |   transition: padding $gl-transition-duration-medium; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .right-sidebar-collapsed { | .right-sidebar-collapsed { | ||||||
|  | @ -109,7 +109,7 @@ | ||||||
|   @include maintain-sidebar-dimensions; |   @include maintain-sidebar-dimensions; | ||||||
|   width: 0; |   width: 0; | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   transition: width $sidebar-transition-duration; |   transition: width $gl-transition-duration-medium; | ||||||
| 
 | 
 | ||||||
|   &.right-sidebar-expanded { |   &.right-sidebar-expanded { | ||||||
|     @include maintain-sidebar-dimensions; |     @include maintain-sidebar-dimensions; | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ $grid-size: 8px; | ||||||
| $gutter-collapsed-width: 62px; | $gutter-collapsed-width: 62px; | ||||||
| $gutter-width: 290px; | $gutter-width: 290px; | ||||||
| $gutter-inner-width: 250px; | $gutter-inner-width: 250px; | ||||||
| $sidebar-transition-duration: 0.3s; |  | ||||||
| $sidebar-breakpoint: 1024px; | $sidebar-breakpoint: 1024px; | ||||||
| $default-transition-duration: 0.15s; | $default-transition-duration: 0.15s; | ||||||
| $contextual-sidebar-width: 256px; | $contextual-sidebar-width: 256px; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| .fade-leave-active, | .fade-leave-active, | ||||||
| .fade-in-enter-active, | .fade-in-enter-active, | ||||||
| .fade-out-leave-active { | .fade-out-leave-active { | ||||||
|   transition: opacity $sidebar-transition-duration $general-hover-transition-curve; |   transition: opacity $gl-transition-duration-medium $general-hover-transition-curve; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .fade-enter, | .fade-enter, | ||||||
|  |  | ||||||
|  | @ -35,7 +35,7 @@ | ||||||
| 
 | 
 | ||||||
| .boards-app { | .boards-app { | ||||||
|   @include media-breakpoint-up(sm) { |   @include media-breakpoint-up(sm) { | ||||||
|     transition: width $sidebar-transition-duration; |     transition: width $gl-transition-duration-medium; | ||||||
|     width: 100%; |     width: 100%; | ||||||
| 
 | 
 | ||||||
|     &.is-compact { |     &.is-compact { | ||||||
|  | @ -349,7 +349,7 @@ | ||||||
| .right-sidebar.right-sidebar-expanded { | .right-sidebar.right-sidebar-expanded { | ||||||
|   &.boards-sidebar-slide-enter-active, |   &.boards-sidebar-slide-enter-active, | ||||||
|   &.boards-sidebar-slide-leave-active { |   &.boards-sidebar-slide-leave-active { | ||||||
|     transition: width $sidebar-transition-duration, padding $sidebar-transition-duration; |     transition: width $gl-transition-duration-medium, padding $gl-transition-duration-medium; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   &.boards-sidebar-slide-enter, |   &.boards-sidebar-slide-enter, | ||||||
|  |  | ||||||
|  | @ -132,7 +132,7 @@ | ||||||
|   // stylelint-disable-next-line length-zero-no-unit |   // stylelint-disable-next-line length-zero-no-unit | ||||||
|   bottom: var(--review-bar-height, 0px); |   bottom: var(--review-bar-height, 0px); | ||||||
|   right: 0; |   right: 0; | ||||||
|   transition: width $sidebar-transition-duration; |   transition: width $gl-transition-duration-medium; | ||||||
|   background-color: $white; |   background-color: $white; | ||||||
|   z-index: 200; |   z-index: 200; | ||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController | ||||||
|   # the number of failed sign in attempts |   # the number of failed sign in attempts | ||||||
|   def failure |   def failure | ||||||
|     if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name) |     if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name) | ||||||
|       user = User.by_login(params[:username]) |       user = User.find_by_login(params[:username]) | ||||||
| 
 | 
 | ||||||
|       user&.increment_failed_attempts! |       user&.increment_failed_attempts! | ||||||
|       log_failed_login(params[:username], failed_strategy.name) |       log_failed_login(params[:username], failed_strategy.name) | ||||||
|  |  | ||||||
|  | @ -215,11 +215,11 @@ class SessionsController < Devise::SessionsController | ||||||
|   def find_user |   def find_user | ||||||
|     strong_memoize(:find_user) do |     strong_memoize(:find_user) do | ||||||
|       if session[:otp_user_id] && user_params[:login] |       if session[:otp_user_id] && user_params[:login] | ||||||
|         User.by_id_and_login(session[:otp_user_id], user_params[:login]).first |         User.by_login(user_params[:login]).find_by_id(session[:otp_user_id]) | ||||||
|       elsif session[:otp_user_id] |       elsif session[:otp_user_id] | ||||||
|         User.find(session[:otp_user_id]) |         User.find(session[:otp_user_id]) | ||||||
|       elsif user_params[:login] |       elsif user_params[:login] | ||||||
|         User.by_login(user_params[:login]) |         User.find_by_login(user_params[:login]) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -448,6 +448,11 @@ class User < ApplicationRecord | ||||||
|   scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) } |   scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) } | ||||||
|   scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) } |   scope :by_username, -> (usernames) { iwhere(username: Array(usernames).map(&:to_s)) } | ||||||
|   scope :by_name, -> (names) { iwhere(name: Array(names)) } |   scope :by_name, -> (names) { iwhere(name: Array(names)) } | ||||||
|  |   scope :by_login, -> (login) do | ||||||
|  |     return none if login.blank? | ||||||
|  | 
 | ||||||
|  |     login.include?('@') ? iwhere(email: login) : iwhere(username: login) | ||||||
|  |   end | ||||||
|   scope :by_user_email, -> (emails) { iwhere(email: Array(emails)) } |   scope :by_user_email, -> (emails) { iwhere(email: Array(emails)) } | ||||||
|   scope :by_emails, -> (emails) { joins(:emails).where(emails: { email: Array(emails).map(&:downcase) }) } |   scope :by_emails, -> (emails) { joins(:emails).where(emails: { email: Array(emails).map(&:downcase) }) } | ||||||
|   scope :for_todos, -> (todos) { where(id: todos.select(:user_id).distinct) } |   scope :for_todos, -> (todos) { where(id: todos.select(:user_id).distinct) } | ||||||
|  | @ -482,7 +487,6 @@ class User < ApplicationRecord | ||||||
|   scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) } |   scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) } | ||||||
|   scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) } |   scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) } | ||||||
|   scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) } |   scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) } | ||||||
|   scope :by_id_and_login, ->(id, login) { where(id: id).where('username = LOWER(:login) OR email = LOWER(:login)', login: login) } |  | ||||||
|   scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) } |   scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) } | ||||||
|   scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) } |   scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) } | ||||||
|   scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) } |   scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) } | ||||||
|  | @ -765,14 +769,8 @@ class User < ApplicationRecord | ||||||
|       true |       true | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def by_login(login) |     def find_by_login(login) | ||||||
|       return unless login |       by_login(login).take | ||||||
| 
 |  | ||||||
|       if login.include?('@') |  | ||||||
|         unscoped.iwhere(email: login).take |  | ||||||
|       else |  | ||||||
|         unscoped.iwhere(username: login).take |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def find_by_username(username) |     def find_by_username(username) | ||||||
|  |  | ||||||
|  | @ -57,6 +57,8 @@ verification methods: | ||||||
| | Blobs    | Pipeline artifacts _(object storage)_           | Geo with API/Managed (*2*)            | _Not implemented_      | | | Blobs    | Pipeline artifacts _(object storage)_           | Geo with API/Managed (*2*)            | _Not implemented_      | | ||||||
| | Blobs    | Pages _(file system)_                           | Geo with API                          | SHA256 checksum        | | | Blobs    | Pages _(file system)_                           | Geo with API                          | SHA256 checksum        | | ||||||
| | Blobs    | Pages _(object storage)_                        | Geo with API/Managed (*2*)            | _Not implemented_      | | | Blobs    | Pages _(object storage)_                        | Geo with API/Managed (*2*)            | _Not implemented_      | | ||||||
|  | | Blobs    | CI Secure Files _(file system)_                 | Geo with API                          | SHA256 checksum        | | ||||||
|  | | Blobs    | CI Secure Files _(object storage)_              | Geo with API/Managed (*2*)            | _Not implemented_      | | ||||||
| 
 | 
 | ||||||
| - (*1*): Redis replication can be used as part of HA with Redis sentinel. It's not used between Geo sites. | - (*1*): Redis replication can be used as part of HA with Redis sentinel. It's not used between Geo sites. | ||||||
| - (*2*): Object storage replication can be performed by Geo or by your object storage provider/appliance | - (*2*): Object storage replication can be performed by Geo or by your object storage provider/appliance | ||||||
|  | @ -194,6 +196,7 @@ successfully, you must replicate their data using some other means. | ||||||
| |[Project snippets](../../../user/snippets.md)                                                                  | **Yes** (10.2)                                                          | **Yes** (10.2)                                                             | N/A                                                                 | N/A                                                             |       | | |[Project snippets](../../../user/snippets.md)                                                                  | **Yes** (10.2)                                                          | **Yes** (10.2)                                                             | N/A                                                                 | N/A                                                             |       | | ||||||
| |[CI job artifacts](../../../ci/pipelines/job_artifacts.md)                                                     | **Yes** (10.4)                                                          | **Yes** (14.10)                                                            | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_job_artifact_replication`, enabled by default in 14.10. | | |[CI job artifacts](../../../ci/pipelines/job_artifacts.md)                                                     | **Yes** (10.4)                                                          | **Yes** (14.10)                                                            | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_job_artifact_replication`, enabled by default in 14.10. | | ||||||
| |[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464)    | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Persists additional artifacts after a pipeline completes. | | |[CI Pipeline Artifacts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/pipeline_artifact.rb) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464) | [**Yes** (13.11)](https://gitlab.com/gitlab-org/gitlab/-/issues/238464)    | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Persists additional artifacts after a pipeline completes. | | ||||||
|  | |[CI Secure Files](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/models/ci/secure_file.rb) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [**Yes** (15.3)](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91430) | [No](object_storage.md#verification-of-files-in-object-storage) | Verification is behind the feature flag `geo_ci_secure_file_replication`, enabled by default in 15.3. | | ||||||
| |[Container Registry](../../packages/container_registry.md)                                                     | **Yes** (12.3)                                                          | No                                                                         | No                                                                  | No                                                              | Disabled by default. See [instructions](container_registry.md) to enable. | | |[Container Registry](../../packages/container_registry.md)                                                     | **Yes** (12.3)                                                          | No                                                                         | No                                                                  | No                                                              | Disabled by default. See [instructions](container_registry.md) to enable. | | ||||||
| |[Infrastructure Registry](../../../user/packages/infrastructure_registry/index.md)                             | **Yes** (14.0)                                                          | **Yes** (14.0)                                                             | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. | | |[Infrastructure Registry](../../../user/packages/infrastructure_registry/index.md)                             | **Yes** (14.0)                                                          | **Yes** (14.0)                                                             | [**Yes** (15.1)](https://gitlab.com/groups/gitlab-org/-/epics/5551) | [No](object_storage.md#verification-of-files-in-object-storage) | Behind feature flag `geo_package_file_replication`, enabled by default. | | ||||||
| |[Project designs repository](../../../user/project/issues/design_management.md)                                | **Yes** (12.7)                                                          | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467)                  | N/A                                                                 | N/A                                                             | Designs also require replication of LFS objects and Uploads. | | |[Project designs repository](../../../user/project/issues/design_management.md)                                | **Yes** (12.7)                                                          | [No](https://gitlab.com/gitlab-org/gitlab/-/issues/32467)                  | N/A                                                                 | N/A                                                             | Designs also require replication of LFS objects and Uploads. | | ||||||
|  |  | ||||||
|  | @ -11891,7 +11891,7 @@ Represents an external issue. | ||||||
| 
 | 
 | ||||||
| ##### `GeoNode.ciSecureFileRegistries` | ##### `GeoNode.ciSecureFileRegistries` | ||||||
| 
 | 
 | ||||||
| Find Ci Secure File registries on this Geo node Available only when feature flag `geo_ci_secure_file_replication` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. | Find Ci Secure File registries on this Geo node. | ||||||
| 
 | 
 | ||||||
| Returns [`CiSecureFileRegistryConnection`](#cisecurefileregistryconnection). | Returns [`CiSecureFileRegistryConnection`](#cisecurefileregistryconnection). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -189,11 +189,14 @@ and are protected at the same time. | ||||||
| 
 | 
 | ||||||
| ### Configure group-level memberships | ### Configure group-level memberships | ||||||
| 
 | 
 | ||||||
|  | > - Operators are required to have Owner+ role from the original Maintainer+ role and this role change is introduced from GitLab 15.3 [with a flag](https://gitlab.com/gitlab-org/gitlab/-/issues/369873) named `group_level_protected_environment_settings_permission`. Disabled by default. | ||||||
|  | > - Original behavior where Operators are required to have Maintainer+ role can be achieved by enabling [flag](https://gitlab.com/gitlab-org/gitlab/-/issues/369875) named `override_group_level_protected_environment_settings_permission`. Disabled by default. | ||||||
|  | 
 | ||||||
| To maximize the effectiveness of group-level protected environments, | To maximize the effectiveness of group-level protected environments, | ||||||
| [group-level memberships](../../user/group/index.md) must be correctly | [group-level memberships](../../user/group/index.md) must be correctly | ||||||
| configured: | configured: | ||||||
| 
 | 
 | ||||||
| - Operators should be given at least the Maintainer role | - Operators should be given at least the Owner role | ||||||
|   for the top-level group. They can maintain CI/CD configurations for |   for the top-level group. They can maintain CI/CD configurations for | ||||||
|   the higher environments (such as production) in the group-level settings page, |   the higher environments (such as production) in the group-level settings page, | ||||||
|   which includes group-level protected environments, |   which includes group-level protected environments, | ||||||
|  | @ -203,7 +206,7 @@ configured: | ||||||
|   This ensures that only operators can configure the organization-wide |   This ensures that only operators can configure the organization-wide | ||||||
|   deployment ruleset. |   deployment ruleset. | ||||||
| - Developers should be given no more than the Developer role | - Developers should be given no more than the Developer role | ||||||
|   for the top-level group, or explicitly given the Maintainer role for a child project |   for the top-level group, or explicitly given the Owner role for a child project | ||||||
|   They do *not* have access to the CI/CD configurations in the |   They do *not* have access to the CI/CD configurations in the | ||||||
|   top-level group, so operators can ensure that the critical configuration won't |   top-level group, so operators can ensure that the critical configuration won't | ||||||
|   be accidentally changed by the developers. |   be accidentally changed by the developers. | ||||||
|  |  | ||||||
|  | @ -62,6 +62,9 @@ Team members' domain expertise can be viewed on the [engineering projects](https | ||||||
| 
 | 
 | ||||||
| ### Reviewer roulette | ### Reviewer roulette | ||||||
| 
 | 
 | ||||||
|  | NOTE: | ||||||
|  | Reviewer roulette is an internal tool for use on GitLab.com, and not available for use on customer installations. | ||||||
|  | 
 | ||||||
| The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for | The [Danger bot](dangerbot.md) randomly picks a reviewer and a maintainer for | ||||||
| each area of the codebase that your merge request seems to touch. It only makes | each area of the codebase that your merge request seems to touch. It only makes | ||||||
| **recommendations** and you should override it if you think someone else is a better | **recommendations** and you should override it if you think someone else is a better | ||||||
|  | @ -487,7 +490,7 @@ Before taking the decision to merge: | ||||||
| - If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test. | - If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test. | ||||||
| 
 | 
 | ||||||
| If a merge request is fundamentally ready, but needs only trivial fixes (such as | If a merge request is fundamentally ready, but needs only trivial fixes (such as | ||||||
| typos), consider demonstrating a [bias for action](https://about.gitlab.com/handbook/values/#bias-for-action)  | typos), consider demonstrating a [bias for action](https://about.gitlab.com/handbook/values/#bias-for-action) | ||||||
| by making those changes directly without going back to the author. You can do this by | by making those changes directly without going back to the author. You can do this by | ||||||
| using the [suggest changes](../user/project/merge_requests/reviews/suggestions.md) feature to apply | using the [suggest changes](../user/project/merge_requests/reviews/suggestions.md) feature to apply | ||||||
| your own suggestions to the merge request. Note that: | your own suggestions to the merge request. Note that: | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ variables: | ||||||
|   SAST_IMAGE_SUFFIX: '-fips' |   SAST_IMAGE_SUFFIX: '-fips' | ||||||
| 
 | 
 | ||||||
| include: | include: | ||||||
|   - template: Security/SAST-IaC.latest.gitlab-ci.yml |   - template: Jobs/SAST-IaC.gitlab-ci.yml | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### Making IaC analyzers available to all GitLab tiers | ### Making IaC analyzers available to all GitLab tiers | ||||||
|  | @ -98,11 +98,11 @@ To configure IaC Scanning for a project you can: | ||||||
| ### Configure IaC Scanning manually | ### Configure IaC Scanning manually | ||||||
| 
 | 
 | ||||||
| To enable IaC Scanning you must [include](../../../ci/yaml/index.md#includetemplate) the | To enable IaC Scanning you must [include](../../../ci/yaml/index.md#includetemplate) the | ||||||
| [`SAST-IaC.latest.gitlab-ci.yml template`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/SAST-IaC.latest.gitlab-ci.yml) provided as part of your GitLab installation. Here is an example of how to include it: | [`SAST-IaC.gitlab-ci.yml template`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml) provided as part of your GitLab installation. Here is an example of how to include it: | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| include: | include: | ||||||
|   - template: Security/SAST-IaC.latest.gitlab-ci.yml |   - template: Jobs/SAST-IaC.gitlab-ci.yml | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| The included template creates IaC scanning jobs in your CI/CD pipeline and scans | The included template creates IaC scanning jobs in your CI/CD pipeline and scans | ||||||
|  |  | ||||||
|  | @ -92,7 +92,7 @@ module Gitlab | ||||||
|         return unless authenticate_using_internal_or_ldap_password? |         return unless authenticate_using_internal_or_ldap_password? | ||||||
| 
 | 
 | ||||||
|         Gitlab::Auth::UniqueIpsLimiter.limit_user! do |         Gitlab::Auth::UniqueIpsLimiter.limit_user! do | ||||||
|           user = User.by_login(login) |           user = User.find_by_login(login) | ||||||
| 
 | 
 | ||||||
|           break if user && !user.can_log_in_with_non_expired_password? |           break if user && !user.can_log_in_with_non_expired_password? | ||||||
| 
 | 
 | ||||||
|  | @ -279,7 +279,7 @@ module Gitlab | ||||||
|           if deploy_key_matches |           if deploy_key_matches | ||||||
|             DeployKey.find(deploy_key_matches[1]) |             DeployKey.find(deploy_key_matches[1]) | ||||||
|           else |           else | ||||||
|             User.by_login(login) |             User.find_by_login(login) | ||||||
|           end |           end | ||||||
| 
 | 
 | ||||||
|         return unless actor |         return unless actor | ||||||
|  |  | ||||||
|  | @ -103,7 +103,7 @@ module Gitlab | ||||||
|         return unless has_basic_credentials?(current_request) |         return unless has_basic_credentials?(current_request) | ||||||
| 
 | 
 | ||||||
|         login, token = user_name_and_password(current_request) |         login, token = user_name_and_password(current_request) | ||||||
|         user = User.by_login(login) |         user = User.find_by_login(login) | ||||||
| 
 | 
 | ||||||
|         user if user && Gitlab::LfsToken.new(user).token_valid?(token) |         user if user && Gitlab::LfsToken.new(user).token_valid?(token) | ||||||
|       end |       end | ||||||
|  |  | ||||||
|  | @ -57,9 +57,9 @@ describe('Clusters', () => { | ||||||
|       it('should show the creating container', () => { |       it('should show the creating container', () => { | ||||||
|         cluster.updateContainer(null, 'creating'); |         cluster.updateContainer(null, 'creating'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(false); | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(window.location.reload).not.toHaveBeenCalled(); |         expect(window.location.reload).not.toHaveBeenCalled(); | ||||||
|       }); |       }); | ||||||
| 
 | 
 | ||||||
|  | @ -67,9 +67,9 @@ describe('Clusters', () => { | ||||||
|         cluster.updateContainer(null, 'creating'); |         cluster.updateContainer(null, 'creating'); | ||||||
|         cluster.updateContainer('creating', 'creating'); |         cluster.updateContainer('creating', 'creating'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeFalsy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(false); | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(window.location.reload).not.toHaveBeenCalled(); |         expect(window.location.reload).not.toHaveBeenCalled(); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|  | @ -80,9 +80,9 @@ describe('Clusters', () => { | ||||||
|         cluster.updateContainer(null, 'creating'); |         cluster.updateContainer(null, 'creating'); | ||||||
|         cluster.updateContainer('creating', 'created'); |         cluster.updateContainer('creating', 'created'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(window.location.reload).toHaveBeenCalled(); |         expect(window.location.reload).toHaveBeenCalled(); | ||||||
|         expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(true); |         expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(true); | ||||||
|       }); |       }); | ||||||
|  | @ -94,9 +94,9 @@ describe('Clusters', () => { | ||||||
|         cluster.updateContainer(null, 'created'); |         cluster.updateContainer(null, 'created'); | ||||||
|         cluster.updateContainer('created', 'created'); |         cluster.updateContainer('created', 'created'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeFalsy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(false); | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(window.location.reload).not.toHaveBeenCalled(); |         expect(window.location.reload).not.toHaveBeenCalled(); | ||||||
|         expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(false); |         expect(cluster.setClusterNewlyCreated).toHaveBeenCalledWith(false); | ||||||
|       }); |       }); | ||||||
|  | @ -108,9 +108,9 @@ describe('Clusters', () => { | ||||||
|         cluster.updateContainer(null, 'created'); |         cluster.updateContainer(null, 'created'); | ||||||
|         cluster.updateContainer('created', 'created'); |         cluster.updateContainer('created', 'created'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(true); | ||||||
|         expect(window.location.reload).not.toHaveBeenCalled(); |         expect(window.location.reload).not.toHaveBeenCalled(); | ||||||
|         expect(cluster.setClusterNewlyCreated).not.toHaveBeenCalled(); |         expect(cluster.setClusterNewlyCreated).not.toHaveBeenCalled(); | ||||||
|       }); |       }); | ||||||
|  | @ -120,11 +120,11 @@ describe('Clusters', () => { | ||||||
|       it('should show the error container', () => { |       it('should show the error container', () => { | ||||||
|         cluster.updateContainer(null, 'errored', 'this is an error'); |         cluster.updateContainer(null, 'errored', 'this is an error'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(false); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); |         expect(cluster.errorReasonContainer.textContent).toContain('this is an error'); | ||||||
|       }); |       }); | ||||||
|  | @ -132,11 +132,11 @@ describe('Clusters', () => { | ||||||
|       it('should show `error` banner when previously `creating`', () => { |       it('should show `error` banner when previously `creating`', () => { | ||||||
|         cluster.updateContainer('creating', 'errored'); |         cluster.updateContainer('creating', 'errored'); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.creatingContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.creatingContainer.classList.contains('hidden')).toBe(true); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.successContainer.classList.contains('hidden')).toBeTruthy(); |         expect(cluster.successContainer.classList.contains('hidden')).toBe(true); | ||||||
| 
 | 
 | ||||||
|         expect(cluster.errorContainer.classList.contains('hidden')).toBeFalsy(); |         expect(cluster.errorContainer.classList.contains('hidden')).toBe(false); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1083,20 +1083,6 @@ RSpec.describe User do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     describe '.by_id_and_login' do |  | ||||||
|       let_it_be(:user) { create(:user) } |  | ||||||
| 
 |  | ||||||
|       it 'finds a user regardless of case' do |  | ||||||
|         expect(described_class.by_id_and_login(user.id, user.username.upcase)) |  | ||||||
|           .to contain_exactly(user) |  | ||||||
|       end |  | ||||||
| 
 |  | ||||||
|       it 'finds a user when login is an email address regardless of case' do |  | ||||||
|         expect(described_class.by_id_and_login(user.id, user.email.upcase)) |  | ||||||
|           .to contain_exactly(user) |  | ||||||
|       end |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     describe '.for_todos' do |     describe '.for_todos' do | ||||||
|       let_it_be(:user1) { create(:user) } |       let_it_be(:user1) { create(:user) } | ||||||
|       let_it_be(:user2) { create(:user) } |       let_it_be(:user2) { create(:user) } | ||||||
|  | @ -3003,17 +2989,53 @@ RSpec.describe User do | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   describe '.by_login' do |   shared_examples "find user by login" do | ||||||
|     let(:username) { 'John' } |     let_it_be(:user) { create(:user) } | ||||||
|     let!(:user) { create(:user, username: username) } |     let_it_be(:invalid_login) { "#{user.username}-NOT-EXISTS" } | ||||||
| 
 | 
 | ||||||
|     it 'gets the correct user' do |     context 'when login is nil or empty' do | ||||||
|       expect(described_class.by_login(user.email.upcase)).to eq user |       it 'returns nil' do | ||||||
|       expect(described_class.by_login(user.email)).to eq user |         expect(login_method(nil)).to be_nil | ||||||
|       expect(described_class.by_login(username.downcase)).to eq user |         expect(login_method('')).to be_nil | ||||||
|       expect(described_class.by_login(username)).to eq user |       end | ||||||
|       expect(described_class.by_login(nil)).to be_nil |     end | ||||||
|       expect(described_class.by_login('')).to be_nil | 
 | ||||||
|  |     context 'when login is invalid' do | ||||||
|  |       it 'returns nil' do | ||||||
|  |         expect(login_method(invalid_login)).to be_nil | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when login is username' do | ||||||
|  |       it 'returns user' do | ||||||
|  |         expect(login_method(user.username)).to eq(user) | ||||||
|  |         expect(login_method(user.username.downcase)).to eq(user) | ||||||
|  |         expect(login_method(user.username.upcase)).to eq(user) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     context 'when login is email' do | ||||||
|  |       it 'returns user' do | ||||||
|  |         expect(login_method(user.email)).to eq(user) | ||||||
|  |         expect(login_method(user.email.downcase)).to eq(user) | ||||||
|  |         expect(login_method(user.email.upcase)).to eq(user) | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '.by_login' do | ||||||
|  |     it_behaves_like "find user by login" do | ||||||
|  |       def login_method(login) | ||||||
|  |         described_class.by_login(login).take | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   describe '.find_by_login' do | ||||||
|  |     it_behaves_like "find user by login" do | ||||||
|  |       def login_method(login) | ||||||
|  |         described_class.find_by_login(login) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue