diff --git a/Gemfile.lock b/Gemfile.lock index fa319de1076..3309a317645 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,7 +443,7 @@ GEM jaeger-client (~> 1.1) opentracing (~> 0.4) redis (> 3.0.0, < 5.0.0) - gitlab-license (1.3.0) + gitlab-license (1.3.1) gitlab-mail_room (0.0.8) gitlab-markup (1.7.1) gitlab-net-dns (0.9.1) diff --git a/app/assets/javascripts/issues_list/components/issuable.vue b/app/assets/javascripts/issues_list/components/issuable.vue index b7af6e098e1..a2970bdc43b 100644 --- a/app/assets/javascripts/issues_list/components/issuable.vue +++ b/app/assets/javascripts/issues_list/components/issuable.vue @@ -25,7 +25,7 @@ import { newDateAsLocaleTime, } from '~/lib/utils/datetime_utility'; import { convertToCamelCase } from '~/lib/utils/text_utility'; -import { mergeUrlParams } from '~/lib/utils/url_utility'; +import { mergeUrlParams, setUrlFragment, isExternal } from '~/lib/utils/url_utility'; import { sprintf, __ } from '~/locale'; import initUserPopovers from '~/user_popovers'; import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; @@ -102,8 +102,14 @@ export default { isJiraIssue() { return this.issuable.external_tracker === 'jira'; }, + webUrl() { + return this.issuable.gitlab_web_url || this.issuable.web_url; + }, + isIssuableUrlExternal() { + return isExternal(this.webUrl); + }, linkTarget() { - return this.isJiraIssue ? '_blank' : null; + return this.isIssuableUrlExternal ? '_blank' : null; }, issueCreatedToday() { return getDayDifference(new Date(this.issuable.created_at), new Date()) < 1; @@ -188,7 +194,7 @@ export default { value: this.issuable.blocking_issues_count, title: __('Blocking issues'), dataTestId: 'blocking-issues', - href: `${this.issuable.web_url}#related-issues`, + href: setUrlFragment(this.webUrl, 'related-issues'), icon: 'issue-block', }, { @@ -197,7 +203,7 @@ export default { value: this.issuable.user_notes_count, title: __('Comments'), dataTestId: 'notes-count', - href: `${this.issuable.web_url}#notes`, + href: setUrlFragment(this.webUrl, 'notes'), class: { 'no-comments': !this.issuable.user_notes_count, 'issuable-comments': true }, icon: 'comments', }, @@ -252,7 +258,7 @@ export default { :class="{ today: issueCreatedToday, closed: isClosed }" :data-id="issuable.id" :data-labels="labelIdsString" - :data-url="issuable.web_url" + :data-url="webUrl" data-qa-selector="issue_container" :data-qa-issue-title="issuable.title" > @@ -284,13 +290,14 @@ export default { :aria-label="$options.confidentialTooltipText" /> {{ issuable.title - }} + {{ issuable.title }} + diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js index 399aebb0c83..ec21d8c84e0 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js @@ -1,7 +1,5 @@ import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request'; import initCheckFormState from './check_form_state'; -document.addEventListener('DOMContentLoaded', () => { - initMergeRequest(); - initCheckFormState(); -}); +initMergeRequest(); +initCheckFormState(); diff --git a/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue b/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue index a5e2c986b12..13d80b5ae0b 100644 --- a/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue +++ b/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue @@ -65,8 +65,8 @@ export default { return axios .get(endpoint) .then(({ data }) => { - this.branches = data.Branches; - this.tags = data.Tags; + this.branches = data.Branches || []; + this.tags = data.Tags || []; }) .catch(() => { createFlash({ diff --git a/changelogs/unreleased/267511-approval-gate-rule-type.yml b/changelogs/unreleased/267511-approval-gate-rule-type.yml new file mode 100644 index 00000000000..ebc2a7d80aa --- /dev/null +++ b/changelogs/unreleased/267511-approval-gate-rule-type.yml @@ -0,0 +1,5 @@ +--- +title: Create ExternalApprovalRule table and associations +merge_request: 54002 +author: +type: added diff --git a/changelogs/unreleased/322724-fix-compare-page-dropdown-loading.yml b/changelogs/unreleased/322724-fix-compare-page-dropdown-loading.yml new file mode 100644 index 00000000000..42359bb81d2 --- /dev/null +++ b/changelogs/unreleased/322724-fix-compare-page-dropdown-loading.yml @@ -0,0 +1,5 @@ +--- +title: Fix issue with loading the repository compare page +merge_request: 55058 +author: +type: fixed diff --git a/changelogs/unreleased/fix-web-url-jira.yml b/changelogs/unreleased/fix-web-url-jira.yml new file mode 100644 index 00000000000..0a0b973bb84 --- /dev/null +++ b/changelogs/unreleased/fix-web-url-jira.yml @@ -0,0 +1,5 @@ +--- +title: Use gitlab_web_url (if it exists) for issue title links in Issue lists +merge_request: 55021 +author: +type: fixed diff --git a/config/metrics/schema.json b/config/metrics/schema.json index 53e52c21545..621c7884c67 100644 --- a/config/metrics/schema.json +++ b/config/metrics/schema.json @@ -26,7 +26,7 @@ }, "status": { "type": ["string"], - "enum": ["data_available", "planned", "in_progress", "implemented"] + "enum": ["data_available", "planned", "in_progress", "implemented", "not_used", "deprecated"] }, "milestone": { "type": ["number", "null"] diff --git a/db/migrate/20210209110019_create_external_approval_rules.rb b/db/migrate/20210209110019_create_external_approval_rules.rb new file mode 100644 index 00000000000..5d6780ec412 --- /dev/null +++ b/db/migrate/20210209110019_create_external_approval_rules.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class CreateExternalApprovalRules < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + DOWNTIME = false + + def up + create_table_with_constraints :external_approval_rules, if_not_exists: true do |t| + t.references :project, foreign_key: { on_delete: :cascade }, null: false, index: false + t.timestamps_with_timezone + t.text :external_url, null: false + t.text_limit :external_url, 255 + t.text :name, null: false + t.text_limit :name, 255 + + t.index([:project_id, :name], + unique: true, + name: 'idx_on_external_approval_rules_project_id_name') + t.index([:project_id, :external_url], + unique: true, + name: 'idx_on_external_approval_rules_project_id_external_url') + end + + create_table :external_approval_rules_protected_branches do |t| + t.bigint :external_approval_rule_id, null: false, index: { name: 'idx_eaprpb_external_approval_rule_id' } + t.bigint :protected_branch_id, null: false + t.index([:protected_branch_id, :external_approval_rule_id], + unique: true, + name: 'idx_protected_branch_id_external_approval_rule_id') + end + end + + def down + with_lock_retries do + drop_table :external_approval_rules_protected_branches, force: :cascade, if_exists: true + end + + with_lock_retries do + drop_table :external_approval_rules, force: :cascade, if_exists: true + end + end +end diff --git a/db/migrate/20210223132934_add_foreign_key_to_external_approval_rules.rb b/db/migrate/20210223132934_add_foreign_key_to_external_approval_rules.rb new file mode 100644 index 00000000000..b5f04672813 --- /dev/null +++ b/db/migrate/20210223132934_add_foreign_key_to_external_approval_rules.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddForeignKeyToExternalApprovalRules < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :external_approval_rules_protected_branches, :external_approval_rules, column: :external_approval_rule_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :external_approval_rules_protected_branches, column: :external_approval_rule_id + end + end +end diff --git a/db/migrate/20210223133116_add_foreign_key_to_external_approval_rules_protected_branches.rb b/db/migrate/20210223133116_add_foreign_key_to_external_approval_rules_protected_branches.rb new file mode 100644 index 00000000000..ad51f765d8a --- /dev/null +++ b/db/migrate/20210223133116_add_foreign_key_to_external_approval_rules_protected_branches.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddForeignKeyToExternalApprovalRulesProtectedBranches < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_foreign_key :external_approval_rules_protected_branches, :protected_branches, column: :protected_branch_id, on_delete: :cascade + end + + def down + with_lock_retries do + remove_foreign_key :external_approval_rules_protected_branches, column: :protected_branch_id + end + end +end diff --git a/db/schema_migrations/20210209110019 b/db/schema_migrations/20210209110019 new file mode 100644 index 00000000000..d11e6e5a167 --- /dev/null +++ b/db/schema_migrations/20210209110019 @@ -0,0 +1 @@ +a24354264df3c12411af040903d26faf298f06a7334ef118f87b3e88b683b326 \ No newline at end of file diff --git a/db/schema_migrations/20210223132934 b/db/schema_migrations/20210223132934 new file mode 100644 index 00000000000..6e2b0696bad --- /dev/null +++ b/db/schema_migrations/20210223132934 @@ -0,0 +1 @@ +99ee6773319af0fa7a1dfef92f67cc95141c892bf7adcf500d46adc1ebd4c70f \ No newline at end of file diff --git a/db/schema_migrations/20210223133116 b/db/schema_migrations/20210223133116 new file mode 100644 index 00000000000..235053e8fe8 --- /dev/null +++ b/db/schema_migrations/20210223133116 @@ -0,0 +1 @@ +991041c8d3092175165834a988eb32141e49d7785cda756c8a78170b4af6db64 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index c3b46a15098..08d79ddf007 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12349,6 +12349,41 @@ CREATE SEQUENCE experiments_id_seq ALTER SEQUENCE experiments_id_seq OWNED BY experiments.id; +CREATE TABLE external_approval_rules ( + id bigint NOT NULL, + project_id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + external_url text NOT NULL, + name text NOT NULL, + CONSTRAINT check_1c64b53ea5 CHECK ((char_length(name) <= 255)), + CONSTRAINT check_b634ca168d CHECK ((char_length(external_url) <= 255)) +); + +CREATE SEQUENCE external_approval_rules_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE external_approval_rules_id_seq OWNED BY external_approval_rules.id; + +CREATE TABLE external_approval_rules_protected_branches ( + id bigint NOT NULL, + external_approval_rule_id bigint NOT NULL, + protected_branch_id bigint NOT NULL +); + +CREATE SEQUENCE external_approval_rules_protected_branches_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE external_approval_rules_protected_branches_id_seq OWNED BY external_approval_rules_protected_branches.id; + CREATE TABLE external_pull_requests ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -18995,6 +19030,10 @@ ALTER TABLE ONLY experiment_users ALTER COLUMN id SET DEFAULT nextval('experimen ALTER TABLE ONLY experiments ALTER COLUMN id SET DEFAULT nextval('experiments_id_seq'::regclass); +ALTER TABLE ONLY external_approval_rules ALTER COLUMN id SET DEFAULT nextval('external_approval_rules_id_seq'::regclass); + +ALTER TABLE ONLY external_approval_rules_protected_branches ALTER COLUMN id SET DEFAULT nextval('external_approval_rules_protected_branches_id_seq'::regclass); + ALTER TABLE ONLY external_pull_requests ALTER COLUMN id SET DEFAULT nextval('external_pull_requests_id_seq'::regclass); ALTER TABLE ONLY feature_gates ALTER COLUMN id SET DEFAULT nextval('feature_gates_id_seq'::regclass); @@ -20219,6 +20258,12 @@ ALTER TABLE ONLY experiment_users ALTER TABLE ONLY experiments ADD CONSTRAINT experiments_pkey PRIMARY KEY (id); +ALTER TABLE ONLY external_approval_rules + ADD CONSTRAINT external_approval_rules_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY external_approval_rules_protected_branches + ADD CONSTRAINT external_approval_rules_protected_branches_pkey PRIMARY KEY (id); + ALTER TABLE ONLY external_pull_requests ADD CONSTRAINT external_pull_requests_pkey PRIMARY KEY (id); @@ -21317,6 +21362,8 @@ CREATE INDEX idx_container_repositories_on_exp_cleanup_status_and_start_date ON CREATE INDEX idx_deployment_clusters_on_cluster_id_and_kubernetes_namespace ON deployment_clusters USING btree (cluster_id, kubernetes_namespace); +CREATE INDEX idx_eaprpb_external_approval_rule_id ON external_approval_rules_protected_branches USING btree (external_approval_rule_id); + CREATE UNIQUE INDEX idx_environment_merge_requests_unique_index ON deployment_merge_requests USING btree (environment_id, merge_request_id); CREATE INDEX idx_geo_con_rep_updated_events_on_container_repository_id ON geo_container_repository_updated_events USING btree (container_repository_id); @@ -21357,6 +21404,10 @@ CREATE INDEX idx_mr_cc_diff_files_on_mr_cc_id_and_sha ON merge_request_context_c CREATE UNIQUE INDEX idx_on_compliance_management_frameworks_namespace_id_name ON compliance_management_frameworks USING btree (namespace_id, name); +CREATE UNIQUE INDEX idx_on_external_approval_rules_project_id_external_url ON external_approval_rules USING btree (project_id, external_url); + +CREATE UNIQUE INDEX idx_on_external_approval_rules_project_id_name ON external_approval_rules USING btree (project_id, name); + CREATE INDEX idx_packages_build_infos_on_package_id ON packages_build_infos USING btree (package_id); CREATE INDEX idx_packages_debian_group_component_files_on_architecture_id ON packages_debian_group_component_files USING btree (architecture_id); @@ -21385,6 +21436,8 @@ CREATE INDEX idx_projects_id_created_at_disable_overriding_approvers_true ON pro CREATE INDEX idx_projects_on_repository_storage_last_repository_updated_at ON projects USING btree (id, repository_storage, last_repository_updated_at); +CREATE UNIQUE INDEX idx_protected_branch_id_external_approval_rule_id ON external_approval_rules_protected_branches USING btree (protected_branch_id, external_approval_rule_id); + CREATE INDEX idx_repository_states_on_last_repository_verification_ran_at ON project_repository_states USING btree (project_id, last_repository_verification_ran_at) WHERE ((repository_verification_checksum IS NOT NULL) AND (last_repository_verification_failure IS NULL)); CREATE INDEX idx_repository_states_on_last_wiki_verification_ran_at ON project_repository_states USING btree (project_id, last_wiki_verification_ran_at) WHERE ((wiki_verification_checksum IS NOT NULL) AND (last_wiki_verification_failure IS NULL)); @@ -24695,6 +24748,12 @@ ALTER TABLE ONLY issues ALTER TABLE ONLY issue_links ADD CONSTRAINT fk_c900194ff2 FOREIGN KEY (source_id) REFERENCES issues(id) ON DELETE CASCADE; +ALTER TABLE ONLY external_approval_rules_protected_branches + ADD CONSTRAINT fk_c9a037a926 FOREIGN KEY (external_approval_rule_id) REFERENCES external_approval_rules(id) ON DELETE CASCADE; + +ALTER TABLE ONLY external_approval_rules_protected_branches + ADD CONSTRAINT fk_ca2ffb55e6 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE; + ALTER TABLE ONLY experiment_subjects ADD CONSTRAINT fk_ccc28f8ceb FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; @@ -26309,6 +26368,9 @@ ALTER TABLE ONLY ci_job_variables ALTER TABLE ONLY packages_nuget_metadata ADD CONSTRAINT fk_rails_fc0c19f5b4 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; +ALTER TABLE ONLY external_approval_rules + ADD CONSTRAINT fk_rails_fd4f9ac573 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY experiment_users ADD CONSTRAINT fk_rails_fd805f771a FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md index 5b8bad1d685..86bc56b7149 100644 --- a/doc/api/merge_request_approvals.md +++ b/doc/api/merge_request_approvals.md @@ -567,6 +567,91 @@ PUT /projects/:id/approvers } ``` +## External Project-level MR approvals **(ULTIMATE)** + +Configuration for approvals on a specific Merge Request which makes a call to an external HTTP resource. + +> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3869) in GitLab 13.10. +> - It's [deployed behind a feature flag](../user/feature_flags.md), disabled by default. +> - It's disabled on GitLab.com. +> - It's not recommended for production use. +> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-external-project-level-mr-approvals). **(ULTIMATE SELF)** + +### Get project external approval rules **(ULTIMATE)** + +You can request information about a project's external approval rules using the following endpoint: + +```plaintext +GET /projects/:id/external_approval_rules +``` + +**Parameters:** + +| Attribute | Type | Required | Description | +|---------------------|---------|----------|---------------------| +| `id` | integer | yes | The ID of a project | + +```json +[ + { + "id": 1, + "name": "Compliance Check", + "project_id": 6, + "external_url": "https://gitlab.com/example/test.json", + "protected_branches": [ + { + "id": 14, + "project_id": 6, + "name": "master", + "created_at": "2020-10-12T14:04:50.787Z", + "updated_at": "2020-10-12T14:04:50.787Z", + "code_owner_approval_required": false + } + ] + } +] +``` + +### Create external approval rule **(ULTIMATE)** + +You can create a new external approval rule for a project using the following endpoint: + +```plaintext +POST /projects/:id/external_approval_rules +``` + +| Attribute | Type | Required | Description | +|------------------------|----------------|----------|----------------------------------------------------| +| `id` | integer | yes | The ID of a project | +| `name` | string | yes | Display name of approval rule | +| `external_url` | string | yes | URL of external approval resource | +| `protected_branch_ids` | array | no | The ids of protected branches to scope the rule by | + +### Enable or disable External Project-level MR approvals **(ULTIMATE SELF)** + +Enable or disable External Project-level MR approvals is under development and not ready for production use. It is +deployed behind a feature flag that is **disabled by default**. +[GitLab administrators with access to the GitLab Rails console](../user/feature_flags.md) +can enable it. + +To enable it: + +```ruby +# For the instance +Feature.enable(:ff_compliance_approval_gates) +# For a single project +Feature.enable(:ff_compliance_approval_gates, Project.find()) +``` + +To disable it: + +```ruby +# For the instance +Feature.disable(:ff_compliance_approval_gates) +# For a single project +Feature.disable(:ff_compliance_approval_gates, Project.find()) +``` + ## Merge Request-level MR approvals Configuration for approvals on a specific Merge Request. Must be authenticated for all endpoints. diff --git a/doc/ci/metrics_reports.md b/doc/ci/metrics_reports.md index 3966e5e3e89..716959143ef 100644 --- a/doc/ci/metrics_reports.md +++ b/doc/ci/metrics_reports.md @@ -26,12 +26,14 @@ Consider the following examples of data that can use Metrics Reports: ## How it works -Metrics are read from the metrics report (default: `metrics.txt`). They are parsed and displayed in the MR widget. +Metrics for a branch are read from the latest metrics report artifact (default filename: `metrics.txt`) as string values. -All values are considered strings and string compare is used to find differences between the latest available `metrics` artifact from: +For an MR, the values of these metrics from the feature branch are compared to the values from the target branch. Then they are displayed in the MR widget in this order: -- `master` -- The feature branch +- Metrics that have been added by the MR. Marked with a **New** badge. +- Existing metrics with changed values. +- Existing metrics with unchanged values. +- Metrics that have been removed by the MR. Marked with a **Removed** badge. ## How to set it up diff --git a/doc/development/usage_ping/metrics_dictionary.md b/doc/development/usage_ping/metrics_dictionary.md index 9f98671050c..f27e258a9c2 100644 --- a/doc/development/usage_ping/metrics_dictionary.md +++ b/doc/development/usage_ping/metrics_dictionary.md @@ -33,7 +33,7 @@ Each metric is defined in a separate YAML file consisting of a number of fields: | `product_group` | yes | The [group](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/stages.yml) that owns the metric. | | `product_category` | no | The [product category](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/categories.yml) for the metric. | | `value_type` | yes | `string`; one of `string`, `number`, `boolean`. | -| `status` | yes | `string`; status of the metric, may be set to `data_available`, `implemented`. | +| `status` | yes | `string`; status of the metric, may be set to `data_available`, `planned`, `in_progress`, `implemented`, `not_used`, `deprecated` | | `time_frame` | yes | `string`; may be set to a value like `7d`, `28d`, `all`, `none`. | | `data_source` | yes | `string`: may be set to a value like `database`, `redis`, `redis_hll`, `prometheus`, `ruby`. | | `distribution` | yes | The [distribution](https://about.gitlab.com/handbook/marketing/strategic-marketing/tiers/#definitions) where the metric applies. | diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 54bcaee7cab..a492b891248 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -26,6 +26,11 @@ To enable the Bitbucket OmniAuth provider you must register your application with Bitbucket.org. Bitbucket generates an application ID and secret key for you to use. +WARNING: +To help prevent an [OAuth 2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/) +vulnerability in which users' GitLab accounts could be compromised, append `/users/auth` +to the end of the Bitbucket authorization callback URL. + 1. Sign in to [Bitbucket.org](https://bitbucket.org). 1. Navigate to your individual user settings (**Bitbucket settings**) or a team's settings (**Manage team**), depending on how you want the application registered. @@ -40,9 +45,7 @@ you to use. - **Application description:** *(Optional)* Fill this in if you wish. - **Callback URL:** (Required in GitLab versions 8.15 and greater) The URL to your GitLab installation, such as - `https://gitlab.example.com/users/auth`. Be sure to append `/users/auth` to - the end of the callback URL to prevent an - [OAuth2 convert redirect](http://tetraph.com/covert_redirect/) vulnerability. + `https://gitlab.example.com/users/auth`. Leaving this field empty [results in an `Invalid redirect_uri` message](https://confluence.atlassian.com/bitbucket/oauth-faq-338365710.html). - **URL:** The URL to your GitLab installation, such as `https://gitlab.example.com`. diff --git a/doc/integration/github.md b/doc/integration/github.md index 4258b1c3c76..4d8adfe42f1 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -10,6 +10,16 @@ You can integrate your GitLab instance with GitHub.com and GitHub Enterprise. Th enables users to import projects from GitHub, or sign in to your GitLab instance with their GitHub account. +## Security check + +Some integrations risk compromising GitLab accounts. To help mitigate this +[OAuth 2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/) +vulnerability, append `/users/auth` to the end of the authorization callback URL. + +However, as far as we know, GitHub does not validate the subdomain part of the `redirect_uri`. +This means that a subdomain takeover, an XSS, or an open redirect on any subdomain of +your website could enable the covert redirect attack. + ## Enabling GitHub OAuth To enable the GitHub OmniAuth provider, you need an OAuth 2 Client ID and Client Secret from GitHub. To get these credentials, sign into GitHub and follow their procedure for [Creating an OAuth App](https://docs.github.com/en/developers/apps/creating-an-oauth-app). @@ -19,9 +29,6 @@ When you create an OAuth 2 app in GitHub, you need the following information: - The URL of your GitLab instance, such as `https://gitlab.example.com`. - The authorization callback URL; in this case, `https://gitlab.example.com/users/auth`. Include the port number if your GitLab instance uses a non-default port. -NOTE: -To prevent an [OAuth2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/) vulnerability, append `/users/auth` to the end of the GitHub authorization callback URL. - See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. After you have configured the GitHub provider, you need the following information. You must substitute that information in the GitLab configuration file in these next steps. diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 4bd0bddfd7d..e8dadd88147 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Auto DevOps **(FREE)** -> - Generally available on GitLab 11.0. +> - Introduced in GitLab 11.0 for general availability. GitLab Auto DevOps helps to reduce the complexity of software delivery by setting up pipelines and integrations for you. Instead of requiring you to @@ -38,7 +38,7 @@ For a developer's guide, read [Auto DevOps development guide](../../development/ ## Enabled by default -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41729) in GitLab 11.3. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41729) in GitLab 11.3. On self-managed instances, Auto DevOps is enabled by default for all projects. It attempts to run on all pipelines in each project. An instance administrator can @@ -205,7 +205,7 @@ After enabling the feature, an Auto DevOps pipeline is triggered on the `master` ### At the group level -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52447) in GitLab 11.10. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/52447) in GitLab 11.10. Only administrators and group owners can enable or disable Auto DevOps at the group level. @@ -232,7 +232,7 @@ Auto DevOps at the group and project level, respectively. ### Deployment strategy -> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/38542) in GitLab 11.0. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/38542) in GitLab 11.0. You can change the deployment strategy used by Auto DevOps by visiting your project's **Settings > CI/CD > Auto DevOps**. The following options diff --git a/spec/factories/alert_management/alerts.rb b/spec/factories/alert_management/alerts.rb index e36e4c38013..ee1225b9542 100644 --- a/spec/factories/alert_management/alerts.rb +++ b/spec/factories/alert_management/alerts.rb @@ -47,10 +47,6 @@ FactoryBot.define do hosts { [FFaker::Internet.ip_v4_address] } end - trait :with_ended_at do - ended_at { Time.current } - end - trait :without_ended_at do ended_at { nil } end @@ -67,7 +63,7 @@ FactoryBot.define do trait :resolved do status { AlertManagement::Alert.status_value(:resolved) } - with_ended_at + ended_at { Time.current } end trait :ignored do diff --git a/spec/factories/prometheus_alert_event.rb b/spec/factories/prometheus_alert_event.rb index 281fbacabe2..7771a8d5cb7 100644 --- a/spec/factories/prometheus_alert_event.rb +++ b/spec/factories/prometheus_alert_event.rb @@ -13,10 +13,5 @@ FactoryBot.define do ended_at { Time.now } payload_key { nil } end - - trait :none do - status { nil } - started_at { nil } - end end end diff --git a/spec/factories/self_managed_prometheus_alert_event.rb b/spec/factories/self_managed_prometheus_alert_event.rb index 238942e2c46..3a48aba5f54 100644 --- a/spec/factories/self_managed_prometheus_alert_event.rb +++ b/spec/factories/self_managed_prometheus_alert_event.rb @@ -8,16 +8,5 @@ FactoryBot.define do title { 'alert' } query_expression { 'vector(2)' } started_at { Time.now } - - trait :resolved do - status { SelfManagedPrometheusAlertEvent.status_value_for(:resolved) } - ended_at { Time.now } - payload_key { nil } - end - - trait :none do - status { nil } - started_at { nil } - end end end diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb index c381fb82ba0..a297b0b72f6 100644 --- a/spec/factories_spec.rb +++ b/spec/factories_spec.rb @@ -7,7 +7,6 @@ RSpec.describe 'factories' do def skipped_traits [ - [:alert_management_alert, :with_ended_at], [:audit_event, :unauthenticated], [:ci_build_trace_chunk, :fog_with_data], [:ci_job_artifact, :remote_store], @@ -32,10 +31,7 @@ RSpec.describe 'factories' do [:pages_domain, :explicit_ecdsa], [:project_member, :blocked], [:project, :remote_mirror], - [:prometheus_alert_event, :none], [:remote_mirror, :ssh], - [:self_managed_prometheus_alert_event, :resolved], - [:self_managed_prometheus_alert_event, :none], [:user_preference, :only_comments] ] end diff --git a/spec/frontend/issues_list/components/issuable_spec.js b/spec/frontend/issues_list/components/issuable_spec.js index a8bf124373b..236eab6e276 100644 --- a/spec/frontend/issues_list/components/issuable_spec.js +++ b/spec/frontend/issues_list/components/issuable_spec.js @@ -1,4 +1,4 @@ -import { GlSprintf, GlLabel, GlIcon } from '@gitlab/ui'; +import { GlSprintf, GlLabel, GlIcon, GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; import { TEST_HOST } from 'helpers/test_constants'; import { trimText } from 'helpers/text_helper'; @@ -31,6 +31,7 @@ const TEST_MILESTONE = { }; const TEXT_CLOSED = 'CLOSED'; const TEST_META_COUNT = 100; +const MOCK_GITLAB_URL = 'http://0.0.0.0:3000'; describe('Issuable component', () => { let issuable; @@ -54,6 +55,7 @@ describe('Issuable component', () => { beforeEach(() => { issuable = { ...simpleIssue }; + gon.gitlab_url = MOCK_GITLAB_URL; }); afterEach(() => { @@ -199,6 +201,33 @@ describe('Issuable component', () => { it('renders no comments', () => { expect(findNotes().classes('no-comments')).toBe(true); }); + + it.each` + gitlabWebUrl | webUrl | expectedHref | expectedTarget | isExternal + ${undefined} | ${`${MOCK_GITLAB_URL}/issue`} | ${`${MOCK_GITLAB_URL}/issue`} | ${undefined} | ${false} + ${undefined} | ${'https://jira.com/issue'} | ${'https://jira.com/issue'} | ${'_blank'} | ${true} + ${'/gitlab-org/issue'} | ${'https://jira.com/issue'} | ${'/gitlab-org/issue'} | ${undefined} | ${false} + `( + 'renders issuable title correctly when `gitlabWebUrl` is `$gitlabWebUrl` and webUrl is `$webUrl`', + async ({ webUrl, gitlabWebUrl, expectedHref, expectedTarget, isExternal }) => { + factory({ + issuable: { + ...issuable, + web_url: webUrl, + gitlab_web_url: gitlabWebUrl, + }, + }); + + const titleEl = findIssuableTitle(); + + expect(titleEl.exists()).toBe(true); + expect(titleEl.find(GlLink).attributes('href')).toBe(expectedHref); + expect(titleEl.find(GlLink).attributes('target')).toBe(expectedTarget); + expect(titleEl.find(GlLink).text()).toBe(issuable.title); + + expect(titleEl.find(GlIcon).exists()).toBe(isExternal); + }, + ); }); describe('with confidential issuable', () => { diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js index 8ed419a4a61..270c89e674c 100644 --- a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js +++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js @@ -62,6 +62,20 @@ describe('RevisionDropdown component', () => { expect(wrapper.vm.tags).toEqual(Tags); }); + it('sets branches and tags to be an empty array when no tags or branches are given', async () => { + axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, { + Branches: undefined, + Tags: undefined, + }); + + createComponent(); + + await axios.waitForAll(); + + expect(wrapper.vm.branches).toEqual([]); + expect(wrapper.vm.tags).toEqual([]); + }); + it('shows flash message on error', async () => { axiosMock.onGet('some/invalid/path').replyOnce(404); diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index d0282e14d5f..37b43066a62 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -335,6 +335,7 @@ container_repositories: - project - name project: +- external_approval_rules - taggings - base_tags - tag_taggings diff --git a/spec/models/prometheus_alert_event_spec.rb b/spec/models/prometheus_alert_event_spec.rb index 913ca7db0be..6bff549bc4b 100644 --- a/spec/models/prometheus_alert_event_spec.rb +++ b/spec/models/prometheus_alert_event_spec.rb @@ -52,7 +52,7 @@ RSpec.describe PrometheusAlertEvent do let(:started_at) { Time.current } context 'when status is none' do - subject { build(:prometheus_alert_event, :none) } + subject { build(:prometheus_alert_event, status: nil, started_at: nil) } it 'fires an event' do result = subject.fire(started_at)