diff options
51 files changed, 343 insertions, 157 deletions
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index aab8705f5cb..4247446365c 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -4,6 +4,8 @@ class Admin::IntegrationsController < Admin::ApplicationController include IntegrationsActions include ServicesHelper + before_action :not_found, unless: -> { instance_level_integrations? } + feature_category :integrations private @@ -12,10 +14,6 @@ class Admin::IntegrationsController < Admin::ApplicationController Service.find_or_initialize_non_project_specific_integration(name, instance: true) end - def integrations_enabled? - instance_level_integrations? - end - def scoped_edit_integration_path(integration) edit_admin_application_settings_integration_path(integration) end diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index 86968b99ce6..f2861052e08 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -6,7 +6,6 @@ module IntegrationsActions included do include ServiceParams - before_action :not_found, unless: :integrations_enabled? before_action :integration, only: [:edit, :update, :test] end @@ -51,10 +50,6 @@ module IntegrationsActions private - def integrations_enabled? - false - end - def integration # Using instance variable `@service` still required as it's used in ServiceParams. # Should be removed once that is refactored to use `@integration`. diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index a66372b3571..8903feaff04 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -25,10 +25,6 @@ module Groups Service.find_or_initialize_non_project_specific_integration(name, group_id: group.id) end - def integrations_enabled? - Feature.enabled?(:group_level_integrations, group, default_enabled: true) - end - def scoped_edit_integration_path(integration) edit_group_settings_integration_path(group, integration) end diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index b7e11d7cbb6..43bc535d1b0 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -122,10 +122,6 @@ module ServicesHelper false end - def group_level_integrations? - @group.present? && Feature.enabled?(:group_level_integrations, @group, default_enabled: true) - end - def instance_level_integrations? !Gitlab.com? end diff --git a/app/services/admin/propagate_integration_service.rb b/app/services/admin/propagate_integration_service.rb index ddd5add42bd..253c3a84fef 100644 --- a/app/services/admin/propagate_integration_service.rb +++ b/app/services/admin/propagate_integration_service.rb @@ -7,7 +7,7 @@ module Admin def propagate if integration.instance? update_inherited_integrations - create_integration_for_groups_without_integration if Feature.enabled?(:group_level_integrations, default_enabled: true) + create_integration_for_groups_without_integration create_integration_for_projects_without_integration else update_inherited_descendant_integrations diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 016c31cbccc..52600f5b88f 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -34,7 +34,7 @@ module Groups if @group.save @group.add_owner(current_user) @group.create_namespace_settings - Service.create_from_active_default_integrations(@group, :group_id) if Feature.enabled?(:group_level_integrations, default_enabled: true) + Service.create_from_active_default_integrations(@group, :group_id) end end diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 5f4b1f8ad45..efe8e57cadf 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -159,11 +159,10 @@ %span = _('General') - - if group_level_integrations? - = nav_link(controller: :integrations) do - = link_to group_settings_integrations_path(@group), title: _('Integrations') do - %span - = _('Integrations') + = nav_link(controller: :integrations) do + = link_to group_settings_integrations_path(@group), title: _('Integrations') do + %span + = _('Integrations') = nav_link(path: 'groups#projects') do = link_to projects_group_path(@group), title: _('Projects') do diff --git a/config/feature_flags/development/geo_snippet_repository_replication.yml b/config/feature_flags/development/geo_snippet_repository_replication.yml index 69aa7beef55..5815c9a1f6d 100644 --- a/config/feature_flags/development/geo_snippet_repository_replication.yml +++ b/config/feature_flags/development/geo_snippet_repository_replication.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/224168 milestone: '13.4' type: development group: group::geo -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/group_level_integrations.yml b/config/feature_flags/development/group_level_integrations.yml deleted file mode 100644 index 2be5207e099..00000000000 --- a/config/feature_flags/development/group_level_integrations.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: group_level_integrations -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27557 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/238575 -milestone: '12.10' -type: development -group: group::ecosystem -default_enabled: true diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt index 25a552de172..571100ca24b 100644 --- a/doc/.vale/gitlab/spelling-exceptions.txt +++ b/doc/.vale/gitlab/spelling-exceptions.txt @@ -8,7 +8,6 @@ allowlists anonymized Ansible Anthos -API approvers architected Artifactory @@ -63,7 +62,6 @@ bundlers burndown burnup cacheable -CAS CentOS Certbot chai @@ -234,7 +232,6 @@ kubectl Kubernetes Kubesec Laravel -LDAP ldapsearch Lefthook Leiningen @@ -299,7 +296,6 @@ namespacing namespacings Nanoc Netlify -NGINX Nokogiri npm Nurtch @@ -407,7 +403,6 @@ reusability reverified reverifies reverify -RHEL rollout rollouts rsync @@ -424,7 +419,6 @@ runit runtime runtimes Salesforce -SAML sandboxing sanitization sbt @@ -447,7 +441,6 @@ Slack Slony smartcard smartcards -SMTP Sobelow Solarized Sourcegraph @@ -456,7 +449,6 @@ sparklines spidering Splunk SpotBugs -SSH Stackdriver storable storages diff --git a/doc/administration/logs.md b/doc/administration/logs.md index d4876adb0d0..c0f5a38b0fe 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -776,7 +776,7 @@ are generated: > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15442) in GitLab 12.3. -Contains details of GitLab's [Database Load Balancing](database_load_balancing.md). +Contains details of GitLab [Database Load Balancing](database_load_balancing.md). It's stored at: - `/var/log/gitlab/gitlab-rails/database_load_balancing.log` for Omnibus GitLab packages. diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md index 56bd9691e12..68dbe9f3cf9 100644 --- a/doc/administration/monitoring/index.md +++ b/doc/administration/monitoring/index.md @@ -13,7 +13,7 @@ Explore our features to monitor your GitLab instance: take action on alerts. - [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance. - [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products. -- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics. +- [GitHub imports](github_imports.md): Monitor the health and progress of the GitHub importer with various Prometheus metrics. - [Monitoring uptime](../../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint. - [IP whitelists](ip_whitelist.md): Configure GitLab for monitoring endpoints that provide health check information when probed. - [`nginx_status`](https://docs.gitlab.com/omnibus/settings/nginx.html#enablingdisabling-nginx_status): Monitor your NGINX server status diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 8692fdbbd5a..c5e0570515d 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -310,7 +310,7 @@ instance (`cache`, `shared_state` etc.). ## Metrics shared directory -GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services. +The GitLab Prometheus client requires a directory to store metrics data shared between multi-process services. Those files are shared among all instances running under Unicorn server. The directory must be accessible to all running Unicorn's processes, or metrics can't function correctly. diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md index 0531c000f06..17115f65b90 100644 --- a/doc/api/epic_issues.md +++ b/doc/api/epic_issues.md @@ -8,11 +8,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w Every API call to epic_issues must be authenticated. -If a user is not a member of a group and the group is private, a `GET` request on that group will -result in a `404` status code. +If a user is not a member of a group and the group is private, a `GET` request on that group +results in a `404` status code. Epics are available only in GitLab [Premium and higher](https://about.gitlab.com/pricing/). -If the Epics feature is not available, a `403` status code will be returned. +If the Epics feature is not available, a `403` status code is returned. ## List issues for an epic diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 0b6bbbe9ba7..435f9e2289d 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -8959,8 +8959,7 @@ type GeoNode { selectiveSyncType: String """ - Find snippet repository registries on this Geo node. Available only when - feature flag `geo_snippet_repository_replication` is enabled + Find snippet repository registries on this Geo node """ snippetRepositoryRegistries( """ @@ -24607,6 +24606,11 @@ Represents a vulnerability """ type Vulnerability implements Noteable { """ + Timestamp of when the vulnerability state was changed to confirmed + """ + confirmedAt: Time + + """ Description of the vulnerability """ description: String diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index de392e3f2c0..36ac6a2daf4 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -25082,7 +25082,7 @@ }, { "name": "snippetRepositoryRegistries", - "description": "Find snippet repository registries on this Geo node. Available only when feature flag `geo_snippet_repository_replication` is enabled", + "description": "Find snippet repository registries on this Geo node", "args": [ { "name": "ids", @@ -71716,6 +71716,20 @@ "description": "Represents a vulnerability", "fields": [ { + "name": "confirmedAt", + "description": "Timestamp of when the vulnerability state was changed to confirmed", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Time", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "description", "description": "Description of the vulnerability", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 0204b00301d..ec93a52b482 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1513,7 +1513,7 @@ Autogenerated return type of EpicTreeReorder. | `selectiveSyncNamespaces` | NamespaceConnection | The namespaces that should be synced, if `selective_sync_type` == `namespaces` | | `selectiveSyncShards` | String! => Array | The repository storages whose projects should be synced, if `selective_sync_type` == `shards` | | `selectiveSyncType` | String | Indicates if syncing is limited to only specific groups, or shards | -| `snippetRepositoryRegistries` | SnippetRepositoryRegistryConnection | Find snippet repository registries on this Geo node. Available only when feature flag `geo_snippet_repository_replication` is enabled | +| `snippetRepositoryRegistries` | SnippetRepositoryRegistryConnection | Find snippet repository registries on this Geo node | | `syncObjectStorage` | Boolean | Indicates if this secondary node will replicate blobs in Object Storage | | `terraformStateVersionRegistries` | TerraformStateVersionRegistryConnection | Find terraform state version registries on this Geo node | | `url` | String | The user-facing URL for this Geo node | @@ -3716,6 +3716,7 @@ Represents a vulnerability. | Field | Type | Description | | ----- | ---- | ----------- | +| `confirmedAt` | Time | Timestamp of when the vulnerability state was changed to confirmed | | `description` | String | Description of the vulnerability | | `detectedAt` | Time! | Timestamp of when the vulnerability was first detected | | `discussions` | DiscussionConnection! | All discussions on this noteable | diff --git a/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png b/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png Binary files differindex 59276bda727..d9b8e71e6cb 100644 --- a/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png +++ b/doc/ci/pipelines/img/ci_efficiency_pipeline_health_grafana_dashboard.png diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md index a78c9e1b26b..de78b8558c4 100644 --- a/doc/ci/pipelines/pipeline_efficiency.md +++ b/doc/ci/pipelines/pipeline_efficiency.md @@ -105,10 +105,10 @@ External monitoring tools can poll the API and verify pipeline health or collect metrics for long term SLA analytics. For example, the [GitLab CI Pipelines Exporter](https://github.com/mvisonneau/gitlab-ci-pipelines-exporter) -for Prometheus fetches metrics from the API. It can check branches in projects automatically +for Prometheus fetches metrics from the API and pipeline events. It can check branches in projects automatically and get the pipeline status and duration. In combination with a Grafana dashboard, this helps build an actionable view for your operations team. Metric graphs can also -be embedded into incidents making problem resolving easier. +be embedded into incidents making problem resolving easier. Additionally, it can also export metrics about jobs and environments. ![Grafana Dashboard for GitLab CI Pipelines Prometheus Exporter](img/ci_efficiency_pipeline_health_grafana_dashboard.png) diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md index 6eb4514730f..9228609aae9 100644 --- a/doc/development/distributed_tracing.md +++ b/doc/development/distributed_tracing.md @@ -49,7 +49,7 @@ all subsystems at GitLab: - Correlation IDs should never be used to pass context (for example, a username or an IP address). - Correlation IDs should never be _parsed_, or manipulated in other ways (for example, split). -The [LabKit library](https://gitlab.com/gitlab-org/labkit) provides a standardized interface for working with GitLab's +The [LabKit library](https://gitlab.com/gitlab-org/labkit) provides a standardized interface for working with GitLab correlation IDs in the Go programming language. LabKit can be used as a reference implementation for developers working with tracing and correlation IDs on non-Go GitLab subsystems. diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 401168179f0..10f945dab69 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -7,7 +7,7 @@ description: Learn how to contribute to GitLab Documentation. # GitLab Documentation guidelines -GitLab's documentation is [intended as the single source of truth (SSOT)](https://about.gitlab.com/handbook/documentation/) for information about how to configure, use, and troubleshoot GitLab. The documentation contains use cases and usage instructions for every GitLab feature, organized by product area and subject. This includes topics and workflows that span multiple GitLab features, and the use of GitLab with other applications. +The GitLab documentation is [intended as the single source of truth (SSOT)](https://about.gitlab.com/handbook/documentation/) for information about how to configure, use, and troubleshoot GitLab. The documentation contains use cases and usage instructions for every GitLab feature, organized by product area and subject. This includes topics and workflows that span multiple GitLab features, and the use of GitLab with other applications. In addition to this page, the following resources can help you craft and contribute to documentation: @@ -230,7 +230,7 @@ Things to note: and prints the file and the line where this file is mentioned. You may ask why the two greps. Since [we use relative paths to link to documentation](styleguide/index.md#links), sometimes it might be useful to search a path deeper. -- The `*.md` extension is not used when a document is linked to GitLab's +- The `*.md` extension is not used when a document is linked to the GitLab built-in help page, which is why we omit it in `git grep`. - Use the checklist on the "Change documentation location" MR description template. diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md index 4856675b2a2..35cd55b199c 100644 --- a/doc/development/experiment_guide/index.md +++ b/doc/development/experiment_guide/index.md @@ -25,7 +25,7 @@ In either case, an outcome of the experiment should be posted to the issue with Experiments' code quality can fail our standards for several reasons. These reasons can include not being added to the codebase for a long time, or because of fast iteration to retrieve data. However, having the experiment run (or not -run) shouldn't impact GitLab's availability. To avoid or identify issues, +run) shouldn't impact GitLab availability. To avoid or identify issues, experiments are initially deployed to a small number of users. Regardless, experiments still need tests. @@ -326,11 +326,11 @@ For visibility, please also share any commands run against production in the `#s ### Manually force the current user to be in the experiment group -You may force the application to put your current user in the experiment group. To do so -add a query string parameter to the path where the experiment runs. If you do so, +You may force the application to put your current user in the experiment group. To do so +add a query string parameter to the path where the experiment runs. If you do so, the experiment will work only for this request and won't work after following links or submitting forms. -For example, to forcibly enable the `EXPERIMENT_KEY` experiment, add `force_experiment=EXPERIMENT_KEY` +For example, to forcibly enable the `EXPERIMENT_KEY` experiment, add `force_experiment=EXPERIMENT_KEY` to the URL: ```shell diff --git a/doc/development/product_analytics/usage_ping.md b/doc/development/product_analytics/usage_ping.md index c43051ece05..37363bbabbc 100644 --- a/doc/development/product_analytics/usage_ping.md +++ b/doc/development/product_analytics/usage_ping.md @@ -619,7 +619,7 @@ alt_usage_data(999) ### Prometheus Queries In those cases where operational metrics should be part of Usage Ping, a database or Redis query is unlikely -to provide useful data. Instead, Prometheus might be more appropriate, since most of GitLab's architectural +to provide useful data. Instead, Prometheus might be more appropriate, since most GitLab architectural components publish metrics to it that can be queried back, aggregated, and included as usage data. NOTE: diff --git a/doc/operations/index.md b/doc/operations/index.md index 4d2eb059656..1d738768531 100644 --- a/doc/operations/index.md +++ b/doc/operations/index.md @@ -81,7 +81,7 @@ infrastructure. GitLab stores and executes your infrastructure as code, whether it's defined in Ansible, Puppet or Chef. We also offer native integration with [Terraform](https://www.terraform.io/), uniting your GitOps and -Infrastructure-as-Code (IaC) workflows with GitLab's authentication, authorization, +Infrastructure-as-Code (IaC) workflows with the GitLab authentication, authorization, and user interface. By lowering the barrier to entry for adopting Terraform, you can manage and provision infrastructure through machine-readable definition files, rather than physical hardware configuration or interactive configuration tools. diff --git a/doc/operations/metrics/embed_grafana.md b/doc/operations/metrics/embed_grafana.md index 5625b1d0fbd..21950354ae9 100644 --- a/doc/operations/metrics/embed_grafana.md +++ b/doc/operations/metrics/embed_grafana.md @@ -23,7 +23,7 @@ For this embed to display correctly, the Copy the link and add an image tag as [inline HTML](../../user/markdown.md#inline-html) in your Markdown. You can tweak the query parameters to meet your needs, such as removing the `&from=` and `&to=` parameters to display a live chart. Here is example -markup for a live chart from GitLab's public dashboard: +markup for a live chart from a GitLab public dashboard: ```html <img src="https://dashboards.gitlab.com/d/RZmbBr7mk/gitlab-triage?orgId=1&refresh=30s&var-env=gprd&var-environment=gprd&var-prometheus=prometheus-01-inf-gprd&var-prometheus_app=prometheus-app-01-inf-gprd&var-backend=All&var-type=All&var-stage=main&from=1580444107655&to=1580465707655"/> diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md index 74d77cc32cf..0b60f16cfb5 100644 --- a/doc/security/two_factor_authentication.md +++ b/doc/security/two_factor_authentication.md @@ -109,3 +109,43 @@ questions that you know someone might ask. Each scenario can be a third-level heading, e.g. `### Getting error message X`. If you have none to add when creating a doc, leave this section in place but commented out to help encourage others to add to it in the future. --> + +## Two-factor Authentication (2FA) for Git over SSH operations + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/270554) in GitLab 13.7. +> - It's [deployed behind a feature flag](<replace with path to>/user/feature_flags.md), disabled by default. +> - It's disabled on GitLab.com. +> - It's not recommended for production use. +> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-two-factor-authentication-2fa-for-git-operations). + +WARNING: +This feature might not be available to you. Check the **version history** note above for details. + +Two-factor authentication can be enforced for Git over SSH operations. The OTP +verification can be done via a GitLab Shell command: + +```shell +ssh git@<hostname> 2fa_verify +``` + +Once the OTP is verified, Git over SSH operations can be used for 15 minutes +with the associated SSH key. + +### Enable or disable Two-factor Authentication (2FA) for Git operations + +Two-factor Authentication (2FA) for Git operations is under development and not +ready for production use. It is deployed behind a feature flag that is +**disabled by default**. [GitLab administrators with access to the GitLab Rails console](<replace with path to>/administration/feature_flags.md) +can enable it. + +To enable it: + +```ruby +Feature.enable(:two_factor_for_cli) +``` + +To disable it: + +```ruby +Feature.disable(:two_factor_for_cli) +``` diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md index a306e9a9947..7fffbf2adff 100644 --- a/doc/topics/offline/quick_start_guide.md +++ b/doc/topics/offline/quick_start_guide.md @@ -26,7 +26,7 @@ to the server's IP address. This better ensures a stable target for our certs' C and makes long-term resolution simpler. ```shell -sudo EXTERNAL_URL="http://my-host.internal" install gitlab-ee +sudo EXTERNAL_URL="http://my-host.internal" apt-get install gitlab-ee ``` ## Enabling SSL diff --git a/doc/user/award_emojis.md b/doc/user/award_emojis.md index 6173e3e0392..4ece94447bc 100644 --- a/doc/user/award_emojis.md +++ b/doc/user/award_emojis.md @@ -4,7 +4,7 @@ group: Project Management info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Award emoji +# Award emoji **(CORE)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/1825) in GitLab 8.2. > - GitLab 9.0 [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/9570) the usage of native emoji if the platform @@ -46,7 +46,7 @@ celebrate an accomplishment or agree with an opinion. To: - Add an award emoji, click the smile in the top right of the comment and pick an emoji from the dropdown. -- Remove an award emoji, click the emoji again and the vote will be removed. +- Remove an award emoji, click the emoji again. ![Picking an emoji for a comment](img/award_emoji_comment_picker.png) diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 9a1dbe1b6a7..9a861cef3d3 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -149,12 +149,11 @@ Shared runners provided by GitLab are **not** configurable. Consider [installing ### Linux shared runners -Linux shared runners on GitLab.com run in [autoscale mode](https://docs.gitlab.com/runner/configuration/autoscale.html) and are powered by Google Cloud Platform. -Autoscaling means reduced waiting times to spin up CI/CD jobs, and isolated VMs for each project, -thus maximizing security. They're free to use for public open source projects and limited -to 400 CI minutes per month per group for private projects. More minutes -[can be purchased](../../subscriptions/gitlab_com/index.md#purchase-additional-ci-minutes), if -needed. Read about all [GitLab.com plans](https://about.gitlab.com/pricing/). +Linux shared runners on GitLab.com run in autoscale mode and are powered by Google Cloud Platform. + +Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each project, thus maximizing security. These shared runners are available for users and customers on GitLab.com. + +GitLab offers Gold tier capabilities and included CI/CD minutes per group per month for our [Open Source](https://about.gitlab.com/solutions/open-source/join/), [Education](https://about.gitlab.com/solutions/education/), and [Startups](https://about.gitlab.com/solutions/startups/) programs. For private projects, GitLab offers various [plans](https://about.gitlab.com/pricing/), starting with a Free tier. All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS and the latest Docker Engine installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index 0ba81692bf0..80db1c960db 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -66,7 +66,7 @@ See [Adding and removing Kubernetes clusters](add_remove_clusters.md) for detail to: - Create a cluster in Google Cloud Platform (GCP) or Amazon Elastic Kubernetes Service - (EKS) using GitLab's UI. + (EKS) using the GitLab UI. - Add an integration to an existing cluster from any Kubernetes platform. ### Multiple Kubernetes clusters @@ -237,7 +237,7 @@ A Kubernetes cluster can be the destination for a deployment job. If [deployment variables](#deployment-variables) are made available to your job and configuration is not required. You can immediately begin interacting with the cluster from your jobs using tools such as `kubectl` or `helm`. -- You don't use GitLab's cluster integration you can still deploy to your +- You don't use the GitLab cluster integration, you can still deploy to your cluster. However, you must configure Kubernetes tools yourself using [environment variables](../../../ci/variables/README.md#custom-environment-variables) before you can interact with the cluster from your jobs. @@ -318,7 +318,7 @@ the need to leave GitLab. #### Deploy Boards -GitLab's Deploy Boards offer a consolidated view of the current health and +GitLab Deploy Boards offer a consolidated view of the current health and status of each CI [environment](../../../ci/environments/index.md) running on Kubernetes, displaying the status of the pods in the deployment. Developers and other teammates can view the progress and status of a rollout, pod by pod, in the diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md index 3102be225bb..f7542ec78f7 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md @@ -27,7 +27,7 @@ NGINX Ingress versions prior to 0.16.0 offer an included [VTS Prometheus metrics ## Configuring NGINX Ingress monitoring -If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors it](#about-managed-nginx-ingress-deployments). +If you have deployed NGINX Ingress using the GitLab [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors it](#about-managed-nginx-ingress-deployments). For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation: diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md index 25565ae1ada..c855e564753 100644 --- a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md +++ b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md @@ -27,7 +27,7 @@ GitLab has support for automatically detecting and monitoring the Kubernetes NGI ## Configuring NGINX Ingress monitoring -If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors](#about-managed-nginx-ingress-deployments) it. +If you have deployed NGINX Ingress using the GitLab [Kubernetes cluster integration](../../clusters/index.md#installing-applications), Prometheus [automatically monitors](#about-managed-nginx-ingress-deployments) it. For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation: diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb index 61ef1d5bde0..332f2f1986f 100644 --- a/lib/api/internal/base.rb +++ b/lib/api/internal/base.rb @@ -300,7 +300,7 @@ module API post '/two_factor_otp_check', feature_category: :authentication_and_authorization do status 200 - break { success: false } unless Feature.enabled?(:two_factor_for_cli) + break { success: false, message: 'Feature flag is disabled' } unless Feature.enabled?(:two_factor_for_cli) actor.update_last_used_at! user = actor.user @@ -316,6 +316,8 @@ module API otp_validation_result = ::Users::ValidateOtpService.new(user).execute(params.fetch(:otp_attempt)) if otp_validation_result[:status] == :success + ::Gitlab::Auth::Otp::SessionEnforcer.new(actor.key).update_session + { success: true } else { success: false, message: 'Invalid OTP' } diff --git a/lib/gitlab/auth/otp/session_enforcer.rb b/lib/gitlab/auth/otp/session_enforcer.rb new file mode 100644 index 00000000000..8cc280756cc --- /dev/null +++ b/lib/gitlab/auth/otp/session_enforcer.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Gitlab + module Auth + module Otp + class SessionEnforcer + OTP_SESSIONS_NAMESPACE = 'session:otp' + DEFAULT_EXPIRATION = 15.minutes.to_i + + def initialize(key) + @key = key + end + + def update_session + Gitlab::Redis::SharedState.with do |redis| + redis.setex(key_name, DEFAULT_EXPIRATION, true) + end + end + + def access_restricted? + Gitlab::Redis::SharedState.with do |redis| + !redis.get(key_name) + end + end + + private + + attr_reader :key + + def key_name + @key_name ||= "#{OTP_SESSIONS_NAMESPACE}:#{key.id}" + end + end + end + end +end diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index 5216643b351..e0b145f69aa 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -77,6 +77,7 @@ module Gitlab check_authentication_abilities! check_command_disabled! check_command_existence! + check_otp_session! custom_action = check_custom_action return custom_action if custom_action @@ -254,6 +255,31 @@ module Gitlab end end + def check_otp_session! + return unless ssh? + return if !key? || deploy_key? + return unless Feature.enabled?(:two_factor_for_cli) + return unless user.two_factor_enabled? + + if ::Gitlab::Auth::Otp::SessionEnforcer.new(actor).access_restricted? + message = "OTP verification is required to access the repository.\n\n"\ + " Use: #{build_ssh_otp_verify_command}" + + raise ForbiddenError, message + end + end + + def build_ssh_otp_verify_command + user = "#{Gitlab.config.gitlab_shell.ssh_user}@" unless Gitlab.config.gitlab_shell.ssh_user.empty? + user_host = "#{user}#{Gitlab.config.gitlab_shell.ssh_host}" + + if Gitlab.config.gitlab_shell.ssh_port != 22 + "ssh #{user_host} -p #{Gitlab.config.gitlab_shell.ssh_port} 2fa_verify" + else + "ssh #{user_host} 2fa_verify" + end + end + def check_db_accessibility! return unless receive_pack? @@ -399,6 +425,10 @@ module Gitlab protocol == 'http' end + def ssh? + protocol == 'ssh' + end + def upload_pack? cmd == 'git-upload-pack' end diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb index ff23907c081..999f352e834 100644 --- a/qa/qa/flow/pipeline.rb +++ b/qa/qa/flow/pipeline.rb @@ -6,12 +6,17 @@ module QA module_function # In some cases we don't need to wait for anything, blocked, running or pending is acceptable - # Some cases only need pipeline to finish with different condition (completion, success or replication) + # Some cases only we do need pipeline to finish with expected condition (completed, succeeded or replicated) def visit_latest_pipeline(pipeline_condition: nil) Page::Project::Menu.perform(&:click_ci_cd_pipelines) Page::Project::Pipeline::Index.perform(&:"wait_for_latest_pipeline_#{pipeline_condition}") if pipeline_condition Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) end + + def wait_for_latest_pipeline(pipeline_condition:) + Page::Project::Menu.perform(&:click_ci_cd_pipelines) + Page::Project::Pipeline::Index.perform(&:"wait_for_latest_pipeline_#{pipeline_condition}") + end end end end diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb index 3bf2a80f32f..0f5a7e8c801 100644 --- a/qa/qa/page/project/pipeline/index.rb +++ b/qa/qa/page/project/pipeline/index.rb @@ -22,11 +22,11 @@ module QA all_elements(:pipeline_url_link, minimum: 1, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME).first.click end - def wait_for_latest_pipeline_success + def wait_for_latest_pipeline_succeeded wait_for_latest_pipeline_status { has_text?('passed') } end - def wait_for_latest_pipeline_completion + def wait_for_latest_pipeline_completed wait_for_latest_pipeline_status { has_text?('passed') || has_text?('failed') } end diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb index b3967fc24c8..c2ea568dbad 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb @@ -34,7 +34,7 @@ module QA add_included_files add_main_ci_file project.visit! - view_the_last_pipeline + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded') end after do @@ -78,12 +78,6 @@ module QA end end - def view_the_last_pipeline - Page::Project::Menu.perform(&:click_ci_cd_pipelines) - Page::Project::Pipeline::Index.perform(&:wait_for_latest_pipeline_success) - Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) - end - def main_ci_file { file_path: '.gitlab-ci.yml', diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb index 8512e34d758..519777b32d9 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb @@ -41,7 +41,7 @@ module QA add_ci_file(downstream_project, downstream_ci_file) add_ci_file(upstream_project, upstream_ci_file) upstream_project.visit! - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded') end after do diff --git a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb index e621cfe239c..c89cda73711 100644 --- a/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb +++ b/qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb @@ -25,7 +25,7 @@ module QA Flow::Login.sign_in add_ci_files project.visit! - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'success') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'succeeded') end after do diff --git a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb index 49a52d25748..be6f3b17ccd 100644 --- a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb @@ -81,8 +81,7 @@ module QA end project.visit! - Page::Project::Menu.perform(&:click_ci_cd_pipelines) - Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) + Flow::Pipeline.visit_latest_pipeline Page::Project::Pipeline::Show.perform do |pipeline| pipeline.click_job('publish') diff --git a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb index f28d8a16893..9a0d832de09 100644 --- a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb +++ b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb @@ -67,8 +67,7 @@ module QA end project.visit! - Page::Project::Menu.perform(&:click_ci_cd_pipelines) - Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline) + Flow::Pipeline.visit_latest_pipeline Page::Project::Pipeline::Show.perform do |pipeline| pipeline.click_job('upload') diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb index ec26e338b28..9ec05fcf5d3 100644 --- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb +++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_dependent_relationship_spec.rb @@ -27,7 +27,7 @@ module QA it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/751' do add_ci_files(success_child_ci_file) - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed') Page::Project::Pipeline::Show.perform do |parent_pipeline| expect(parent_pipeline).to have_child_pipeline @@ -37,7 +37,7 @@ module QA it 'parent pipeline fails if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/752' do add_ci_files(fail_child_ci_file) - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed') Page::Project::Pipeline::Show.perform do |parent_pipeline| expect(parent_pipeline).to have_child_pipeline diff --git a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb index d7f5a326b0e..ed8c8baae0e 100644 --- a/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb +++ b/qa/qa/specs/features/browser_ui/6_release/pipeline/parent_child_pipelines_independent_relationship_spec.rb @@ -27,7 +27,7 @@ module QA it 'parent pipelines passes if child passes', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/754' do add_ci_files(success_child_ci_file) - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed') Page::Project::Pipeline::Show.perform do |parent_pipeline| expect(parent_pipeline).to have_child_pipeline @@ -37,7 +37,7 @@ module QA it 'parent pipeline passes even if child fails', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/753' do add_ci_files(fail_child_ci_file) - Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completion') + Flow::Pipeline.visit_latest_pipeline(pipeline_condition: 'completed') Page::Project::Pipeline::Show.perform do |parent_pipeline| expect(parent_pipeline).to have_child_pipeline diff --git a/spec/controllers/groups/settings/integrations_controller_spec.rb b/spec/controllers/groups/settings/integrations_controller_spec.rb index beb2ad3afec..4887d68a243 100644 --- a/spec/controllers/groups/settings/integrations_controller_spec.rb +++ b/spec/controllers/groups/settings/integrations_controller_spec.rb @@ -24,16 +24,6 @@ RSpec.describe Groups::Settings::IntegrationsController do group.add_owner(user) end - context 'when group_level_integrations not enabled' do - it 'returns not_found' do - stub_feature_flags(group_level_integrations: false) - - get :index, params: { group_id: group } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - it 'successfully displays the template' do get :index, params: { group_id: group } @@ -57,16 +47,6 @@ RSpec.describe Groups::Settings::IntegrationsController do group.add_owner(user) end - context 'when group_level_integrations not enabled' do - it 'returns not_found' do - stub_feature_flags(group_level_integrations: false) - - get :edit, params: { group_id: group, id: Service.available_services_names(include_project_specific: false).sample } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - Service.available_services_names(include_project_specific: false).each do |integration_name| context "#{integration_name}" do it 'successfully displays the template' do diff --git a/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap b/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap index 0336f4c0325..1e08394dd56 100644 --- a/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap +++ b/spec/frontend/vue_shared/components/security_reports/__snapshots__/security_summary_spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Severity Summary given the message {"countMessage": "%{criticalStart}0 Critical%{criticalEnd} %{highStart}1 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 0, "high": 1, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = ` +exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}0 Critical%{criticalEnd} %{highStart}1 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 0, "high": 1, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = ` <span> Security scanning detected <strong> @@ -43,7 +43,7 @@ exports[`Severity Summary given the message {"countMessage": "%{criticalStart}0 </span> `; -exports[`Severity Summary given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}0 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 0, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = ` +exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}0 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 0, "message": "Security scanning detected %{totalStart}1%{totalEnd} potential vulnerability", "other": 0, "status": "", "total": 1} interpolates correctly 1`] = ` <span> Security scanning detected <strong> @@ -86,7 +86,7 @@ exports[`Severity Summary given the message {"countMessage": "%{criticalStart}1 </span> `; -exports[`Severity Summary given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}2 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 2, "message": "Security scanning detected %{totalStart}3%{totalEnd} potential vulnerabilities", "other": 0, "status": "", "total": 3} interpolates correctly 1`] = ` +exports[`SecuritySummary component given the message {"countMessage": "%{criticalStart}1 Critical%{criticalEnd} %{highStart}2 High%{highEnd} and %{otherStart}0 Others%{otherEnd}", "critical": 1, "high": 2, "message": "Security scanning detected %{totalStart}3%{totalEnd} potential vulnerabilities", "other": 0, "status": "", "total": 3} interpolates correctly 1`] = ` <span> Security scanning detected <strong> @@ -129,14 +129,14 @@ exports[`Severity Summary given the message {"countMessage": "%{criticalStart}1 </span> `; -exports[`Severity Summary given the message {"message": ""} interpolates correctly 1`] = ` +exports[`SecuritySummary component given the message {"message": ""} interpolates correctly 1`] = ` <span> <!----> </span> `; -exports[`Severity Summary given the message {"message": "foo"} interpolates correctly 1`] = ` +exports[`SecuritySummary component given the message {"message": "foo"} interpolates correctly 1`] = ` <span> foo <!----> diff --git a/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js b/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js index 4217fef9fbf..e57152c3cbf 100644 --- a/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js +++ b/spec/frontend/vue_shared/components/security_reports/security_summary_spec.js @@ -3,7 +3,7 @@ import { shallowMount } from '@vue/test-utils'; import SecuritySummary from '~/vue_shared/security_reports/components/security_summary.vue'; import { groupedTextBuilder } from '~/vue_shared/security_reports/store/utils'; -describe('Severity Summary', () => { +describe('SecuritySummary component', () => { let wrapper; const createWrapper = message => { diff --git a/spec/helpers/services_helper_spec.rb b/spec/helpers/services_helper_spec.rb index 29c83b7c4da..ae3a4301fd8 100644 --- a/spec/helpers/services_helper_spec.rb +++ b/spec/helpers/services_helper_spec.rb @@ -28,38 +28,6 @@ RSpec.describe ServicesHelper do end end - describe '#group_level_integrations?' do - subject { helper.group_level_integrations? } - - context 'when no group is present' do - it { is_expected.to eq(false) } - end - - context 'when group is present' do - let(:group) { build_stubbed(:group) } - - before do - assign(:group, group) - end - - context 'when `group_level_integrations` is not enabled' do - it 'returns false' do - stub_feature_flags(group_level_integrations: false) - - is_expected.to eq(false) - end - end - - context 'when `group_level_integrations` is enabled for the group' do - it 'returns true' do - stub_feature_flags(group_level_integrations: group) - - is_expected.to eq(true) - end - end - end - end - describe '#scoped_reset_integration_path' do let(:integration) { build_stubbed(:jira_service) } let(:group) { nil } diff --git a/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb b/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb new file mode 100644 index 00000000000..928aade4008 --- /dev/null +++ b/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Auth::Otp::SessionEnforcer, :clean_gitlab_redis_shared_state do + let_it_be(:key) { create(:key)} + + describe '#update_session' do + it 'registers a session in Redis' do + redis = double(:redis) + expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis) + + expect(redis).to( + receive(:setex) + .with("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", + described_class::DEFAULT_EXPIRATION, + true) + .once) + + described_class.new(key).update_session + end + end + + describe '#access_restricted?' do + subject { described_class.new(key).access_restricted? } + + context 'with existing session' do + before do + Gitlab::Redis::SharedState.with do |redis| + redis.set("#{described_class::OTP_SESSIONS_NAMESPACE}:#{key.id}", true ) + end + end + + it { is_expected.to be_falsey } + end + + context 'without an existing session' do + it { is_expected.to be_truthy } + end + end +end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index cd465a92fec..780f4329bcc 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -387,6 +387,108 @@ RSpec.describe Gitlab::GitAccess do end end + describe '#check_otp_session!' do + let_it_be(:user) { create(:user, :two_factor_via_otp)} + let_it_be(:key) { create(:key, user: user) } + let_it_be(:actor) { key } + + before do + project.add_developer(user) + stub_feature_flags(two_factor_for_cli: true) + end + + context 'with an OTP session', :clean_gitlab_redis_shared_state do + before do + Gitlab::Redis::SharedState.with do |redis| + redis.set("#{Gitlab::Auth::Otp::SessionEnforcer::OTP_SESSIONS_NAMESPACE}:#{key.id}", true) + end + end + + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end + end + end + + context 'without OTP session' do + it 'does not allow push or pull access' do + user = 'jane.doe' + host = 'fridge.ssh' + port = 42 + + stub_config( + gitlab_shell: { + ssh_user: user, + ssh_host: host, + ssh_port: port + } + ) + + error_message = "OTP verification is required to access the repository.\n\n"\ + " Use: ssh #{user}@#{host} -p #{port} 2fa_verify" + + aggregate_failures do + expect { push_access_check }.to raise_forbidden(error_message) + expect { pull_access_check }.to raise_forbidden(error_message) + end + end + + context 'when protocol is HTTP' do + let(:protocol) { 'http' } + + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end + end + end + + context 'when actor is not an SSH key' do + let(:deploy_key) { create(:deploy_key, user: user) } + let(:actor) { deploy_key } + + before do + deploy_key.deploy_keys_projects.create(project: project, can_push: true) + end + + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end + end + end + + context 'when 2FA is not enabled for the user' do + let(:user) { create(:user)} + let(:actor) { create(:key, user: user) } + + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end + end + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(two_factor_for_cli: false) + end + + it 'allows push and pull access' do + aggregate_failures do + expect { push_access_check }.not_to raise_error + expect { pull_access_check }.not_to raise_error + end + end + end + end + end + describe '#check_db_accessibility!' do context 'when in a read-only GitLab instance' do before do diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb index c079ee3da32..a540c85d4c7 100644 --- a/spec/requests/api/internal/base_spec.rb +++ b/spec/requests/api/internal/base_spec.rb @@ -1336,9 +1336,13 @@ RSpec.describe API::Internal::Base do end context 'when the OTP is valid' do - it 'returns success' do + it 'registers a new OTP session and returns success' do allow_any_instance_of(Users::ValidateOtpService).to receive(:execute).with(otp).and_return(status: :success) + expect_next_instance_of(::Gitlab::Auth::Otp::SessionEnforcer) do |session_enforcer| + expect(session_enforcer).to receive(:update_session).once + end + subject expect(json_response['success']).to be_truthy |