diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-30 15:09:03 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-06-30 15:09:03 +0000 |
commit | e7b262a4c5cf70fed6eb25ba7a0eb1336e6eb639 (patch) | |
tree | 4e1037d8fd8d93883e52d1fbd8b542dfc81a0436 | |
parent | b0139a824fba85e5b71e69f2c99d423700ff76cc (diff) | |
download | gitlab-ce-e7b262a4c5cf70fed6eb25ba7a0eb1336e6eb639.tar.gz |
Add latest changes from gitlab-org/gitlab@master
66 files changed, 1109 insertions, 164 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 4b7f60c1bac..e188475485b 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1501,6 +1501,12 @@ changes: ["vendor/gems/ipynbdiff/**/*"] - <<: *if-merge-request-labels-run-all-rspec +.vendor:rules:omniauth-gitlab: + rules: + - <<: *if-merge-request + changes: ["vendor/gems/omniauth-gitlab/**/*"] + - <<: *if-merge-request-labels-run-all-rspec + ################## # Releases rules # ################## diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml index ce71820100f..6dd6e19a818 100644 --- a/.gitlab/ci/vendored-gems.gitlab-ci.yml +++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml @@ -5,6 +5,7 @@ vendor mail-smtp_pool: trigger: include: vendor/gems/mail-smtp_pool/.gitlab-ci.yml strategy: depend + vendor ipynbdiff: extends: - .vendor:rules:ipynbdiff @@ -12,3 +13,11 @@ vendor ipynbdiff: trigger: include: vendor/gems/ipynbdiff/.gitlab-ci.yml strategy: depend + +vendor omniauth-gitlab: + extends: + - .vendor:rules:omniauth-gitlab + needs: [] + trigger: + include: vendor/gems/omniauth-gitlab/.gitlab-ci.yml + strategy: depend diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 9d3f426a228..c5c87040175 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -88,7 +88,6 @@ Layout/LineLength: - 'app/controllers/projects/issues_controller.rb' - 'app/controllers/projects/jobs_controller.rb' - 'app/controllers/projects/labels_controller.rb' - - 'app/controllers/projects/logs_controller.rb' - 'app/controllers/projects/merge_requests/conflicts_controller.rb' - 'app/controllers/projects/merge_requests/creations_controller.rb' - 'app/controllers/projects/merge_requests/diffs_controller.rb' @@ -696,8 +695,6 @@ Layout/LineLength: - 'app/services/pages/migrate_legacy_storage_to_deployment_service.rb' - 'app/services/personal_access_tokens/create_service.rb' - 'app/services/personal_access_tokens/revoke_service.rb' - - 'app/services/pod_logs/elasticsearch_service.rb' - - 'app/services/pod_logs/kubernetes_service.rb' - 'app/services/projects/branches_by_mode_service.rb' - 'app/services/projects/container_repository/cleanup_tags_service.rb' - 'app/services/projects/container_repository/gitlab/delete_tags_service.rb' @@ -3485,7 +3482,6 @@ Layout/LineLength: - 'lib/gitlab/diff/highlight_cache.rb' - 'lib/gitlab/diff/parser.rb' - 'lib/gitlab/diff/rendered/notebook/diff_file.rb' - - 'lib/gitlab/elasticsearch/logs/lines.rb' - 'lib/gitlab/email/failure_handler.rb' - 'lib/gitlab/email/handler/create_issue_handler.rb' - 'lib/gitlab/email/handler/create_merge_request_handler.rb' @@ -5007,7 +5003,6 @@ Layout/LineLength: - 'spec/lib/gitlab/diff/position_tracer/image_strategy_spec.rb' - 'spec/lib/gitlab/diff/position_tracer/line_strategy_spec.rb' - 'spec/lib/gitlab/diff/suggestion_spec.rb' - - 'spec/lib/gitlab/elasticsearch/logs/lines_spec.rb' - 'spec/lib/gitlab/email/failure_handler_spec.rb' - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb' - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb' @@ -6090,8 +6085,6 @@ Layout/LineLength: - 'spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb' - 'spec/services/personal_access_tokens/create_service_spec.rb' - 'spec/services/personal_access_tokens/revoke_service_spec.rb' - - 'spec/services/pod_logs/elasticsearch_service_spec.rb' - - 'spec/services/pod_logs/kubernetes_service_spec.rb' - 'spec/services/post_receive_service_spec.rb' - 'spec/services/projects/apple_target_platform_detector_service_spec.rb' - 'spec/services/projects/autocomplete_service_spec.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index 0d517c6cd55..49968873ccb 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -1471,7 +1471,6 @@ RSpec/ContextWording: - 'spec/features/ics/project_issues_spec.rb' - 'spec/features/ide/clientside_preview_csp_spec.rb' - 'spec/features/ide/static_object_external_storage_csp_spec.rb' - - 'spec/features/incidents/incident_details_spec.rb' - 'spec/features/incidents/user_creates_new_incident_spec.rb' - 'spec/features/invites_spec.rb' - 'spec/features/issuables/markdown_references/internal_references_spec.rb' diff --git a/.rubocop_todo/rspec/repeated_example_group_body.yml b/.rubocop_todo/rspec/repeated_example_group_body.yml index 87148442b08..e006396d0d6 100644 --- a/.rubocop_todo/rspec/repeated_example_group_body.yml +++ b/.rubocop_todo/rspec/repeated_example_group_body.yml @@ -23,7 +23,6 @@ RSpec/RepeatedExampleGroupBody: - 'spec/controllers/projects/blob_controller_spec.rb' - 'spec/controllers/projects/graphs_controller_spec.rb' - 'spec/controllers/projects/registry/repositories_controller_spec.rb' - - 'spec/features/incidents/incident_details_spec.rb' - 'spec/features/issues/spam_akismet_issue_creation_spec.rb' - 'spec/features/merge_request/user_sees_closing_issues_message_spec.rb' - 'spec/features/projects/commit/cherry_pick_spec.rb' @@ -44,7 +44,7 @@ gem 'omniauth-dingtalk-oauth2', '~> 1.0' gem 'omniauth-alicloud', '~> 1.0.1' gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-github', '~> 1.4' -gem 'omniauth-gitlab', '~> 1.0.2' +gem 'omniauth-gitlab', '~> 4.0.0', path: 'vendor/gems/omniauth-gitlab' # See vendor/gems/omniauth-gitlab/README.md gem 'omniauth-google-oauth2', '~> 0.6.0' gem 'omniauth-oauth2-generic', '~> 0.2.2' gem 'omniauth-saml', '~> 1.10' @@ -407,7 +407,7 @@ group :development, :test do end group :development, :test, :danger do - gem 'gitlab-dangerfiles', '~> 3.4.1', require: false + gem 'gitlab-dangerfiles', '~> 3.4.2', require: false end group :development, :test, :coverage do diff --git a/Gemfile.lock b/Gemfile.lock index aea6f448587..02068c3a243 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,6 +12,13 @@ PATH connection_pool (~> 2.0) mail (~> 2.7) +PATH + remote: vendor/gems/omniauth-gitlab + specs: + omniauth-gitlab (4.0.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.7.1) + GEM remote: https://rubygems.org/ specs: @@ -482,7 +489,7 @@ GEM terminal-table (~> 1.5, >= 1.5.1) gitlab-chronic (0.10.5) numerizer (~> 0.2) - gitlab-dangerfiles (3.4.1) + gitlab-dangerfiles (3.4.2) danger (>= 8.4.5) danger-gitlab (>= 8.0.0) rake @@ -872,9 +879,6 @@ GEM omniauth-github (1.4.0) omniauth (~> 1.5) omniauth-oauth2 (>= 1.4.0, < 2.0) - omniauth-gitlab (1.0.3) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.0) omniauth-google-oauth2 (0.6.0) jwt (>= 2.0) omniauth (>= 1.1.1) @@ -1538,7 +1542,7 @@ DEPENDENCIES gitaly (~> 15.1.0.pre.rc1) github-markup (~> 1.7.0) gitlab-chronic (~> 0.10.5) - gitlab-dangerfiles (~> 3.4.1) + gitlab-dangerfiles (~> 3.4.2) gitlab-experiment (~> 0.7.1) gitlab-fog-azure-rm (~> 1.3.0) gitlab-labkit (~> 0.23.0) @@ -1623,7 +1627,7 @@ DEPENDENCIES omniauth-dingtalk-oauth2 (~> 1.0) omniauth-facebook (~> 4.0.0) omniauth-github (~> 1.4) - omniauth-gitlab (~> 1.0.2) + omniauth-gitlab (~> 4.0.0)! omniauth-google-oauth2 (~> 0.6.0) omniauth-oauth2-generic (~> 0.2.2) omniauth-salesforce (~> 1.0.5) diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js index fd064e7ca8f..d4efe409fef 100644 --- a/app/assets/javascripts/blob/3d_viewer/index.js +++ b/app/assets/javascripts/blob/3d_viewer/index.js @@ -98,9 +98,9 @@ export default class Renderer { requestAnimationFrame(this.renderWrapper); } - changeObjectMaterials(type) { + changeObjectMaterials(material) { this.objects.forEach((obj) => { - obj.changeMaterial(type); + obj.changeMaterial(material); }); } diff --git a/app/assets/javascripts/blob/3d_viewer/mesh_object.js b/app/assets/javascripts/blob/3d_viewer/mesh_object.js index cb7fcff8674..c55a9ca8926 100644 --- a/app/assets/javascripts/blob/3d_viewer/mesh_object.js +++ b/app/assets/javascripts/blob/3d_viewer/mesh_object.js @@ -30,7 +30,7 @@ export default class MeshObject extends Mesh { } } - changeMaterial(type) { - this.material = materials[type]; + changeMaterial(materialKey) { + this.material = materials[materialKey]; } } diff --git a/app/assets/javascripts/blob/stl_viewer.js b/app/assets/javascripts/blob/stl_viewer.js index 0ea623a705a..768bbce9c57 100644 --- a/app/assets/javascripts/blob/stl_viewer.js +++ b/app/assets/javascripts/blob/stl_viewer.js @@ -5,15 +5,15 @@ export default () => { [].slice.call(document.querySelectorAll('.js-material-changer')).forEach((el) => { el.addEventListener('click', (e) => { - const { target } = e; + const { currentTarget } = e; e.preventDefault(); document.querySelector('.js-material-changer.selected').classList.remove('selected'); - target.classList.add('selected'); - target.blur(); + currentTarget.classList.add('selected'); + currentTarget.blur(); - viewer.changeObjectMaterials(target.dataset.type); + viewer.changeObjectMaterials(currentTarget.dataset.material); }); }); }; diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json index 1c0e27ac3ea..45c5cca68cc 100644 --- a/app/assets/javascripts/graphql_shared/possible_types.json +++ b/app/assets/javascripts/graphql_shared/possible_types.json @@ -133,6 +133,7 @@ "WorkItemWidget": [ "WorkItemWidgetAssignees", "WorkItemWidgetDescription", - "WorkItemWidgetHierarchy" + "WorkItemWidgetHierarchy", + "WorkItemWidgetWeight" ] } diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js index 00fe0bcf89b..f208280af27 100644 --- a/app/assets/javascripts/profile/account/index.js +++ b/app/assets/javascripts/profile/account/index.js @@ -30,7 +30,7 @@ export default () => { deleteAccountModal, }, mounted() { - deleteAccountButton.classList.remove('disabled'); + deleteAccountButton.disabled = false; deleteAccountButton.addEventListener('click', () => { this.$root.$emit(BV_SHOW_MODAL, 'delete-account-modal', '#delete-account-button'); }); diff --git a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb index 7af55d42f2a..cc15bff0916 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/update_arguments.rb @@ -18,6 +18,9 @@ module Mutations argument :description_widget, ::Types::WorkItems::Widgets::DescriptionInputType, required: false, description: 'Input for description widget.' + argument :weight_widget, ::Types::WorkItems::Widgets::WeightInputType, + required: false, + description: 'Input for weight widget.' end end end diff --git a/app/graphql/types/work_items/widget_interface.rb b/app/graphql/types/work_items/widget_interface.rb index 6520de39b7e..1b752393296 100644 --- a/app/graphql/types/work_items/widget_interface.rb +++ b/app/graphql/types/work_items/widget_interface.rb @@ -18,6 +18,8 @@ module Types ::Types::WorkItems::Widgets::HierarchyType when ::WorkItems::Widgets::Assignees ::Types::WorkItems::Widgets::AssigneesType + when ::WorkItems::Widgets::Weight + ::Types::WorkItems::Widgets::WeightType else raise "Unknown GraphQL type for widget #{object}" end @@ -25,7 +27,8 @@ module Types orphan_types ::Types::WorkItems::Widgets::DescriptionType, ::Types::WorkItems::Widgets::HierarchyType, - ::Types::WorkItems::Widgets::AssigneesType + ::Types::WorkItems::Widgets::AssigneesType, + ::Types::WorkItems::Widgets::WeightType end end end diff --git a/app/graphql/types/work_items/widgets/weight_input_type.rb b/app/graphql/types/work_items/widgets/weight_input_type.rb new file mode 100644 index 00000000000..a01c63222a5 --- /dev/null +++ b/app/graphql/types/work_items/widgets/weight_input_type.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + class WeightInputType < BaseInputObject + graphql_name 'WorkItemWidgetWeightInput' + + argument :weight, GraphQL::Types::Int, + required: true, + description: 'Weight of the work item.' + end + end + end +end diff --git a/app/graphql/types/work_items/widgets/weight_type.rb b/app/graphql/types/work_items/widgets/weight_type.rb new file mode 100644 index 00000000000..c8eaf560268 --- /dev/null +++ b/app/graphql/types/work_items/widgets/weight_type.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Types + module WorkItems + module Widgets + # Disabling widget level authorization as it might be too granular + # and we already authorize the parent work item + # rubocop:disable Graphql/AuthorizeTypes + class WeightType < BaseObject + graphql_name 'WorkItemWidgetWeight' + description 'Represents a weight widget' + + implements Types::WorkItems::WidgetInterface + + field :weight, GraphQL::Types::Int, null: true, + description: 'Weight of the work item.' + end + # rubocop:enable Graphql/AuthorizeTypes + end + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index a185448d5ea..b15143c8c9c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -26,6 +26,13 @@ class Ability end end + # A list of users that can read confidential notes in a project + def users_that_can_read_internal_notes(users, note_parent) + DeclarativePolicy.subject_scope do + users.select { |u| allowed?(u, :reporter_access, note_parent) } + end + end + # Returns an Array of Issues that can be read by the given user. # # issues - The issues to reduce down to those readable by the user. diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 99dbe464a7c..9ee0fd1db1d 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -172,7 +172,7 @@ module CacheMarkdownField refs = all_references(self.author) references = {} - references[:mentioned_users_ids] = refs.mentioned_user_ids.presence + references[:mentioned_users_ids] = mentioned_filtered_user_ids_for(refs) references[:mentioned_groups_ids] = refs.mentioned_group_ids.presence references[:mentioned_projects_ids] = refs.mentioned_project_ids.presence @@ -185,6 +185,13 @@ module CacheMarkdownField true end + # Overriden on objects that needs to filter + # mentioned users that cannot read them, for example, + # guest users that are referenced on a confidential note. + def mentioned_filtered_user_ids_for(refs) + refs.mentioned_user_ids.presence + end + def mentionable_attributes_changed?(changes = saved_changes) return false unless is_a?(Mentionable) diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index 20743ebcb52..f59b5d1ecc8 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -92,7 +92,13 @@ module Participable end def raw_participants(current_user = nil, verify_access: false) - ext = Gitlab::ReferenceExtractor.new(project, current_user) + extractor = Gitlab::ReferenceExtractor.new(project, current_user) + + # Used to extract references from confidential notes. + # Referenced users that cannot read confidential notes are + # later removed from participants array. + internal_notes_extractor = Gitlab::ReferenceExtractor.new(project, current_user) + participants = Set.new process = [self] @@ -107,6 +113,8 @@ module Participable source.class.participant_attrs.each do |attr| if attr.respond_to?(:call) + ext = use_internal_notes_extractor_for?(source) ? internal_notes_extractor : extractor + source.instance_exec(current_user, ext, &attr) else process << source.__send__(attr) # rubocop:disable GitlabSecurity/PublicSend @@ -121,7 +129,18 @@ module Participable end end - participants.merge(ext.users) + participants.merge(users_that_can_read_internal_notes(internal_notes_extractor)) + participants.merge(extractor.users) + end + + def use_internal_notes_extractor_for?(source) + source.is_a?(Note) && source.confidential? + end + + def users_that_can_read_internal_notes(extractor) + return [] unless self.is_a?(Noteable) && self.try(:resource_parent) + + Ability.users_that_can_read_internal_notes(extractor.users, self.resource_parent) end def source_visible_to_user?(source, user) diff --git a/app/models/note.rb b/app/models/note.rb index 41e45a8759f..f2ddf0efe47 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -665,6 +665,25 @@ class Note < ApplicationRecord ) end + def mentioned_users(current_user = nil) + users = super + + return users unless confidential? + + Ability.users_that_can_read_internal_notes(users, resource_parent) + end + + def mentioned_filtered_user_ids_for(references) + return super unless confidential? + + user_ids = references.mentioned_user_ids.presence + + return [] if user_ids.blank? + + users = User.where(id: user_ids) + Ability.users_that_can_read_internal_notes(users, resource_parent).pluck(:id) + end + private def system_note_viewable_by?(user) diff --git a/app/models/work_items/type.rb b/app/models/work_items/type.rb index 843e7a7fb32..c52565196a1 100644 --- a/app/models/work_items/type.rb +++ b/app/models/work_items/type.rb @@ -21,11 +21,11 @@ module WorkItems }.freeze WIDGETS_FOR_TYPE = { - issue: [Widgets::Description, Widgets::Hierarchy, Widgets::Assignees], + issue: [Widgets::Assignees, Widgets::Description, Widgets::Hierarchy, Widgets::Weight], incident: [Widgets::Description], test_case: [Widgets::Description], requirement: [Widgets::Description], - task: [Widgets::Description, Widgets::Hierarchy, Widgets::Assignees] + task: [Widgets::Assignees, Widgets::Description, Widgets::Hierarchy, Widgets::Weight] }.freeze cache_markdown_field :description, pipeline: :single_line diff --git a/app/models/work_items/widgets/weight.rb b/app/models/work_items/widgets/weight.rb new file mode 100644 index 00000000000..f589378f307 --- /dev/null +++ b/app/models/work_items/widgets/weight.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + class Weight < Base + delegate :weight, to: :work_item + end + end +end diff --git a/app/services/work_items/widgets/weight_service/update_service.rb b/app/services/work_items/widgets/weight_service/update_service.rb new file mode 100644 index 00000000000..cd62a25358f --- /dev/null +++ b/app/services/work_items/widgets/weight_service/update_service.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + module WeightService + class UpdateService < WorkItems::Widgets::BaseService + def update(params: {}) + return unless params.present? && params[:weight] + + widget.work_item.weight = params[:weight] + end + end + end + end +end diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 745d3c62c5d..de09e78c695 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -68,7 +68,7 @@ = render 'users/deletion_guidance', user: current_user -# Delete button here - %button#delete-account-button.gl-button.btn.btn-danger.disabled{ data: { qa_selector: 'delete_account_button' } } + = render Pajamas::ButtonComponent.new(variant: :danger, button_options: { id: 'delete-account-button', disabled: true, data: { qa_selector: 'delete_account_button' }}) do = s_('Profiles|Delete account') #delete-account-modal{ data: { action_url: user_registration_path, diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml index 8bf0339fc3c..7206a969fb7 100644 --- a/app/views/projects/blob/viewers/_stl.html.haml +++ b/app/views/projects/blob/viewers/_stl.html.haml @@ -3,7 +3,7 @@ = gl_loading_icon(size: "md", css_class: "gl-my-4") .text-center.gl-mt-3.gl-mb-3.stl-controls .btn-group - %button.gl-button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } } - Wireframe - %button.gl-button.btn.btn-default.btn-sm.selected.js-material-changer{ data: { type: 'default' } } - Solid + = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-material-changer', data: { material: 'wireframe' } }) do + = _('Wireframe') + = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: 'js-material-changer selected', data: { material: 'default' } }) do + = _('Solid') diff --git a/config/feature_flags/development/fix_sliding_list_partitioning.yml b/config/feature_flags/development/fix_sliding_list_partitioning.yml index 90b4bf87740..7d553ea938c 100644 --- a/config/feature_flags/development/fix_sliding_list_partitioning.yml +++ b/config/feature_flags/development/fix_sliding_list_partitioning.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362246 milestone: '15.1' type: development group: group::sharding -default_enabled: false +default_enabled: true diff --git a/config/routes/project.rb b/config/routes/project.rb index 6ecd002e970..f996cd1945f 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -238,13 +238,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do end end - resources :logs, only: [:index] do - collection do - get :k8s - get :elasticsearch - end - end - resources :starrers, only: [:index] resources :forks, only: [:index, :new, :create] resources :group_links, only: [:update, :destroy], constraints: { id: /\d+|:id/ } diff --git a/doc/administration/postgresql/replication_and_failover.md b/doc/administration/postgresql/replication_and_failover.md index e9b607ad5d4..37471a4f491 100644 --- a/doc/administration/postgresql/replication_and_failover.md +++ b/doc/administration/postgresql/replication_and_failover.md @@ -996,7 +996,7 @@ You can switch an exiting database cluster to use Patroni instead of repmgr with ### Upgrading PostgreSQL major version in a Patroni cluster -As of GitLab 13.3, PostgreSQL 11.7 and 12.3 are both shipped with Omnibus GitLab by default. As of GitLab 13.7, PostgreSQL 12 is the default. If you want to upgrade to PostgreSQL 12 in versions prior to GitLab 13.7, you must ask for it explicitly. +As of GitLab 14.1, PostgreSQL 12.6 and 13.3 are both shipped with Omnibus GitLab by default. As of GitLab 15.0, PostgreSQL 13 is the default. If you want to upgrade to PostgreSQL 13 in versions prior to GitLab 15.0, you must ask for it explicitly. WARNING: The procedure for upgrading PostgreSQL in a Patroni cluster is different than when upgrading using repmgr. @@ -1046,7 +1046,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade: 1. Upgrade PostgreSQL on **the leader node** and make sure that the upgrade is completed successfully: ```shell - sudo gitlab-ctl pg-upgrade -V 12 + sudo gitlab-ctl pg-upgrade -V 13 ``` NOTE: @@ -1073,7 +1073,7 @@ Considering these, you should carefully plan your PostgreSQL upgrade: 1. Upgrade PostgreSQL **on replicas** (you can do this in parallel on all of them): ```shell - sudo gitlab-ctl pg-upgrade -V 12 + sudo gitlab-ctl pg-upgrade -V 13 ``` NOTE: diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 5c179e18a4b..5b38f524ede 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -5655,6 +5655,7 @@ Input type: `WorkItemUpdateInput` | <a id="mutationworkitemupdateid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. | | <a id="mutationworkitemupdatestateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. | | <a id="mutationworkitemupdatetitle"></a>`title` | [`String`](#string) | Title of the work item. | +| <a id="mutationworkitemupdateweightwidget"></a>`weightWidget` | [`WorkItemWidgetWeightInput`](#workitemwidgetweightinput) | Input for weight widget. | #### Fields @@ -18518,6 +18519,17 @@ Represents a hierarchy widget. | <a id="workitemwidgethierarchyparent"></a>`parent` | [`WorkItem`](#workitem) | Parent work item. | | <a id="workitemwidgethierarchytype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. | +### `WorkItemWidgetWeight` + +Represents a weight widget. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="workitemwidgetweighttype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. | +| <a id="workitemwidgetweightweight"></a>`weight` | [`Int`](#int) | Weight of the work item. | + ## Enumeration types Also called _Enums_, enumeration types are a special kind of scalar that @@ -20372,6 +20384,7 @@ Type of a work item widget. | <a id="workitemwidgettypeassignees"></a>`ASSIGNEES` | Assignees widget. | | <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. | | <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. | +| <a id="workitemwidgettypeweight"></a>`WEIGHT` | Weight widget. | ## Scalar types @@ -21593,6 +21606,7 @@ Implementations: - [`WorkItemWidgetAssignees`](#workitemwidgetassignees) - [`WorkItemWidgetDescription`](#workitemwidgetdescription) - [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy) +- [`WorkItemWidgetWeight`](#workitemwidgetweight) ##### Fields @@ -22082,6 +22096,7 @@ A time-frame defined as a closed inclusive range of two dates. | <a id="workitemupdatedtaskinputid"></a>`id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. | | <a id="workitemupdatedtaskinputstateevent"></a>`stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. | | <a id="workitemupdatedtaskinputtitle"></a>`title` | [`String`](#string) | Title of the work item. | +| <a id="workitemupdatedtaskinputweightwidget"></a>`weightWidget` | [`WorkItemWidgetWeightInput`](#workitemwidgetweightinput) | Input for weight widget. | ### `WorkItemWidgetDescriptionInput` @@ -22090,3 +22105,11 @@ A time-frame defined as a closed inclusive range of two dates. | Name | Type | Description | | ---- | ---- | ----------- | | <a id="workitemwidgetdescriptioninputdescription"></a>`description` | [`String!`](#string) | Description of the work item. | + +### `WorkItemWidgetWeightInput` + +#### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="workitemwidgetweightinputweight"></a>`weight` | [`Int!`](#int) | Weight of the work item. | diff --git a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md index 5356ebb84f1..5894efa95ab 100644 --- a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md +++ b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md @@ -248,9 +248,13 @@ set `CI_AWS_ECS_WAIT_FOR_ROLLOUT_COMPLETE_DISABLED` to a non-empty value. ## Set up Review Apps -In order to use [Review Apps](../../../development/testing_guide/review_apps.md) with ECS, you should create another -[service](#create-an-ecs-service) and specify its name using the `CI_AWS_ECS_SERVICE` variable scoped to `review/*`. -Since this service is shared by all review apps, there is a limitation that only one Review App can be deployed at a time. +To use [Review Apps](../../../development/testing_guide/review_apps.md) with ECS: + +1. Set up a new [service](#create-an-ecs-service). +1. Use the `CI_AWS_ECS_SERVICE` variable to set the name. +1. Set the environment scope to `review/*`. + +Only one Review App at a time can be deployed because this service is shared by all review apps. ## Set up Security Testing @@ -275,6 +279,20 @@ include: - template: Security/DAST.gitlab-ci.yml ``` +To use DAST on the default branch: + +1. Set up a new [service](#create-an-ecs-service). This service will be used to deploy a temporary +DAST environment. +1. Use the `CI_AWS_ECS_SERVICE` variable to set the name. +1. Set the scope to the `dast-default` environment. +1. Add the following to your `.gitlab-ci.yml` file: + +```yaml +include: + - template: Security/DAST.gitlab-ci.yml + - template: Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml +``` + For more details and configuration options, see the [DAST documentation](../../../user/application_security/dast/index.md). ## Further reading diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md index 1a51ee88c58..a68c0630b18 100644 --- a/doc/development/integrations/secure.md +++ b/doc/development/integrations/secure.md @@ -451,7 +451,7 @@ The `identifiers` array describes the detected vulnerability. An identifier obje `value` fields are used to tell if two identifiers are the same. The user interface uses the object's `name` and `url` fields to display the identifier. -It is recommended to reuse the identifiers the GitLab scanners already define: +We recommend that you use the identifiers the GitLab scanners already define: | Identifier | Type | Example value | |------------|------|---------------| diff --git a/doc/user/application_security/generate_test_vulnerabilities/index.md b/doc/user/application_security/generate_test_vulnerabilities/index.md new file mode 100644 index 00000000000..aafbebb91cd --- /dev/null +++ b/doc/user/application_security/generate_test_vulnerabilities/index.md @@ -0,0 +1,28 @@ +--- +type: reference, howto +stage: Secure +group: Threat Insights +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 +--- + +# Generate test vulnerabilities + +You can generate test vulnerabilities when you work on the [Vulnerability Report](../vulnerability_report/index.md). + +1. Go to `/-/profile/personal_access_tokens` and generate a personal access token with `api` permissions. +1. Go to your project page and find the project ID. You can find the project ID below the project title. +1. Open a terminal and go to the `gitlab/qa` directory. +1. Run the following command: + +```shell +GITLAB_QA_ACCESS_TOKEN=<your_personal_access_token> GITLAB_URL="http://localhost:3000" bundle exec rake vulnerabilities:setup\[<your_project_id>,<vulnerability_count>\] --trace +``` + +Make sure you do the following: + +- Replace `<your_personal_access_token>` with the token you generated in step one. +- Double check the `GITLAB_URL`. It should point to the running local instance. +- Replace `<your_project_id>` with the ID you obtained in step two. +- Replace `<vulnerability_count>` with the number of vulnerabilities you'd like to generate. + +The script creates the specified amount of vulnerabilities in the project. diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml index 8f1124373c4..b41e92e3a56 100644 --- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml @@ -4,6 +4,14 @@ variables: .dast-auto-deploy: image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}" +.common_rules: &common_rules + - if: $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME + when: never + - if: $DAST_DISABLED || $DAST_DISABLED_FOR_DEFAULT_BRANCH + when: never + - if: $DAST_WEBSITE # we don't need to create a review app if a URL is already given + when: never + dast_environment_deploy: extends: .dast-auto-deploy stage: review @@ -23,12 +31,7 @@ dast_environment_deploy: artifacts: paths: [environment_url.txt] rules: - - if: $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME - when: never - - if: $DAST_DISABLED || $DAST_DISABLED_FOR_DEFAULT_BRANCH - when: never - - if: $DAST_WEBSITE # we don't need to create a review app if a URL is already given - when: never + - *common_rules - if: $CI_COMMIT_BRANCH && ($CI_KUBERNETES_ACTIVE || $KUBECONFIG) && $GITLAB_FEATURES =~ /\bdast\b/ @@ -47,13 +50,53 @@ stop_dast_environment: action: stop needs: ["dast"] rules: - - if: $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME - when: never - - if: $DAST_DISABLED || $DAST_DISABLED_FOR_DEFAULT_BRANCH - when: never - - if: $DAST_WEBSITE # we don't need to create a review app if a URL is already given - when: never + - *common_rules - if: $CI_COMMIT_BRANCH && ($CI_KUBERNETES_ACTIVE || $KUBECONFIG) && $GITLAB_FEATURES =~ /\bdast\b/ when: always + +.ecs_image: + image: 'registry.gitlab.com/gitlab-org/cloud-deploy/aws-ecs:latest' + +.ecs_rules: &ecs_rules + - if: $AUTO_DEVOPS_PLATFORM_TARGET != "ECS" + when: never + - if: $CI_KUBERNETES_ACTIVE || $KUBECONFIG + when: never + +dast_ecs_environment_deploy: + extends: .ecs_image + stage: review + script: + - ecs update-task-definition + - echo "http://$(ecs get-task-hostname)" > environment_url.txt + environment: + name: dast-default + on_stop: stop_dast_ecs_environment + artifacts: + paths: + - environment_url.txt + rules: + - *common_rules + - *ecs_rules + - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bdast\b/ + +stop_dast_ecs_environment: + extends: .ecs_image + stage: cleanup + variables: + GIT_STRATEGY: none + script: + - ecs stop-task + allow_failure: true + environment: + name: dast-default + action: stop + needs: + - dast + rules: + - *common_rules + - *ecs_rules + - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bdast\b/ + when: always diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index 311c44f5f80..23e1a95c401 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -9,7 +9,6 @@ module Sidebars return false unless context.project.feature_available?(:operations, context.current_user) add_item(metrics_dashboard_menu_item) - add_item(logs_menu_item) add_item(error_tracking_menu_item) add_item(alert_management_menu_item) add_item(incidents_menu_item) @@ -56,21 +55,6 @@ module Sidebars ) end - def logs_menu_item - if !Feature.enabled?(:monitor_logging, context.project) || - !can?(context.current_user, :read_environment, context.project) || - !can?(context.current_user, :read_pod_logs, context.project) - return ::Sidebars::NilMenuItem.new(item_id: :logs) - end - - ::Sidebars::MenuItem.new( - title: _('Logs'), - link: project_logs_path(context.project), - active_routes: { path: 'logs#index' }, - item_id: :logs - ) - end - def error_tracking_menu_item unless can?(context.current_user, :read_sentry_issue, context.project) return ::Sidebars::NilMenuItem.new(item_id: :error_tracking) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 258220d1366..8541420acc3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6222,12 +6222,6 @@ msgstr "" msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after June 22, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group." msgstr "" -msgid "Billing|Your free group is now limited to %{free_user_limit} members" -msgstr "" - -msgid "Billing|Your group recently changed to use the Free plan. Free groups are limited to %{free_user_limit} members and the remaining members will get a status of over-limit and lose access to the group. You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier." -msgstr "" - msgid "Bitbucket Server Import" msgstr "" @@ -36098,6 +36092,9 @@ msgstr "" msgid "Snowplow" msgstr "" +msgid "Solid" +msgstr "" + msgid "Solution" msgstr "" @@ -43551,6 +43548,9 @@ msgstr "" msgid "Will deploy to" msgstr "" +msgid "Wireframe" +msgstr "" + msgid "With requirements, you can set criteria to check your products against." msgstr "" diff --git a/package.json b/package.json index 75e58835f6d..e33699b0402 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@babel/preset-env": "^7.18.2", "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", - "@gitlab/svgs": "2.21.0", - "@gitlab/ui": "42.9.0", + "@gitlab/svgs": "2.22.0", + "@gitlab/ui": "42.11.0", "@gitlab/visual-review-tools": "1.7.3", "@rails/actioncable": "6.1.4-7", "@rails/ujs": "6.1.4-7", diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb index dad3dfd3440..7c24943eb6f 100644 --- a/spec/features/incidents/incident_details_spec.rb +++ b/spec/features/incidents/incident_details_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Incident details', :js do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:incident) { create(:incident, project: project, author: developer, description: 'description') } + let_it_be(:issue) { create(:issue, project: project, author: developer, description: 'Issue description') } let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: incident) } before_all do @@ -14,23 +15,24 @@ RSpec.describe 'Incident details', :js do before do sign_in(developer) - - visit project_issues_incident_path(project, incident) - wait_for_requests end context 'when a developer+ displays the incident' do - it 'shows the incident' do + before do + visit project_issues_incident_path(project, incident) + wait_for_requests + end + + it 'shows correct elements on the page', :aggregate_failures do + # shows the incident page.within('.issuable-details') do expect(find('h1')).to have_content(incident.title) end - end - it 'does not show design management' do + # does not show design management expect(page).not_to have_selector('.js-design-management') - end - it 'shows the incident tabs' do + # shows the incident tabs page.within('.issuable-details') do incident_tabs = find('[data-testid="incident-tabs"]') @@ -38,9 +40,8 @@ RSpec.describe 'Incident details', :js do expect(incident_tabs).to have_content('Summary') expect(incident_tabs).to have_content(incident.description) end - end - it 'shows the right sidebar mounted with type issue' do + # shows the right sidebar mounted with type issue page.within('.layout-page') do sidebar = find('.right-sidebar') @@ -51,12 +52,12 @@ RSpec.describe 'Incident details', :js do end end - context 'escalation status' do + describe 'escalation status' do let(:sidebar) { page.find('.right-sidebar') } let(:widget) { sidebar.find('[data-testid="escalation_status_container"]') } let(:expected_dropdown_options) { escalation_status.class::STATUSES.keys.take(3).map { |key| key.to_s.titleize } } - it 'has an interactable escalation status widget' do + it 'has an interactable escalation status widget', :aggregate_failures do expect(current_status).to have_text(escalation_status.status_name.to_s.titleize) # list the available statuses @@ -87,41 +88,41 @@ RSpec.describe 'Incident details', :js do end end - context 'when an incident `issue_type` is edited by a signed in user' do - it 'routes the user to the incident details page when the `issue_type` is set to incident' do - wait_for_requests - project_path = "/#{project.full_path}" - click_button 'Edit title and description' - wait_for_requests + it 'routes the user to the incident details page when the `issue_type` is set to incident' do + visit project_issue_path(project, issue) + wait_for_requests + + project_path = "/#{project.full_path}" + click_button 'Edit title and description' + wait_for_requests - page.within('[data-testid="issuable-form"]') do - click_button 'Incident' - click_button 'Issue' - click_button 'Save changes' + page.within('[data-testid="issuable-form"]') do + click_button 'Issue' + click_button 'Incident' + click_button 'Save changes' - wait_for_requests + wait_for_requests - expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") - end + expect(page).to have_current_path("#{project_path}/-/issues/incident/#{issue.iid}") end end - context 'when incident details are edited by a signed in user' do - it 'routes the user to the incident details page when the `issue_type` is set to incident' do - wait_for_requests - project_path = "/#{project.full_path}" - click_button 'Edit title and description' - wait_for_requests + it 'routes the user to the issue details page when the `issue_type` is set to issue' do + visit project_issues_incident_path(project, incident) + wait_for_requests - page.within('[data-testid="issuable-form"]') do - click_button 'Incident' - click_button 'Issue' - click_button 'Save changes' + project_path = "/#{project.full_path}" + click_button 'Edit title and description' + wait_for_requests - wait_for_requests + page.within('[data-testid="issuable-form"]') do + click_button 'Incident' + click_button 'Issue' + click_button 'Save changes' - expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") - end + wait_for_requests + + expect(page).to have_current_path("#{project_path}/-/issues/#{incident.iid}") end end end diff --git a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap index ec5b6a5597b..4693d5a47e4 100644 --- a/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap +++ b/spec/frontend/alerts_settings/components/__snapshots__/alerts_form_spec.js.snap @@ -17,6 +17,7 @@ exports[`Alert integration settings form default state should match the default <gl-form-checkbox-stub checked="true" data-qa-selector="create_issue_checkbox" + id="2" > <span> Create an incident. Incidents are created for each alert triggered. @@ -87,7 +88,9 @@ exports[`Alert integration settings form default state should match the default labeldescription="" optionaltext="(optional)" > - <gl-form-checkbox-stub> + <gl-form-checkbox-stub + id="3" + > <span> Send a single email notification to Owners and Maintainers for new alerts. </span> @@ -101,6 +104,7 @@ exports[`Alert integration settings form default state should match the default > <gl-form-checkbox-stub checked="true" + id="4" > <span> Automatically close associated incident when a recovery alert notification resolves an alert diff --git a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap index 2691e11e616..ba8215f4e00 100644 --- a/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap +++ b/spec/frontend/artifacts_settings/components/__snapshots__/keep_latest_artifact_checkbox_spec.js.snap @@ -7,6 +7,7 @@ exports[`Keep latest artifact checkbox when application keep latest artifact set <b-form-checkbox-stub checked="true" class="gl-form-checkbox" + id="4" value="true" > <strong diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index 5bd69ad9fad..422dd9a463b 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -151,6 +151,38 @@ RSpec.describe Ability do end end + describe '.users_that_can_read_internal_note' do + shared_examples 'filtering users that can read internal note' do + let_it_be(:guest) { create(:user) } + let_it_be(:reporter) { create(:user) } + + let(:users) { [reporter, guest] } + + before do + parent.add_guest(guest) + parent.add_reporter(reporter) + end + + it 'returns users that can read internal notes' do + result = described_class.users_that_can_read_internal_notes(users, parent) + + expect(result).to match_array([reporter]) + end + end + + context 'for groups' do + it_behaves_like 'filtering users that can read internal note' do + let(:parent) { create(:group) } + end + end + + context 'for projects' do + it_behaves_like 'filtering users that can read internal note' do + let(:parent) { create(:project) } + end + end + end + describe '.merge_requests_readable_by_user' do context 'with an admin when admin mode is enabled', :enable_admin_mode do it 'returns all merge requests' do diff --git a/spec/models/concerns/participable_spec.rb b/spec/models/concerns/participable_spec.rb index 99a3a0fb79a..b92c7c52f0b 100644 --- a/spec/models/concerns/participable_spec.rb +++ b/spec/models/concerns/participable_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Participable do expect(instance).to receive(:foo).and_return(user2) expect(instance).to receive(:bar).and_return(user3) - expect(instance).to receive(:project).twice.and_return(project) + expect(instance).to receive(:project).thrice.and_return(project) participants = instance.participants(user1) @@ -66,7 +66,7 @@ RSpec.describe Participable do expect(instance).to receive(:foo).and_return(other) expect(other).to receive(:bar).and_return(user2) - expect(instance).to receive(:project).twice.and_return(project) + expect(instance).to receive(:project).thrice.and_return(project) expect(instance.participants(user1)).to eq([user2]) end @@ -86,7 +86,7 @@ RSpec.describe Participable do instance = model.new - expect(instance).to receive(:project).twice.and_return(project) + expect(instance).to receive(:project).thrice.and_return(project) instance.participants(user1) @@ -138,7 +138,7 @@ RSpec.describe Participable do allow(instance).to receive_message_chain(:model_name, :element) { 'class' } expect(instance).to receive(:foo).and_return(user2) expect(instance).to receive(:bar).and_return(user3) - expect(instance).to receive(:project).twice.and_return(project) + expect(instance).to receive(:project).thrice.and_return(project) participants = instance.visible_participants(user1) @@ -159,7 +159,7 @@ RSpec.describe Participable do allow(instance).to receive_message_chain(:model_name, :element) { 'class' } allow(instance).to receive(:bar).and_return(user2) - expect(instance).to receive(:project).twice.and_return(project) + expect(instance).to receive(:project).thrice.and_return(project) expect(instance.visible_participants(user1)).to be_empty end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index d45a23a7ef8..9f864afc213 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -744,25 +744,11 @@ RSpec.describe Issue do end describe '#participants' do - context 'using a public project' do - let_it_be(:public_project) { create(:project, :public) } - let_it_be(:issue) { create(:issue, project: public_project) } - - let!(:note1) do - create(:note_on_issue, noteable: issue, project: public_project, note: 'a') - end - - let!(:note2) do - create(:note_on_issue, noteable: issue, project: public_project, note: 'b') - end - - it 'includes the issue author' do - expect(issue.participants).to include(issue.author) - end + it_behaves_like 'issuable participants' do + let_it_be(:issuable_parent) { create(:project, :public) } + let_it_be_with_refind(:issuable) { create(:issue, project: issuable_parent) } - it 'includes the authors of the notes' do - expect(issue.participants).to include(note1.author, note2.author) - end + let(:params) { { noteable: issuable, project: issuable_parent } } end context 'using a private project' do diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb index d49c7452421..f3874155dd1 100644 --- a/spec/models/work_item_spec.rb +++ b/spec/models/work_item_spec.rb @@ -39,7 +39,8 @@ RSpec.describe WorkItem do it 'returns instances of supported widgets' do is_expected.to match_array([instance_of(WorkItems::Widgets::Description), instance_of(WorkItems::Widgets::Hierarchy), - instance_of(WorkItems::Widgets::Assignees)]) + instance_of(WorkItems::Widgets::Assignees), + instance_of(WorkItems::Widgets::Weight)]) end end diff --git a/spec/models/work_items/type_spec.rb b/spec/models/work_items/type_spec.rb index 342c1eb5f50..e91617effc0 100644 --- a/spec/models/work_items/type_spec.rb +++ b/spec/models/work_items/type_spec.rb @@ -66,7 +66,8 @@ RSpec.describe WorkItems::Type do it 'returns list of all possible widgets' do is_expected.to match_array([::WorkItems::Widgets::Description, ::WorkItems::Widgets::Hierarchy, - ::WorkItems::Widgets::Assignees]) + ::WorkItems::Widgets::Assignees, + ::WorkItems::Widgets::Weight]) end end diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb index 7a160819a41..8e801eec74d 100644 --- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb @@ -113,5 +113,29 @@ RSpec.describe 'Update a work item' do end end end + + context 'with weight widget input' do + let(:fields) do + <<~FIELDS + workItem { + widgets { + type + ... on WorkItemWidgetWeight { + weight + } + } + } + errors + FIELDS + end + + it_behaves_like 'update work item weight widget' do + let(:new_weight) { 2 } + + let(:input) do + { 'weightWidget' => { 'weight' => new_weight } } + end + end + end end end diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb index a7edfc6ee2d..70fa8100411 100644 --- a/spec/requests/api/graphql/work_item_spec.rb +++ b/spec/requests/api/graphql/work_item_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'Query.work_item(id)' do let_it_be(:developer) { create(:user) } let_it_be(:guest) { create(:user) } let_it_be(:project) { create(:project, :private) } - let_it_be(:work_item) { create(:work_item, project: project, description: '- List item') } + let_it_be(:work_item) { create(:work_item, project: project, description: '- List item', weight: 1) } let_it_be(:child_item1) { create(:work_item, :task, project: project) } let_it_be(:child_item2) { create(:work_item, :task, confidential: true, project: project) } let_it_be(:child_link1) { create(:parent_link, work_item_parent: work_item, work_item: child_item1) } @@ -163,6 +163,32 @@ RSpec.describe 'Query.work_item(id)' do end end + describe 'weight widget' do + let(:work_item_fields) do + <<~GRAPHQL + id + widgets { + type + ... on WorkItemWidgetWeight { + weight + } + } + GRAPHQL + end + + it 'returns widget information' do + expect(work_item_data).to include( + 'id' => work_item.to_gid.to_s, + 'widgets' => include( + hash_including( + 'type' => 'WEIGHT', + 'weight' => work_item.weight + ) + ) + ) + end + end + describe 'assignees widget' do let(:assignees) { create_list(:user, 2) } let(:work_item) { create(:work_item, project: project, assignees: assignees) } diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 032f35cfc29..8e0f964d965 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -147,6 +147,34 @@ RSpec.describe NotificationService, :mailer do end end + shared_examples 'participating by confidential note notification' do + context 'when user is mentioned on confidential note' do + let_it_be(:guest_1) { create(:user) } + let_it_be(:guest_2) { create(:user) } + let_it_be(:reporter) { create(:user) } + + before do + issuable.resource_parent.add_guest(guest_1) + issuable.resource_parent.add_guest(guest_2) + issuable.resource_parent.add_reporter(reporter) + end + + it 'only emails authorized users' do + confidential_note_text = "#{guest_1.to_reference} and #{guest_2.to_reference} and #{reporter.to_reference}" + note_text = "Mentions #{guest_2.to_reference}" + create(:note_on_issue, noteable: issuable, project_id: project.id, note: confidential_note_text, confidential: true) + create(:note_on_issue, noteable: issuable, project_id: project.id, note: note_text) + reset_delivered_emails! + + notification_trigger + + should_not_email(guest_1) + should_email(guest_2) + should_email(reporter) + end + end + end + shared_examples 'participating by assignee notification' do it 'emails the participant' do issuable.assignees << participant @@ -736,6 +764,20 @@ RSpec.describe NotificationService, :mailer do let(:notification_target) { note } let(:notification_trigger) { notification.new_note(note) } end + + context 'when note is confidential' do + let(:note) { create(:note_on_issue, author: author, noteable: issue, project_id: issue.project_id, note: '@all mentioned', confidential: true) } + let(:guest) { create(:user) } + + it 'does not notify users that cannot read note' do + project.add_guest(guest) + reset_delivered_emails! + + notification.new_note(note) + + should_not_email(guest) + end + end end end @@ -1376,6 +1418,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.reassigned_issue(issue, @u_disabled, [assignee]) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.reassigned_issue(issue, @u_disabled, [assignee]) } + end + it_behaves_like 'project emails are disabled' do let(:notification_target) { issue } let(:notification_trigger) { notification.reassigned_issue(issue, @u_disabled, [assignee]) } @@ -1494,6 +1541,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_target) { issue } let(:notification_trigger) { notification.removed_milestone_issue(issue, issue.author) } end + + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.removed_milestone_issue(issue, issue.author) } + end end context 'confidential issues' do @@ -1616,6 +1668,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.close_issue(issue, @u_disabled) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.close_issue(issue, @u_disabled) } + end + it 'adds "subscribed" reason to subscriber emails' do user_1 = create(:user) issue.subscribe(user_1) @@ -1658,6 +1715,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.reopen_issue(issue, @u_disabled) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.reopen_issue(issue, @u_disabled) } + end + it_behaves_like 'project emails are disabled' do let(:notification_target) { issue } let(:notification_trigger) { notification.reopen_issue(issue, @u_disabled) } @@ -1689,6 +1751,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.issue_moved(issue, new_issue, @u_disabled) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.issue_moved(issue, new_issue, @u_disabled) } + end + it_behaves_like 'project emails are disabled' do let(:notification_target) { issue } let(:notification_trigger) { notification.issue_moved(issue, new_issue, @u_disabled) } @@ -1720,6 +1787,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.issue_cloned(issue, new_issue, @u_disabled) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.issue_cloned(issue, new_issue, @u_disabled) } + end + it_behaves_like 'project emails are disabled' do let(:notification_target) { issue } let(:notification_trigger) { notification.issue_cloned(issue, new_issue, @u_disabled) } @@ -1765,6 +1837,11 @@ RSpec.describe NotificationService, :mailer do let(:notification_trigger) { notification.issue_due(issue) } end + it_behaves_like 'participating by confidential note notification' do + let(:issuable) { issue } + let(:notification_trigger) { notification.issue_due(issue) } + end + it_behaves_like 'project emails are disabled' do let(:notification_target) { issue } let(:notification_trigger) { notification.issue_due(issue) } diff --git a/spec/services/work_items/widgets/weight_service/update_service_spec.rb b/spec/services/work_items/widgets/weight_service/update_service_spec.rb new file mode 100644 index 00000000000..97e17f1c526 --- /dev/null +++ b/spec/services/work_items/widgets/weight_service/update_service_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::Widgets::WeightService::UpdateService do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be_with_reload(:work_item) { create(:work_item, project: project, weight: 1) } + + let(:widget) { work_item.widgets.find {|widget| widget.is_a?(WorkItems::Widgets::Weight) } } + + describe '#update' do + subject { described_class.new(widget: widget, current_user: user).update(params: params) } # rubocop:disable Rails/SaveBang + + context 'when weight param is present' do + let(:params) { { weight: 2 } } + + it 'correctly sets work item weight value' do + subject + + expect(work_item.weight).to eq(2) + end + end + + context 'when weight param is not present' do + let(:params) { {} } + + it 'does not change work item weight value', :aggregate_failures do + expect { subject } + .to not_change { work_item.weight } + + expect(work_item.weight).to eq(1) + end + end + end +end diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb index 50e6d4aad1b..b523af385f4 100644 --- a/spec/support/shared_contexts/navbar_structure_context.rb +++ b/spec/support/shared_contexts/navbar_structure_context.rb @@ -83,7 +83,6 @@ RSpec.shared_context 'project navbar structure' do nav_item: _('Monitor'), nav_sub_items: [ _('Metrics'), - _('Logs'), _('Error Tracking'), _('Alerts'), _('Incidents'), diff --git a/spec/support/shared_examples/graphql/mutations/work_items/update_weight_widget_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/work_items/update_weight_widget_shared_examples.rb new file mode 100644 index 00000000000..3c32b7e0310 --- /dev/null +++ b/spec/support/shared_examples/graphql/mutations/work_items/update_weight_widget_shared_examples.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'update work item weight widget' do + it 'updates the weight widget' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.to change(work_item, :weight).from(nil).to(new_weight) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['workItem']['widgets']).to include( + { + 'weight' => new_weight, + 'type' => 'WEIGHT' + } + ) + end + + context 'when the updated work item is not valid' do + it 'returns validation errors without the work item' do + errors = ActiveModel::Errors.new(work_item).tap { |e| e.add(:weight, 'error message') } + + allow_next_found_instance_of(::WorkItem) do |instance| + allow(instance).to receive(:valid?).and_return(false) + allow(instance).to receive(:errors).and_return(errors) + end + + post_graphql_mutation(mutation, current_user: current_user) + + expect(mutation_response['workItem']).to be_nil + expect(mutation_response['errors']).to match_array(['Weight error message']) + end + end +end diff --git a/spec/support/shared_examples/models/issuable_participants_shared_examples.rb b/spec/support/shared_examples/models/issuable_participants_shared_examples.rb new file mode 100644 index 00000000000..c3eaae0ace2 --- /dev/null +++ b/spec/support/shared_examples/models/issuable_participants_shared_examples.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'issuable participants' do + context 'when resource parent is public' do + context 'and users are referenced on notes' do + let_it_be(:notes_author) { create(:user) } + + let(:note_params) { params.merge(author: notes_author) } + + before do + create(:note, note_params) + end + + it 'includes the issue author' do + expect(issuable.participants).to include(issuable.author) + end + + it 'includes the authors of the notes' do + expect(issuable.participants).to include(notes_author) + end + + context 'and note is confidential' do + context 'and mentions users' do + let_it_be(:guest_1) { create(:user) } + let_it_be(:guest_2) { create(:user) } + let_it_be(:reporter) { create(:user) } + + before do + issuable_parent.add_guest(guest_1) + issuable_parent.add_guest(guest_2) + issuable_parent.add_reporter(reporter) + + confidential_note_params = + note_params.merge( + confidential: true, + note: "mentions #{guest_1.to_reference} and #{guest_2.to_reference} and #{reporter.to_reference}" + ) + + regular_note_params = + note_params.merge(note: "Mentions #{guest_2.to_reference}") + + create(:note, confidential_note_params) + create(:note, regular_note_params) + end + + it 'only includes users that can read the note as participants' do + expect(issuable.participants).to contain_exactly(issuable.author, notes_author, reporter, guest_2) + end + end + end + end + end +end diff --git a/spec/support/shared_examples/models/mentionable_shared_examples.rb b/spec/support/shared_examples/models/mentionable_shared_examples.rb index e23658d1774..f9612dd61be 100644 --- a/spec/support/shared_examples/models/mentionable_shared_examples.rb +++ b/spec/support/shared_examples/models/mentionable_shared_examples.rb @@ -260,6 +260,25 @@ RSpec.shared_examples 'mentions in notes' do |mentionable_type| expect(mentionable.referenced_projects(user)).to eq [mentionable.project].compact # epic.project is nil, and we want empty [] expect(mentionable.referenced_groups(user)).to eq [group] end + + if [:epic, :issue].include?(mentionable_type) + context 'and note is confidential' do + let_it_be(:guest) { create(:user) } + + let(:note_desc) { "#{guest.to_reference} and #{user2.to_reference} and #{user.to_reference}" } + + before do + note.resource_parent.add_reporter(user2) + note.resource_parent.add_guest(guest) + # Bypass :confidential update model validation for testing purposes + note.update_attribute(:confidential, true) + end + + it 'returns only mentioned users that has permissions' do + expect(note.mentioned_users).to contain_exactly(user, user2) + end + end + end end end @@ -294,6 +313,26 @@ RSpec.shared_examples 'load mentions from DB' do |mentionable_type| end end + if [:epic, :issue].include?(mentionable_type) + context 'and note is confidential' do + let_it_be(:guest) { create(:user) } + + let(:note_desc) { "#{guest.to_reference} and #{mentioned_user.to_reference}" } + + before do + note.resource_parent.add_reporter(mentioned_user) + note.resource_parent.add_guest(guest) + # Bypass :confidential update model validation for testing purposes + note.update_attribute(:confidential, true) + note.store_mentions! + end + + it 'stores only mentioned users that has permissions' do + expect(mentionable.referenced_users).to contain_exactly(mentioned_user) + end + end + end + context 'when private projects and groups are mentioned' do let(:mega_user) { create(:user) } let(:private_project) { create(:project, :private) } diff --git a/vendor/gems/omniauth-gitlab/.gitignore b/vendor/gems/omniauth-gitlab/.gitignore new file mode 100644 index 00000000000..062d4acf0a9 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/.gitignore @@ -0,0 +1,17 @@ +*.gem +*.rbc +.bundle +.config +.yardoc +.rvmrc +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/vendor/gems/omniauth-gitlab/.gitlab-ci.yml b/vendor/gems/omniauth-gitlab/.gitlab-ci.yml new file mode 100644 index 00000000000..ad9545e2998 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/.gitlab-ci.yml @@ -0,0 +1,30 @@ +workflow: + rules: + - if: $CI_MERGE_REQUEST_ID + +.rspec: + cache: + key: omniauth-gitlab-ruby + paths: + - vendor/gems/omniauth-gitlab/vendor/ruby + before_script: + - cd vendor/gems/omniauth-gitlab + - ruby -v # Print out ruby version for debugging + - gem install bundler --no-document # Bundler is not installed with the image + - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby + - bundle config set with 'development' + - bundle install -j $(nproc) + script: + - bundle exec rspec + +rspec-2.6: + image: "ruby:2.6" + extends: .rspec + +rspec-2.7: + image: "ruby:2.7" + extends: .rspec + +rspec-3.0: + image: "ruby:3.0" + extends: .rspec
\ No newline at end of file diff --git a/vendor/gems/omniauth-gitlab/Gemfile b/vendor/gems/omniauth-gitlab/Gemfile new file mode 100644 index 00000000000..ad0c84b6ac8 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in omniauth-gitlab.gemspec +gemspec diff --git a/vendor/gems/omniauth-gitlab/Gemfile.lock b/vendor/gems/omniauth-gitlab/Gemfile.lock new file mode 100644 index 00000000000..b5979104080 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/Gemfile.lock @@ -0,0 +1,73 @@ +PATH + remote: . + specs: + omniauth-gitlab (4.0.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.7.1) + +GEM + remote: https://rubygems.org/ + specs: + diff-lcs (1.5.0) + docile (1.4.0) + faraday (2.3.0) + faraday-net_http (~> 2.0) + ruby2_keywords (>= 0.0.4) + faraday-net_http (2.0.3) + hashie (5.0.0) + jwt (2.4.1) + multi_xml (0.6.0) + oauth2 (2.0.1) + faraday (>= 0.17.3, < 3.0) + jwt (>= 1.0, < 3.0) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + rash_alt (>= 0.4, < 1) + version_gem (~> 1.0) + omniauth (1.9.1) + hashie (>= 3.4.6) + rack (>= 1.6.2, < 3) + omniauth-oauth2 (1.7.3) + oauth2 (>= 1.4, < 3) + omniauth (>= 1.9, < 3) + rack (2.2.3.1) + rake (13.0.6) + rash_alt (0.4.12) + hashie (>= 3.4) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-its (1.3.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) + rspec-mocks (3.11.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) + ruby2_keywords (0.0.5) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + version_gem (1.0.0) + +PLATFORMS + ruby + +DEPENDENCIES + omniauth-gitlab! + rake (>= 12.0) + rspec (~> 3.1) + rspec-its (~> 1.0) + simplecov + +BUNDLED WITH + 2.3.15 diff --git a/vendor/gems/omniauth-gitlab/LICENSE.txt b/vendor/gems/omniauth-gitlab/LICENSE.txt new file mode 100644 index 00000000000..ccdae5ea15c --- /dev/null +++ b/vendor/gems/omniauth-gitlab/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2013 ssein + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/vendor/gems/omniauth-gitlab/README.md b/vendor/gems/omniauth-gitlab/README.md new file mode 100644 index 00000000000..f22f14266f6 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/README.md @@ -0,0 +1,94 @@ +# Omniauth::Gitlab + +This is fork of [omniauth-gitlab](https://github.com/linchus/omniauth-gitlab) to support: + +1. OmniAuth v1 and v2. OmniAuth v2 disables GET requests by default + and defaults to POST. GitLab already has patched v1 to use POST, + but other dependencies need to be updated: + https://gitlab.com/gitlab-org/gitlab/-/issues/30073. + +2. [`oauth2`](https://github.com/oauth-xx/oauth2) v1.4.9 and up. + [v1.4.9 fixed relative URL handling](https://github.com/oauth-xx/oauth2/pull/469). + + However, this breaks the default GitLab.com configuration and + existing configurations that use the `/api/v4` suffix in the `site` + parameter. + [omniauth-gitlab v4.0.0 fixed the first issue](https://github.com/linchus/omniauth-gitlab/pull/22), + but the second issue requires an admin to update `site` to drop the suffix. + + This fork restores backwards compatibility that was removed in omniauth-gitlab v2.0.0: + https://github.com/linchus/omniauth-gitlab/commit/bb4cec2c9f8f067fdfe1f9aa219973e5d8e4a0a3 + +[![Join the chat at https://gitter.im/linchus/omniauth-gitlab](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/linchus/omniauth-gitlab?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +This is the OAuth2 strategy for authenticating to your GitLab service. + +## Requirements + +Gitlab 7.7.0+ + +## Installation + +Add this line to your application's Gemfile: + + gem 'omniauth-gitlab' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install omniauth-gitlab + +## Basic Usage + + use OmniAuth::Builder do + provider :gitlab, ENV['GITLAB_KEY'], ENV['GITLAB_SECRET'] + end + +## Standalone Usage + + use OmniAuth::Builder do + provider :gitlab, ENV['GITLAB_KEY'], ENV['GITLAB_SECRET'], + { + client_options: { + site: 'https://gitlab.YOURDOMAIN.com/api/v4' + } + } + end + +## Custom scopes + +By default, the `api` scope is requested and must be allowed in GitLab's application configuration. To use different scopes: + + use OmniAuth::Builder do + provider :gitlab, ENV['GITLAB_KEY'], ENV['GITLAB_SECRET'], scope: 'read_user openid' + end + +Requesting a scope that is not configured will result the error "The requested scope is invalid, unknown, or malformed.". + +## Old API version + +API V3 will be unsupported from GitLab 9.5 and will be removed in GitLab 9.5 or later. + +[https://gitlab.com/help/api/v3_to_v4.md](https://gitlab.com/help/api/v3_to_v4.md) + +If you use GitLab 9.0 and below you could configure V3 API: + + use OmniAuth::Builder do + provider :gitlab, ENV['GITLAB_KEY'], ENV['GITLAB_SECRET'], + { + client_options: { + site: 'https://gitlab.YOURDOMAIN.com/api/v3' + } + } + end + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request diff --git a/vendor/gems/omniauth-gitlab/Rakefile b/vendor/gems/omniauth-gitlab/Rakefile new file mode 100644 index 00000000000..db953c9a1c5 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/Rakefile @@ -0,0 +1,7 @@ +require 'bundler/gem_tasks' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new + +desc 'Run specs' +task default: :spec diff --git a/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab.rb b/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab.rb new file mode 100644 index 00000000000..dfe9d2131c9 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab.rb @@ -0,0 +1,2 @@ +require 'omniauth-gitlab/version' +require 'omniauth/strategies/gitlab' diff --git a/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab/version.rb b/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab/version.rb new file mode 100644 index 00000000000..6465d4ca199 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/lib/omniauth-gitlab/version.rb @@ -0,0 +1,5 @@ +module Omniauth + module Gitlab + VERSION = '4.0.0' + end +end diff --git a/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb b/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb new file mode 100644 index 00000000000..19ee02e78c4 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'omniauth-oauth2' + +module OmniAuth + module Strategies + class GitLab < OmniAuth::Strategies::OAuth2 + API_SUFFIX_REGEX = %r{/api/v(\d+)/?$}.freeze + + option :client_options, site: 'https://gitlab.com' + + option :redirect_url + + uid { raw_info['id'].to_s } + + info do + { + name: raw_info['name'], + username: raw_info['username'], + email: raw_info['email'], + image: raw_info['avatar_url'] + } + end + + extra do + { raw_info: raw_info } + end + + def raw_info + @raw_info ||= access_token.get(user_endpoint_url).parsed + end + + private + + def user_endpoint_url + options.client_options.site.match(API_SUFFIX_REGEX) ? 'user' : 'api/v4/user' + end + + def callback_url + options.redirect_url || (full_host + script_name + callback_path) + end + end + end +end + +OmniAuth.config.add_camelization 'gitlab', 'GitLab' diff --git a/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec b/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec new file mode 100644 index 00000000000..be25cb50af6 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'omniauth-gitlab/version' + +Gem::Specification.new do |gem| + gem.name = 'omniauth-gitlab' + gem.version = Omniauth::Gitlab::VERSION + gem.authors = ['Sergey Sein'] + gem.email = ['linchus@gmail.com'] + gem.description = 'This is the strategy for authenticating to your GitLab service' + gem.summary = 'This is the strategy for authenticating to your GitLab service' + gem.homepage = 'https://github.com/linchus/omniauth-gitlab' + + gem.files = Dir['lib/**/*.rb'] + gem.test_files = Dir['spec/**/*.rb'] + gem.require_paths = ['lib'] + + gem.add_dependency 'omniauth', '~> 1.0' + gem.add_dependency 'omniauth-oauth2', '~> 1.7.1' + gem.add_development_dependency 'rspec', '~> 3.1' + gem.add_development_dependency 'rspec-its', '~> 1.0' + gem.add_development_dependency 'simplecov' + gem.add_development_dependency 'rake', '>= 12.0' +end diff --git a/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb b/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb new file mode 100644 index 00000000000..a599386b26c --- /dev/null +++ b/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe OmniAuth::Strategies::GitLab do + let(:access_token) { double('AccessToken') } + let(:parsed_response) { double('ParsedResponse') } + let(:response) { double('Response', parsed: parsed_response) } + + let(:enterprise_site) { 'https://some.other.site.com' } + + let(:gitlab_service) { OmniAuth::Strategies::GitLab.new({}) } + let(:enterprise) do + OmniAuth::Strategies::GitLab.new( + 'GITLAB_KEY', + 'GITLAB_SECRET', + client_options: { site: enterprise_site }, + redirect_url: 'http://localhost:9292/callback_url' + ) + end + + subject { gitlab_service } + + before(:each) do + allow(subject).to receive(:access_token).and_return(access_token) + end + + describe 'client options' do + context 'with defaults' do + subject { gitlab_service.options.client_options } + + its(:site) { is_expected.to eq 'https://gitlab.com' } + end + + context 'with override' do + subject { enterprise.options.client_options } + + its(:site) { is_expected.to eq enterprise_site } + end + end + + describe 'redirect_url' do + context 'with defaults' do + subject { gitlab_service.options } + its(:redirect_url) { is_expected.to be_nil } + end + + context 'with customs' do + subject { enterprise.options } + its(:redirect_url) { is_expected.to eq 'http://localhost:9292/callback_url' } + end + end + + describe '#raw_info' do + context 'with new configuration' do + it 'sent request to current user endpoint' do + expect(access_token).to receive(:get).with('api/v4/user').and_return(response) + expect(subject.raw_info).to eq(parsed_response) + end + end + + context 'with old style configuration' do + let(:enterprise_site) { 'https://some.other.site.com/api/v4' } + + subject { enterprise } + + it 'sent request to current user endpoint' do + expect(access_token).to receive(:get).with('user').and_return(response) + expect(subject.raw_info).to eq(parsed_response) + end + + context 'with a trailing slash' do + let(:enterprise_site) { 'https://some.other.site.com/api/v4/' } + + it 'sent request to current user endpoint' do + expect(access_token).to receive(:get).with('user').and_return(response) + expect(subject.raw_info).to eq(parsed_response) + end + end + end + end +end diff --git a/vendor/gems/omniauth-gitlab/spec/spec_helper.rb b/vendor/gems/omniauth-gitlab/spec/spec_helper.rb new file mode 100644 index 00000000000..38553547e98 --- /dev/null +++ b/vendor/gems/omniauth-gitlab/spec/spec_helper.rb @@ -0,0 +1,8 @@ +$LOAD_PATH.unshift File.expand_path('..', __FILE__) +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'simplecov' +SimpleCov.start +require 'rspec' +require 'rspec/its' +require 'omniauth' +require 'omniauth-gitlab' diff --git a/yarn.lock b/yarn.lock index 60129b671c9..e5b5cf166ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1048,15 +1048,15 @@ stylelint-declaration-strict-value "1.8.0" stylelint-scss "4.2.0" -"@gitlab/svgs@2.21.0": - version "2.21.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.21.0.tgz#bc71951dc35a61647fb2c0267cca6fb55a04d317" - integrity sha512-cVa5cgvVmY2MsRdV61id+rLTsY/tAGPq7Og9ETblUuZXl06ciw8H/g7cYPMxN39DdEfDklzbUnS98OJlMmD9TQ== - -"@gitlab/ui@42.9.0": - version "42.9.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-42.9.0.tgz#506a642b9bef9eb5d363684c8ef81129a9b284ef" - integrity sha512-i575fmHOXYPGWdaaSqt7cdgpfhPvnbIwhavslgbj9g9LNzffH7fea4P6BobKakb1AZuOfahJfarqfOJ2pYuRSQ== +"@gitlab/svgs@2.22.0": + version "2.22.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.22.0.tgz#66211ba26418b8dfb3ac796d795a0e3392d22ada" + integrity sha512-9Es97o/VByIsCNNfSF28oTYW5XIJ2dZSK0YjSpyy5yBF0QPwEjzRhxapYGz2c8YBFVC9WkoGHjp5PJ9tJgmmuA== + +"@gitlab/ui@42.11.0": + version "42.11.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-42.11.0.tgz#68b1cca7414a9f8e6d7e18ab8325daac000588f8" + integrity sha512-IxawSvXyL4ysvriOcQ+2TpdEzZxDumj9H/K3MTxmvKYtrFEygNE7wCPasQa3/jxmdfn6sYuPDHVg1tk1ziXyVQ== dependencies: "@popperjs/core" "^2.11.2" bootstrap-vue "2.20.1" |