diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index ba6d6e1407b..94c51267d77 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -2124,7 +2124,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 47d83c46c06..8022d8e9086 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -965,7 +965,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index 60365bb7ee0..f60630f5856 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -2072,7 +2072,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Supported modifications for lower user counts (HA)
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 913fde9d528..8c51f4a71a4 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -2138,7 +2138,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 071a90a4c00..6377f6ae62d 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -2066,7 +2066,7 @@ unavailable from GitLab 15.0. No further enhancements are planned for this featu
Read:
-- The [Gitaly and NFS deprecation notice](../gitaly/index.md#nfs-deprecation-notice).
+- [Gitaly and NFS Deprecation](../nfs.md#gitaly-and-nfs-deprecation).
- About the [correct mount options to use](../nfs.md#upgrade-to-gitaly-cluster-or-disable-caching-if-experiencing-data-loss).
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index 6d328739f3e..ed50d0e7263 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -174,4 +174,4 @@ information.
## Move repositories
To move a repository to a different repository storage (for example, from `default` to `storage2`), use the
-same process as [migrating to Gitaly Cluster](gitaly/index.md#migrate-to-gitaly-cluster).
+same process as [migrating to Gitaly Cluster](gitaly/index.md#migrating-to-gitaly-cluster).
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index 8706b1e7e76..7dc3fd1db21 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -42,6 +42,7 @@ The following API resources are available in the project context:
| [Events](events.md) | `/projects/:id/events` (also available for users and standalone) |
| [Feature Flags](feature_flags.md) | `/projects/:id/feature_flags` |
| [Feature Flag User Lists](feature_flag_user_lists.md) | `/projects/:id/feature_flags_user_lists` |
+| [Integrations](integrations.md) | `/projects/:id/integrations` |
| [Invitations](invitations.md) | `/projects/:id/invitations` (also available for groups) |
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
| [Issues Statistics](issues_statistics.md) | `/projects/:id/issues_statistics` (also available for groups and standalone) |
@@ -82,7 +83,7 @@ The following API resources are available in the project context:
| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
-| [Services](services.md) | `/projects/:id/services` |
+| [Services](services.md) (renamed to [Integrations](integrations.md)) | `/projects/:id/services` |
| [Tags](tags.md) | `/projects/:id/repository/tags` |
| [User-starred metrics dashboards](metrics_user_starred_dashboards.md ) | `/projects/:id/metrics/user_starred_dashboards` |
| [Visual Review discussions](visual_review_discussions.md) **(PREMIUM)** | `/projects/:id/merge_requests/:merge_request_id/visual_review_discussions` |
diff --git a/doc/api/dependencies.md b/doc/api/dependencies.md
index c8b928ab5b2..6e9c37980ac 100644
--- a/doc/api/dependencies.md
+++ b/doc/api/dependencies.md
@@ -11,6 +11,9 @@ This API is in an alpha stage and considered unstable.
The response payload may be subject to change or breakage
across GitLab releases.
+> - Introduced in GitLab 12.1.
+> - Pagination introduced in 14.4.
+
Every call to this endpoint requires authentication. To perform this call, user should be authorized to read repository.
To see vulnerabilities in response, user should be authorized to read
[Project Security Dashboard](../user/application_security/security_dashboard/index.md#project-security-dashboard).
@@ -60,3 +63,10 @@ Example response:
}
]
```
+
+## Dependencies pagination
+
+By default, `GET` requests return 20 results at a time because the API results
+are paginated.
+
+Read more on [pagination](index.md#pagination).
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 4e69179e1a4..b180df9c6f3 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -16493,6 +16493,7 @@ Name of the feature that the callout is for.
|
`REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
|
`SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
|
`SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
+|
`SECURITY_NEWSLETTER_CALLOUT` | Callout feature name for security_newsletter_callout. |
|
`SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
|
`SUGGEST_POPOVER_DISMISSED` | Callout feature name for suggest_popover_dismissed. |
|
`TABS_POSITION_HIGHLIGHT` | Callout feature name for tabs_position_highlight. |
diff --git a/doc/api/group_repository_storage_moves.md b/doc/api/group_repository_storage_moves.md
index a893bffb1f5..9d4120ec355 100644
--- a/doc/api/group_repository_storage_moves.md
+++ b/doc/api/group_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53016) in GitLab 13.9.
Group repositories can be moved between storages. This API can help you when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster), for
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster), for
example, or to migrate a [group wiki](../user/project/wiki/index.md#group-wikis).
As group repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/integrations.md b/doc/api/integrations.md
new file mode 100644
index 00000000000..75e5047f395
--- /dev/null
+++ b/doc/api/integrations.md
@@ -0,0 +1,1473 @@
+---
+stage: Ecosystem
+group: Integrations
+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
+---
+
+# Integrations API **(FREE)**
+
+This API enables you to work with external services that integrate with GitLab.
+
+NOTE:
+In GitLab 14.4, the `services` endpoint was [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/334500) to `integrations`.
+Calls to the Integrations API can be made to both `/projects/:id/services` and `/projects/:id/integrations`.
+The examples in this document refer to the endpoint at `/projects/:id/integrations`.
+
+This API requires an access token with the [Maintainer or Owner role](../user/permissions.md).
+
+## List all active integrations
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21330) in GitLab 12.7.
+
+Get a list of all active project integrations.
+
+```plaintext
+GET /projects/:id/integrations
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 75,
+ "title": "Jenkins CI",
+ "slug": "jenkins",
+ "created_at": "2019-11-20T11:20:25.297Z",
+ "updated_at": "2019-11-20T12:24:37.498Z",
+ "active": true,
+ "commit_events": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": false,
+ "note_events": true,
+ "confidential_note_events": true,
+ "pipeline_events": true,
+ "wiki_page_events": true,
+ "job_events": true,
+ "comment_on_event_enabled": true
+ },
+ {
+ "id": 76,
+ "title": "Alerts endpoint",
+ "slug": "alerts",
+ "created_at": "2019-11-20T11:20:25.297Z",
+ "updated_at": "2019-11-20T12:24:37.498Z",
+ "active": true,
+ "commit_events": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "confidential_note_events": true,
+ "pipeline_events": true,
+ "wiki_page_events": true,
+ "job_events": true,
+ "comment_on_event_enabled": true
+ }
+]
+```
+
+## Asana
+
+Add commit messages as comments to Asana tasks.
+
+See also the [Asana integration documentation](../user/project/integrations/asana.md).
+
+### Create/Edit Asana integration
+
+Set Asana integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/asana
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_key` | string | true | User API token. User must have access to task. All comments are attributed to this user. |
+| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Asana integration
+
+Delete Asana integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/asana
+```
+
+### Get Asana integration settings
+
+Get Asana integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/asana
+```
+
+## Assembla
+
+Project Management Software (Source Commits Endpoint)
+
+### Create/Edit Assembla integration
+
+Set Assembla integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/assembla
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | The authentication token
+| `subdomain` | string | false | The subdomain setting |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Assembla integration
+
+Delete Assembla integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/assembla
+```
+
+### Get Assembla integration settings
+
+Get Assembla integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/assembla
+```
+
+## Atlassian Bamboo CI
+
+A continuous integration and build server
+
+### Create/Edit Atlassian Bamboo CI integration
+
+Set Atlassian Bamboo CI integration for a project.
+
+> You must set up automatic revision labeling and a repository trigger in Bamboo.
+
+```plaintext
+PUT /projects/:id/integrations/bamboo
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `bamboo_url` | string | true | Bamboo root URL. For example, `https://bamboo.example.com`. |
+| `build_key` | string | true | Bamboo build plan key like KEY |
+| `username` | string | true | A user with API access, if applicable |
+| `password` | string | true | Password of the user |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Atlassian Bamboo CI integration
+
+Delete Atlassian Bamboo CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/bamboo
+```
+
+### Get Atlassian Bamboo CI integration settings
+
+Get Atlassian Bamboo CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/bamboo
+```
+
+## Bugzilla
+
+Bugzilla Issue Tracker
+
+### Create/Edit Bugzilla integration
+
+Set Bugzilla integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/bugzilla
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `title` | string | false | Title |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Bugzilla integration
+
+Delete Bugzilla integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/bugzilla
+```
+
+### Get Bugzilla integration settings
+
+Get Bugzilla integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/bugzilla
+```
+
+## Buildkite
+
+Continuous integration and deployments
+
+### Create/Edit Buildkite integration
+
+Set Buildkite integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/buildkite
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Buildkite project GitLab token |
+| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
+| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Buildkite integration
+
+Delete Buildkite integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/buildkite
+```
+
+### Get Buildkite integration settings
+
+Get Buildkite integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/buildkite
+```
+
+## Campfire
+
+Send notifications about push events to Campfire chat rooms.
+[New users can no longer sign up for Campfire](https://basecamp.com/retired/campfire).
+
+### Create/Edit Campfire integration
+
+Set Campfire integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/campfire
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+|---------------|---------|----------|---------------------------------------------------------------------------------------------|
+| `token` | string | true | Campfire API token. To find it, log into Campfire and select **My info**. |
+| `subdomain` | string | false | Campfire subdomain. Text between `https://` and `.campfirenow.com` when you're logged in. |
+| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
+| `push_events` | boolean | false | Enable notifications for push events. |
+
+### Delete Campfire integration
+
+Delete Campfire integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/campfire
+```
+
+### Get Campfire integration settings
+
+Get Campfire integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/campfire
+```
+
+## Unify Circuit
+
+Unify Circuit RTC and collaboration tool.
+
+### Create/Edit Unify Circuit integration
+
+Set Unify Circuit integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/unify-circuit
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Unify Circuit webhook. For example, `https://circuit.com/rest/v2/webhooks/incoming/...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Unify Circuit integration
+
+Delete Unify Circuit integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/unify-circuit
+```
+
+### Get Unify Circuit integration settings
+
+Get Unify Circuit integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/unify-circuit
+```
+
+## Webex Teams
+
+Webex Teams collaboration tool.
+
+### Create/Edit Webex Teams integration
+
+Set Webex Teams integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/webex-teams
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Webex Teams webhook. For example, `https://api.ciscospark.com/v1/webhooks/incoming/...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Webex Teams integration
+
+Delete Webex Teams integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/webex-teams
+```
+
+### Get Webex Teams integration settings
+
+Get Webex Teams integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/webex-teams
+```
+
+## Custom Issue Tracker
+
+Custom issue tracker
+
+### Create/Edit Custom Issue Tracker integration
+
+Set Custom Issue Tracker integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/custom-issue-tracker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `title` | string | false | Title |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Custom Issue Tracker integration
+
+Delete Custom Issue Tracker integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/custom-issue-tracker
+```
+
+### Get Custom Issue Tracker integration settings
+
+Get Custom Issue Tracker integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/custom-issue-tracker
+```
+
+## Drone CI
+
+Drone is a Continuous Integration platform built on Docker, written in Go
+
+### Create/Edit Drone CI integration
+
+Set Drone CI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/drone-ci
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Drone CI project specific token |
+| `drone_url` | string | true | `http://drone.example.com` |
+| `enable_ssl_verification` | boolean | false | Enable SSL verification |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+
+### Delete Drone CI integration
+
+Delete Drone CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/drone-ci
+```
+
+### Get Drone CI integration settings
+
+Get Drone CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/drone-ci
+```
+
+## Emails on Push
+
+Email the commits and diff of each push to a list of recipients.
+
+### Create/Edit Emails on Push integration
+
+Set Emails on Push integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/emails-on-push
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | true | Emails separated by whitespace |
+| `disable_diffs` | boolean | false | Disable code diffs |
+| `send_from_committer_email` | boolean | false | Send from committer |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
+
+### Delete Emails on Push integration
+
+Delete Emails on Push integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/emails-on-push
+```
+
+### Get Emails on Push integration settings
+
+Get Emails on Push integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/emails-on-push
+```
+
+## Confluence integration
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220934) in GitLab 13.2.
+
+Replaces the link to the internal wiki with a link to a Confluence Cloud Workspace.
+
+### Create/Edit Confluence integration
+
+Set Confluence integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/confluence
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. |
+
+### Delete Confluence integration
+
+Delete Confluence integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/confluence
+```
+
+### Get Confluence integration settings
+
+Get Confluence integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/confluence
+```
+
+## External wiki
+
+Replaces the link to the internal wiki with a link to an external wiki.
+
+### Create/Edit External wiki integration
+
+Set External wiki integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/external-wiki
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `external_wiki_url` | string | true | The URL of the external wiki |
+
+### Delete External wiki integration
+
+Delete External wiki integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/external-wiki
+```
+
+### Get External wiki integration settings
+
+Get External wiki integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/external-wiki
+```
+
+## Flowdock
+
+Flowdock is a ChatOps application for collaboration in software engineering
+companies. You can send notifications from GitLab events to Flowdock flows.
+For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
+
+### Create/Edit Flowdock integration
+
+Set Flowdock integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/flowdock
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | Flowdock Git source token |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Flowdock integration
+
+Delete Flowdock integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/flowdock
+```
+
+### Get Flowdock integration settings
+
+Get Flowdock integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/flowdock
+```
+
+## GitHub **(PREMIUM)**
+
+Code collaboration software.
+
+### Create/Edit GitHub integration
+
+Set GitHub integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/github
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | GitHub API token with `repo:status` OAuth scope |
+| `repository_url` | string | true | GitHub repository URL |
+| `static_context` | boolean | false | Append instance name instead of branch to [status check name](../user/project/integrations/github.md#static--dynamic-status-check-names) |
+
+### Delete GitHub integration
+
+Delete GitHub integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/github
+```
+
+### Get GitHub integration settings
+
+Get GitHub integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/github
+```
+
+## Hangouts Chat
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20290) in GitLab 11.2.
+
+Google Workspace team collaboration tool.
+
+### Create/Edit Hangouts Chat integration
+
+Set Hangouts Chat integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/hangouts-chat
+```
+
+NOTE:
+Specific event parameters (for example, `push_events` flag) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Hangouts Chat integration
+
+Delete Hangouts Chat integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/hangouts-chat
+```
+
+### Get Hangouts Chat integration settings
+
+Get Hangouts Chat integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/hangouts-chat
+```
+
+## Irker (IRC gateway)
+
+Send IRC messages, on update, to a list of recipients through an irker gateway.
+
+For more information, see the [irker integration documentation](../user/project/integrations/irker.md).
+
+### Create/Edit Irker (IRC gateway) integration
+
+Set Irker (IRC gateway) integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/irker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | true | Recipients/channels separated by whitespaces |
+| `default_irc_uri` | string | false | `irc://irc.network.net:6697/` |
+| `server_host` | string | false | localhost |
+| `server_port` | integer | false | 6659 |
+| `colorize_messages` | boolean | false | Colorize messages |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Irker (IRC gateway) integration
+
+Delete Irker (IRC gateway) integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/irker
+```
+
+### Get Irker (IRC gateway) integration settings
+
+Get Irker (IRC gateway) integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/irker
+```
+
+## Jira
+
+Jira issue tracker.
+
+### Get Jira integration settings
+
+Get Jira integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jira
+```
+
+### Create/Edit Jira integration
+
+Set Jira integration for a project.
+
+> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
+> `project_url` are replaced by `url`. If you are using an
+> older version, [follow this documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/8-13-stable-ee/doc/api/services.md#jira).
+
+```plaintext
+PUT /projects/:id/integrations/jira
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `url` | string | yes | The URL to the Jira project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
+| `api_url` | string | no | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. |
+| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
+| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
+| `active` | boolean | no | Activates or deactivates the integration. Defaults to false (deactivated). |
+| `jira_issue_transition_automatic` | boolean | no | Enable [automatic issue transitions](../integration/jira/issues.md#automatic-issue-transitions). Takes precedence over `jira_issue_transition_id` if enabled. Defaults to `false` |
+| `jira_issue_transition_id` | string | no | The ID of one or more transitions for [custom issue transitions](../integration/jira/issues.md#custom-issue-transitions). Ignored if `jira_issue_transition_automatic` is enabled. Defaults to a blank string, which disables custom transitions. |
+| `commit_events` | boolean | false | Enable notifications for commit events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
+
+### Delete Jira integration
+
+Remove all previously Jira integrations from a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jira
+```
+
+## Slack Slash Commands
+
+Ability to receive slash commands from a Slack chat instance.
+
+### Get Slack Slash Command integration settings
+
+Get Slack Slash Command integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/slack-slash-commands
+```
+
+Example response:
+
+```json
+{
+ "id": 4,
+ "title": "Slack slash commands",
+ "slug": "slack-slash-commands",
+ "created_at": "2017-06-27T05:51:39-07:00",
+ "updated_at": "2017-06-27T05:51:39-07:00",
+ "active": true,
+ "push_events": true,
+ "issues_events": true,
+ "confidential_issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "job_events": true,
+ "pipeline_events": true,
+ "comment_on_event_enabled": false,
+ "properties": {
+ "token": "
"
+ }
+}
+```
+
+### Create/Edit Slack Slash Commands integration
+
+Set Slack Slash Command for a project.
+
+```plaintext
+PUT /projects/:id/integrations/slack-slash-commands
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | yes | The Slack token |
+
+### Delete Slack Slash Command integration
+
+Delete Slack Slash Command integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/slack-slash-commands
+```
+
+## Mattermost Slash Commands
+
+Ability to receive slash commands from a Mattermost chat instance.
+
+### Get Mattermost Slash Command integration settings
+
+Get Mattermost Slash Command integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mattermost-slash-commands
+```
+
+### Create/Edit Mattermost Slash Command integration
+
+Set Mattermost Slash Command for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mattermost-slash-commands
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | yes | The Mattermost token |
+| `username` | string | no | The username to use to post the message |
+
+### Delete Mattermost Slash Command integration
+
+Delete Mattermost Slash Command integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mattermost-slash-commands
+```
+
+## Packagist
+
+Update your project on Packagist (the main Composer repository) when commits or tags are pushed to GitLab.
+
+### Create/Edit Packagist integration
+
+Set Packagist integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/packagist
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `username` | string | yes | The username of a Packagist account |
+| `token` | string | yes | API token to the Packagist server |
+| `server` | boolean | no | URL of the Packagist server. Leave blank for default: |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+
+### Delete Packagist integration
+
+Delete Packagist integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/packagist
+```
+
+### Get Packagist integration settings
+
+Get Packagist integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/packagist
+```
+
+## Pipeline-Emails
+
+Get emails for GitLab CI/CD pipelines.
+
+### Create/Edit Pipeline-Emails integration
+
+Set Pipeline-Emails integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pipelines-email
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `recipients` | string | yes | Comma-separated list of recipient email addresses |
+| `add_pusher` | boolean | no | Add pusher to recipients list |
+| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected. The default value is "default" |
+| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+
+### Delete Pipeline-Emails integration
+
+Delete Pipeline-Emails integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pipelines-email
+```
+
+### Get Pipeline-Emails integration settings
+
+Get Pipeline-Emails integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pipelines-email
+```
+
+## Pivotal Tracker
+
+Add commit messages as comments to Pivotal Tracker stories.
+
+See also the [Pivotal Tracker integration documentation](../user/project/integrations/pivotal_tracker.md).
+
+### Create/Edit Pivotal Tracker integration
+
+Set Pivotal Tracker integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pivotaltracker
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `token` | string | true | The Pivotal Tracker token |
+| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Pivotal Tracker integration
+
+Delete Pivotal Tracker integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pivotaltracker
+```
+
+### Get Pivotal Tracker integration settings
+
+Get Pivotal Tracker integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pivotaltracker
+```
+
+## Prometheus
+
+Prometheus is a powerful time-series monitoring service.
+
+### Create/Edit Prometheus integration
+
+Set Prometheus integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/prometheus
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
+| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
+| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
+
+### Delete Prometheus integration
+
+Delete Prometheus integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/prometheus
+```
+
+### Get Prometheus integration settings
+
+Get Prometheus integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/prometheus
+```
+
+## Pushover
+
+Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.
+
+### Create/Edit Pushover integration
+
+Set Pushover integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/pushover
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `api_key` | string | true | Your application key |
+| `user_key` | string | true | Your user key |
+| `priority` | string | true | The priority |
+| `device` | string | false | Leave blank for all active devices |
+| `sound` | string | false | The sound of the notification |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Pushover integration
+
+Delete Pushover integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/pushover
+```
+
+### Get Pushover integration settings
+
+Get Pushover integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/pushover
+```
+
+## Redmine
+
+Redmine issue tracker
+
+### Create/Edit Redmine integration
+
+Set Redmine integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/redmine
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `new_issue_url` | string | true | New Issue URL |
+| `project_url` | string | true | Project URL |
+| `issues_url` | string | true | Issue URL |
+| `description` | string | false | Description |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete Redmine integration
+
+Delete Redmine integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/redmine
+```
+
+### Get Redmine integration settings
+
+Get Redmine integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/redmine
+```
+
+## Slack notifications
+
+Receive event notifications in Slack
+
+### Create/Edit Slack integration
+
+Set Slack integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/slack
+```
+
+NOTE:
+Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | `https://hooks.slack.com/services/...` |
+| `username` | string | false | username |
+| `channel` | string | false | Default channel to use if others are not configured |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `commit_events` | boolean | false | Enable notifications for commit events |
+| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `deployment_channel` | string | false | The name of the channel to receive deployment events notifications |
+| `deployment_events` | boolean | false | Enable notifications for deployment events |
+| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `job_events` | boolean | false | Enable notifications for job events |
+| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `note_channel` | string | false | The name of the channel to receive note events notifications |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `push_channel` | string | false | The name of the channel to receive push events notifications |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Slack integration
+
+Delete Slack integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/slack
+```
+
+### Get Slack integration settings
+
+Get Slack integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/slack
+```
+
+## Microsoft Teams
+
+Group Chat Software
+
+### Create/Edit Microsoft Teams integration
+
+Set Microsoft Teams integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/microsoft-teams
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+
+### Delete Microsoft Teams integration
+
+Delete Microsoft Teams integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/microsoft-teams
+```
+
+### Get Microsoft Teams integration settings
+
+Get Microsoft Teams integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/microsoft-teams
+```
+
+## Mattermost notifications
+
+Receive event notifications in Mattermost
+
+### Create/Edit Mattermost notifications integration
+
+Set Mattermost notifications integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mattermost
+```
+
+NOTE:
+Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `webhook` | string | true | The Mattermost webhook. For example, `http://mattermost_host/hooks/...` |
+| `username` | string | false | username |
+| `channel` | string | false | Default channel to use if others are not configured |
+| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
+| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
+| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
+| `push_events` | boolean | false | Enable notifications for push events |
+| `issues_events` | boolean | false | Enable notifications for issue events |
+| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events |
+| `note_events` | boolean | false | Enable notifications for note events |
+| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
+| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
+| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
+| `push_channel` | string | false | The name of the channel to receive push events notifications |
+| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
+| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
+| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
+| `note_channel` | string | false | The name of the channel to receive note events notifications |
+| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
+| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
+| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
+| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
+
+### Delete Mattermost notifications integration
+
+Delete Mattermost notifications integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mattermost
+```
+
+### Get Mattermost notifications integration settings
+
+Get Mattermost notifications integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mattermost
+```
+
+## JetBrains TeamCity CI
+
+A continuous integration and build server
+
+### Create/Edit JetBrains TeamCity CI integration
+
+Set JetBrains TeamCity CI integration for a project.
+
+> The build configuration in TeamCity must use the build format number `%build.vcs.number%`. Configure monitoring of all branches so merge requests build. That setting is in the VSC root advanced settings.
+
+```plaintext
+PUT /projects/:id/integrations/teamcity
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `teamcity_url` | string | true | TeamCity root URL. For example, `https://teamcity.example.com` |
+| `build_type` | string | true | Build configuration ID |
+| `username` | string | true | A user with permissions to trigger a manual build |
+| `password` | string | true | The password of the user |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete JetBrains TeamCity CI integration
+
+Delete JetBrains TeamCity CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/teamcity
+```
+
+### Get JetBrains TeamCity CI integration settings
+
+Get JetBrains TeamCity CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/teamcity
+```
+
+## Jenkins CI
+
+A continuous integration and build server
+
+### Create/Edit Jenkins CI integration
+
+Set Jenkins CI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/jenkins
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `jenkins_url` | string | true | Jenkins URL like `http://jenkins.example.com`. |
+| `project_name` | string | true | The URL-friendly project name. Example: `my_project_name`. |
+| `username` | string | false | Username for authentication with the Jenkins server, if authentication is required by the server. |
+| `password` | string | false | Password for authentication with the Jenkins server, if authentication is required by the server. |
+| `push_events` | boolean | false | Enable notifications for push events. |
+| `merge_requests_events` | boolean | false | Enable notifications for merge request events. |
+| `tag_push_events` | boolean | false | Enable notifications for tag push events. |
+
+### Delete Jenkins CI integration
+
+Delete Jenkins CI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jenkins
+```
+
+### Get Jenkins CI integration settings
+
+Get Jenkins CI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jenkins
+```
+
+## Jenkins CI (Deprecated) integration
+
+A continuous integration and build server
+
+NOTE:
+This integration was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1600) in GitLab 13.0.
+
+### Create/Edit Jenkins CI (Deprecated) integration
+
+Set Jenkins CI (Deprecated) integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/jenkins-deprecated
+```
+
+Parameters:
+
+- `project_url` (**required**) - Jenkins project URL like `http://jenkins.example.com/job/my-project/`
+- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
+- `pass_unstable` (optional) - Unstable builds are treated as passing
+
+### Delete Jenkins CI (Deprecated) integration
+
+Delete Jenkins CI (Deprecated) integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/jenkins-deprecated
+```
+
+### Get Jenkins CI (Deprecated) integration settings
+
+Get Jenkins CI (Deprecated) integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/jenkins-deprecated
+```
+
+## MockCI
+
+Mock an external CI. See [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service) for an example of a companion mock integration.
+
+This integration is only available when your environment is set to development.
+
+### Create/Edit MockCI integration
+
+Set MockCI integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/mock-ci
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `mock_service_url` | string | true | `http://localhost:4004` |
+
+### Delete MockCI integration
+
+Delete MockCI integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/mock-ci
+```
+
+### Get MockCI integration settings
+
+Get MockCI integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/mock-ci
+```
+
+## YouTrack
+
+YouTrack issue tracker
+
+### Create/Edit YouTrack integration
+
+Set YouTrack integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/youtrack
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `issues_url` | string | true | Issue URL |
+| `project_url` | string | true | Project URL |
+| `description` | string | false | Description |
+| `push_events` | boolean | false | Enable notifications for push events |
+
+### Delete YouTrack integration
+
+Delete YouTrack integration for a project.
+
+```plaintext
+DELETE /projects/:id/integrations/youtrack
+```
+
+### Get YouTrack integration settings
+
+Get YouTrack integration settings for a project.
+
+```plaintext
+GET /projects/:id/integrations/youtrack
+```
diff --git a/doc/api/project_repository_storage_moves.md b/doc/api/project_repository_storage_moves.md
index ebb15e1c1d6..b779a0046c6 100644
--- a/doc/api/project_repository_storage_moves.md
+++ b/doc/api/project_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31285) in GitLab 13.0.
Project repositories including wiki and design repositories can be moved between storages. This can be useful when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster),
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster),
for example.
As project repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/services.md b/doc/api/services.md
index cf6c5ec511b..7587e53c9db 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -1,1471 +1,9 @@
---
-stage: Ecosystem
-group: Integrations
-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
+redirect_to: 'integrations.md'
+remove_date: '2021-11-09'
---
-# Services API **(FREE)**
+This file was moved to [another location](integrations.md).
-NOTE:
-This API requires an access token with the [Maintainer or Owner role](../user/permissions.md).
-
-## List all active services
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21330) in GitLab 12.7.
-
-Get a list of all active project services.
-
-```plaintext
-GET /projects/:id/services
-```
-
-Example response:
-
-```json
-[
- {
- "id": 75,
- "title": "Jenkins CI",
- "slug": "jenkins",
- "created_at": "2019-11-20T11:20:25.297Z",
- "updated_at": "2019-11-20T12:24:37.498Z",
- "active": true,
- "commit_events": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": false,
- "note_events": true,
- "confidential_note_events": true,
- "pipeline_events": true,
- "wiki_page_events": true,
- "job_events": true,
- "comment_on_event_enabled": true
- },
- {
- "id": 76,
- "title": "Alerts endpoint",
- "slug": "alerts",
- "created_at": "2019-11-20T11:20:25.297Z",
- "updated_at": "2019-11-20T12:24:37.498Z",
- "active": true,
- "commit_events": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": true,
- "note_events": true,
- "confidential_note_events": true,
- "pipeline_events": true,
- "wiki_page_events": true,
- "job_events": true,
- "comment_on_event_enabled": true
- }
-]
-```
-
-## Asana
-
-Add commit messages as comments to Asana tasks.
-
-See also the [Asana service documentation](../user/project/integrations/asana.md).
-
-### Create/Edit Asana service
-
-Set Asana service for a project.
-
-```plaintext
-PUT /projects/:id/services/asana
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | User API token. User must have access to task. All comments are attributed to this user. |
-| `restrict_to_branch` | string | false | Comma-separated list of branches to be are automatically inspected. Leave blank to include all branches. |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Asana service
-
-Delete Asana service for a project.
-
-```plaintext
-DELETE /projects/:id/services/asana
-```
-
-### Get Asana service settings
-
-Get Asana service settings for a project.
-
-```plaintext
-GET /projects/:id/services/asana
-```
-
-## Assembla
-
-Project Management Software (Source Commits Endpoint)
-
-### Create/Edit Assembla service
-
-Set Assembla service for a project.
-
-```plaintext
-PUT /projects/:id/services/assembla
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | The authentication token
-| `subdomain` | string | false | The subdomain setting |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Assembla service
-
-Delete Assembla service for a project.
-
-```plaintext
-DELETE /projects/:id/services/assembla
-```
-
-### Get Assembla service settings
-
-Get Assembla service settings for a project.
-
-```plaintext
-GET /projects/:id/services/assembla
-```
-
-## Atlassian Bamboo CI
-
-A continuous integration and build server
-
-### Create/Edit Atlassian Bamboo CI service
-
-Set Atlassian Bamboo CI service for a project.
-
-> You must set up automatic revision labeling and a repository trigger in Bamboo.
-
-```plaintext
-PUT /projects/:id/services/bamboo
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `bamboo_url` | string | true | Bamboo root URL. For example, `https://bamboo.example.com`. |
-| `build_key` | string | true | Bamboo build plan key like KEY |
-| `username` | string | true | A user with API access, if applicable |
-| `password` | string | true | Password of the user |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Atlassian Bamboo CI service
-
-Delete Atlassian Bamboo CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/bamboo
-```
-
-### Get Atlassian Bamboo CI service settings
-
-Get Atlassian Bamboo CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/bamboo
-```
-
-## Bugzilla
-
-Bugzilla Issue Tracker
-
-### Create/Edit Bugzilla service
-
-Set Bugzilla service for a project.
-
-```plaintext
-PUT /projects/:id/services/bugzilla
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `title` | string | false | Title |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Bugzilla Service
-
-Delete Bugzilla service for a project.
-
-```plaintext
-DELETE /projects/:id/services/bugzilla
-```
-
-### Get Bugzilla Service Settings
-
-Get Bugzilla service settings for a project.
-
-```plaintext
-GET /projects/:id/services/bugzilla
-```
-
-## Buildkite
-
-Continuous integration and deployments
-
-### Create/Edit Buildkite service
-
-Set Buildkite service for a project.
-
-```plaintext
-PUT /projects/:id/services/buildkite
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Buildkite project GitLab token |
-| `project_url` | string | true | Pipeline URL. For example, `https://buildkite.com/example/pipeline` |
-| `enable_ssl_verification` | boolean | false | DEPRECATED: This parameter has no effect since SSL verification is always enabled |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Buildkite service
-
-Delete Buildkite service for a project.
-
-```plaintext
-DELETE /projects/:id/services/buildkite
-```
-
-### Get Buildkite service settings
-
-Get Buildkite service settings for a project.
-
-```plaintext
-GET /projects/:id/services/buildkite
-```
-
-## Campfire
-
-Send notifications about push events to Campfire chat rooms.
-[New users can no longer sign up for Campfire](https://basecamp.com/retired/campfire).
-
-### Create/Edit Campfire service
-
-Set Campfire service for a project.
-
-```plaintext
-PUT /projects/:id/services/campfire
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-|---------------|---------|----------|---------------------------------------------------------------------------------------------|
-| `token` | string | true | Campfire API token. To find it, log into Campfire and select **My info**. |
-| `subdomain` | string | false | Campfire subdomain. Text between `https://` and `.campfirenow.com` when you're logged in. |
-| `room` | string | false | Campfire room. The last part of the URL when you're in a room. |
-| `push_events` | boolean | false | Enable notifications for push events. |
-
-### Delete Campfire service
-
-Delete Campfire service for a project.
-
-```plaintext
-DELETE /projects/:id/services/campfire
-```
-
-### Get Campfire service settings
-
-Get Campfire service settings for a project.
-
-```plaintext
-GET /projects/:id/services/campfire
-```
-
-## Unify Circuit
-
-Unify Circuit RTC and collaboration tool.
-
-### Create/Edit Unify Circuit service
-
-Set Unify Circuit service for a project.
-
-```plaintext
-PUT /projects/:id/services/unify-circuit
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Unify Circuit webhook. For example, `https://circuit.com/rest/v2/webhooks/incoming/...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Unify Circuit service
-
-Delete Unify Circuit service for a project.
-
-```plaintext
-DELETE /projects/:id/services/unify-circuit
-```
-
-### Get Unify Circuit service settings
-
-Get Unify Circuit service settings for a project.
-
-```plaintext
-GET /projects/:id/services/unify-circuit
-```
-
-## Webex Teams
-
-Webex Teams collaboration tool.
-
-### Create/Edit Webex Teams service
-
-Set Webex Teams service for a project.
-
-```plaintext
-PUT /projects/:id/services/webex-teams
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Webex Teams webhook. For example, `https://api.ciscospark.com/v1/webhooks/incoming/...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Webex Teams service
-
-Delete Webex Teams service for a project.
-
-```plaintext
-DELETE /projects/:id/services/webex-teams
-```
-
-### Get Webex Teams service settings
-
-Get Webex Teams service settings for a project.
-
-```plaintext
-GET /projects/:id/services/webex-teams
-```
-
-## Custom Issue Tracker
-
-Custom issue tracker
-
-### Create/Edit Custom Issue Tracker service
-
-Set Custom Issue Tracker service for a project.
-
-```plaintext
-PUT /projects/:id/services/custom-issue-tracker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `title` | string | false | Title |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Custom Issue Tracker service
-
-Delete Custom Issue Tracker service for a project.
-
-```plaintext
-DELETE /projects/:id/services/custom-issue-tracker
-```
-
-### Get Custom Issue Tracker service settings
-
-Get Custom Issue Tracker service settings for a project.
-
-```plaintext
-GET /projects/:id/services/custom-issue-tracker
-```
-
-## Drone CI
-
-Drone is a Continuous Integration platform built on Docker, written in Go
-
-### Create/Edit Drone CI service
-
-Set Drone CI service for a project.
-
-```plaintext
-PUT /projects/:id/services/drone-ci
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Drone CI project specific token |
-| `drone_url` | string | true | `http://drone.example.com` |
-| `enable_ssl_verification` | boolean | false | Enable SSL verification |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-
-### Delete Drone CI service
-
-Delete Drone CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/drone-ci
-```
-
-### Get Drone CI service settings
-
-Get Drone CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/drone-ci
-```
-
-## Emails on push
-
-Email the commits and diff of each push to a list of recipients.
-
-### Create/Edit Emails on push service
-
-Set Emails on push service for a project.
-
-```plaintext
-PUT /projects/:id/services/emails-on-push
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | true | Emails separated by whitespace |
-| `disable_diffs` | boolean | false | Disable code diffs |
-| `send_from_committer_email` | boolean | false | Send from committer |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". Notifications are always fired for tag pushes. The default value is "all" |
-
-### Delete Emails on push service
-
-Delete Emails on push service for a project.
-
-```plaintext
-DELETE /projects/:id/services/emails-on-push
-```
-
-### Get Emails on push service settings
-
-Get Emails on push service settings for a project.
-
-```plaintext
-GET /projects/:id/services/emails-on-push
-```
-
-## Confluence service
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220934) in GitLab 13.2.
-
-Replaces the link to the internal wiki with a link to a Confluence Cloud Workspace.
-
-### Create/Edit Confluence service
-
-Set Confluence service for a project.
-
-```plaintext
-PUT /projects/:id/services/confluence
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `confluence_url` | string | true | The URL of the Confluence Cloud Workspace hosted on atlassian.net. |
-
-### Delete Confluence service
-
-Delete Confluence service for a project.
-
-```plaintext
-DELETE /projects/:id/services/confluence
-```
-
-### Get Confluence service settings
-
-Get Confluence service settings for a project.
-
-```plaintext
-GET /projects/:id/services/confluence
-```
-
-## External wiki
-
-Replaces the link to the internal wiki with a link to an external wiki.
-
-### Create/Edit External wiki service
-
-Set External wiki service for a project.
-
-```plaintext
-PUT /projects/:id/services/external-wiki
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `external_wiki_url` | string | true | The URL of the external wiki |
-
-### Delete External wiki service
-
-Delete External wiki service for a project.
-
-```plaintext
-DELETE /projects/:id/services/external-wiki
-```
-
-### Get External wiki service settings
-
-Get External wiki service settings for a project.
-
-```plaintext
-GET /projects/:id/services/external-wiki
-```
-
-## Flowdock
-
-Flowdock is a ChatOps application for collaboration in software engineering
-companies. You can send notifications from GitLab events to Flowdock flows.
-For integration instructions, see the [Flowdock documentation](https://www.flowdock.com/help/gitlab).
-
-### Create/Edit Flowdock service
-
-Set Flowdock service for a project.
-
-```plaintext
-PUT /projects/:id/services/flowdock
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | Flowdock Git source token |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Flowdock service
-
-Delete Flowdock service for a project.
-
-```plaintext
-DELETE /projects/:id/services/flowdock
-```
-
-### Get Flowdock service settings
-
-Get Flowdock service settings for a project.
-
-```plaintext
-GET /projects/:id/services/flowdock
-```
-
-## GitHub **(PREMIUM)**
-
-Code collaboration software.
-
-### Create/Edit GitHub service
-
-Set GitHub service for a project.
-
-```plaintext
-PUT /projects/:id/services/github
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | GitHub API token with `repo:status` OAuth scope |
-| `repository_url` | string | true | GitHub repository URL |
-| `static_context` | boolean | false | Append instance name instead of branch to [status check name](../user/project/integrations/github.md#static--dynamic-status-check-names) |
-
-### Delete GitHub service
-
-Delete GitHub service for a project.
-
-```plaintext
-DELETE /projects/:id/services/github
-```
-
-### Get GitHub service settings
-
-Get GitHub service settings for a project.
-
-```plaintext
-GET /projects/:id/services/github
-```
-
-## Hangouts Chat
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/20290) in GitLab 11.2.
-
-Google Workspace team collaboration tool.
-
-### Create/Edit Hangouts Chat service
-
-Set Hangouts Chat service for a project.
-
-```plaintext
-PUT /projects/:id/services/hangouts-chat
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Hangouts Chat webhook. For example, `https://chat.googleapis.com/v1/spaces...`. |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Hangouts Chat service
-
-Delete Hangouts Chat service for a project.
-
-```plaintext
-DELETE /projects/:id/services/hangouts-chat
-```
-
-### Get Hangouts Chat service settings
-
-Get Hangouts Chat service settings for a project.
-
-```plaintext
-GET /projects/:id/services/hangouts-chat
-```
-
-## irker (IRC gateway)
-
-Send IRC messages, on update, to a list of recipients through an irker gateway.
-
-For more information, see the [irker integration documentation](../user/project/integrations/irker.md).
-
-### Create/Edit irker (IRC gateway) service
-
-Set irker (IRC gateway) service for a project.
-
-```plaintext
-PUT /projects/:id/services/irker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | true | Recipients/channels separated by whitespaces |
-| `default_irc_uri` | string | false | `irc://irc.network.net:6697/` |
-| `server_host` | string | false | localhost |
-| `server_port` | integer | false | 6659 |
-| `colorize_messages` | boolean | false | Colorize messages |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete irker (IRC gateway) service
-
-Delete irker (IRC gateway) service for a project.
-
-```plaintext
-DELETE /projects/:id/services/irker
-```
-
-### Get irker (IRC gateway) service settings
-
-Get irker (IRC gateway) service settings for a project.
-
-```plaintext
-GET /projects/:id/services/irker
-```
-
-## Jira
-
-Jira issue tracker.
-
-### Get Jira service settings
-
-Get Jira service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jira
-```
-
-### Create/Edit Jira service
-
-Set Jira service for a project.
-
-> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
-> `project_url` are replaced by `url`. If you are using an
-> older version, [follow this documentation](https://gitlab.com/gitlab-org/gitlab/-/blob/8-13-stable-ee/doc/api/services.md#jira).
-
-```plaintext
-PUT /projects/:id/services/jira
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `url` | string | yes | The URL to the Jira project which is being linked to this GitLab project. For example, `https://jira.example.com`. |
-| `api_url` | string | no | The base URL to the Jira instance API. Web URL value is used if not set. For example, `https://jira-api.example.com`. |
-| `username` | string | yes | The username of the user created to be used with GitLab/Jira. |
-| `password` | string | yes | The password of the user created to be used with GitLab/Jira. |
-| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
-| `jira_issue_transition_automatic` | boolean | no | Enable [automatic issue transitions](../integration/jira/issues.md#automatic-issue-transitions). Takes precedence over `jira_issue_transition_id` if enabled. Defaults to `false` |
-| `jira_issue_transition_id` | string | no | The ID of one or more transitions for [custom issue transitions](../integration/jira/issues.md#custom-issue-transitions). Ignored if `jira_issue_transition_automatic` is enabled. Defaults to a blank string, which disables custom transitions. |
-| `commit_events` | boolean | false | Enable notifications for commit events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
-
-### Delete Jira service
-
-Remove all previously Jira settings from a project.
-
-```plaintext
-DELETE /projects/:id/services/jira
-```
-
-## Slack slash commands
-
-Ability to receive slash commands from a Slack chat instance.
-
-### Get Slack slash command service settings
-
-Get Slack slash command service settings for a project.
-
-```plaintext
-GET /projects/:id/services/slack-slash-commands
-```
-
-Example response:
-
-```json
-{
- "id": 4,
- "title": "Slack slash commands",
- "slug": "slack-slash-commands",
- "created_at": "2017-06-27T05:51:39-07:00",
- "updated_at": "2017-06-27T05:51:39-07:00",
- "active": true,
- "push_events": true,
- "issues_events": true,
- "confidential_issues_events": true,
- "merge_requests_events": true,
- "tag_push_events": true,
- "note_events": true,
- "job_events": true,
- "pipeline_events": true,
- "comment_on_event_enabled": false,
- "properties": {
- "token": ""
- }
-}
-```
-
-### Create/Edit Slack slash command service
-
-Set Slack slash command for a project.
-
-```plaintext
-PUT /projects/:id/services/slack-slash-commands
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | yes | The Slack token |
-
-### Delete Slack slash command service
-
-Delete Slack slash command service for a project.
-
-```plaintext
-DELETE /projects/:id/services/slack-slash-commands
-```
-
-## Mattermost slash commands
-
-Ability to receive slash commands from a Mattermost chat instance.
-
-### Get Mattermost slash command service settings
-
-Get Mattermost slash command service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mattermost-slash-commands
-```
-
-### Create/Edit Mattermost slash command service
-
-Set Mattermost slash command for a project.
-
-```plaintext
-PUT /projects/:id/services/mattermost-slash-commands
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | yes | The Mattermost token |
-| `username` | string | no | The username to use to post the message |
-
-### Delete Mattermost slash command service
-
-Delete Mattermost slash command service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mattermost-slash-commands
-```
-
-## Packagist
-
-Update your project on Packagist (the main Composer repository) when commits or tags are pushed to GitLab.
-
-### Create/Edit Packagist service
-
-Set Packagist service for a project.
-
-```plaintext
-PUT /projects/:id/services/packagist
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `username` | string | yes | The username of a Packagist account |
-| `token` | string | yes | API token to the Packagist server |
-| `server` | boolean | no | URL of the Packagist server. Leave blank for default: |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-
-### Delete Packagist service
-
-Delete Packagist service for a project.
-
-```plaintext
-DELETE /projects/:id/services/packagist
-```
-
-### Get Packagist service settings
-
-Get Packagist service settings for a project.
-
-```plaintext
-GET /projects/:id/services/packagist
-```
-
-## Pipeline-Emails
-
-Get emails for GitLab CI/CD pipelines.
-
-### Create/Edit Pipeline-Emails service
-
-Set Pipeline-Emails service for a project.
-
-```plaintext
-PUT /projects/:id/services/pipelines-email
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `recipients` | string | yes | Comma-separated list of recipient email addresses |
-| `add_pusher` | boolean | no | Add pusher to recipients list |
-| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected. The default value is "default" |
-| `notify_only_default_branch` | boolean | no | Send notifications only for the default branch ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/28271)) |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-
-### Delete Pipeline-Emails service
-
-Delete Pipeline-Emails service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pipelines-email
-```
-
-### Get Pipeline-Emails service settings
-
-Get Pipeline-Emails service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pipelines-email
-```
-
-## Pivotal Tracker
-
-Add commit messages as comments to Pivotal Tracker stories.
-
-See also the [Pivotal Tracker service documentation](../user/project/integrations/pivotal_tracker.md).
-
-### Create/Edit Pivotal Tracker service
-
-Set Pivotal Tracker service for a project.
-
-```plaintext
-PUT /projects/:id/services/pivotaltracker
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `token` | string | true | The Pivotal Tracker token |
-| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Pivotal Tracker service
-
-Delete Pivotal Tracker service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pivotaltracker
-```
-
-### Get Pivotal Tracker service settings
-
-Get Pivotal Tracker service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pivotaltracker
-```
-
-## Prometheus
-
-Prometheus is a powerful time-series monitoring service.
-
-### Create/Edit Prometheus service
-
-Set Prometheus service for a project.
-
-```plaintext
-PUT /projects/:id/services/prometheus
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_url` | string | true | Prometheus API Base URL. For example, `http://prometheus.example.com/`. |
-| `google_iap_audience_client_id` | string | false | Client ID of the IAP secured resource (looks like IAP_CLIENT_ID.apps.googleusercontent.com) |
-| `google_iap_service_account_json` | string | false | `credentials.json` file for your service account, like { "type": "service_account", "project_id": ... } |
-
-### Delete Prometheus service
-
-Delete Prometheus service for a project.
-
-```plaintext
-DELETE /projects/:id/services/prometheus
-```
-
-### Get Prometheus service settings
-
-Get Prometheus service settings for a project.
-
-```plaintext
-GET /projects/:id/services/prometheus
-```
-
-## Pushover
-
-Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop.
-
-### Create/Edit Pushover service
-
-Set Pushover service for a project.
-
-```plaintext
-PUT /projects/:id/services/pushover
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `api_key` | string | true | Your application key |
-| `user_key` | string | true | Your user key |
-| `priority` | string | true | The priority |
-| `device` | string | false | Leave blank for all active devices |
-| `sound` | string | false | The sound of the notification |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Pushover service
-
-Delete Pushover service for a project.
-
-```plaintext
-DELETE /projects/:id/services/pushover
-```
-
-### Get Pushover service settings
-
-Get Pushover service settings for a project.
-
-```plaintext
-GET /projects/:id/services/pushover
-```
-
-## Redmine
-
-Redmine issue tracker
-
-### Create/Edit Redmine service
-
-Set Redmine service for a project.
-
-```plaintext
-PUT /projects/:id/services/redmine
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `new_issue_url` | string | true | New Issue URL |
-| `project_url` | string | true | Project URL |
-| `issues_url` | string | true | Issue URL |
-| `description` | string | false | Description |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete Redmine service
-
-Delete Redmine service for a project.
-
-```plaintext
-DELETE /projects/:id/services/redmine
-```
-
-### Get Redmine service settings
-
-Get Redmine service settings for a project.
-
-```plaintext
-GET /projects/:id/services/redmine
-```
-
-## Slack notifications
-
-Receive event notifications in Slack
-
-### Create/Edit Slack service
-
-Set Slack service for a project.
-
-```plaintext
-PUT /projects/:id/services/slack
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | `https://hooks.slack.com/services/...` |
-| `username` | string | false | username |
-| `channel` | string | false | Default channel to use if others are not configured |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `commit_events` | boolean | false | Enable notifications for commit events |
-| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `deployment_channel` | string | false | The name of the channel to receive deployment events notifications |
-| `deployment_events` | boolean | false | Enable notifications for deployment events |
-| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `job_events` | boolean | false | Enable notifications for job events |
-| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `note_channel` | string | false | The name of the channel to receive note events notifications |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `push_channel` | string | false | The name of the channel to receive push events notifications |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-| `vulnerability_channel` | string | false | **(ULTIMATE)** The name of the channel to receive vulnerability event notifications. |
-| `vulnerability_events` | boolean | false | **(ULTIMATE)** Enable notifications for vulnerability events |
-
-### Delete Slack service
-
-Delete Slack service for a project.
-
-```plaintext
-DELETE /projects/:id/services/slack
-```
-
-### Get Slack service settings
-
-Get Slack service settings for a project.
-
-```plaintext
-GET /projects/:id/services/slack
-```
-
-## Microsoft Teams
-
-Group Chat Software
-
-### Create/Edit Microsoft Teams service
-
-Set Microsoft Teams service for a project.
-
-```plaintext
-PUT /projects/:id/services/microsoft-teams
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Microsoft Teams webhook. For example, `https://outlook.office.com/webhook/...` |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-
-### Delete Microsoft Teams service
-
-Delete Microsoft Teams service for a project.
-
-```plaintext
-DELETE /projects/:id/services/microsoft-teams
-```
-
-### Get Microsoft Teams service settings
-
-Get Microsoft Teams service settings for a project.
-
-```plaintext
-GET /projects/:id/services/microsoft-teams
-```
-
-## Mattermost notifications
-
-Receive event notifications in Mattermost
-
-### Create/Edit Mattermost notifications service
-
-Set Mattermost service for a project.
-
-```plaintext
-PUT /projects/:id/services/mattermost
-```
-
-NOTE:
-Specific event parameters (for example, `push_events` flag and `push_channel`) were [introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/11435) in GitLab 10.4.
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `webhook` | string | true | The Mattermost webhook. For example, `http://mattermost_host/hooks/...` |
-| `username` | string | false | username |
-| `channel` | string | false | Default channel to use if others are not configured |
-| `notify_only_broken_pipelines` | boolean | false | Send notifications for broken pipelines |
-| `notify_only_default_branch` | boolean | false | DEPRECATED: This parameter has been replaced with `branches_to_be_notified` |
-| `branches_to_be_notified` | string | false | Branches to send notifications for. Valid options are "all", "default", "protected", and "default_and_protected". The default value is "default" |
-| `push_events` | boolean | false | Enable notifications for push events |
-| `issues_events` | boolean | false | Enable notifications for issue events |
-| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events |
-| `note_events` | boolean | false | Enable notifications for note events |
-| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
-| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
-| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
-| `vulnerability_events` | boolean | false | **(ULTIMATE)** Enable notifications for vulnerability events |
-| `push_channel` | string | false | The name of the channel to receive push events notifications |
-| `issue_channel` | string | false | The name of the channel to receive issues events notifications |
-| `confidential_issue_channel` | string | false | The name of the channel to receive confidential issues events notifications |
-| `merge_request_channel` | string | false | The name of the channel to receive merge request events notifications |
-| `note_channel` | string | false | The name of the channel to receive note events notifications |
-| `confidential_note_channel` | string | false | The name of the channel to receive confidential note events notifications |
-| `tag_push_channel` | string | false | The name of the channel to receive tag push events notifications |
-| `pipeline_channel` | string | false | The name of the channel to receive pipeline events notifications |
-| `wiki_page_channel` | string | false | The name of the channel to receive wiki page events notifications |
-| `vulnerability_channel` | string | false | **(ULTIMATE)** The name of the channel to receive vulnerability events notifications |
-
-### Delete Mattermost notifications service
-
-Delete Mattermost Notifications service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mattermost
-```
-
-### Get Mattermost notifications service settings
-
-Get Mattermost notifications service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mattermost
-```
-
-## JetBrains TeamCity CI
-
-A continuous integration and build server
-
-### Create/Edit JetBrains TeamCity CI service
-
-Set JetBrains TeamCity CI service for a project.
-
-> The build configuration in TeamCity must use the build format number `%build.vcs.number%`. Configure monitoring of all branches so merge requests build. That setting is in the VSC root advanced settings.
-
-```plaintext
-PUT /projects/:id/services/teamcity
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `teamcity_url` | string | true | TeamCity root URL. For example, `https://teamcity.example.com` |
-| `build_type` | string | true | Build configuration ID |
-| `username` | string | true | A user with permissions to trigger a manual build |
-| `password` | string | true | The password of the user |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete JetBrains TeamCity CI service
-
-Delete JetBrains TeamCity CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/teamcity
-```
-
-### Get JetBrains TeamCity CI service settings
-
-Get JetBrains TeamCity CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/teamcity
-```
-
-## Jenkins CI
-
-A continuous integration and build server
-
-### Create/Edit Jenkins CI service
-
-Set Jenkins CI service for a project.
-
-```plaintext
-PUT /projects/:id/services/jenkins
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `jenkins_url` | string | true | Jenkins URL like `http://jenkins.example.com`. |
-| `project_name` | string | true | The URL-friendly project name. Example: `my_project_name`. |
-| `username` | string | false | Username for authentication with the Jenkins server, if authentication is required by the server. |
-| `password` | string | false | Password for authentication with the Jenkins server, if authentication is required by the server. |
-| `push_events` | boolean | false | Enable notifications for push events. |
-| `merge_requests_events` | boolean | false | Enable notifications for merge request events. |
-| `tag_push_events` | boolean | false | Enable notifications for tag push events. |
-
-### Delete Jenkins CI service
-
-Delete Jenkins CI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/jenkins
-```
-
-### Get Jenkins CI service settings
-
-Get Jenkins CI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jenkins
-```
-
-## Jenkins CI (Deprecated) Service
-
-A continuous integration and build server
-
-NOTE:
-This service was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/1600) in GitLab 13.0.
-
-### Create/Edit Jenkins CI (Deprecated) service
-
-Set Jenkins CI (Deprecated) service for a project.
-
-```plaintext
-PUT /projects/:id/services/jenkins-deprecated
-```
-
-Parameters:
-
-- `project_url` (**required**) - Jenkins project URL like `http://jenkins.example.com/job/my-project/`
-- `multiproject_enabled` (optional) - Multi-project mode is configured in Jenkins GitLab Hook plugin
-- `pass_unstable` (optional) - Unstable builds are treated as passing
-
-### Delete Jenkins CI (Deprecated) service
-
-Delete Jenkins CI (Deprecated) service for a project.
-
-```plaintext
-DELETE /projects/:id/services/jenkins-deprecated
-```
-
-### Get Jenkins CI (Deprecated) service settings
-
-Get Jenkins CI (Deprecated) service settings for a project.
-
-```plaintext
-GET /projects/:id/services/jenkins-deprecated
-```
-
-## MockCI
-
-Mock an external CI. See [`gitlab-org/gitlab-mock-ci-service`](https://gitlab.com/gitlab-org/gitlab-mock-ci-service) for an example of a companion mock service.
-
-This service is only available when your environment is set to development.
-
-### Create/Edit MockCI service
-
-Set MockCI service for a project.
-
-```plaintext
-PUT /projects/:id/services/mock-ci
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `mock_service_url` | string | true | `http://localhost:4004` |
-
-### Delete MockCI service
-
-Delete MockCI service for a project.
-
-```plaintext
-DELETE /projects/:id/services/mock-ci
-```
-
-### Get MockCI service settings
-
-Get MockCI service settings for a project.
-
-```plaintext
-GET /projects/:id/services/mock-ci
-```
-
-## YouTrack
-
-YouTrack issue tracker
-
-### Create/Edit YouTrack service
-
-Set YouTrack service for a project.
-
-```plaintext
-PUT /projects/:id/services/youtrack
-```
-
-Parameters:
-
-| Parameter | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `issues_url` | string | true | Issue URL |
-| `project_url` | string | true | Project URL |
-| `description` | string | false | Description |
-| `push_events` | boolean | false | Enable notifications for push events |
-
-### Delete YouTrack Service
-
-Delete YouTrack service for a project.
-
-```plaintext
-DELETE /projects/:id/services/youtrack
-```
-
-### Get YouTrack Service Settings
-
-Get YouTrack service settings for a project.
-
-```plaintext
-GET /projects/:id/services/youtrack
-```
+
+
diff --git a/doc/api/snippet_repository_storage_moves.md b/doc/api/snippet_repository_storage_moves.md
index a73542c8505..81473fdff91 100644
--- a/doc/api/snippet_repository_storage_moves.md
+++ b/doc/api/snippet_repository_storage_moves.md
@@ -10,7 +10,7 @@ type: reference
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49228) in GitLab 13.8.
Snippet repositories can be moved between storages. This can be useful when
-[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrate-to-gitaly-cluster), for
+[migrating to Gitaly Cluster](../administration/gitaly/index.md#migrating-to-gitaly-cluster), for
example.
As snippet repository storage moves are processed, they transition through different states. Values
diff --git a/doc/api/usage_data.md b/doc/api/usage_data.md
index f244c681086..4809166f357 100644
--- a/doc/api/usage_data.md
+++ b/doc/api/usage_data.md
@@ -13,7 +13,7 @@ The Service Data API is associated with [Service Ping](../development/service_pi
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57270) in GitLab 13.11.
-Export all metric definitions as a single YAML file, similar to the [Metrics Dictionary](https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary), for easier importing.
+Export all metric definitions as a single YAML file, similar to the [Metrics Dictionary](https://metrics.gitlab.com/index.html), for easier importing.
```plaintext
GET /usage_data/metric_definitions
diff --git a/doc/api/users.md b/doc/api/users.md
index 17864526f8a..d8effc4d38f 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -259,7 +259,7 @@ GET /users?with_custom_attributes=true
## Single user
-Get a single user. This endpoint can be accessed without authentication.
+Get a single user.
### For user
@@ -806,7 +806,7 @@ Example response:
### Followers and following
-Get the followers of a user. This endpoint can be accessed without authentication.
+Get the followers of a user.
```plaintext
GET /users/:id/followers
diff --git a/doc/development/fe_guide/storybook.md b/doc/development/fe_guide/storybook.md
index 3ccb13b5bff..d4b65f3232a 100644
--- a/doc/development/fe_guide/storybook.md
+++ b/doc/development/fe_guide/storybook.md
@@ -49,3 +49,9 @@ To add a story:
- Specify the `title` field of the story as the component's file path from the `javascripts/` directory,
e.g. if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`, specify the `title` as
`vue_shared/components/To-do Button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
+
+## Mock backend APIs
+
+GitLab’s Storybook uses [MirajeJS](https://miragejs.com/) to mock REST and GraphQL APIs. Storybook shares the MirajeJS server
+with the [frontend integration tests](../testing_guide/testing_levels.md#frontend-integration-tests). You can find the MirajeJS
+configuration files in `spec/frontend_integration/mock_server`.
diff --git a/doc/development/redis.md b/doc/development/redis.md
index 13ec923c19a..a04ca94ad91 100644
--- a/doc/development/redis.md
+++ b/doc/development/redis.md
@@ -15,6 +15,8 @@ GitLab uses [Redis](https://redis.io) for the following distinct purposes:
- To manage the shared application state.
- To store CI trace chunks.
- As a Pub/Sub queue backend for ActionCable.
+- CI trace chunk storage.
+- Rate limiting state storage.
In most environments (including the GDK), all of these point to the same
Redis instance.
diff --git a/doc/development/service_ping/dictionary.md b/doc/development/service_ping/dictionary.md
index e7e8464ff7a..810c789bc03 100644
--- a/doc/development/service_ping/dictionary.md
+++ b/doc/development/service_ping/dictionary.md
@@ -1,4 +1,4 @@
---
-redirect_to: 'https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary'
+redirect_to: 'https://metrics.gitlab.com/index.html'
remove_date: '2021-11-10'
---
diff --git a/doc/development/service_ping/metrics_dictionary.md b/doc/development/service_ping/metrics_dictionary.md
index a2eb0ddb821..c1478e6290e 100644
--- a/doc/development/service_ping/metrics_dictionary.md
+++ b/doc/development/service_ping/metrics_dictionary.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Metrics Dictionary Guide
[Service Ping](index.md) metrics are defined in the
-[Metrics Dictionary](https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary).
+[Metrics Dictionary](https://metrics.gitlab.com/index.html).
This guide describes the dictionary and how it's implemented.
## Metrics Definition and validation
diff --git a/doc/development/service_ping/review_guidelines.md b/doc/development/service_ping/review_guidelines.md
index 048b705636f..eb64d460b5a 100644
--- a/doc/development/service_ping/review_guidelines.md
+++ b/doc/development/service_ping/review_guidelines.md
@@ -14,7 +14,7 @@ general best practices for code reviews, refer to our [code review guide](../cod
## Resources for reviewers
- [Service Ping Guide](index.md)
-- [Metrics Dictionary](https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary)
+- [Metrics Dictionary](https://metrics.gitlab.com/index.html)
## Review process
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index e7e8464ff7a..810c789bc03 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -1,4 +1,4 @@
---
-redirect_to: 'https://gitlab-org.gitlab.io/growth/product-intelligence/metric-dictionary'
+redirect_to: 'https://metrics.gitlab.com/index.html'
remove_date: '2021-11-10'
---
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 14e6f4dad3a..44537707db6 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -75,6 +75,7 @@ To enable 2FA:
1. **In GitLab:**
1. Enter the six-digit pin number from the entry on your device into the **Pin
code** field.
+ 1. Enter your current password.
1. Select **Submit**.
If the pin you entered was correct, a message displays indicating that
@@ -365,7 +366,8 @@ If you ever need to disable 2FA:
1. Sign in to your GitLab account.
1. Go to your [**User settings**](../index.md#access-your-user-settings).
1. Go to **Account**.
-1. Click **Disable**, under **Two-Factor Authentication**.
+1. Select **Manage two-factor authentication**.
+1. Under **Two-Factor Authentication**, enter your current password and select **Disable**.
This clears all your two-factor authentication registrations, including mobile
applications and U2F / WebAuthn devices.
@@ -460,7 +462,7 @@ To regenerate 2FA recovery codes, you need access to a desktop browser:
1. Go to your [**User settings**](../index.md#access-your-user-settings).
1. Select **Account > Two-Factor Authentication (2FA)**.
1. If you've already configured 2FA, click **Manage two-factor authentication**.
-1. In the **Register Two-Factor Authenticator** pane, click **Regenerate recovery codes**.
+1. In the **Register Two-Factor Authenticator** pane, enter your current password and select **Regenerate recovery codes**.
NOTE:
If you regenerate 2FA recovery codes, save them. You can't use any previously created 2FA codes.
diff --git a/doc/user/project/integrations/asana.md b/doc/user/project/integrations/asana.md
index e1e926da19b..963fca34827 100644
--- a/doc/user/project/integrations/asana.md
+++ b/doc/user/project/integrations/asana.md
@@ -4,9 +4,9 @@ group: Integrations
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
---
-# Asana service **(FREE)**
+# Asana integration **(FREE)**
-This service adds commit messages as comments to Asana tasks.
+This integration adds commit messages as comments to Asana tasks.
Once enabled, commit messages are checked for Asana task URLs (for example,
`https://app.asana.com/0/123456/987654`) or task IDs starting with `#`
(for example, `#987654`). Every task ID found gets the commit comment added to it.
@@ -23,7 +23,7 @@ You can use either of these words:
- `closed`
- `closing`
-See also the [Asana service API documentation](../../../api/services.md#asana).
+See also the [Asana integration API documentation](../../../api/integrations.md#asana).
## Setup
diff --git a/doc/user/project/integrations/overview.md b/doc/user/project/integrations/overview.md
index 249f6c4305c..cbe68bd1802 100644
--- a/doc/user/project/integrations/overview.md
+++ b/doc/user/project/integrations/overview.md
@@ -32,7 +32,7 @@ Click on the service links to see further configuration instructions and details
| [Bugzilla](bugzilla.md) | Use Bugzilla as the issue tracker. | **{dotted-circle}** No |
| Buildkite | Run CI/CD pipelines with Buildkite. | **{check-circle}** Yes |
| Campfire | Connect to chat. | **{dotted-circle}** No |
-| [Confluence Workspace](../../../api/services.md#confluence-service) | Replace the link to the internal wiki with a link to a Confluence Cloud Workspace. | **{dotted-circle}** No |
+| [Confluence Workspace](../../../api/integrations.md#confluence-integration) | Replace the link to the internal wiki with a link to a Confluence Cloud Workspace. | **{dotted-circle}** No |
| [Custom issue tracker](custom_issue_tracker.md) | Use a custom issue tracker. | **{dotted-circle}** No |
| [Datadog](../../../integration/datadog.md) | Trace your GitLab pipelines with Datadog. | **{check-circle}** Yes |
| [Discord Notifications](discord_notifications.md) | Send notifications about project events to a Discord channel. | **{dotted-circle}** No |
@@ -40,7 +40,7 @@ Click on the service links to see further configuration instructions and details
| [Emails on push](emails_on_push.md) | Send commits and diff of each push by email. | **{dotted-circle}** No |
| [EWM](ewm.md) | Use IBM Engineering Workflow Management as the issue tracker. | **{dotted-circle}** No |
| [External wiki](../wiki/index.md#link-an-external-wiki) | Link an external wiki. | **{dotted-circle}** No |
-| [Flowdock](../../../api/services.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No |
+| [Flowdock](../../../api/integrations.md#flowdock) | Send notifications from GitLab to Flowdock flows. | **{dotted-circle}** No |
| [GitHub](github.md) | Obtain statuses for commits and pull requests. | **{dotted-circle}** No |
| [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat.| **{dotted-circle}** No |
| [irker (IRC gateway)](irker.md) | Send IRC messages. | **{dotted-circle}** No |
diff --git a/doc/user/project/integrations/pivotal_tracker.md b/doc/user/project/integrations/pivotal_tracker.md
index d464007dd5e..93a3490e4b6 100644
--- a/doc/user/project/integrations/pivotal_tracker.md
+++ b/doc/user/project/integrations/pivotal_tracker.md
@@ -29,7 +29,7 @@ Read more about the
[Source Commits endpoint](https://www.pivotaltracker.com/help/api/rest/v5#Source_Commits) in
the Pivotal Tracker API documentation.
-See also the [Pivotal Tracker service API documentation](../../../api/services.md#pivotal-tracker).
+See also the [Pivotal Tracker integration API documentation](../../../api/integrations.md#pivotal-tracker).
## Set up Pivotal Tracker
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 8d14e461e49..f23ac2c3349 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -135,9 +135,11 @@ The following items are **not** exported:
- Build traces and artifacts
- Container registry images
- CI/CD variables
+- Pipeline triggers
- Webhooks
- Any encrypted tokens
- Merge Request Approvers
+- Repository size limits
These content rules also apply to creating projects from templates on the
[group](../../group/custom_project_templates.md)
@@ -261,7 +263,7 @@ reduce the repository size for another import attempt.
git gc --prune=now --aggressive
# Prepare recreating an importable file
- git bundle create ../project.bundle smaller-tmp-main
+ git bundle create ../project.bundle
cd ..
mv project/ ../"$EXPORT"-project
cd ..
diff --git a/lib/api/api.rb b/lib/api/api.rb
index a68d3267740..a5340d9baaf 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -260,7 +260,7 @@ module API
mount ::API::ResourceAccessTokens
mount ::API::RubygemPackages
mount ::API::Search
- mount ::API::Services
+ mount ::API::Integrations
mount ::API::Settings
mount ::API::SidekiqMetrics
mount ::API::SnippetRepositoryStorageMoves
diff --git a/lib/api/helpers/integrations_helpers.rb b/lib/api/helpers/integrations_helpers.rb
index 06539772568..0aec038484a 100644
--- a/lib/api/helpers/integrations_helpers.rb
+++ b/lib/api/helpers/integrations_helpers.rb
@@ -2,7 +2,7 @@
module API
module Helpers
- # Helpers module for API::Services
+ # Helpers module for API::Integrations
#
# The data structures inside this model are returned using class methods,
# allowing EE to extend them where necessary.
diff --git a/lib/api/import_bitbucket_server.rb b/lib/api/import_bitbucket_server.rb
index ecd78c6e6db..0f0d62dcbfb 100644
--- a/lib/api/import_bitbucket_server.rb
+++ b/lib/api/import_bitbucket_server.rb
@@ -4,6 +4,10 @@ module API
class ImportBitbucketServer < ::API::Base
feature_category :importers
+ before do
+ forbidden! unless Gitlab::CurrentSettings.import_sources&.include?('bitbucket_server')
+ end
+
helpers do
def client
@client ||= BitbucketServer::Client.new(credentials)
diff --git a/lib/api/integrations.rb b/lib/api/integrations.rb
new file mode 100644
index 00000000000..926cde340a0
--- /dev/null
+++ b/lib/api/integrations.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+module API
+ class Integrations < ::API::Base
+ feature_category :integrations
+
+ integrations = Helpers::IntegrationsHelpers.integrations
+ integration_classes = Helpers::IntegrationsHelpers.integration_classes
+
+ if Rails.env.development?
+ integrations['mock-ci'] = [
+ {
+ required: true,
+ name: :mock_service_url,
+ type: String,
+ desc: 'URL to the mock integration'
+ }
+ ]
+ integrations['mock-deployment'] = []
+ integrations['mock-monitoring'] = []
+
+ integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
+ end
+
+ INTEGRATIONS = integrations.freeze
+
+ integration_classes.each do |integration|
+ event_names = integration.try(:event_names) || next
+ event_names.each do |event_name|
+ INTEGRATIONS[integration.to_param.tr("_", "-")] << {
+ required: false,
+ name: event_name.to_sym,
+ type: String,
+ desc: IntegrationsHelper.integration_event_description(integration, event_name)
+ }
+ end
+ end
+
+ TRIGGER_INTEGRATIONS = {
+ 'mattermost-slash-commands' => [
+ {
+ name: :token,
+ type: String,
+ desc: 'The Mattermost token'
+ }
+ ],
+ 'slack-slash-commands' => [
+ {
+ name: :token,
+ type: String,
+ desc: 'The Slack token'
+ }
+ ]
+ }.freeze
+
+ helpers do
+ def integration_attributes(integration)
+ integration.fields.inject([]) do |arr, hash|
+ arr << hash[:name].to_sym
+ end
+ end
+ end
+
+ # The API officially documents only the `:id/integrations` API paths.
+ # We support the older `id:/services` path for backwards-compatibility in API V4.
+ # The support for `:id/services` can be dropped if we create an API V5.
+ [':id/services', ':id/integrations'].each do |path|
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ before { authenticate! }
+ before { authorize_admin_project }
+
+ desc 'Get all active project integrations' do
+ success Entities::ProjectIntegrationBasic
+ end
+ get path do
+ integrations = user_project.integrations.active
+
+ present integrations, with: Entities::ProjectIntegrationBasic
+ end
+
+ INTEGRATIONS.each do |slug, settings|
+ desc "Set #{slug} integration for project"
+ params do
+ settings.each do |setting|
+ if setting[:required]
+ requires setting[:name], type: setting[:type], desc: setting[:desc]
+ else
+ optional setting[:name], type: setting[:type], desc: setting[:desc]
+ end
+ end
+ end
+ put "#{path}/#{slug}" do
+ integration = user_project.find_or_initialize_integration(slug.underscore)
+ params = declared_params(include_missing: false).merge(active: true)
+
+ if integration.update(params)
+ present integration, with: Entities::ProjectIntegration
+ else
+ render_api_error!('400 Bad Request', 400)
+ end
+ end
+ end
+
+ desc "Delete an integration from a project"
+ params do
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
+ end
+ delete "#{path}/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
+
+ destroy_conditionally!(integration) do
+ attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
+
+ render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
+ end
+ end
+
+ desc 'Get the integration settings for a project' do
+ success Entities::ProjectIntegration
+ end
+ params do
+ requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the integration'
+ end
+ get "#{path}/:slug" do
+ integration = user_project.find_or_initialize_integration(params[:slug].underscore)
+
+ not_found!('Integration') unless integration&.persisted?
+
+ present integration, with: Entities::ProjectIntegration
+ end
+ end
+
+ TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
+ helpers do
+ def slash_command_integration(project, integration_slug, params)
+ project.integrations.active.find do |integration|
+ integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
+ end
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc "Trigger a slash command for #{integration_slug}" do
+ detail 'Added in GitLab 8.13'
+ end
+ params do
+ settings.each do |setting|
+ requires setting[:name], type: setting[:type], desc: setting[:desc]
+ end
+ end
+ post "#{path}/#{integration_slug.underscore}/trigger" do
+ project = find_project(params[:id])
+
+ # This is not accurate, but done to prevent leakage of the project names
+ not_found!('Integration') unless project
+
+ integration = slash_command_integration(project, integration_slug, params)
+ result = integration.try(:trigger, params)
+
+ if result
+ status result[:status] || 200
+ present result
+ else
+ not_found!('Integration')
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+API::Integrations.prepend_mod_with('API::Integrations')
diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb
index 1f437ad5bd3..5cade301d81 100644
--- a/lib/api/invitations.rb
+++ b/lib/api/invitations.rb
@@ -46,6 +46,8 @@ module API
source = find_source(source_type, params[:id])
query = params[:query]
+ authorize_admin_source!(source_type, source)
+
invitations = paginate(retrieve_member_invitations(source, query))
present_member_invitations invitations
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index a92d904be84..34e0b528ced 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -89,6 +89,10 @@ module API
Gitlab::AppLogger.info({ message: "File exceeds maximum size", file_bytes: file.size, project_id: user_project.id, project_path: user_project.full_path, upload_allowed: allowed })
end
end
+
+ def check_import_by_url_is_enabled
+ forbidden! unless Gitlab::CurrentSettings.import_sources&.include?('git')
+ end
end
helpers do
@@ -267,6 +271,7 @@ module API
attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs)
+ check_import_by_url_is_enabled if params[:import_url].present?
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
diff --git a/lib/api/services.rb b/lib/api/services.rb
deleted file mode 100644
index a37b6f4626a..00000000000
--- a/lib/api/services.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-# frozen_string_literal: true
-module API
- class Services < ::API::Base
- feature_category :integrations
-
- integrations = Helpers::IntegrationsHelpers.integrations
- integration_classes = Helpers::IntegrationsHelpers.integration_classes
-
- if Rails.env.development?
- integrations['mock-ci'] = [
- {
- required: true,
- name: :mock_service_url,
- type: String,
- desc: 'URL to the mock service'
- }
- ]
- integrations['mock-deployment'] = []
- integrations['mock-monitoring'] = []
-
- integration_classes += Helpers::IntegrationsHelpers.development_integration_classes
- end
-
- INTEGRATIONS = integrations.freeze
-
- integration_classes.each do |integration|
- event_names = integration.try(:event_names) || next
- event_names.each do |event_name|
- INTEGRATIONS[integration.to_param.tr("_", "-")] << {
- required: false,
- name: event_name.to_sym,
- type: String,
- desc: IntegrationsHelper.integration_event_description(integration, event_name)
- }
- end
- end
-
- TRIGGER_INTEGRATIONS = {
- 'mattermost-slash-commands' => [
- {
- name: :token,
- type: String,
- desc: 'The Mattermost token'
- }
- ],
- 'slack-slash-commands' => [
- {
- name: :token,
- type: String,
- desc: 'The Slack token'
- }
- ]
- }.freeze
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- before { authenticate! }
- before { authorize_admin_project }
-
- helpers do
- def integration_attributes(integration)
- integration.fields.inject([]) do |arr, hash|
- arr << hash[:name].to_sym
- end
- end
- end
-
- desc 'Get all active project integrations' do
- success Entities::ProjectIntegrationBasic
- end
- get ":id/services" do
- integrations = user_project.integrations.active
-
- present integrations, with: Entities::ProjectIntegrationBasic
- end
-
- INTEGRATIONS.each do |slug, settings|
- desc "Set #{slug} integration for project"
- params do
- settings.each do |setting|
- if setting[:required]
- requires setting[:name], type: setting[:type], desc: setting[:desc]
- else
- optional setting[:name], type: setting[:type], desc: setting[:desc]
- end
- end
- end
- put ":id/services/#{slug}" do
- integration = user_project.find_or_initialize_integration(slug.underscore)
- params = declared_params(include_missing: false).merge(active: true)
-
- if integration.update(params)
- present integration, with: Entities::ProjectIntegration
- else
- render_api_error!('400 Bad Request', 400)
- end
- end
- end
-
- desc "Delete an integration from a project"
- params do
- requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
- end
- delete ":id/services/:slug" do
- integration = user_project.find_or_initialize_integration(params[:slug].underscore)
-
- destroy_conditionally!(integration) do
- attrs = integration_attributes(integration).index_with { nil }.merge(active: false)
-
- render_api_error!('400 Bad Request', 400) unless integration.update(attrs)
- end
- end
-
- desc 'Get the integration settings for a project' do
- success Entities::ProjectIntegration
- end
- params do
- requires :slug, type: String, values: INTEGRATIONS.keys, desc: 'The name of the service'
- end
- get ":id/services/:slug" do
- integration = user_project.find_or_initialize_integration(params[:slug].underscore)
-
- not_found!('Service') unless integration&.persisted?
-
- present integration, with: Entities::ProjectIntegration
- end
- end
-
- TRIGGER_INTEGRATIONS.each do |integration_slug, settings|
- helpers do
- def slash_command_integration(project, integration_slug, params)
- project.integrations.active.find do |integration|
- integration.try(:token) == params[:token] && integration.to_param == integration_slug.underscore
- end
- end
- end
-
- params do
- requires :id, type: String, desc: 'The ID of a project'
- end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc "Trigger a slash command for #{integration_slug}" do
- detail 'Added in GitLab 8.13'
- end
- params do
- settings.each do |setting|
- requires setting[:name], type: setting[:type], desc: setting[:desc]
- end
- end
- post ":id/services/#{integration_slug.underscore}/trigger" do
- project = find_project(params[:id])
-
- # This is not accurate, but done to prevent leakage of the project names
- not_found!('Service') unless project
-
- integration = slash_command_integration(project, integration_slug, params)
- result = integration.try(:trigger, params)
-
- if result
- status result[:status] || 200
- present result
- else
- not_found!('Service')
- end
- end
- end
- end
- end
-end
-
-API::Services.prepend_mod_with('API::Services')
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 1cab668a248..74fa98128e8 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -140,7 +140,10 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ":id", feature_category: :users do
+ forbidden!('Not authorized!') unless current_user
+
user = User.find_by(id: params[:id])
+
not_found!('User') unless user && can?(current_user, :read_user, user)
opts = { with: current_user&.admin? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
@@ -156,6 +159,7 @@ module API
end
get ":user_id/status", requirements: API::USER_REQUIREMENTS, feature_category: :users do
user = find_user(params[:user_id])
+
not_found!('User') unless user && can?(current_user, :read_user, user)
present user.status || {}, with: Entities::UserStatus
@@ -203,6 +207,8 @@ module API
use :pagination
end
get ':id/following', feature_category: :users do
+ forbidden!('Not authorized!') unless current_user
+
user = find_user(params[:id])
not_found!('User') unless user && can?(current_user, :read_user_profile, user)
@@ -217,6 +223,8 @@ module API
use :pagination
end
get ':id/followers', feature_category: :users do
+ forbidden!('Not authorized!') unless current_user
+
user = find_user(params[:id])
not_found!('User') unless user && can?(current_user, :read_user_profile, user)
diff --git a/lib/banzai/filter/spaced_link_filter.rb b/lib/banzai/filter/spaced_link_filter.rb
index ca26e6d1581..f8d03fd6e50 100644
--- a/lib/banzai/filter/spaced_link_filter.rb
+++ b/lib/banzai/filter/spaced_link_filter.rb
@@ -26,14 +26,17 @@ module Banzai
# Pattern to match a standard markdown link
#
# Rubular: http://rubular.com/r/2EXEQ49rg5
- LINK_OR_IMAGE_PATTERN = %r{
- (?!)?
- \[(?.+?)\]
- \(
- (?.+?)
- (?\ ".+?")?
- \)
- }x.freeze
+ #
+ # This pattern is vulnerable to malicious inputs, so use Gitlab::UntrustedRegexp
+ # to place bounds on execution time
+ LINK_OR_IMAGE_PATTERN = Gitlab::UntrustedRegexp.new(
+ '(?P!)?' \
+ '\[(?P.+?)\]' \
+ '\(' \
+ '(?P.+?)' \
+ '(?P\ ".+?")?' \
+ '\)'
+ )
# Text matching LINK_OR_IMAGE_PATTERN inside these elements will not be linked
IGNORE_PARENTS = %w(a code kbd pre script style).to_set
@@ -48,7 +51,7 @@ module Banzai
doc.xpath(TEXT_QUERY).each do |node|
content = node.to_html
- next unless content.match(LINK_OR_IMAGE_PATTERN)
+ next unless LINK_OR_IMAGE_PATTERN.match(content)
html = spaced_link_filter(content)
diff --git a/lib/bulk_imports/ndjson_pipeline.rb b/lib/bulk_imports/ndjson_pipeline.rb
index 93fd6986173..f01ce22a46d 100644
--- a/lib/bulk_imports/ndjson_pipeline.rb
+++ b/lib/bulk_imports/ndjson_pipeline.rb
@@ -14,7 +14,7 @@ module BulkImports
relation_definition = import_export_config.top_relation_tree(relation)
deep_transform_relation!(relation_hash, relation, relation_definition) do |key, hash|
- Gitlab::ImportExport::Group::RelationFactory.create(
+ relation_factory.create(
relation_index: relation_index,
relation_sym: key.to_sym,
relation_hash: hash,
@@ -83,6 +83,10 @@ module BulkImports
"Gitlab::ImportExport::#{portable.class}::ObjectBuilder".constantize
end
+ def relation_factory
+ "Gitlab::ImportExport::#{portable.class}::RelationFactory".constantize
+ end
+
def relation
self.class.relation
end
diff --git a/lib/bulk_imports/projects/pipelines/issues_pipeline.rb b/lib/bulk_imports/projects/pipelines/issues_pipeline.rb
new file mode 100644
index 00000000000..5d5bd58f1eb
--- /dev/null
+++ b/lib/bulk_imports/projects/pipelines/issues_pipeline.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module BulkImports
+ module Projects
+ module Pipelines
+ class IssuesPipeline
+ include NdjsonPipeline
+
+ relation_name 'issues'
+
+ extractor ::BulkImports::Common::Extractors::NdjsonExtractor, relation: relation
+ end
+ end
+ end
+end
diff --git a/lib/bulk_imports/projects/stage.rb b/lib/bulk_imports/projects/stage.rb
index eb5e3c9cb0a..26b9b02efd1 100644
--- a/lib/bulk_imports/projects/stage.rb
+++ b/lib/bulk_imports/projects/stage.rb
@@ -15,9 +15,13 @@ module BulkImports
pipeline: BulkImports::Common::Pipelines::LabelsPipeline,
stage: 1
},
+ issues: {
+ pipeline: BulkImports::Projects::Pipelines::IssuesPipeline,
+ stage: 2
+ },
finisher: {
pipeline: BulkImports::Common::Pipelines::EntityFinisher,
- stage: 2
+ stage: 3
}
}
end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 1afb2eda149..0970b92723b 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -172,7 +172,11 @@ module Gitlab
user = find_with_user_password(login, password)
return unless user
- raise Gitlab::Auth::MissingPersonalAccessTokenError if user.two_factor_enabled?
+ verifier = TwoFactorAuthVerifier.new(user)
+
+ if user.two_factor_enabled? || verifier.two_factor_authentication_enforced?
+ raise Gitlab::Auth::MissingPersonalAccessTokenError
+ end
Gitlab::Auth::Result.new(user, nil, :gitlab_or_ldap, full_authentication_abilities)
end
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb
index f6ee08defcf..9c33a5fc872 100644
--- a/lib/gitlab/auth/auth_finders.rb
+++ b/lib/gitlab/auth/auth_finders.rb
@@ -342,6 +342,10 @@ module Gitlab
Gitlab::PathRegex.repository_git_lfs_route_regex.match?(current_request.path)
end
+ def git_or_lfs_request?
+ git_request? || git_lfs_request?
+ end
+
def archive_request?
current_request.path.include?('/-/archive/')
end
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index dfc682e8a5c..08214bbd449 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -35,13 +35,31 @@ module Gitlab
find_user_from_static_object_token(request_format) ||
find_user_from_basic_auth_job ||
find_user_from_job_token ||
- find_user_from_lfs_token ||
- find_user_from_personal_access_token ||
- find_user_from_basic_auth_password
+ find_user_from_personal_access_token_for_api_or_git ||
+ find_user_for_git_or_lfs_request
rescue Gitlab::Auth::AuthenticationError
nil
end
+ # To prevent Rack Attack from incorrectly rate limiting
+ # authenticated Git activity, we need to authenticate the user
+ # from other means (e.g. HTTP Basic Authentication) only if the
+ # request originated from a Git or Git LFS
+ # request. Repositories::GitHttpClientController or
+ # Repositories::LfsApiController normally does the authentication,
+ # but Rack Attack runs before those controllers.
+ def find_user_for_git_or_lfs_request
+ return unless git_or_lfs_request?
+
+ find_user_from_lfs_token || find_user_from_basic_auth_password
+ end
+
+ def find_user_from_personal_access_token_for_api_or_git
+ return unless api_request? || git_or_lfs_request?
+
+ find_user_from_personal_access_token
+ end
+
def valid_access_token?(scopes: [])
validate_access_token!(scopes: scopes)
diff --git a/lib/gitlab/auth/two_factor_auth_verifier.rb b/lib/gitlab/auth/two_factor_auth_verifier.rb
index 86552ef1267..5a203a1fe9c 100644
--- a/lib/gitlab/auth/two_factor_auth_verifier.rb
+++ b/lib/gitlab/auth/two_factor_auth_verifier.rb
@@ -9,6 +9,10 @@ module Gitlab
@current_user = current_user
end
+ def two_factor_authentication_enforced?
+ two_factor_authentication_required? && two_factor_grace_period_expired?
+ end
+
def two_factor_authentication_required?
Gitlab::CurrentSettings.require_two_factor_authentication? ||
current_user&.require_two_factor_authentication_from_group?
diff --git a/lib/gitlab/fogbugz_import.rb b/lib/gitlab/fogbugz_import.rb
new file mode 100644
index 00000000000..a4a52edd83e
--- /dev/null
+++ b/lib/gitlab/fogbugz_import.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'fogbugz'
+
+module Gitlab
+ module FogbugzImport
+ # Custom adapter to validate the URL before each request
+ # This way we avoid DNS rebinds or other unsafe requests
+ ::Fogbugz.adapter[:http] = HttpAdapter
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/client.rb b/lib/gitlab/fogbugz_import/client.rb
index dd747a79673..024c1ae0439 100644
--- a/lib/gitlab/fogbugz_import/client.rb
+++ b/lib/gitlab/fogbugz_import/client.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require 'fogbugz'
-
module Gitlab
module FogbugzImport
class Client
diff --git a/lib/gitlab/fogbugz_import/http_adapter.rb b/lib/gitlab/fogbugz_import/http_adapter.rb
new file mode 100644
index 00000000000..bfae7a10f5b
--- /dev/null
+++ b/lib/gitlab/fogbugz_import/http_adapter.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module FogbugzImport
+ class HttpAdapter
+ def initialize(options = {})
+ @root_url = options[:uri]
+ end
+
+ def request(action, options = {})
+ uri = Gitlab::Utils.append_path(@root_url, 'api.asp')
+
+ params = { 'cmd' => action }.merge(options.fetch(:params, {}))
+
+ response = Gitlab::HTTP.post(uri, body: params)
+
+ response.body
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/health_checks/redis/rate_limiting_check.rb b/lib/gitlab/health_checks/redis/rate_limiting_check.rb
new file mode 100644
index 00000000000..67c14e26361
--- /dev/null
+++ b/lib/gitlab/health_checks/redis/rate_limiting_check.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module HealthChecks
+ module Redis
+ class RateLimitingCheck
+ extend SimpleAbstractCheck
+
+ class << self
+ def check_up
+ check
+ end
+
+ private
+
+ def metric_prefix
+ 'redis_rate_limiting_ping'
+ end
+
+ def successful?(result)
+ result == 'PONG'
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def check
+ catch_timeout 10.seconds do
+ Gitlab::Redis::RateLimiting.with(&:ping)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/health_checks/redis/redis_check.rb b/lib/gitlab/health_checks/redis/redis_check.rb
index 44b85bf886e..2fa39308b9a 100644
--- a/lib/gitlab/health_checks/redis/redis_check.rb
+++ b/lib/gitlab/health_checks/redis/redis_check.rb
@@ -21,7 +21,8 @@ module Gitlab
::Gitlab::HealthChecks::Redis::CacheCheck.check_up &&
::Gitlab::HealthChecks::Redis::QueuesCheck.check_up &&
::Gitlab::HealthChecks::Redis::SharedStateCheck.check_up &&
- ::Gitlab::HealthChecks::Redis::TraceChunksCheck.check_up
+ ::Gitlab::HealthChecks::Redis::TraceChunksCheck.check_up &&
+ ::Gitlab::HealthChecks::Redis::RateLimitingCheck.check_up
end
end
end
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index 630f918a78b..f7ab1677001 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -37,6 +37,7 @@ excluded_attributes:
- :trial_ends_on
- :shared_runners_minute_limit
- :extra_shared_runners_minutes_limit
+ - :repository_size_limit
epics:
- :state_id
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index fe0974d27a6..8046fedc4f3 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -88,7 +88,6 @@ tree:
- :external_pull_request
- :merge_request
- :auto_devops
- - :triggers
- :pipeline_schedules
- :container_expiration_policy
- protected_branches:
@@ -211,6 +210,7 @@ excluded_attributes:
- :show_default_award_emojis
- :services
- :exported_protected_branches
+ - :repository_size_limit
namespaces:
- :runners_token
- :runners_token_encrypted
diff --git a/lib/gitlab/instrumentation/redis.rb b/lib/gitlab/instrumentation/redis.rb
index ab0e56adc32..ea1d54ff867 100644
--- a/lib/gitlab/instrumentation/redis.rb
+++ b/lib/gitlab/instrumentation/redis.rb
@@ -9,8 +9,9 @@ module Gitlab
Queues = Class.new(RedisBase)
SharedState = Class.new(RedisBase).enable_redis_cluster_validation
TraceChunks = Class.new(RedisBase).enable_redis_cluster_validation
+ RateLimiting = Class.new(RedisBase).enable_redis_cluster_validation
- STORAGES = [ActionCable, Cache, Queues, SharedState, TraceChunks].freeze
+ STORAGES = [ActionCable, Cache, Queues, SharedState, TraceChunks, RateLimiting].freeze
# Milliseconds represented in seconds (from 1 millisecond to 2 seconds).
QUERY_TIME_BUCKETS = [0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2].freeze
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index 4482610523e..48a8e0ce6d7 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -8,9 +8,10 @@ module Gitlab
attr_reader :access_token, :host, :api_version, :wait_for_rate_limit_reset
- def initialize(access_token, host: nil, api_version: 'v3', wait_for_rate_limit_reset: true)
+ def initialize(access_token, host: nil, api_version: 'v3', wait_for_rate_limit_reset: true, hostname: nil)
@access_token = access_token
@host = host.to_s.sub(%r{/+\z}, '')
+ @hostname = hostname
@api_version = api_version
@users = {}
@wait_for_rate_limit_reset = wait_for_rate_limit_reset
@@ -28,7 +29,8 @@ module Gitlab
# If there is no config, we're connecting to github.com and we
# should verify ssl.
connection_options: {
- ssl: { verify: config ? config['verify_ssl'] : true }
+ ssl: { verify: config ? config['verify_ssl'] : true },
+ headers: { host: @hostname }.compact
}
)
end
diff --git a/lib/gitlab/metrics/dashboard/service_selector.rb b/lib/gitlab/metrics/dashboard/service_selector.rb
index 641c0c76f8f..6d4b49676e5 100644
--- a/lib/gitlab/metrics/dashboard/service_selector.rb
+++ b/lib/gitlab/metrics/dashboard/service_selector.rb
@@ -30,7 +30,7 @@ module Gitlab
# Returns a class which inherits from the BaseService
# class that can be used to obtain a dashboard for
# the provided params.
- # @return [Gitlab::Metrics::Dashboard::Services::BaseService]
+ # @return [Metrics::Dashboard::BaseService]
def call(params)
service = services.find do |service_class|
service_class.valid_params?(params)
diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb
index 98b66080b42..a2c7b5e29db 100644
--- a/lib/gitlab/redis/cache.rb
+++ b/lib/gitlab/redis/cache.rb
@@ -5,12 +5,15 @@ module Gitlab
class Cache < ::Gitlab::Redis::Wrapper
CACHE_NAMESPACE = 'cache:gitlab'
- private
-
- def raw_config_hash
- config = super
- config[:url] = 'redis://localhost:6380' if config[:url].blank?
- config
+ # Full list of options:
+ # https://api.rubyonrails.org/classes/ActiveSupport/Cache/RedisCacheStore.html#method-c-new
+ def self.active_support_config
+ {
+ redis: pool,
+ compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
+ namespace: CACHE_NAMESPACE,
+ expires_in: 2.weeks # Cache should not grow forever
+ }
end
end
end
diff --git a/lib/gitlab/redis/queues.rb b/lib/gitlab/redis/queues.rb
index 9e291a73bb6..e60e59dcf01 100644
--- a/lib/gitlab/redis/queues.rb
+++ b/lib/gitlab/redis/queues.rb
@@ -2,21 +2,12 @@
# We need this require for MailRoom
require_relative 'wrapper' unless defined?(::Gitlab::Redis::Wrapper)
-require 'active_support/core_ext/object/blank'
module Gitlab
module Redis
class Queues < ::Gitlab::Redis::Wrapper
SIDEKIQ_NAMESPACE = 'resque:gitlab'
MAILROOM_NAMESPACE = 'mail_room:gitlab'
-
- private
-
- def raw_config_hash
- config = super
- config[:url] = 'redis://localhost:6381' if config[:url].blank?
- config
- end
end
end
end
diff --git a/lib/gitlab/redis/rate_limiting.rb b/lib/gitlab/redis/rate_limiting.rb
new file mode 100644
index 00000000000..44681b4d5be
--- /dev/null
+++ b/lib/gitlab/redis/rate_limiting.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Redis
+ class RateLimiting < ::Gitlab::Redis::Wrapper
+ # The data we store on RateLimiting used to be stored on Cache.
+ def self.config_fallback
+ Cache
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/redis/shared_state.rb b/lib/gitlab/redis/shared_state.rb
index d62516bd287..1250eabb041 100644
--- a/lib/gitlab/redis/shared_state.rb
+++ b/lib/gitlab/redis/shared_state.rb
@@ -7,14 +7,6 @@ module Gitlab
USER_SESSIONS_NAMESPACE = 'session:user:gitlab'
USER_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:user:gitlab'
IP_SESSIONS_LOOKUP_NAMESPACE = 'session:lookup:ip:gitlab2'
-
- private
-
- def raw_config_hash
- config = super
- config[:url] = 'redis://localhost:6382' if config[:url].blank?
- config
- end
end
end
end
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index 3c8ac07215d..db1e24a3201 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -6,6 +6,7 @@
# Rails.
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/module/delegation'
+require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/inflections'
# Explicitly load Redis::Store::Factory so we can read Redis configuration in
@@ -150,11 +151,35 @@ module Gitlab
def raw_config_hash
config_data = fetch_config
- if config_data
- config_data.is_a?(String) ? { url: config_data } : config_data.deep_symbolize_keys
- else
- { url: '' }
+ config_hash =
+ if config_data
+ config_data.is_a?(String) ? { url: config_data } : config_data.deep_symbolize_keys
+ else
+ { url: '' }
+ end
+
+ if config_hash[:url].blank?
+ config_hash[:url] = legacy_fallback_urls[self.class.store_name] || legacy_fallback_urls[self.class.config_fallback.store_name]
end
+
+ config_hash
+ end
+
+ # These URLs were defined for cache, queues, and shared_state in
+ # code. They are used only when no config file exists at all for a
+ # given instance. The configuration does not seem particularly
+ # useful - it uses different ports on localhost - but we cannot
+ # confidently delete it as we don't know if any instances rely on
+ # this.
+ #
+ # DO NOT ADD new instances here. All new instances should define a
+ # `.config_fallback`, which will then be used to look up this URL.
+ def legacy_fallback_urls
+ {
+ 'Cache' => 'redis://localhost:6380',
+ 'Queues' => 'redis://localhost:6381',
+ 'SharedState' => 'redis://localhost:6382'
+ }
end
def fetch_config
diff --git a/lib/gitlab/string_regex_marker.rb b/lib/gitlab/string_regex_marker.rb
index f1982ff914c..8e0167a433e 100644
--- a/lib/gitlab/string_regex_marker.rb
+++ b/lib/gitlab/string_regex_marker.rb
@@ -2,18 +2,20 @@
module Gitlab
class StringRegexMarker < StringRangeMarker
- # rubocop: disable CodeReuse/ActiveRecord
def mark(regex, group: 0, &block)
ranges = []
+ offset = 0
- raw_line.scan(regex) do
- begin_index, end_index = Regexp.last_match.offset(group)
+ while match = regex.match(raw_line[offset..])
+ begin_index = match.begin(group) + offset
+ end_index = match.end(group) + offset
ranges << (begin_index..(end_index - 1))
+
+ offset = end_index
end
super(ranges, &block)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/tasks/frontend.rake b/lib/tasks/frontend.rake
index b2d2c4e3f2b..95f26cc0800 100644
--- a/lib/tasks/frontend.rake
+++ b/lib/tasks/frontend.rake
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+require 'yaml'
unless Rails.env.production?
namespace :frontend do
@@ -13,12 +14,28 @@ unless Rails.env.production?
t.rspec_opts = '--format documentation'
end
+ desc 'GitLab | Frontend | Generate fixtures for JavaScript integration tests'
+ RSpec::Core::RakeTask.new(:mock_server_rspec_fixtures) do |t, args|
+ base_path = Pathname.new('spec/frontend_integration/fixture_generators.yml')
+ ee_path = Pathname.new('ee') + base_path
+
+ fixtures = YAML.safe_load(base_path.read)
+ fixtures.concat(Array(YAML.safe_load(ee_path.read))) if Gitlab.ee? && ee_path.exist?
+
+ t.pattern = fixtures.join(',')
+ ENV['NO_KNAPSACK'] = 'true'
+ t.rspec_opts = '--format documentation'
+ end
+
desc 'GitLab | Frontend | Run JavaScript tests'
task tests: ['yarn:check'] do
sh "yarn test" do |ok, res|
abort('rake frontend:tests failed') unless ok
end
end
+
+ desc 'GitLab | Frontend | Shortcut for generating all fixtures used by MirajeJS mock server'
+ task mock_server_fixtures: ['frontend:mock_server_rspec_fixtures', 'gitlab:graphql:schema:dump']
end
desc 'GitLab | Frontend | Shortcut for frontend:fixtures and frontend:tests'
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f32e19b726a..b5b3932a70b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2285,6 +2285,9 @@ msgstr ""
msgid "AdminArea|Features"
msgstr ""
+msgid "AdminArea|Get security updates from GitLab and stay up to date"
+msgstr ""
+
msgid "AdminArea|Groups"
msgstr ""
@@ -2324,6 +2327,12 @@ msgstr ""
msgid "AdminArea|Reporter"
msgstr ""
+msgid "AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates."
+msgstr ""
+
+msgid "AdminArea|Sign up for the GitLab newsletter"
+msgstr ""
+
msgid "AdminArea|Stop all jobs"
msgstr ""
@@ -38575,6 +38584,9 @@ msgstr ""
msgid "You are already a member of this %{member_source}."
msgstr ""
+msgid "You are already impersonating another user"
+msgstr ""
+
msgid "You are an admin, which means granting access to %{client_name} will allow them to interact with GitLab as an admin as well. Proceed with caution."
msgstr ""
@@ -39289,6 +39301,9 @@ msgstr ""
msgid "Your commit email is used for web based operations, such as edits and merges."
msgstr ""
+msgid "Your current password is required to register a two-factor authenticator app."
+msgstr ""
+
msgid "Your dashboard has been copied. You can %{web_ide_link_start}edit it here%{web_ide_link_end}."
msgstr ""
diff --git a/package.json b/package.json
index 5f2c72beea2..6dee235a575 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"postinstall": "node ./scripts/frontend/postinstall.js",
"storybook:install": "yarn --cwd ./storybook install",
"storybook:build": "yarn --cwd ./storybook build",
- "storybook:start": "yarn --cwd ./storybook start",
+ "storybook:start": "./scripts/frontend/start_storybook.sh",
"stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
"webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js",
"webpack-vendor": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.vendor.config.js",
diff --git a/qa/Gemfile b/qa/Gemfile
index 9b1735fe646..aae3c81bd83 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -26,7 +26,7 @@ gem 'webdrivers', '~> 4.6'
gem 'zeitwerk', '~> 2.4'
gem 'influxdb-client', '~> 1.17'
-gem 'chemlab', '~> 0.8'
+gem 'chemlab', '~> 0.9'
gem 'chemlab-library-www-gitlab-com', '~> 0.1'
gem 'deprecation_toolkit', '~> 1.5.1', require: false
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index ded3abe74b4..f105274b3ac 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -41,7 +41,7 @@ GEM
capybara-screenshot (1.0.23)
capybara (>= 1.0, < 4)
launchy
- chemlab (0.8.1)
+ chemlab (0.9.1)
colorize (~> 0.8)
i18n (~> 1.8)
rake (>= 12, < 14)
@@ -244,7 +244,7 @@ DEPENDENCIES
allure-rspec (~> 2.14.5)
capybara (~> 3.35.0)
capybara-screenshot (~> 1.0.23)
- chemlab (~> 0.8)
+ chemlab (~> 0.9)
chemlab-library-www-gitlab-com (~> 0.1)
deprecation_toolkit (~> 1.5.1)
faker (~> 2.19, >= 2.19.0)
diff --git a/qa/chemlab-library-gitlab.gemspec b/qa/chemlab-library-gitlab.gemspec
index d42d0e5e926..34a55ba8927 100644
--- a/qa/chemlab-library-gitlab.gemspec
+++ b/qa/chemlab-library-gitlab.gemspec
@@ -4,7 +4,7 @@ $:.unshift(File.expand_path('lib', __dir__))
Gem::Specification.new do |spec|
spec.name = 'chemlab-library-gitlab'
- spec.version = '0.2.0'
+ spec.version = '0.3.0'
spec.authors = ['GitLab Quality']
spec.email = ['quality@gitlab.com']
@@ -18,5 +18,5 @@ Gem::Specification.new do |spec|
spec.require_paths = ['lib']
- spec.add_runtime_dependency 'chemlab', '~> 0.8'
+ spec.add_runtime_dependency 'chemlab', '~> 0.9'
end
diff --git a/qa/lib/gitlab/page/main/login.rb b/qa/lib/gitlab/page/main/login.rb
index 9f20a040550..de05df1a086 100644
--- a/qa/lib/gitlab/page/main/login.rb
+++ b/qa/lib/gitlab/page/main/login.rb
@@ -10,11 +10,27 @@ module Gitlab
text_field :password_field
button :sign_in_button
- def sign_in_as(username:, password:)
+ button :accept_terms, text: 'Accept terms'
+
+ # password change tab
+ text_field :password_confirmation_field
+ button :change_password_button
+
+ # Sign in using a given username and password
+ # @note this will also automatically accept terms if prompted
+ # @param [String] username the username to sign in with
+ # @param [String] password the password to sign in with
+ # @example
+ # Page::Main::Login.perform do |login|
+ # login.sign_in_as(username: 'username', password: 'password')
+ # login.sign_in_as(username: 'username', password: 'password', accept_terms: false)
+ # end
+ def sign_in_as(username:, password:, accept_terms: true)
self.login_field = username
self.password_field = password
sign_in_button
+ self.accept_terms if accept_terms && accept_terms?
end
end
end
diff --git a/qa/lib/gitlab/page/main/login.stub.rb b/qa/lib/gitlab/page/main/login.stub.rb
index a4cef291616..a819ca4bcc8 100644
--- a/qa/lib/gitlab/page/main/login.stub.rb
+++ b/qa/lib/gitlab/page/main/login.stub.rb
@@ -95,6 +95,88 @@ module Gitlab
def sign_in_button?
# This is a stub, used for indexing. The method is dynamically generated.
end
+
+ # @note Defined as +button :accept_terms+
+ # Clicks +accept_terms+
+ def accept_terms
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login.accept_terms_element).to exist
+ # end
+ # @return [Watir::Button] The raw +Button+ element
+ def accept_terms_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login).to be_accept_terms
+ # end
+ # @return [Boolean] true if the +accept_terms+ element is present on the page
+ def accept_terms?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +text_field :password_confirmation_field+
+ # @return [String] The text content or value of +password_confirmation_field+
+ def password_confirmation_field
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # Set the value of password_confirmation_field
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # login.password_confirmation_field = 'value'
+ # end
+ # @param value [String] The value to set.
+ def password_confirmation_field=(value)
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login.password_confirmation_field_element).to exist
+ # end
+ # @return [Watir::TextField] The raw +TextField+ element
+ def password_confirmation_field_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login).to be_password_confirmation_field
+ # end
+ # @return [Boolean] true if the +password_confirmation_field+ element is present on the page
+ def password_confirmation_field?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @note Defined as +button :change_password_button+
+ # Clicks +change_password_button+
+ def change_password_button
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login.change_password_button_element).to exist
+ # end
+ # @return [Watir::Button] The raw +Button+ element
+ def change_password_button_element
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
+
+ # @example
+ # Gitlab::Page::Main::Login.perform do |login|
+ # expect(login).to be_change_password_button
+ # end
+ # @return [Boolean] true if the +change_password_button+ element is present on the page
+ def change_password_button?
+ # This is a stub, used for indexing. The method is dynamically generated.
+ end
end
end
end
diff --git a/scripts/frontend/start_storybook.sh b/scripts/frontend/start_storybook.sh
new file mode 100755
index 00000000000..7ae0a21b15b
--- /dev/null
+++ b/scripts/frontend/start_storybook.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+bold=$(tput bold)
+normal=$(tput sgr0)
+
+echo -e "Storybook provides a mock server that allows creating stories for components that make HTTP requests."
+echo -e "${bold}Storybook will fail to start if it can’t find the fixtures used by the mock server.${normal}\n"
+read -rp "Would you like to generate/update the frontend fixtures used by the mock server (y/N)? " answer
+
+if [[ "$answer" =~ ^(Y|y)$ ]] ; then
+ bundle exec rake frontend:mock_server_fixtures
+fi
+
+if ! [[ -d storybook/node_modules ]] ; then
+ yarn storybook:install
+fi
+
+yarn --cwd ./storybook start
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index f3b9ac56082..4b528696322 100644
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -38,18 +38,6 @@ sed -i 's|url:.*$|url: redis://redis:6379|g' config/cable.yml
cp config/resque.yml.example config/resque.yml
sed -i 's|url:.*$|url: redis://redis:6379|g' config/resque.yml
-cp config/redis.cache.yml.example config/redis.cache.yml
-sed -i 's|url:.*$|url: redis://redis:6379/10|g' config/redis.cache.yml
-
-cp config/redis.queues.yml.example config/redis.queues.yml
-sed -i 's|url:.*$|url: redis://redis:6379/11|g' config/redis.queues.yml
-
-cp config/redis.shared_state.yml.example config/redis.shared_state.yml
-sed -i 's|url:.*$|url: redis://redis:6379/12|g' config/redis.shared_state.yml
-
-cp config/redis.trace_chunks.yml.example config/redis.trace_chunks.yml
-sed -i 's|url:.*$|url: redis://redis:6379/13|g' config/redis.trace_chunks.yml
-
if [ "$SETUP_DB" != "false" ]; then
setup_db
elif getent hosts postgres; then
diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb
index 744c0712d6b..ccf4454c349 100644
--- a/spec/controllers/admin/impersonations_controller_spec.rb
+++ b/spec/controllers/admin/impersonations_controller_spec.rb
@@ -92,6 +92,14 @@ RSpec.describe Admin::ImpersonationsController do
expect(warden.user).to eq(impersonator)
end
+
+ it 'clears token session keys' do
+ session[:bitbucket_token] = SecureRandom.hex(8)
+
+ delete :destroy
+
+ expect(session[:bitbucket_token]).to be_nil
+ end
end
# base case
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 015c36c9335..3a2b5dcb99d 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -794,6 +794,14 @@ RSpec.describe Admin::UsersController do
expect(flash[:alert]).to eq("You are now impersonating #{user.username}")
end
+
+ it 'clears token session keys' do
+ session[:github_access_token] = SecureRandom.hex(8)
+
+ post :impersonate, params: { id: user.username }
+
+ expect(session[:github_access_token]).to be_nil
+ end
end
context "when impersonation is disabled" do
@@ -807,5 +815,20 @@ RSpec.describe Admin::UsersController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ context 'when impersonating an admin and attempting to impersonate again' do
+ let(:admin2) { create(:admin) }
+
+ before do
+ post :impersonate, params: { id: admin2.username }
+ end
+
+ it 'does not allow double impersonation', :aggregate_failures do
+ post :impersonate, params: { id: user.username }
+
+ expect(flash[:alert]).to eq(_('You are already impersonating another user'))
+ expect(warden.user).to eq(admin2)
+ end
+ end
end
end
diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb
index 3e4b159271a..568712d29cb 100644
--- a/spec/controllers/import/gitea_controller_spec.rb
+++ b/spec/controllers/import/gitea_controller_spec.rb
@@ -54,6 +54,48 @@ RSpec.describe Import::GiteaController do
end
end
end
+
+ context 'when DNS Rebinding protection is enabled' do
+ let(:token) { 'gitea token' }
+
+ let(:ip_uri) { 'http://167.99.148.217' }
+ let(:uri) { 'try.gitea.io' }
+ let(:https_uri) { "https://#{uri}" }
+ let(:http_uri) { "http://#{uri}" }
+
+ before do
+ session[:gitea_access_token] = token
+
+ allow(Gitlab::UrlBlocker).to receive(:validate!).with(https_uri, anything).and_return([Addressable::URI.parse(https_uri), uri])
+ allow(Gitlab::UrlBlocker).to receive(:validate!).with(http_uri, anything).and_return([Addressable::URI.parse(ip_uri), uri])
+
+ allow(Gitlab::LegacyGithubImport::Client).to receive(:new).and_return(double('Gitlab::LegacyGithubImport::Client', repos: [], orgs: []))
+ end
+
+ context 'when provided host url is using https' do
+ let(:host_url) { https_uri }
+
+ it 'uses unchanged host url to send request to Gitea' do
+ expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with(token, host: https_uri, api_version: 'v1', hostname: 'try.gitea.io')
+
+ get :status, format: :json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+
+ context 'when provided host url is using http' do
+ let(:host_url) { http_uri }
+
+ it 'uses changed host url to send request to Gitea' do
+ expect(Gitlab::LegacyGithubImport::Client).to receive(:new).with(token, host: 'http://167.99.148.217', api_version: 'v1', hostname: 'try.gitea.io')
+
+ get :status, format: :json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
end
end
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
index f21ef324884..5bf3b4c48bf 100644
--- a/spec/controllers/oauth/applications_controller_spec.rb
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -98,6 +98,19 @@ RSpec.describe Oauth::ApplicationsController do
end
describe 'POST #create' do
+ let(:oauth_params) do
+ {
+ doorkeeper_application: {
+ name: 'foo',
+ redirect_uri: redirect_uri,
+ scopes: scopes
+ }
+ }
+ end
+
+ let(:redirect_uri) { 'http://example.org' }
+ let(:scopes) { ['api'] }
+
subject { post :create, params: oauth_params }
it 'creates an application' do
@@ -116,38 +129,42 @@ RSpec.describe Oauth::ApplicationsController do
expect(response).to redirect_to(profile_path)
end
- context 'redirect_uri' do
+ context 'when redirect_uri is invalid' do
+ let(:redirect_uri) { 'javascript://alert()' }
+
render_views
it 'shows an error for a forbidden URI' do
- invalid_uri_params = {
- doorkeeper_application: {
- name: 'foo',
- redirect_uri: 'javascript://alert()',
- scopes: ['api']
- }
- }
-
- post :create, params: invalid_uri_params
+ subject
expect(response.body).to include 'Redirect URI is forbidden by the server'
+ expect(response).to render_template('doorkeeper/applications/index')
end
end
context 'when scopes are not present' do
+ let(:scopes) { [] }
+
render_views
it 'shows an error for blank scopes' do
- invalid_uri_params = {
- doorkeeper_application: {
- name: 'foo',
- redirect_uri: 'http://example.org'
- }
- }
-
- post :create, params: invalid_uri_params
+ subject
expect(response.body).to include 'Scopes can't be blank'
+ expect(response).to render_template('doorkeeper/applications/index')
+ end
+ end
+
+ context 'when scopes are invalid' do
+ let(:scopes) { %w(api foo) }
+
+ render_views
+
+ it 'shows an error for invalid scopes' do
+ subject
+
+ expect(response.body).to include 'Scopes doesn't match configured on the server.'
+ expect(response).to render_template('doorkeeper/applications/index')
end
end
@@ -185,14 +202,4 @@ RSpec.describe Oauth::ApplicationsController do
def disable_user_oauth
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:user_oauth_applications?).and_return(false)
end
-
- def oauth_params
- {
- doorkeeper_application: {
- name: 'foo',
- redirect_uri: 'http://example.org',
- scopes: ['api']
- }
- }
- end
end
diff --git a/spec/controllers/profiles/two_factor_auths_controller_spec.rb b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
index 073180cbafd..a0e2cf671af 100644
--- a/spec/controllers/profiles/two_factor_auths_controller_spec.rb
+++ b/spec/controllers/profiles/two_factor_auths_controller_spec.rb
@@ -35,6 +35,27 @@ RSpec.describe Profiles::TwoFactorAuthsController do
end
end
+ shared_examples 'user must enter a valid current password' do
+ let(:current_password) { '123' }
+
+ it 'requires the current password', :aggregate_failures do
+ go
+
+ expect(response).to redirect_to(profile_two_factor_auth_path)
+ expect(flash[:alert]).to eq(_('You must provide a valid current password'))
+ end
+
+ context 'when the user is on the last sign in attempt' do
+ it do
+ user.update!(failed_attempts: User.maximum_attempts.pred)
+
+ go
+
+ expect(user.reload).to be_access_locked
+ end
+ end
+ end
+
describe 'GET show' do
let_it_be_with_reload(:user) { create(:user) }
@@ -69,9 +90,10 @@ RSpec.describe Profiles::TwoFactorAuthsController do
let_it_be_with_reload(:user) { create(:user) }
let(:pin) { 'pin-code' }
+ let(:current_password) { user.password }
def go
- post :create, params: { pin_code: pin }
+ post :create, params: { pin_code: pin, current_password: current_password }
end
context 'with valid pin' do
@@ -136,21 +158,25 @@ RSpec.describe Profiles::TwoFactorAuthsController do
end
end
+ it_behaves_like 'user must enter a valid current password'
+
it_behaves_like 'user must first verify their primary email address'
end
describe 'POST codes' do
let_it_be_with_reload(:user) { create(:user, :two_factor) }
+ let(:current_password) { user.password }
+
it 'presents plaintext codes for the user to save' do
expect(user).to receive(:generate_otp_backup_codes!).and_return(%w(a b c))
- post :codes
+ post :codes, params: { current_password: current_password }
expect(assigns[:codes]).to match_array %w(a b c)
end
it 'persists the generated codes' do
- post :codes
+ post :codes, params: { current_password: current_password }
user.reload
expect(user.otp_backup_codes).not_to be_empty
@@ -159,12 +185,18 @@ RSpec.describe Profiles::TwoFactorAuthsController do
it 'dismisses the `TWO_FACTOR_AUTH_RECOVERY_SETTINGS_CHECK` callout' do
expect(controller.helpers).to receive(:dismiss_two_factor_auth_recovery_settings_check)
- post :codes
+ post :codes, params: { current_password: current_password }
+ end
+
+ it_behaves_like 'user must enter a valid current password' do
+ let(:go) { post :codes, params: { current_password: current_password } }
end
end
describe 'DELETE destroy' do
- subject { delete :destroy }
+ subject { delete :destroy, params: { current_password: current_password } }
+
+ let(:current_password) { user.password }
context 'for a user that has 2FA enabled' do
let_it_be_with_reload(:user) { create(:user, :two_factor) }
@@ -187,6 +219,10 @@ RSpec.describe Profiles::TwoFactorAuthsController do
expect(flash[:notice])
.to eq _('Two-factor authentication has been disabled successfully!')
end
+
+ it_behaves_like 'user must enter a valid current password' do
+ let(:go) { delete :destroy, params: { current_password: current_password } }
+ end
end
context 'for a user that does not have 2FA enabled' do
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index be5c1f0d428..c352524ec14 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -624,9 +624,9 @@ RSpec.describe Projects::ProjectMembersController do
end
end
- context 'when user can access source project members' do
+ context 'when user can admin source project members' do
before do
- another_project.add_guest(user)
+ another_project.add_maintainer(user)
end
include_context 'import applied'
@@ -640,7 +640,11 @@ RSpec.describe Projects::ProjectMembersController do
end
end
- context 'when user is not member of a source project' do
+ context "when user can't admin source project members" do
+ before do
+ another_project.add_developer(user)
+ end
+
include_context 'import applied'
it 'does not import team members' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index a7aa0f1a1b8..2bb5fad9231 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -419,6 +419,47 @@ RSpec.describe ProjectsController do
end
end
+ describe 'POST create' do
+ let!(:params) do
+ {
+ path: 'foo',
+ description: 'bar',
+ import_url: project.http_url_to_repo,
+ namespace_id: user.namespace.id
+ }
+ end
+
+ subject { post :create, params: { project: params } }
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when import by url is disabled' do
+ before do
+ stub_application_setting(import_sources: [])
+ end
+
+ it 'does not create project and reports an error' do
+ expect { subject }.not_to change { Project.count }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when import by url is enabled' do
+ before do
+ stub_application_setting(import_sources: ['git'])
+ end
+
+ it 'creates project' do
+ expect { subject }.to change { Project.count }
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ end
+ end
+ end
+
describe 'GET edit' do
it 'allows an admin user to access the page', :enable_admin_mode do
sign_in(create(:user, :admin))
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 043fd97f1ad..2aa9b86b20e 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -666,6 +666,6 @@ RSpec.describe UploadsController do
def post_authorize(verified: true)
request.headers.merge!(workhorse_internal_api_request_header) if verified
- post :authorize, params: { model: 'personal_snippet', id: model.id }, format: :json
+ post :authorize, params: params, format: :json
end
end
diff --git a/spec/features/callouts/security_newsletter_callout_spec.rb b/spec/features/callouts/security_newsletter_callout_spec.rb
new file mode 100644
index 00000000000..b17bb372456
--- /dev/null
+++ b/spec/features/callouts/security_newsletter_callout_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Security newsletter callout', :js do
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:non_admin) { create(:user) }
+
+ shared_examples 'hidden callout' do
+ it 'does not display callout' do
+ expect(page).not_to have_content 'Sign up for the GitLab Security Newsletter to get notified for security updates.'
+ end
+ end
+
+ context 'when an admin is logged in' do
+ before do
+ sign_in(admin)
+ gitlab_enable_admin_mode_sign_in(admin)
+
+ visit admin_root_path
+ end
+
+ it 'displays callout' do
+ expect(page).to have_content 'Sign up for the GitLab Security Newsletter to get notified for security updates.'
+ expect(page).to have_link 'Sign up for the GitLab newsletter', href: 'https://about.gitlab.com/company/preference-center/'
+ end
+
+ context 'when link is clicked' do
+ before do
+ find_link('Sign up for the GitLab newsletter').click
+
+ visit admin_root_path
+ end
+
+ it_behaves_like 'hidden callout'
+ end
+
+ context 'when callout is dismissed' do
+ before do
+ find('[data-testid="close-security-newsletter-callout"]').click
+
+ visit admin_root_path
+ end
+
+ it_behaves_like 'hidden callout'
+ end
+ end
+
+ context 'when a non-admin is logged in' do
+ before do
+ sign_in(non_admin)
+ visit admin_root_path
+ end
+
+ it_behaves_like 'hidden callout'
+ end
+end
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index 275a87ca391..d2bde320c54 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User selects branches for new MR', :js do
click_button "Check out branch"
- expect(page).to have_content 'git checkout -b "orphaned-branch" "origin/orphaned-branch"'
+ expect(page).to have_content 'git checkout -b \'orphaned-branch\' \'origin/orphaned-branch\''
end
it 'allows filtering multiple dropdowns' do
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index c9059395377..893dd2c76e0 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -78,40 +78,80 @@ RSpec.describe 'Profile > Password' do
end
end
- context 'Change passowrd' do
+ context 'Change password' do
+ let(:new_password) { '22233344' }
+
before do
sign_in(user)
visit(edit_profile_password_path)
end
- it 'does not change user passowrd without old one' do
- page.within '.update-password' do
- fill_passwords('22233344', '22233344')
+ shared_examples 'user enters an incorrect current password' do
+ subject do
+ page.within '.update-password' do
+ fill_in 'user_current_password', with: user_current_password
+ fill_passwords(new_password, new_password)
+ end
end
- page.within '.flash-container' do
- expect(page).to have_content 'You must provide a valid current password'
+ it 'handles the invalid password attempt, and prompts the user to try again', :aggregate_failures do
+ expect(Gitlab::AppLogger).to receive(:info)
+ .with(message: 'Invalid current password when attempting to update user password', username: user.username, ip: user.current_sign_in_ip)
+
+ subject
+
+ user.reload
+
+ expect(user.failed_attempts).to eq(1)
+ expect(user.valid_password?(new_password)).to eq(false)
+ expect(current_path).to eq(edit_profile_password_path)
+
+ page.within '.flash-container' do
+ expect(page).to have_content('You must provide a valid current password')
+ end
+ end
+
+ it 'locks the user account when user passes the maximum attempts threshold', :aggregate_failures do
+ user.update!(failed_attempts: User.maximum_attempts.pred)
+
+ subject
+
+ expect(current_path).to eq(new_user_session_path)
+
+ page.within '.flash-container' do
+ expect(page).to have_content('Your account is locked.')
+ end
end
end
- it 'does not change password with invalid old password' do
- page.within '.update-password' do
- fill_in 'user_current_password', with: 'invalid'
- fill_passwords('password', 'confirmation')
- end
+ context 'when current password is blank' do
+ let(:user_current_password) { nil }
- page.within '.flash-container' do
- expect(page).to have_content 'You must provide a valid current password'
- end
+ it_behaves_like 'user enters an incorrect current password'
end
- it 'changes user password' do
- page.within '.update-password' do
- fill_in "user_current_password", with: user.password
- fill_passwords('22233344', '22233344')
+ context 'when current password is incorrect' do
+ let(:user_current_password) {'invalid' }
+
+ it_behaves_like 'user enters an incorrect current password'
+ end
+
+ context 'when the password reset is successful' do
+ subject do
+ page.within '.update-password' do
+ fill_in "user_current_password", with: user.password
+ fill_passwords(new_password, new_password)
+ end
end
- expect(current_path).to eq new_user_session_path
+ it 'changes the password, logs the user out and prompts them to sign in again', :aggregate_failures do
+ expect { subject }.to change { user.reload.valid_password?(new_password) }.to(true)
+ expect(current_path).to eq new_user_session_path
+
+ page.within '.flash-container' do
+ expect(page).to have_content('Password was successfully updated. Please sign in again.')
+ end
+ end
end
end
diff --git a/spec/features/profiles/two_factor_auths_spec.rb b/spec/features/profiles/two_factor_auths_spec.rb
new file mode 100644
index 00000000000..e1feca5031a
--- /dev/null
+++ b/spec/features/profiles/two_factor_auths_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Two factor auths' do
+ context 'when signed in' do
+ before do
+ allow(Gitlab).to receive(:com?) { true }
+ end
+
+ context 'when user has two-factor authentication disabled' do
+ let(:user) { create(:user ) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'requires the current password to set up two factor authentication', :js do
+ visit profile_two_factor_auth_path
+
+ register_2fa(user.reload.current_otp, '123')
+
+ expect(page).to have_content('You must provide a valid current password')
+
+ register_2fa(user.reload.current_otp, user.password)
+
+ expect(page).to have_content('Please copy, download, or print your recovery codes before proceeding.')
+
+ click_button 'Copy codes'
+ click_link 'Proceed'
+
+ expect(page).to have_content('Status: Enabled')
+ end
+ end
+
+ context 'when user has two-factor authentication enabled' do
+ let(:user) { create(:user, :two_factor) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'requires the current_password to disable two-factor authentication', :js do
+ visit profile_two_factor_auth_path
+
+ fill_in 'current_password', with: '123'
+
+ click_button 'Disable two-factor authentication'
+
+ page.accept_alert
+
+ expect(page).to have_content('You must provide a valid current password')
+
+ fill_in 'current_password', with: user.password
+
+ click_button 'Disable two-factor authentication'
+
+ page.accept_alert
+
+ expect(page).to have_content('Two-factor authentication has been disabled successfully!')
+ expect(page).to have_content('Enable two-factor authentication')
+ end
+
+ it 'requires the current_password to regernate recovery codes', :js do
+ visit profile_two_factor_auth_path
+
+ fill_in 'current_password', with: '123'
+
+ click_button 'Regenerate recovery codes'
+
+ expect(page).to have_content('You must provide a valid current password')
+
+ fill_in 'current_password', with: user.password
+
+ click_button 'Regenerate recovery codes'
+
+ expect(page).to have_content('Please copy, download, or print your recovery codes before proceeding.')
+ end
+ end
+
+ def register_2fa(pin, password)
+ fill_in 'pin_code', with: pin
+ fill_in 'current_password', with: password
+
+ click_button 'Register with two-factor app'
+ end
+ end
+end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 2c88860aef2..d9c919dae3d 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -807,6 +807,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_shared_state do
expect(current_path).to eq(profile_two_factor_auth_path)
fill_in 'pin_code', with: user.reload.current_otp
+ fill_in 'current_password', with: user.password
click_button 'Register with two-factor app'
click_button 'Copy codes'
diff --git a/spec/fixtures/api/schemas/public_api/v4/service.json b/spec/fixtures/api/schemas/public_api/v4/integration.json
similarity index 100%
rename from spec/fixtures/api/schemas/public_api/v4/service.json
rename to spec/fixtures/api/schemas/public_api/v4/integration.json
diff --git a/spec/fixtures/api/schemas/public_api/v4/integrations.json b/spec/fixtures/api/schemas/public_api/v4/integrations.json
new file mode 100644
index 00000000000..e7ebe7652c9
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/integrations.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "integration.json" }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/services.json b/spec/fixtures/api/schemas/public_api/v4/services.json
deleted file mode 100644
index 78c59ecfa10..00000000000
--- a/spec/fixtures/api/schemas/public_api/v4/services.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type": "array",
- "items": { "$ref": "service.json" }
-}
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json
index e3aeace6383..1072e63b20b 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json
@@ -7579,23 +7579,6 @@
}
}
],
- "triggers": [
- {
- "id": 123,
- "token": "cdbfasdf44a5958c83654733449e585",
- "project_id": 5,
- "owner_id": 1,
- "created_at": "2017-01-16T15:25:28.637Z",
- "updated_at": "2017-01-16T15:25:28.637Z"
- },
- {
- "id": 456,
- "token": "33a66349b5ad01fc00174af87804e40",
- "project_id": 5,
- "created_at": "2017-01-16T15:25:29.637Z",
- "updated_at": "2017-01-16T15:25:29.637Z"
- }
- ],
"pipeline_schedules": [
{
"id": 1,
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson
index 93619f4fb44..2b5bda687b8 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/triggers.ndjson
@@ -1,2 +1,2 @@
{"id":123,"token":"cdbfasdf44a5958c83654733449e585","project_id":5,"owner_id":1,"created_at":"2017-01-16T15:25:28.637Z","updated_at":"2017-01-16T15:25:28.637Z"}
-{"id":456,"token":"33a66349b5ad01fc00174af87804e40","project_id":5,"created_at":"2017-01-16T15:25:29.637Z","updated_at":"2017-01-16T15:25:29.637Z"}
+{"id":456,"token":"33a66349b5ad01fc00174af87804e40","project_id":5,"created_at":"2017-01-16T15:25:29.637Z","updated_at":"2017-01-16T15:25:29.637Z"}
\ No newline at end of file
diff --git a/spec/frontend/authentication/two_factor_auth/components/__snapshots__/manage_two_factor_form_spec.js.snap b/spec/frontend/authentication/two_factor_auth/components/__snapshots__/manage_two_factor_form_spec.js.snap
new file mode 100644
index 00000000000..3fe0e570a54
--- /dev/null
+++ b/spec/frontend/authentication/two_factor_auth/components/__snapshots__/manage_two_factor_form_spec.js.snap
@@ -0,0 +1,99 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ManageTwoFactorForm Disable button renders the component correctly 1`] = `
+VueWrapper {
+ "_emitted": Object {},
+ "_emittedByOrder": Array [],
+ "isFunctionalComponent": undefined,
+}
+`;
+
+exports[`ManageTwoFactorForm Disable button renders the component correctly 2`] = `
+
+`;
diff --git a/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js b/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js
new file mode 100644
index 00000000000..384579c6876
--- /dev/null
+++ b/spec/frontend/authentication/two_factor_auth/components/manage_two_factor_form_spec.js
@@ -0,0 +1,98 @@
+import { within } from '@testing-library/dom';
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import ManageTwoFactorForm, {
+ i18n,
+} from '~/authentication/two_factor_auth/components/manage_two_factor_form.vue';
+
+describe('ManageTwoFactorForm', () => {
+ let wrapper;
+
+ const createComponent = (options = {}) => {
+ wrapper = extendedWrapper(
+ mount(ManageTwoFactorForm, {
+ provide: {
+ webauthnEnabled: options?.webauthnEnabled || false,
+ profileTwoFactorAuthPath: '2fa_auth_path',
+ profileTwoFactorAuthMethod: '2fa_auth_method',
+ codesProfileTwoFactorAuthPath: '2fa_codes_path',
+ codesProfileTwoFactorAuthMethod: '2fa_codes_method',
+ },
+ }),
+ );
+ };
+
+ const queryByText = (text, options) => within(wrapper.element).queryByText(text, options);
+ const queryByLabelText = (text, options) =>
+ within(wrapper.element).queryByLabelText(text, options);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ describe('Current password field', () => {
+ it('renders the current password field', () => {
+ expect(queryByLabelText(i18n.currentPassword).tagName).toEqual('INPUT');
+ });
+ });
+
+ describe('Disable button', () => {
+ it('renders the component correctly', () => {
+ expect(wrapper).toMatchSnapshot();
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it('has the right confirm text', () => {
+ expect(wrapper.findByTestId('test-2fa-disable-button').element.dataset.confirm).toEqual(
+ i18n.confirm,
+ );
+ });
+
+ describe('when webauthnEnabled', () => {
+ beforeEach(() => {
+ createComponent({
+ webauthnEnabled: true,
+ });
+ });
+
+ it('has the right confirm text', () => {
+ expect(wrapper.findByTestId('test-2fa-disable-button').element.dataset.confirm).toEqual(
+ i18n.confirmWebAuthn,
+ );
+ });
+ });
+
+ it('modifies the form action and method when submitted through the button', async () => {
+ const form = wrapper.find('form');
+ const disableButton = wrapper.findByTestId('test-2fa-disable-button').element;
+ const methodInput = wrapper.findByTestId('test-2fa-method-field').element;
+
+ form.trigger('submit', { submitter: disableButton });
+
+ await wrapper.vm.$nextTick();
+
+ expect(form.element.getAttribute('action')).toEqual('2fa_auth_path');
+ expect(methodInput.getAttribute('value')).toEqual('2fa_auth_method');
+ });
+ });
+
+ describe('Regenerate recovery codes button', () => {
+ it('renders the button', () => {
+ expect(queryByText(i18n.regenerateRecoveryCodes)).toEqual(expect.any(HTMLElement));
+ });
+
+ it('modifies the form action and method when submitted through the button', async () => {
+ const form = wrapper.find('form');
+ const regenerateCodesButton = wrapper.findByTestId('test-2fa-regenerate-codes-button')
+ .element;
+ const methodInput = wrapper.findByTestId('test-2fa-method-field').element;
+
+ form.trigger('submit', { submitter: regenerateCodesButton });
+
+ await wrapper.vm.$nextTick();
+
+ expect(form.element.getAttribute('action')).toEqual('2fa_codes_path');
+ expect(methodInput.getAttribute('value')).toEqual('2fa_codes_method');
+ });
+ });
+});
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 211ed064762..94ad7759110 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -574,6 +574,15 @@ describe('GfmAutoComplete', () => {
}),
).toBe('grp/proj#5 Some Issue');
});
+
+ it('escapes title in the template as it is user input', () => {
+ expect(
+ GfmAutoComplete.Issues.templateFunction({
+ id: 5,
+ title: '${search}