From 38c1da5195bdcaab0b20bf6303a675b9283ac476 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 10 May 2023 00:08:52 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .gitlab/ci/rules.gitlab-ci.yml | 6 - .gitlab/ci/vendored-gems.gitlab-ci.yml | 8 - Gemfile | 4 +- Gemfile.lock | 11 +- .../components/delete_merged_branches.vue | 1 + app/controllers/application_controller.rb | 15 - .../omniauth_callbacks_controller.rb | 17 +- .../concerns/time_frame_arguments.rb | 42 +-- app/graphql/resolvers/milestones_resolver.rb | 2 - app/models/users/banned_user.rb | 2 + app/models/users/credit_card_validation.rb | 30 +- app/policies/identity_provider_policy.rb | 4 +- app/services/projects/create_service.rb | 3 + app/views/groups/new.html.haml | 11 +- .../ci_remove_legacy_predefined_variables.yml | 2 +- .../development/environment_details_vue.yml | 2 +- config/gitlab.yml.example | 27 +- config/initializers/1_settings.rb | 6 +- .../16_0/16-0-cas3-authentication.yml | 23 ++ doc/api/graphql/reference/index.md | 22 +- doc/integration/cas.md | 9 +- doc/update/removals.md | 8 + .../settings/account_and_limit_settings.md | 11 + .../dependency_scanning/index.md | 8 +- doc/user/gitlab_com/index.md | 16 ++ doc/user/project/import/bitbucket_server.md | 4 +- doc/user/project/import/fogbugz.md | 3 + doc/user/project/import/gitea.md | 3 + doc/user/project/import/github.md | 5 + doc/user/project/import/manifest.md | 3 + doc/user/project/import/repo_by_url.md | 3 + doc/user/project/merge_requests/commits.md | 59 ++++ .../merge_requests/img/commit_nav_v16_0.png | Bin 0 -> 10423 bytes .../img/previously_merged_commits_v16_0.png | Bin 0 -> 15992 bytes lib/api/project_import.rb | 12 +- lib/gitlab/auth/o_auth/session.rb | 23 -- .../Jobs/Dependency-Scanning.gitlab-ci.yml | 2 +- .../Dependency-Scanning.latest.gitlab-ci.yml | 2 +- .../ci/templates/Jobs/SAST.gitlab-ci.yml | 18 +- .../templates/Jobs/SAST.latest.gitlab-ci.yml | 24 +- .../database/migrations/pg_backend_pid.rb | 2 + lib/gitlab/omniauth_initializer.rb | 14 - locale/gitlab.pot | 9 +- qa/qa/page/component/import/selection.rb | 4 + qa/qa/page/project/branches/show.rb | 2 + qa/qa/page/project/new.rb | 8 +- .../import/import_github_repo_spec.rb | 4 + .../import/import_github_repo_spec.rb | 2 + .../revert/reverting_merge_request_spec.rb | 2 + .../view_merge_request_diff_patch_spec.rb | 2 + .../online_garbage_collection_spec.rb | 2 + .../terraform_module_registry_spec.rb | 2 + .../maven/maven_group_level_spec.rb | 2 + .../maven/maven_project_level_spec.rb | 2 + qa/qa/support/helpers/import_source.rb | 19 ++ .../import/fogbugz_controller_spec.rb | 2 + .../import/gitea_controller_spec.rb | 4 + .../import/manifest_controller_spec.rb | 2 + .../profiles/accounts_controller_spec.rb | 16 +- spec/features/admin/admin_settings_spec.rb | 11 - .../file_uploads/project_import_spec.rb | 4 + spec/features/import/manifest_import_spec.rb | 2 + spec/features/oauth_login_spec.rb | 2 +- spec/features/oauth_registration_spec.rb | 1 - .../environments/environments_spec.rb | 2 +- .../import_export/import_file_spec.rb | 1 + spec/features/projects/new_project_spec.rb | 4 + .../delete_merged_branches_spec.js.snap | 1 + .../notes/components/notes_app_spec.js | 4 +- .../new/preserve_url_fragment_spec.js | 10 - .../group_milestones_resolver_spec.rb | 66 ++--- .../project_milestones_resolver_spec.rb | 38 --- spec/helpers/profiles_helper_spec.rb | 42 +-- .../migrations/pg_backend_pid_spec.rb | 8 + .../fogbugz_import/project_creator_spec.rb | 6 +- .../project_creator_spec.rb | 2 + .../manifest_import/project_creator_spec.rb | 4 +- spec/lib/gitlab/omniauth_initializer_spec.rb | 8 - .../users/credit_card_validation_spec.rb | 103 +++++++ .../policies/identity_provider_policy_spec.rb | 10 +- .../api/graphql/group/milestones_spec.rb | 6 - .../api/graphql/project/milestones_spec.rb | 29 -- spec/requests/api/project_import_spec.rb | 20 ++ spec/requests/api/projects_spec.rb | 1 + .../requests/import/github_controller_spec.rb | 2 + .../import/github_groups_controller_spec.rb | 2 + .../import/gitlab_projects_controller_spec.rb | 2 + spec/services/import/fogbugz_service_spec.rb | 1 + .../create_project_service_spec.rb | 1 + spec/services/projects/create_service_spec.rb | 4 + vendor/gems/omniauth-cas3/.gitlab-ci.yml | 28 -- vendor/gems/omniauth-cas3/Gemfile | 4 - vendor/gems/omniauth-cas3/Gemfile.lock | 65 ----- vendor/gems/omniauth-cas3/LICENSE | 23 -- vendor/gems/omniauth-cas3/README.md | 134 --------- vendor/gems/omniauth-cas3/Rakefile | 15 - .../gems/omniauth-cas3/lib/omniauth-cas3.rb | 1 - .../gems/omniauth-cas3/lib/omniauth/cas3.rb | 2 - .../lib/omniauth/cas3/version.rb | 5 - .../lib/omniauth/strategies/cas3.rb | 227 --------------- .../strategies/cas3/logout_request.rb | 73 ----- .../cas3/service_ticket_validator.rb | 103 ------- .../gems/omniauth-cas3/omniauth-cas3.gemspec | 27 -- .../spec/fixtures/cas_failure.xml | 4 - .../spec/fixtures/cas_success.xml | 14 - .../spec/fixtures/cas_success_jasig.xml | 16 -- .../strategies/cas3/logout_request_spec.rb | 127 --------- .../cas3/service_ticket_validator_spec.rb | 55 ---- .../spec/omniauth/strategies/cas3_spec.rb | 266 ------------------ vendor/gems/omniauth-cas3/spec/spec_helper.rb | 13 - 110 files changed, 506 insertions(+), 1644 deletions(-) create mode 100644 data/removals/16_0/16-0-cas3-authentication.yml create mode 100644 doc/user/project/merge_requests/commits.md create mode 100644 doc/user/project/merge_requests/img/commit_nav_v16_0.png create mode 100644 doc/user/project/merge_requests/img/previously_merged_commits_v16_0.png delete mode 100644 lib/gitlab/auth/o_auth/session.rb create mode 100644 qa/qa/support/helpers/import_source.rb delete mode 100644 vendor/gems/omniauth-cas3/.gitlab-ci.yml delete mode 100644 vendor/gems/omniauth-cas3/Gemfile delete mode 100644 vendor/gems/omniauth-cas3/Gemfile.lock delete mode 100644 vendor/gems/omniauth-cas3/LICENSE delete mode 100644 vendor/gems/omniauth-cas3/README.md delete mode 100644 vendor/gems/omniauth-cas3/Rakefile delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth-cas3.rb delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth/cas3.rb delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth/cas3/version.rb delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/logout_request.rb delete mode 100644 vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/service_ticket_validator.rb delete mode 100644 vendor/gems/omniauth-cas3/omniauth-cas3.gemspec delete mode 100644 vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml delete mode 100644 vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml delete mode 100644 vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml delete mode 100644 vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb delete mode 100644 vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb delete mode 100644 vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb delete mode 100644 vendor/gems/omniauth-cas3/spec/spec_helper.rb diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 87641327d97..82f46df80a7 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -2042,12 +2042,6 @@ changes: ["vendor/gems/omniauth-azure-oauth2/**/*"] - <<: *if-merge-request-labels-run-all-rspec -.vendor:rules:omniauth-cas3: - rules: - - <<: *if-merge-request - changes: ["vendor/gems/omniauth-cas3/**/*"] - - <<: *if-merge-request-labels-run-all-rspec - .vendor:rules:omniauth_crowd: rules: - <<: *if-merge-request diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml index a2af802c659..3773a343d6d 100644 --- a/.gitlab/ci/vendored-gems.gitlab-ci.yml +++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml @@ -38,14 +38,6 @@ vendor omniauth-azure-oauth2: include: vendor/gems/omniauth-azure-oauth2/.gitlab-ci.yml strategy: depend -vendor omniauth-cas3: - extends: - - .vendor:rules:omniauth-cas3 - needs: [] - trigger: - include: vendor/gems/omniauth-cas3/.gitlab-ci.yml - strategy: depend - vendor omniauth_crowd: extends: - .vendor:rules:omniauth_crowd diff --git a/Gemfile b/Gemfile index b60a3e3181d..e1d07891f06 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,6 @@ gem 'omniauth', '~> 2.1.0' gem 'omniauth-auth0', '~> 3.1' gem 'omniauth-azure-activedirectory-v2', '~> 2.0' gem 'omniauth-azure-oauth2', '~> 0.0.9', path: 'vendor/gems/omniauth-azure-oauth2' # See gem README.md -gem 'omniauth-cas3', '~> 1.1.4', path: 'vendor/gems/omniauth-cas3' # See vendor/gems/omniauth-cas3/README.md gem 'omniauth-dingtalk-oauth2', '~> 1.0' gem 'omniauth-alicloud', '~> 2.0.1' gem 'omniauth-facebook', '~> 4.0.0' @@ -457,7 +456,8 @@ group :test do gem 'capybara', '~> 3.39' gem 'capybara-screenshot', '~> 1.0.26' - gem 'selenium-webdriver', '~> 4.9.0' + # 4.9.1 drops Ruby 2.7 support. We can upgrade further after we drop Ruby 2.7 support. + gem 'selenium-webdriver', '= 4.9.0' gem 'graphlyte', '~> 1.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index ddd70e5f856..c1fd5ac738f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,14 +61,6 @@ PATH omniauth (~> 2.0) omniauth-oauth2 (~> 1.4) -PATH - remote: vendor/gems/omniauth-cas3 - specs: - omniauth-cas3 (1.1.4) - addressable (~> 2.3) - nokogiri (~> 1.7, >= 1.7.1) - omniauth (~> 2.0) - PATH remote: vendor/gems/omniauth-gitlab specs: @@ -1849,7 +1841,6 @@ DEPENDENCIES omniauth-auth0 (~> 3.1) omniauth-azure-activedirectory-v2 (~> 2.0) omniauth-azure-oauth2 (~> 0.0.9)! - omniauth-cas3 (~> 1.1.4)! omniauth-dingtalk-oauth2 (~> 1.0) omniauth-facebook (~> 4.0.0) omniauth-github (= 2.0.1) @@ -1920,7 +1911,7 @@ DEPENDENCIES sassc-rails (~> 2.1.0) sd_notify (~> 0.1.0) seed-fu (~> 2.3.7) - selenium-webdriver (~> 4.9.0) + selenium-webdriver (= 4.9.0) semver_dialects (~> 1.2.1) sentry-rails (~> 5.8.0) sentry-raven (~> 3.1) diff --git a/app/assets/javascripts/branches/components/delete_merged_branches.vue b/app/assets/javascripts/branches/components/delete_merged_branches.vue index 73d7a59e67e..d9d8f1d742d 100644 --- a/app/assets/javascripts/branches/components/delete_merged_branches.vue +++ b/app/assets/javascripts/branches/components/delete_merged_branches.vue @@ -102,6 +102,7 @@ export default { category="tertiary" no-caret placement="right" + data-qa-selector="delete_merged_branches_dropdown_button" :items="dropdownItems" /> { !current_user } before_action :authenticate_user!, except: [:route_not_found] before_action :enforce_terms!, if: :should_enforce_terms? - before_action :validate_user_service_ticket! before_action :check_password_expiration, if: :html_request? before_action :ldap_security_check before_action :default_headers @@ -326,20 +325,6 @@ class ApplicationController < ActionController::Base headers['Content-Disposition'] = "attachment; filename=\"#{csv_filename}\"" end - def validate_user_service_ticket! - return unless signed_in? && session[:service_tickets] - - valid = session[:service_tickets].all? do |provider, ticket| - Gitlab::Auth::OAuth::Session.valid?(provider, ticket) - end - - unless valid - session[:service_tickets] = nil - sign_out current_user - redirect_to new_user_session_path - end - end - def check_password_expiration return if session[:impersonator_id] || !current_user&.allow_password_authentication? diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b9964e8ca01..a2e0670d7e1 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -10,7 +10,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController after_action :verify_known_sign_in - protect_from_forgery except: [:cas3, :failure] + AuthHelper.saml_providers, with: :exception, prepend: true + protect_from_forgery except: [:failure] + AuthHelper.saml_providers, with: :exception, prepend: true feature_category :system_access @@ -57,15 +57,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_unverified_saml_initiation end - def cas3 - ticket = params['ticket'] - if ticket - handle_service_ticket oauth['provider'], ticket - end - - handle_omniauth - end - def auth0 if oauth['uid'].blank? fail_auth0_login @@ -146,12 +137,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to profile_account_path, notice: _('Authentication method updated') end - def handle_service_ticket(provider, ticket) - Gitlab::Auth::OAuth::Session.create provider, ticket - session[:service_tickets] ||= {} - session[:service_tickets][provider] = ticket - end - def build_auth_user(auth_user_class) auth_user_class.new(oauth) end diff --git a/app/graphql/resolvers/concerns/time_frame_arguments.rb b/app/graphql/resolvers/concerns/time_frame_arguments.rb index 87b7a96045c..c26898bb2f1 100644 --- a/app/graphql/resolvers/concerns/time_frame_arguments.rb +++ b/app/graphql/resolvers/concerns/time_frame_arguments.rb @@ -3,51 +3,15 @@ module TimeFrameArguments extend ActiveSupport::Concern - OVERLAPPING_TIMEFRAME_DESC = 'List items overlapping a time frame defined by startDate..endDate (if one date is provided, both must be present)' - included do - argument :start_date, Types::TimeType, - required: false, - description: OVERLAPPING_TIMEFRAME_DESC, - deprecated: { reason: 'Use timeframe.start', milestone: '13.5' } - - argument :end_date, Types::TimeType, - required: false, - description: OVERLAPPING_TIMEFRAME_DESC, - deprecated: { reason: 'Use timeframe.end', milestone: '13.5' } - argument :timeframe, Types::TimeframeInputType, required: false, description: 'List items overlapping the given timeframe.' end - # TODO: remove when the start_date and end_date arguments are removed - def validate_timeframe_params!(args) - return unless %i[start_date end_date timeframe].any? { |k| args[k].present? } - - # the timeframe is passed in as a TimeframeInputType - timeframe = args[:timeframe].to_h if args[:timeframe] - return if timeframe && %i[start_date end_date].all? { |k| args[k].nil? } - - error_message = - if timeframe.present? - "startDate and endDate are deprecated in favor of timeframe. Please use only timeframe." - elsif args[:start_date].nil? || args[:end_date].nil? - "Both startDate and endDate must be present." - elsif args[:start_date] > args[:end_date] - "startDate is after endDate" - end - - if error_message - raise Gitlab::Graphql::Errors::ArgumentError, error_message - end - end - def transform_timeframe_parameters(args) - if args[:timeframe] - args[:timeframe].to_h.transform_keys { |k| :"#{k}_date" } - else - args.slice(:start_date, :end_date) - end + return {} unless args[:timeframe] + + args[:timeframe].to_h.transform_keys { |k| :"#{k}_date" } end end diff --git a/app/graphql/resolvers/milestones_resolver.rb b/app/graphql/resolvers/milestones_resolver.rb index 25ff783b408..563c6594665 100644 --- a/app/graphql/resolvers/milestones_resolver.rb +++ b/app/graphql/resolvers/milestones_resolver.rb @@ -40,8 +40,6 @@ module Resolvers NON_STABLE_CURSOR_SORTS = %i[expired_last_due_date_asc expired_last_due_date_desc].freeze def resolve_with_lookahead(**args) - validate_timeframe_params!(args) - milestones = apply_lookahead(MilestonesFinder.new(milestones_finder_params(args)).execute) if non_stable_cursor_sort?(args[:sort]) diff --git a/app/models/users/banned_user.rb b/app/models/users/banned_user.rb index 466fc71f83a..8a62744c7d6 100644 --- a/app/models/users/banned_user.rb +++ b/app/models/users/banned_user.rb @@ -5,6 +5,8 @@ module Users self.primary_key = :user_id belongs_to :user + has_one :credit_card_validation, class_name: '::Users::CreditCardValidation', primary_key: 'user_id', + foreign_key: 'user_id', inverse_of: :banned_user validates :user, presence: true validates :user_id, uniqueness: { message: N_("banned user already exists") } diff --git a/app/models/users/credit_card_validation.rb b/app/models/users/credit_card_validation.rb index 272f31aa9ce..1b0fd8682db 100644 --- a/app/models/users/credit_card_validation.rb +++ b/app/models/users/credit_card_validation.rb @@ -7,6 +7,8 @@ module Users self.table_name = 'user_credit_card_validations' belongs_to :user + belongs_to :banned_user, class_name: '::Users::BannedUser', foreign_key: :user_id, + inverse_of: :credit_card_validation validates :holder_name, length: { maximum: 50 } validates :network, length: { maximum: 32 } @@ -14,18 +16,32 @@ module Users greater_than_or_equal_to: 0, less_than_or_equal_to: 9999 } + scope :by_banned_user, -> { joins(:banned_user) } + scope :similar_by_holder_name, ->(holder_name) do + if holder_name.present? + where('lower(holder_name) = lower(:value)', value: holder_name) + else + none + end + end + scope :similar_to, ->(credit_card_validation) do + where( + expiration_date: credit_card_validation.expiration_date, + last_digits: credit_card_validation.last_digits, + network: credit_card_validation.network + ) + end + def similar_records - self.class.where( - expiration_date: expiration_date, - last_digits: last_digits, - network: network - ).order(credit_card_validated_at: :desc).includes(:user) + self.class.similar_to(self).order(credit_card_validated_at: :desc).includes(:user) end def similar_holder_names_count - return 0 unless holder_name + self.class.similar_by_holder_name(holder_name).count + end - self.class.where('lower(holder_name) = lower(:value)', value: holder_name).count + def used_by_banned_user? + self.class.by_banned_user.similar_to(self).similar_by_holder_name(holder_name).exists? end end end diff --git a/app/policies/identity_provider_policy.rb b/app/policies/identity_provider_policy.rb index c539fc64d3f..1e748c78555 100644 --- a/app/policies/identity_provider_policy.rb +++ b/app/policies/identity_provider_policy.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class IdentityProviderPolicy < BasePolicy - desc "Provider is SAML or CAS3" - condition(:protected_provider, scope: :subject, score: 0) { %w(saml cas3).include?(@subject.to_s) } + desc "Provider is SAML" + condition(:protected_provider, scope: :subject, score: 0) { @subject.to_s == 'saml' } rule { anonymous }.prevent_all diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index ac19f21ffc7..8ad2b0ac761 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -294,6 +294,9 @@ module Projects return if INTERNAL_IMPORT_SOURCES.include?(import_type) + # Skip validation when creating project from a built in template + return if @params[:import_export_upload].present? && import_type == 'gitlab_project' + unless ::Gitlab::CurrentSettings.import_sources&.include?(import_type) raise ImportSourceDisabledError, "#{import_type} import source is disabled" end diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 1d306d4d3b8..88cb8d989fa 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -16,11 +16,6 @@ = render 'new_group_fields', f: f, group_name_id: 'create-group-name' #import-group-pane.tab-pane - - if import_sources_enabled? - = render 'import_group_from_another_instance_panel' - .gl-mt-7.gl-border-b-solid.gl-border-gray-100.gl-border-1 - = render 'import_group_from_file_panel' - - else - .nothing-here-block - %h4= s_('GroupsNew|No import options available') - %p= s_('GroupsNew|Contact an administrator to enable options for importing your group.') + = render 'import_group_from_another_instance_panel' + .gl-mt-7.gl-border-b-solid.gl-border-gray-100.gl-border-1 + = render 'import_group_from_file_panel' diff --git a/config/feature_flags/development/ci_remove_legacy_predefined_variables.yml b/config/feature_flags/development/ci_remove_legacy_predefined_variables.yml index dd54b9d6d2a..48f09f39dff 100644 --- a/config/feature_flags/development/ci_remove_legacy_predefined_variables.yml +++ b/config/feature_flags/development/ci_remove_legacy_predefined_variables.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/404533 milestone: '15.11' type: development group: group::pipeline authoring -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/development/environment_details_vue.yml b/config/feature_flags/development/environment_details_vue.yml index c757329c271..c90038a600c 100644 --- a/config/feature_flags/development/environment_details_vue.yml +++ b/config/feature_flags/development/environment_details_vue.yml @@ -5,4 +5,4 @@ rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/384914" milestone: '15.7' type: development group: group::configure -default_enabled: false +default_enabled: true diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 4c1ca26e004..e46bc5b646c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -127,8 +127,8 @@ production: &base # username_changing_enabled: false # default: true - User can change their username/namespace ## Default theme ID ## 1 - Indigo - ## 2 - Dark - ## 3 - Light + ## 2 - Gray + ## 3 - Light Gray ## 4 - Blue ## 5 - Green ## 6 - Light Indigo @@ -136,6 +136,7 @@ production: &base ## 8 - Light Green ## 9 - Red ## 10 - Light Red + ## 11 - Dark Mode (alpha) # default_theme: 1 # default: 1 ## Automatic issue closing @@ -1001,7 +1002,7 @@ production: &base # auto_sign_in_with_provider: saml # Sync user's profile from the specified Omniauth providers every time the user logs in (default: empty). - # Define the allowed providers using an array, e.g. ["cas3", "saml", "twitter"], + # Define the allowed providers using an array, e.g. ["saml", "twitter"], # or as true/false to allow all providers or none. # When authenticating using LDAP, the user's email is always synced. # sync_profile_from_provider: [] @@ -1067,15 +1068,6 @@ production: &base # - { name: 'alicloud', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' } - # See omniauth-cas3 for more configuration details - # - { name: 'cas3', - # label: 'cas3', - # args: { - # url: 'https://sso.example.com', - # disable_ssl_verification: false, - # login_url: '/cas/login', - # service_validate_url: '/cas/p3/serviceValidate', - # logout_url: '/cas/logout' } } # - { name: 'github', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', @@ -1139,10 +1131,6 @@ production: &base # client_secret: 'YOUR_AUTH0_CLIENT_SECRET', # namespace: 'YOUR_AUTH0_DOMAIN' } } - # SSO maximum session duration in seconds. Defaults to CAS default of 8 hours. - # cas3: - # session_duration: 28800 - # FortiAuthenticator settings forti_authenticator: # Allow using FortiAuthenticator as OTP provider @@ -1583,13 +1571,6 @@ test: - { name: 'alicloud', app_id: 'YOUR_APP_ID', app_secret: 'YOUR_APP_SECRET' } - - { name: 'cas3', - label: 'cas3', - args: { url: 'https://sso.example.com', - disable_ssl_verification: false, - login_url: '/cas/login', - service_validate_url: '/cas/p3/serviceValidate', - logout_url: '/cas/logout' } } - { name: 'github', app_id: 'YOUR_APP_ID', app_secret: 'YOUR_APP_SECRET', diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 6b41b32e15c..a3076f0014d 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -112,10 +112,6 @@ if Settings.omniauth['sync_email_from_provider'] end Settings.omniauth['providers'] ||= [] -Settings.omniauth['cas3'] ||= {} -Settings.omniauth.cas3['session_duration'] ||= 8.hours -Settings.omniauth['session_tickets'] ||= {} -Settings.omniauth.session_tickets['cas3'] = 'ticket' # Handle backward compatibility with the renamed kerberos_spnego provider # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96335#note_1094265436 @@ -232,7 +228,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.__send__(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['domain_allowlist'] ||= [] -Settings.gitlab['import_sources'] ||= Gitlab::ImportSources.values +Settings.gitlab['import_sources'] ||= [] Settings.gitlab['trusted_proxies'] ||= [] Settings.gitlab['content_security_policy'] ||= {} Settings.gitlab['allowed_hosts'] ||= [] diff --git a/data/removals/16_0/16-0-cas3-authentication.yml b/data/removals/16_0/16-0-cas3-authentication.yml new file mode 100644 index 00000000000..cd85f1bd38d --- /dev/null +++ b/data/removals/16_0/16-0-cas3-authentication.yml @@ -0,0 +1,23 @@ +# This is a template for announcing a feature removal or other important change. +# +# Please refer to the deprecation guidelines to confirm your understanding of GitLab's definitions. +# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology +# +# If this is a breaking change, it must happen in a major release. +# +# For more information please refer to the handbook documentation here: +# https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-and-other-planned-breaking-change-announcements +# +# Please delete this line and above before submitting your merge request. +# +# REQUIRED FIELDS +# +- title: "CAS OmniAuth provider is removed" # (required) Clearly explain the change. For example, "The `confidential` field for a `Note` is removed" or "CI/CD job names are limited to 250 characters." + announcement_milestone: "15.3" # (required) The milestone when this feature was deprecated. + removal_milestone: "16.0" # (required) The milestone when this feature is being removed. + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: jessieay # (required) GitLab username of the person reporting the removal + stage: Manage # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369127 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + The `omniauth-cas3` gem that provides GitLab with the CAS OmniAuth provider is being removed. You can no longer authenticate into a GitLab instance through CAS. This gem sees very little use. [The gem](https://rubygems.org/gems/omniauth-cas3/) has not had a new release in almost 5 years, which means that its dependencies are out of date and required manual patching during GitLab's [upgrade to OmniAuth 2.0](https://gitlab.com/gitlab-org/gitlab/-/issues/30073). diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ed67905e6b1..560f1a4c60a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -11815,7 +11815,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -11829,7 +11828,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -11854,7 +11852,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -11868,7 +11865,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -14011,7 +14007,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -14025,7 +14020,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -14050,7 +14044,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -14064,7 +14057,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -15126,7 +15118,6 @@ Returns [`Epic`](#epic). | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -15140,7 +15131,6 @@ Returns [`Epic`](#epic). | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -15177,7 +15167,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `confidential` | [`Boolean`](#boolean) | Filter epics by given confidentiality. | | `createdAfter` | [`Time`](#time) | Epics created after this date. | | `createdBefore` | [`Time`](#time) | Epics created before this date. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `iid` | [`ID`](#id) | IID of the epic, e.g., "1". | | `iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. | | `iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. | @@ -15191,7 +15180,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `or` **{warning-solid}** | [`UnionedEpicFilterInput`](#unionedepicfilterinput) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. List of arguments with inclusive OR. Ignored unless `or_issuable_queries` flag is enabled. | | `search` | [`String`](#string) | Search query for title or description. | | `sort` | [`EpicSort`](#epicsort) | List epics by sort order. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`EpicState`](#epicstate) | Filter epics by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `topLevelHierarchyOnly` | [`Boolean`](#boolean) | Filter epics with a top-level hierarchy. | @@ -15317,7 +15305,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `id` | [`ID`](#id) | Global ID of the Iteration to look up. | | `iid` | [`ID`](#id) | Internal ID of the Iteration to look up. | | `in` | [`[IterationSearchableField!]`](#iterationsearchablefield) | Fields in which the fuzzy-search should be performed with the query given in the argument `search`. Defaults to `[title]`. | @@ -15325,7 +15312,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `iterationCadenceIds` | [`[IterationsCadenceID!]`](#iterationscadenceid) | Global iteration cadence IDs by which to look up the iterations. | | `search` | [`String`](#string) | Query used for fuzzy-searching in the fields selected in the argument `in`. Returns all iterations if empty. | | `sort` | [`IterationSort`](#iterationsort) | List iterations by sort order. If unspecified, an arbitrary order (subject to change) is used. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`IterationState`](#iterationstate) | Filter iterations by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `title` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. The argument will be removed in 15.4. Please use `search` and `in` fields instead. | @@ -15428,13 +15414,11 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `containingDate` | [`Time`](#time) | Date the milestone contains. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `ids` | [`[ID!]`](#id) | Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`. | | `includeAncestors` | [`Boolean`](#boolean) | Include milestones from all parent groups. | | `includeDescendants` | [`Boolean`](#boolean) | Include milestones from all subgroups and subprojects. | | `searchTitle` | [`String`](#string) | Search string for the title. | | `sort` | [`MilestoneSort`](#milestonesort) | Sort milestones by this criteria. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`MilestoneStateEnum`](#milestonestateenum) | Filter milestones by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `title` | [`String`](#string) | Title of the milestone. | @@ -18897,7 +18881,6 @@ Represents a product analytics dashboard visualization. | `issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user. | | `jiraImportStatus` | [`String`](#string) | Status of Jira import background job of the project. | | `jiraImports` | [`JiraImportConnection`](#jiraimportconnection) | Jira imports into the project. (see [Connections](#connections)) | -| `jitsuKey` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.7. This feature is an Experiment. It can be changed or removed at any time. Jitsu key assigned to the project. | | `jobsEnabled` | [`Boolean`](#boolean) | Indicates if CI/CD pipeline jobs are enabled for the current user. | | `languages` | [`[RepositoryLanguage!]`](#repositorylanguage) | Programming languages used in the project. | | `lastActivityAt` | [`Time`](#time) | Timestamp of the project last activity. | @@ -18945,6 +18928,7 @@ Represents a product analytics dashboard visualization. | `terraformStates` | [`TerraformStateConnection`](#terraformstateconnection) | Terraform states associated with the project. (see [Connections](#connections)) | | `timelogCategories` **{warning-solid}** | [`TimeTrackingTimelogCategoryConnection`](#timetrackingtimelogcategoryconnection) | **Introduced** in 15.3. This feature is an Experiment. It can be changed or removed at any time. Timelog categories for the project. | | `topics` | [`[String!]`](#string) | List of project topics. | +| `trackingKey` **{warning-solid}** | [`String`](#string) | **Introduced** in 16.0. This feature is an Experiment. It can be changed or removed at any time. Tracking key assigned to the project. | | `userAccessAuthorizedAgents` | [`ClusterAgentAuthorizationUserAccessConnection`](#clusteragentauthorizationuseraccessconnection) | Authorized cluster agents for the project through user_access keyword. (see [Connections](#connections)) | | `userPermissions` | [`ProjectPermissions!`](#projectpermissions) | Permissions for the current user on the resource. | | `visibility` | [`String`](#string) | Visibility of the project. | @@ -19614,7 +19598,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `id` | [`ID`](#id) | Global ID of the Iteration to look up. | | `iid` | [`ID`](#id) | Internal ID of the Iteration to look up. | | `in` | [`[IterationSearchableField!]`](#iterationsearchablefield) | Fields in which the fuzzy-search should be performed with the query given in the argument `search`. Defaults to `[title]`. | @@ -19622,7 +19605,6 @@ four standard [pagination arguments](#connection-pagination-arguments): | `iterationCadenceIds` | [`[IterationsCadenceID!]`](#iterationscadenceid) | Global iteration cadence IDs by which to look up the iterations. | | `search` | [`String`](#string) | Query used for fuzzy-searching in the fields selected in the argument `in`. Returns all iterations if empty. | | `sort` | [`IterationSort`](#iterationsort) | List iterations by sort order. If unspecified, an arbitrary order (subject to change) is used. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`IterationState`](#iterationstate) | Filter iterations by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `title` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. The argument will be removed in 15.4. Please use `search` and `in` fields instead. | @@ -19746,12 +19728,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `containingDate` | [`Time`](#time) | Date the milestone contains. | -| `endDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.end. | | `ids` | [`[ID!]`](#id) | Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`. | | `includeAncestors` | [`Boolean`](#boolean) | Also return milestones in the project's parent group and its ancestors. | | `searchTitle` | [`String`](#string) | Search string for the title. | | `sort` | [`MilestoneSort`](#milestonesort) | Sort milestones by this criteria. | -| `startDate` **{warning-solid}** | [`Time`](#time) | **Deprecated** in 13.5. Use timeframe.start. | | `state` | [`MilestoneStateEnum`](#milestonestateenum) | Filter milestones by state. | | `timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. | | `title` | [`String`](#string) | Title of the milestone. | diff --git a/doc/integration/cas.md b/doc/integration/cas.md index 9cbd8cf03d5..bfeabde8a36 100644 --- a/doc/integration/cas.md +++ b/doc/integration/cas.md @@ -2,13 +2,16 @@ stage: Manage group: Authentication and Authorization info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +remove_date: '2023-08-15' +redirect_to: '../administration/auth/index.md' --- -# CAS OmniAuth provider (deprecated) **(FREE SELF)** +# CAS OmniAuth provider (removed) **(FREE SELF)** WARNING: -This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369127) in GitLab 15.3 and is planned for -removal in 16.0. +This feature was +[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369127) in GitLab +15.3 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/369128) in 16.0. To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab supplies to CAS. It should be diff --git a/doc/update/removals.md b/doc/update/removals.md index 17bcbd8d72d..3e72b2a7a5e 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -59,6 +59,14 @@ The Azure Storage Driver used to write to `//` as the default root directory. Th In GitLab 16.0, the new default configuration for the storage driver uses `trimlegacyrootprefix: true`, and `/` is the default root directory. You can set your configuration to `trimlegacyrootprefix: false` if needed, to revert to the previous behavior. +### CAS OmniAuth provider is removed + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The `omniauth-cas3` gem that provides GitLab with the CAS OmniAuth provider is being removed. You can no longer authenticate into a GitLab instance through CAS. This gem sees very little use. [The gem](https://rubygems.org/gems/omniauth-cas3/) has not had a new release in almost 5 years, which means that its dependencies are out of date and required manual patching during GitLab's [upgrade to OmniAuth 2.0](https://gitlab.com/gitlab-org/gitlab/-/issues/30073). + ### CiCdSettingsUpdate mutation renamed to ProjectCiCdSettingsUpdate WARNING: diff --git a/doc/user/admin_area/settings/account_and_limit_settings.md b/doc/user/admin_area/settings/account_and_limit_settings.md index e4176ae16e6..0abaf32bad0 100644 --- a/doc/user/admin_area/settings/account_and_limit_settings.md +++ b/doc/user/admin_area/settings/account_and_limit_settings.md @@ -332,6 +332,17 @@ By default, newly created users have a public profile. GitLab administrators can 1. On the left sidebar, select **Settings > General**, then expand **Account and limit**. 1. Select the **Make new users' profiles private by default** checkbox. +## Prevent users from deleting their accounts **(FREE SELF)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/26053) in GitLab 16.0 [with a flag](../../../administration/feature_flags.md) named `deleting_account_disabled_for_users`. Disabled by default. + +By default, users can delete their own accounts. GitLab administrators can prevent +users from deleting their own accounts: + +1. On the top bar, select **Main menu > Admin**. +1. On the left sidebar, select **Settings > General**, then expand **Account and limit**. +1. Clear the **Allows users to delete their own accounts** checkbox. + ## Troubleshooting ### 413 Request Entity Too Large diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index ba70a77d8c0..91f10b5ab60 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -331,7 +331,7 @@ table.supported-languages ul {
gemnasium-dependency_scanning:
   image:
-    name: $CI_TEMPLATE_REGISTRY_HOST/security-products/gemnasium-python:3-python-3.10
+ name: $CI_TEMPLATE_REGISTRY_HOST/security-products/gemnasium-python:4-python-3.10

  • @@ -1010,9 +1010,9 @@ import the following default dependency scanning analyzer images from `registry. your [local Docker container registry](../../packages/container_registry/index.md): ```plaintext -registry.gitlab.com/security-products/gemnasium:3 -registry.gitlab.com/security-products/gemnasium-maven:3 -registry.gitlab.com/security-products/gemnasium-python:3 +registry.gitlab.com/security-products/gemnasium:4 +registry.gitlab.com/security-products/gemnasium-maven:4 +registry.gitlab.com/security-products/gemnasium-python:4 ``` The process for importing Docker images into a local offline Docker registry depends on diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index 76cd94452a8..c90bfba6830 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -208,6 +208,22 @@ NOTE: Cloudflare. Git LFS and imports other than a file upload are not affected by this limit. Repository limits apply to both public and private projects. +## Import sources + +GitLab.com has the following import sources enabled. + +| Import source | GitLab.com default | Default (self-managed) | +|------------------------|--------------------| ---------------------------| +| Bitbucket Cloud | **{check-circle}** Yes | **{dotted-circle}** No | +| Bitbucket Server | **{check-circle}** Yes | **{dotted-circle}** No | +| FogBugz | **{check-circle}** Yes | **{dotted-circle}** No | +| GitLab Direct Transfer | **{check-circle}** Yes | **{dotted-circle}** No | +| Gitea | **{check-circle}** Yes | **{dotted-circle}** No | +| GitHub | **{check-circle}** Yes | **{dotted-circle}** No | +| GitLab export | **{check-circle}** Yes | **{dotted-circle}** No | +| Manifest file | **{check-circle}** Yes | **{dotted-circle}** No | +| Repository by URL | **{check-circle}** Yes | **{dotted-circle}** No | + ## IP range GitLab.com uses the IP ranges `34.74.90.64/28` and `34.74.226.0/24` for traffic from its Web/API diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md index c7dfc739b71..22f81365755 100644 --- a/doc/user/project/import/bitbucket_server.md +++ b/doc/user/project/import/bitbucket_server.md @@ -27,7 +27,9 @@ created as private in GitLab as well. Prerequisites: -- An administrator must enable **Bitbucket Server** in **Admin > Settings > General > Visibility and access controls > Import sources**. +- [Bitbucket Server import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) +must be enabled. If not enabled, ask your GitLab administrator to enable it. The Bitbucket Server import source is enabled +by default on GitLab.com. - At least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0. diff --git a/doc/user/project/import/fogbugz.md b/doc/user/project/import/fogbugz.md index d66b1dbaa5d..6e378ccbb44 100644 --- a/doc/user/project/import/fogbugz.md +++ b/doc/user/project/import/fogbugz.md @@ -17,6 +17,9 @@ users. Prerequisite: +- [FogBugz import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) +must be enabled. If not enabled, ask your GitLab administrator to enable it. The FogBugz import source is enabled +by default on GitLab.com. - At least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0. diff --git a/doc/user/project/import/gitea.md b/doc/user/project/import/gitea.md index f11dc3e18dc..2eb798fab0f 100644 --- a/doc/user/project/import/gitea.md +++ b/doc/user/project/import/gitea.md @@ -15,6 +15,9 @@ This requires Gitea `v1.0.0` or later. Prerequisite: +- [Gitea import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) +must be enabled. If not enabled, ask your GitLab administrator to enable it. The Gitea import source is enabled +by default on GitLab.com. - At least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0. diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md index 1bd60a037fb..4bbb8cee534 100644 --- a/doc/user/project/import/github.md +++ b/doc/user/project/import/github.md @@ -38,6 +38,9 @@ For an overview of the import process, see [Migrating from GitHub to GitLab](htt To import projects from GitHub: +- [GitHub import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) + must be enabled. If not enabled, ask your GitLab administrator to enable it. The GitHub import source is enabled + by default on GitLab.com. - You must have at least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0. @@ -61,6 +64,8 @@ perimeter is specified in the [OmniAuth configuration](../../../integration/gith If you are importing from GitHub Enterprise to a self-managed GitLab instance: - You must first enable the [GitHub integration](../../../integration/github.md). +- GitHub must be enabled as an import source in the + [Admin Area](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources). - For GitLab 15.10 and earlier, you must add `github.com` and `api.github.com` entries in the [allowlist for local requests](../../../security/webhooks.md#allow-outbound-requests-to-certain-ip-addresses-and-domains). diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md index 8c7f749036c..545fd7810dc 100644 --- a/doc/user/project/import/manifest.md +++ b/doc/user/project/import/manifest.md @@ -17,6 +17,9 @@ repositories like the Android Open Source Project (AOSP). ## Requirements +- [Manifest import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) +must be enabled. If not enabled, ask your GitLab administrator to enable it. The Manifest import source is enabled +by default on GitLab.com. - GitLab must use PostgreSQL for its database, because [subgroups](../../group/subgroups/index.md) are needed for the manifest import to work. Read more about the [database requirements](../../../install/requirements.md#database). - At least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was diff --git a/doc/user/project/import/repo_by_url.md b/doc/user/project/import/repo_by_url.md index 0e7a32b785c..9f6d16cc04a 100644 --- a/doc/user/project/import/repo_by_url.md +++ b/doc/user/project/import/repo_by_url.md @@ -8,6 +8,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w Prerequisite: +- [Repository by URL import source](../../admin_area/settings/visibility_and_access_controls.md#configure-allowed-import-sources) +must be enabled. If not enabled, ask your GitLab administrator to enable it. The Repository by URL import source is enabled +by default on GitLab.com. - At least the Maintainer role on the destination group to import to. Using the Developer role for this purpose was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0. diff --git a/doc/user/project/merge_requests/commits.md b/doc/user/project/merge_requests/commits.md new file mode 100644 index 00000000000..cc6ecd8398f --- /dev/null +++ b/doc/user/project/merge_requests/commits.md @@ -0,0 +1,59 @@ +--- +stage: Create +group: Code Review +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +type: index, reference +--- + +# Merge request commits **(FREE)** + +Each merge request has a history of the commits made to the source branch +after the merge request was created. + +These commits are displayed on the merge request's **Commits** tab. +From this tab, you can review commit messages and copy a commit's SHA when you need to +[cherry-pick changes](cherry_pick_changes.md). + +## Navigate merge request commits + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18140) in GitLab 13.0. + +To navigate commits in a merge request: + +1. Select the **Commits** tab. +1. Select the commit link. The most recent commit is displayed. +1. Navigate through the commits by either: + + - Selecting **Prev** and **Next** buttons below the tab buttons. + - Using the X and C keyboard shortcuts. + +![Merge requests commit navigation](img/commit_nav_v16_0.png) + +## View merge request commits in context + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29274) in GitLab 13.12 [with a flag](../../../administration/feature_flags.md) named `context_commits`. Enabled by default. +> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.8. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) in GitLab 14.9. [Feature flag `context_commits`](https://gitlab.com/gitlab-org/gitlab/-/issues/320757) removed. + +When reviewing a merge request, it helps to have more context about the changes +made. That includes unchanged lines in unchanged files, and previous commits +that have already merged that the change is built on. + +To add previously merged commits to a merge request for more context: + +1. Go to your merge request. +1. Select the **Commits** tab. +1. Scroll to the end of the list of commits, and select **Add previously merged commits**: +1. Select the commits that you want to add. +1. Select **Save changes**. + +## View diffs between commits + +To view the changes between previously merged commits: + +1. On your merge request, select the **Changes** tab. +1. By **Compare**, select the commit you want to view: + + ![Previously merged commits](img/previously_merged_commits_v16_0.png) + +If you selected to add previously merged commits, they are displayed in the list. diff --git a/doc/user/project/merge_requests/img/commit_nav_v16_0.png b/doc/user/project/merge_requests/img/commit_nav_v16_0.png new file mode 100644 index 0000000000000000000000000000000000000000..6005e516fff9b48687701a2f8eedb2d0019309d9 GIT binary patch literal 10423 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 { ); }); - it('should render form comment button as disabled', () => { + // https://gitlab.com/gitlab-org/gitlab/-/issues/410409 + // eslint-disable-next-line jest/no-disabled-tests + it.skip('should render form comment button as disabled', () => { expect(findCommentButton().props('disabled')).toEqual(true); }); diff --git a/spec/frontend/pages/sessions/new/preserve_url_fragment_spec.js b/spec/frontend/pages/sessions/new/preserve_url_fragment_spec.js index e554521bfb5..6ff2bb42d8d 100644 --- a/spec/frontend/pages/sessions/new/preserve_url_fragment_spec.js +++ b/spec/frontend/pages/sessions/new/preserve_url_fragment_spec.js @@ -31,8 +31,6 @@ describe('preserve_url_fragment', () => { it('does not add an empty query parameter to OmniAuth login buttons', () => { preserveUrlFragment(); - expect(findFormAction('#oauth-login-cas3')).toBe('http://test.host/users/auth/cas3'); - expect(findFormAction('#oauth-login-auth0')).toBe('http://test.host/users/auth/auth0'); }); @@ -40,10 +38,6 @@ describe('preserve_url_fragment', () => { it('when "remember_me" is not present', () => { preserveUrlFragment('#L65'); - expect(findFormAction('#oauth-login-cas3')).toBe( - 'http://test.host/users/auth/cas3?redirect_fragment=L65', - ); - expect(findFormAction('#oauth-login-auth0')).toBe( 'http://test.host/users/auth/auth0?redirect_fragment=L65', ); @@ -56,10 +50,6 @@ describe('preserve_url_fragment', () => { preserveUrlFragment('#L65'); - expect(findFormAction('#oauth-login-cas3')).toBe( - 'http://test.host/users/auth/cas3?remember_me=1&redirect_fragment=L65', - ); - expect(findFormAction('#oauth-login-auth0')).toBe( 'http://test.host/users/auth/auth0?remember_me=1&redirect_fragment=L65', ); diff --git a/spec/graphql/resolvers/group_milestones_resolver_spec.rb b/spec/graphql/resolvers/group_milestones_resolver_spec.rb index a32a031a88f..b9b8ef1870b 100644 --- a/spec/graphql/resolvers/group_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/group_milestones_resolver_spec.rb @@ -2,15 +2,15 @@ require 'spec_helper' -RSpec.describe Resolvers::GroupMilestonesResolver do +RSpec.describe Resolvers::GroupMilestonesResolver, feature_category: :team_planning do using RSpec::Parameterized::TableSyntax include GraphqlHelpers describe '#resolve' do let_it_be(:current_user) { create(:user) } - def resolve_group_milestones(args = {}, context = { current_user: current_user }) - resolve(described_class, obj: group, args: args, ctx: context, arg_style: :internal) + def resolve_group_milestones(args: {}, context: { current_user: current_user }, arg_style: :internal) + resolve(described_class, obj: group, args: args, ctx: context, arg_style: arg_style) end let_it_be(:now) { Time.now } @@ -45,7 +45,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do end context 'with parameters' do - it 'calls MilestonesFinder with correct parameters' do + it 'timeframe argument' do start_date = now end_date = start_date + 1.hour @@ -53,18 +53,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date)) .and_call_original - resolve_group_milestones(start_date: start_date, end_date: end_date, state: 'closed') - end - - it 'understands the timeframe argument' do - start_date = now - end_date = start_date + 1.hour - - expect(MilestonesFinder).to receive(:new) - .with(args(group_ids: group.id, state: 'closed', start_date: start_date, end_date: end_date)) - .and_call_original - - resolve_group_milestones(timeframe: { start: start_date, end: end_date }, state: 'closed') + resolve_group_milestones(args: { timeframe: { start: start_date, end: end_date }, state: 'closed' }) end end @@ -76,7 +65,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(ids: [milestone.id.to_s], group_ids: group.id, state: 'all')) .and_call_original - resolve_group_milestones(ids: [milestone.to_global_id]) + resolve_group_milestones(args: { ids: [milestone.to_global_id] }) end end @@ -86,12 +75,12 @@ RSpec.describe Resolvers::GroupMilestonesResolver do .with(args(group_ids: group.id, state: 'all', sort: :due_date_desc)) .and_call_original - resolve_group_milestones(sort: :due_date_desc) + resolve_group_milestones(args: { sort: :due_date_desc }) end %i[expired_last_due_date_asc expired_last_due_date_desc].each do |sort_by| it "uses offset-pagination when sorting by #{sort_by}" do - resolved = resolve_group_milestones(sort: sort_by) + resolved = resolve_group_milestones(args: { sort: sort_by }) expect(resolved).to be_a(::Gitlab::Graphql::Pagination::OffsetActiveRecordRelationConnection) end @@ -99,31 +88,18 @@ RSpec.describe Resolvers::GroupMilestonesResolver do end context 'by timeframe' do - context 'when start_date and end_date are present' do - context 'when start date is after end_date' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, "startDate is after endDate") do - resolve_group_milestones(start_date: now, end_date: now - 2.days) + context 'when timeframe start and end are present' do + context 'when start is after end' do + it 'raises error' do + expect_graphql_error_to_be_created(::Gitlab::Graphql::Errors::ArgumentError, 'start must be before end') do + resolve_group_milestones( + args: { timeframe: { start: now.to_date, end: now.to_date - 2.days } }, + arg_style: :internal_prepared + ) end end end end - - context 'when only start_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_group_milestones(start_date: now) - end - end - end - - context 'when only end_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_group_milestones(end_date: now) - end - end - end end context 'when including descendant milestones in a public group' do @@ -143,7 +119,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do create(:milestone, group: inaccessible_group) create(:milestone, project: inaccessible_project) - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3]) end end @@ -169,7 +145,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { {} } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1]) end end @@ -177,7 +153,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_descendants: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3]) end end @@ -185,7 +161,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_ancestors: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone6]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone6]) end end @@ -193,7 +169,7 @@ RSpec.describe Resolvers::GroupMilestonesResolver do let(:args) { { include_descendants: true, include_ancestors: true } } it 'finds milestones only in accessible projects and groups' do - expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3, milestone6]) + expect(resolve_group_milestones(args: args)).to match_array([milestone1, milestone2, milestone3, milestone6]) end end end diff --git a/spec/graphql/resolvers/project_milestones_resolver_spec.rb b/spec/graphql/resolvers/project_milestones_resolver_spec.rb index ad1190e3df7..af6b16804b0 100644 --- a/spec/graphql/resolvers/project_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/project_milestones_resolver_spec.rb @@ -94,44 +94,6 @@ RSpec.describe 'Resolvers::ProjectMilestonesResolver' do end context 'by timeframe' do - context 'when start_date and end_date are present' do - it 'calls MilestonesFinder with correct parameters' do - start_date = now - end_date = now + 5.days - - expect(MilestonesFinder).to receive(:new) - .with(args(project_ids: project.id, state: 'all', - start_date: start_date, end_date: end_date, sort: :due_date_asc)) - .and_call_original - - resolve_project_milestones(start_date: start_date, end_date: end_date) - end - - context 'when start date is after end_date' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'startDate is after endDate') do - resolve_project_milestones(start_date: now, end_date: now - 2.days) - end - end - end - end - - context 'when only start_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_project_milestones(start_date: now) - end - end - end - - context 'when only end_date is present' do - it 'generates an error' do - expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, /Both startDate and endDate/) do - resolve_project_milestones(end_date: now) - end - end - end - context 'when passing a timeframe' do it 'calls MilestonesFinder with correct parameters' do start_date = now_date diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb index 7de8ca89d3d..ebe86ccb08d 100644 --- a/spec/helpers/profiles_helper_spec.rb +++ b/spec/helpers/profiles_helper_spec.rb @@ -33,28 +33,28 @@ RSpec.describe ProfilesHelper do end it "returns omniauth provider label for users with external attributes" do - stub_omniauth_setting(sync_profile_from_provider: ['cas3']) + stub_omniauth_setting(sync_profile_from_provider: [example_omniauth_provider]) stub_omniauth_setting(sync_profile_attributes: true) - stub_cas_omniauth_provider - cas_user = create(:omniauth_user, provider: 'cas3') - cas_user.create_user_synced_attributes_metadata(provider: 'cas3', name_synced: true, email_synced: true, location_synced: true) - allow(helper).to receive(:current_user).and_return(cas_user) + stub_auth0_omniauth_provider + auth0_user = create(:omniauth_user, provider: example_omniauth_provider) + auth0_user.create_user_synced_attributes_metadata(provider: example_omniauth_provider, name_synced: true, email_synced: true, location_synced: true) + allow(helper).to receive(:current_user).and_return(auth0_user) - expect(helper.attribute_provider_label(:email)).to eq('CAS') - expect(helper.attribute_provider_label(:name)).to eq('CAS') - expect(helper.attribute_provider_label(:location)).to eq('CAS') + expect(helper.attribute_provider_label(:email)).to eq(example_omniauth_provider_label) + expect(helper.attribute_provider_label(:name)).to eq(example_omniauth_provider_label) + expect(helper.attribute_provider_label(:location)).to eq(example_omniauth_provider_label) end it "returns the correct omniauth provider label for users with some external attributes" do - stub_omniauth_setting(sync_profile_from_provider: ['cas3']) + stub_omniauth_setting(sync_profile_from_provider: [example_omniauth_provider]) stub_omniauth_setting(sync_profile_attributes: true) - stub_cas_omniauth_provider - cas_user = create(:omniauth_user, provider: 'cas3') - cas_user.create_user_synced_attributes_metadata(provider: 'cas3', name_synced: false, email_synced: true, location_synced: false) - allow(helper).to receive(:current_user).and_return(cas_user) + stub_auth0_omniauth_provider + auth0_user = create(:omniauth_user, provider: example_omniauth_provider) + auth0_user.create_user_synced_attributes_metadata(provider: example_omniauth_provider, name_synced: false, email_synced: true, location_synced: false) + allow(helper).to receive(:current_user).and_return(auth0_user) expect(helper.attribute_provider_label(:name)).to be_nil - expect(helper.attribute_provider_label(:email)).to eq('CAS') + expect(helper.attribute_provider_label(:email)).to eq(example_omniauth_provider_label) expect(helper.attribute_provider_label(:location)).to be_nil end @@ -118,12 +118,20 @@ RSpec.describe ProfilesHelper do end end - def stub_cas_omniauth_provider + def stub_auth0_omniauth_provider provider = OpenStruct.new( - 'name' => 'cas3', - 'label' => 'CAS' + 'name' => example_omniauth_provider, + 'label' => example_omniauth_provider_label ) stub_omniauth_setting(providers: [provider]) end + + def example_omniauth_provider + "auth0" + end + + def example_omniauth_provider_label + "Auth0" + end end diff --git a/spec/lib/gitlab/database/migrations/pg_backend_pid_spec.rb b/spec/lib/gitlab/database/migrations/pg_backend_pid_spec.rb index 515f59345ee..33e83ea2575 100644 --- a/spec/lib/gitlab/database/migrations/pg_backend_pid_spec.rb +++ b/spec/lib/gitlab/database/migrations/pg_backend_pid_spec.rb @@ -40,5 +40,13 @@ RSpec.describe Gitlab::Database::Migrations::PgBackendPid, feature_category: :da expect { described_class.say(conn) }.to output(expected_output).to_stdout end + + it 'outputs nothing if ActiveRecord::Migration.verbose is false' do + conn = ActiveRecord::Base.connection + + allow(ActiveRecord::Migration).to receive(:verbose).and_return(false) + + expect { described_class.say(conn) }.not_to output.to_stdout + end end end diff --git a/spec/lib/gitlab/fogbugz_import/project_creator_spec.rb b/spec/lib/gitlab/fogbugz_import/project_creator_spec.rb index 8be9f55dbb6..39dad1360a5 100644 --- a/spec/lib/gitlab/fogbugz_import/project_creator_spec.rb +++ b/spec/lib/gitlab/fogbugz_import/project_creator_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::FogbugzImport::ProjectCreator do +RSpec.describe Gitlab::FogbugzImport::ProjectCreator, feature_category: :importers do let(:user) { create(:user) } let(:repo) do instance_double(Gitlab::FogbugzImport::Repository, @@ -22,6 +22,10 @@ RSpec.describe Gitlab::FogbugzImport::ProjectCreator do project_creator.execute end + before do + stub_application_setting(import_sources: ['fogbugz']) + end + it 'creates project with private visibility level' do expect(subject.persisted?).to eq(true) expect(subject.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb index f1dbe2bf8e2..15624a0558e 100644 --- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb @@ -23,6 +23,8 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do allow_next_instance_of(Project) do |project| allow(project).to receive(:add_import_job) end + + stub_application_setting(import_sources: ['github']) end describe '#execute' do diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb index 0ab5b277552..2d878e5496e 100644 --- a/spec/lib/gitlab/manifest_import/project_creator_spec.rb +++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::ManifestImport::ProjectCreator do +RSpec.describe Gitlab::ManifestImport::ProjectCreator, feature_category: :importers do let(:group) { create(:group) } let(:user) { create(:user) } let(:repository) do @@ -14,6 +14,8 @@ RSpec.describe Gitlab::ManifestImport::ProjectCreator do before do group.add_owner(user) + + stub_application_setting(import_sources: ['manifest']) end subject { described_class.new(repository, group, user) } diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb index daef280dbaa..112fdb183ab 100644 --- a/spec/lib/gitlab/omniauth_initializer_spec.rb +++ b/spec/lib/gitlab/omniauth_initializer_spec.rb @@ -216,14 +216,6 @@ RSpec.describe Gitlab::OmniauthInitializer do expect { subject.execute([hash_config]) }.to raise_error(NameError) end - it 'configures on_single_sign_out proc for cas3' do - cas3_config = { 'name' => 'cas3', 'args' => {} } - - expect(devise_config).to receive(:omniauth).with(:cas3, { on_single_sign_out: an_instance_of(Proc) }) - - subject.execute([cas3_config]) - end - it 'configures defaults for google_oauth2' do google_config = { 'name' => 'google_oauth2', diff --git a/spec/models/users/credit_card_validation_spec.rb b/spec/models/users/credit_card_validation_spec.rb index 58b529ff18a..4db3683c057 100644 --- a/spec/models/users/credit_card_validation_spec.rb +++ b/spec/models/users/credit_card_validation_spec.rb @@ -51,4 +51,107 @@ RSpec.describe Users::CreditCardValidation do end end end + + describe 'scopes' do + describe '.by_banned_user' do + let(:banned_user) { create(:banned_user) } + let!(:credit_card) { create(:credit_card_validation) } + let!(:banned_user_credit_card) { create(:credit_card_validation, user: banned_user.user) } + + it 'returns only records associated to banned users' do + expect(described_class.by_banned_user).to match_array([banned_user_credit_card]) + end + end + + describe '.similar_by_holder_name' do + let!(:credit_card) { create(:credit_card_validation, holder_name: 'CARD MCHODLER') } + let!(:credit_card2) { create(:credit_card_validation, holder_name: 'RICHIE RICH') } + + it 'returns only records that case-insensitive match the given holder name' do + expect(described_class.similar_by_holder_name('card mchodler')).to match_array([credit_card]) + end + + context 'when given holder name is falsey' do + it 'returns [] when given holder name is ""' do + expect(described_class.similar_by_holder_name('')).to match_array([]) + end + + it 'returns [] when given holder name is nil' do + expect(described_class.similar_by_holder_name(nil)).to match_array([]) + end + end + end + + describe '.similar_to' do + let(:credit_card) { create(:credit_card_validation) } + + let!(:credit_card2) do + create(:credit_card_validation, + expiration_date: credit_card.expiration_date, + last_digits: credit_card.last_digits, + network: credit_card.network + ) + end + + let!(:credit_card3) do + create(:credit_card_validation, + expiration_date: credit_card.expiration_date, + last_digits: credit_card.last_digits, + network: 'UnknownCCNetwork' + ) + end + + it 'returns only records with similar expiration_date, last_digits, and network attribute values' do + expect(described_class.similar_to(credit_card)).to match_array([credit_card, credit_card2]) + end + end + end + + describe '#used_by_banned_user?' do + let(:credit_card_details) do + { + holder_name: 'Christ McLovin', + expiration_date: 2.years.from_now.end_of_month, + last_digits: 4242, + network: 'Visa' + } + end + + let!(:credit_card) { create(:credit_card_validation, credit_card_details) } + + subject { credit_card } + + context 'when there is a similar credit card associated to a banned user' do + let_it_be(:banned_user) { create(:banned_user) } + + let(:attrs) { credit_card_details.merge({ user: banned_user.user }) } + let!(:similar_credit_card) { create(:credit_card_validation, attrs) } + + it { is_expected.to be_used_by_banned_user } + + context 'when holder names do not match' do + let!(:similar_credit_card) do + create(:credit_card_validation, attrs.merge({ holder_name: 'Mary Goody' })) + end + + it { is_expected.not_to be_used_by_banned_user } + end + + context 'when .similar_to returns nothing' do + let!(:similar_credit_card) do + create(:credit_card_validation, attrs.merge({ network: 'DifferentNetwork' })) + end + + it { is_expected.not_to be_used_by_banned_user } + end + end + + context 'when there is a similar credit card not associated to a banned user' do + let!(:similar_credit_card) do + create(:credit_card_validation, credit_card_details) + end + + it { is_expected.not_to be_used_by_banned_user } + end + end end diff --git a/spec/policies/identity_provider_policy_spec.rb b/spec/policies/identity_provider_policy_spec.rb index f6b4e15cff9..cd617a28364 100644 --- a/spec/policies/identity_provider_policy_spec.rb +++ b/spec/policies/identity_provider_policy_spec.rb @@ -19,13 +19,11 @@ RSpec.describe IdentityProviderPolicy do it { is_expected.not_to be_allowed(:unlink) } end - %w[saml cas3].each do |provider_name| - context "when provider is #{provider_name}" do - let(:provider) { provider_name } + context "when provider is saml" do + let(:provider) { 'saml' } - it { is_expected.to be_allowed(:link) } - it { is_expected.not_to be_allowed(:unlink) } - end + it { is_expected.to be_allowed(:link) } + it { is_expected.not_to be_allowed(:unlink) } end end end diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb index 28cd68493c0..209588835f2 100644 --- a/spec/requests/api/graphql/group/milestones_spec.rb +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -35,12 +35,6 @@ RSpec.describe 'Milestones through GroupQuery', feature_category: :team_planning end context 'when filtering by timeframe' do - it 'fetches milestones between start_date and due_date' do - fetch_milestones(user, { start_date: now.to_s, end_date: (now + 2.days).to_s }) - - expect_array_response(milestone_2.to_global_id.to_s, milestone_3.to_global_id.to_s) - end - it 'fetches milestones between timeframe start and end arguments' do today = Date.today fetch_milestones(user, { timeframe: { start: today.to_s, end: (today + 2.days).to_s } }) diff --git a/spec/requests/api/graphql/project/milestones_spec.rb b/spec/requests/api/graphql/project/milestones_spec.rb index 3b31da77a75..7a79bf2184a 100644 --- a/spec/requests/api/graphql/project/milestones_spec.rb +++ b/spec/requests/api/graphql/project/milestones_spec.rb @@ -137,18 +137,6 @@ RSpec.describe 'getting milestone listings nested in a project', feature_categor it_behaves_like 'searching with parameters' end - context 'searching by custom range' do - let(:expected) { [no_end, fully_future] } - let(:search_params) do - { - start_date: (today + 6.days).iso8601, - end_date: (today + 7.days).iso8601 - } - end - - it_behaves_like 'searching with parameters' - end - context 'using timeframe argument' do let(:expected) { [no_end, fully_future] } let(:search_params) do @@ -188,23 +176,6 @@ RSpec.describe 'getting milestone listings nested in a project', feature_categor end end - it 'is invalid to provide timeframe and start_date/end_date' do - query = <<~GQL - query($path: ID!, $tstart: Date!, $tend: Date!, $start: Time!, $end: Time!) { - project(fullPath: $path) { - milestones(timeframe: { start: $tstart, end: $tend }, startDate: $start, endDate: $end) { - nodes { id } - } - } - } - GQL - - post_graphql(query, current_user: current_user, - variables: vars.merge(vars.transform_keys { |k| :"t#{k}" })) - - expect(graphql_errors).to contain_exactly(a_hash_including('message' => include('deprecated in favor of timeframe'))) - end - it 'is invalid to invert the timeframe arguments' do query = <<~GQL query($path: ID!, $start: Date!, $end: Date!) { diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb index 8ad5aaa8bc3..4496e3aa7c3 100644 --- a/spec/requests/api/project_import_spec.rb +++ b/spec/requests/api/project_import_spec.rb @@ -14,6 +14,8 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor before do namespace.add_owner(user) if user + + stub_application_setting(import_sources: ['gitlab_project']) end shared_examples 'requires authentication' do @@ -26,6 +28,20 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor end end + shared_examples 'requires import source to be enabled' do + context 'when gitlab_project import_sources is disabled' do + before do + stub_application_setting(import_sources: []) + end + + it 'returns 403' do + subject + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + describe 'POST /projects/import' do subject { upload_archive(file_upload, workhorse_headers, params) } @@ -43,6 +59,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor end it_behaves_like 'requires authentication' + it_behaves_like 'requires import source to be enabled' it 'executes a limited number of queries', :use_clean_rails_redis_caching do control_count = ActiveRecord::QueryRecorder.new { subject }.count @@ -337,6 +354,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor end it_behaves_like 'requires authentication' + it_behaves_like 'requires import source to be enabled' context 'when the response is successful' do it 'schedules the import successfully' do @@ -402,6 +420,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor end it_behaves_like 'requires authentication' + it_behaves_like 'requires import source to be enabled' context 'when the response is successful' do it 'schedules the import successfully' do @@ -496,6 +515,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures, feature_category: :impor subject { post api('/projects/import/authorize', user), headers: workhorse_headers } it_behaves_like 'requires authentication' + it_behaves_like 'requires import source to be enabled' it 'authorizes importing project with workhorse header' do subject diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index b4524ea0881..725268d9b6a 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1431,6 +1431,7 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :projects d body: '001e# service=git-upload-pack', headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } } + stub_application_setting(import_sources: ['git']) stub_full_request(endpoint_url, method: :get).to_return(git_response) project_params = { import_url: url, path: 'path-project-Foo', name: 'Foo Project' } diff --git a/spec/requests/import/github_controller_spec.rb b/spec/requests/import/github_controller_spec.rb index 5ac97e3d330..8d57c2895de 100644 --- a/spec/requests/import/github_controller_spec.rb +++ b/spec/requests/import/github_controller_spec.rb @@ -9,6 +9,8 @@ RSpec.describe Import::GithubController, feature_category: :importers do let_it_be(:user) { create(:user) } before do + stub_application_setting(import_sources: ['github']) + login_as(user) end diff --git a/spec/requests/import/github_groups_controller_spec.rb b/spec/requests/import/github_groups_controller_spec.rb index 6393dd35a98..dada84758f3 100644 --- a/spec/requests/import/github_groups_controller_spec.rb +++ b/spec/requests/import/github_groups_controller_spec.rb @@ -11,6 +11,8 @@ RSpec.describe Import::GithubGroupsController, feature_category: :importers do let(:params) { {} } before do + stub_application_setting(import_sources: ['github']) + login_as(user) end diff --git a/spec/requests/import/gitlab_projects_controller_spec.rb b/spec/requests/import/gitlab_projects_controller_spec.rb index fe3ea9e9c9e..732851c7828 100644 --- a/spec/requests/import/gitlab_projects_controller_spec.rb +++ b/spec/requests/import/gitlab_projects_controller_spec.rb @@ -12,6 +12,8 @@ RSpec.describe Import::GitlabProjectsController, feature_category: :importers do before do login_as(user) + + stub_application_setting(import_sources: ['gitlab_project']) end describe 'POST create' do diff --git a/spec/services/import/fogbugz_service_spec.rb b/spec/services/import/fogbugz_service_spec.rb index ad02dc31da1..e9c676dcd23 100644 --- a/spec/services/import/fogbugz_service_spec.rb +++ b/spec/services/import/fogbugz_service_spec.rb @@ -18,6 +18,7 @@ RSpec.describe Import::FogbugzService, feature_category: :importers do before do allow(subject).to receive(:authorized?).and_return(true) + stub_application_setting(import_sources: ['fogbugz']) end context 'when no repo is found' do diff --git a/spec/services/import/gitlab_projects/create_project_service_spec.rb b/spec/services/import/gitlab_projects/create_project_service_spec.rb index 35378bcee92..a77e9bdfce1 100644 --- a/spec/services/import/gitlab_projects/create_project_service_spec.rb +++ b/spec/services/import/gitlab_projects/create_project_service_spec.rb @@ -35,6 +35,7 @@ RSpec.describe ::Import::GitlabProjects::CreateProjectService, :aggregate_failur before do stub_const('FakeStrategy', fake_file_acquisition_strategy) + stub_application_setting(import_sources: ['gitlab_project']) end describe 'validation' do diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 1c903954658..303a98cb35b 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -256,6 +256,10 @@ RSpec.describe Projects::CreateService, '#execute', feature_category: :projects it_behaves_like 'has sync-ed traversal_ids' context 'when project is an import' do + before do + stub_application_setting(import_sources: ['gitlab_project']) + end + context 'when user is not allowed to import projects' do let(:group) do create(:group).tap do |group| diff --git a/vendor/gems/omniauth-cas3/.gitlab-ci.yml b/vendor/gems/omniauth-cas3/.gitlab-ci.yml deleted file mode 100644 index e728d704d21..00000000000 --- a/vendor/gems/omniauth-cas3/.gitlab-ci.yml +++ /dev/null @@ -1,28 +0,0 @@ -workflow: - rules: - - if: $CI_MERGE_REQUEST_ID - -.rspec: - cache: - key: omniauth-cas3-ruby - paths: - - vendor/gems/omniauth-cas3/vendor/ruby - before_script: - - cd vendor/gems/omniauth-cas3 - - 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 config set --local frozen 'true' # Disallow Gemfile.lock changes on CI - - bundle config # Show bundler configuration - - bundle install -j $(nproc) - script: - - bundle exec rspec - -rspec-2.7: - image: "ruby:2.7" - extends: .rspec - -rspec-3.0: - image: "ruby:3.0" - extends: .rspec diff --git a/vendor/gems/omniauth-cas3/Gemfile b/vendor/gems/omniauth-cas3/Gemfile deleted file mode 100644 index adc6d8b37a3..00000000000 --- a/vendor/gems/omniauth-cas3/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -source 'https://rubygems.org' - -# Specify your gem's dependencies in omniauth-cas3.gemspec -gemspec diff --git a/vendor/gems/omniauth-cas3/Gemfile.lock b/vendor/gems/omniauth-cas3/Gemfile.lock deleted file mode 100644 index a856e78f00f..00000000000 --- a/vendor/gems/omniauth-cas3/Gemfile.lock +++ /dev/null @@ -1,65 +0,0 @@ -PATH - remote: . - specs: - omniauth-cas3 (1.1.4) - addressable (~> 2.3) - nokogiri (~> 1.7, >= 1.7.1) - omniauth (~> 2.0) - -GEM - remote: https://rubygems.org/ - specs: - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - awesome_print (1.9.2) - crack (0.4.5) - rexml - diff-lcs (1.5.0) - hashdiff (1.0.1) - hashie (5.0.0) - nokogiri (1.13.7) - racc (~> 1.4) - omniauth (2.1.0) - hashie (>= 3.4.6) - rack (>= 2.2.3) - rack-protection - public_suffix (5.0.0) - racc (1.6.0) - rack (2.2.4) - rack-protection (2.2.2) - rack - rack-test (0.8.3) - rack (>= 1.0, < 3) - rake (10.5.0) - rexml (3.2.5) - 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-mocks (3.11.1) - diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) - webmock (3.18.1) - addressable (>= 2.8.0) - crack (>= 0.3.2) - hashdiff (>= 0.4.0, < 2.0.0) - -PLATFORMS - ruby - -DEPENDENCIES - awesome_print - omniauth-cas3! - rack-test (~> 0.6) - rake (~> 10.0) - rspec (>= 3.4) - webmock - -BUNDLED WITH - 2.3.21 diff --git a/vendor/gems/omniauth-cas3/LICENSE b/vendor/gems/omniauth-cas3/LICENSE deleted file mode 100644 index 402cb6e4380..00000000000 --- a/vendor/gems/omniauth-cas3/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2011 Derek Lindahl and CustomInk, LLC -Copyright (c) 2015 tduehr - -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-cas3/README.md b/vendor/gems/omniauth-cas3/README.md deleted file mode 100644 index 3c66341defb..00000000000 --- a/vendor/gems/omniauth-cas3/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# OmniAuth CAS Strategy [![Gem Version][version_badge]][version] [![Build Status][travis_status]][travis] - -This is a fork of [omniauth-cas3](https://github.com/tduehr/omniauth-cas3) 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. We may deprecate this library entirely in the future: - https://gitlab.com/gitlab-org/gitlab/-/issues/366212 - -[version_badge]: https://badge.fury.io/rb/omniauth-cas3.png -[version]: http://badge.fury.io/rb/omniauth-cas3 -[travis]: http://travis-ci.org/tduehr/omniauth-cas3 -[travis_status]: https://secure.travis-ci.org/dlindahl/omniauth-cas3.png -[releases]: https://github.com/tduehr/omniauth-cas3/releases - -This is a OmniAuth 1.0 compatible port of the previously available -[OmniAuth CAS strategy][old_omniauth_cas] that was bundled with OmniAuth 0.3. This strategy has also been updated for CAS protocol version 3.0 and patched to deal with namespace issues. - -* [View the documentation][document_up] -* [Changelog][releases] - -## Installation - -Add this line to your application's Gemfile: - - gem 'omniauth-cas3' - -And then execute: - - $ bundle - -Or install it yourself as: - - $ gem install omniauth-cas3 - -## Usage - -Use like any other OmniAuth strategy: - -```ruby -Rails.application.config.middleware.use OmniAuth::Builder do - provider :cas3, host: 'cas.yourdomain.com' -end -``` - -### Configuration Options - -#### Required - -OmniAuth CAS requires at least one of the following two configuration options: - - * `url` - Defines the URL of your CAS server (i.e. `http://example.org:8080`) - * `host` - Defines the host of your CAS server (i.e. `example.org`). - -#### Optional - -Other configuration options: - - * `port` - The port to use for your configured CAS `host`. Optional if using `url`. - * `ssl` - TRUE to connect to your CAS server over SSL. Optional if using `url`. - * `service_validate_url` - The URL to use to validate a user. Defaults to `'/serviceValidate'`. - * `callback_url` - The URL custom URL path which CAS uses to call back to the service. Defaults to `/users/auth/cas3/callback`. - * `logout_url` - The URL to use to logout a user. Defaults to `'/logout'`. - * `login_url` - Defines the URL used to prompt users for their login information. Defaults to `/login` If no `host` is configured, the host application's domain will be used. - * `uid_field` - The user data attribute to use as your user's unique identifier. Defaults to `'user'` (which usually contains the user's login name). - * `ca_path` - Optional when `ssl` is `true`. Sets path of a CA certification directory. See [Net::HTTP][net_http] for more details. - * `disable_ssl_verification` - Optional when `ssl` is true. Disables verification. - * `on_single_sign_out` - Optional. Callback used when a [CAS 3.1 Single Sign Out][sso] - request is received. - * `fetch_raw_info` - Optional. Callback used to return additional "raw" user - info from other sources. - - ```ruby - provider :cas3, - fetch_raw_info: lambda { |strategy, options, ticket, user_info| - ExternalService.get(user_info[:user]).attributes - } - ``` - -Configurable options for values returned by CAS: - - * `uid_key` - The user ID data attribute to use as your user's unique identifier. Defaults to `'user'` (which usually contains the user's login name). - * `name_key` - The data attribute containing user first and last name. Defaults to `'name'`. - * `email_key` - The data attribute containing user email address. Defaults to `'email'`. - * `nickname_key` - The data attribute containing user's nickname. Defaults to `'user'`. - * `first_name_key` - The data attribute containing user first name. Defaults to `'first_name'`. - * `last_name_key` - The data attribute containing user last name. Defaults to `'last_name'`. - * `location_key` - The data attribute containing user location/address. Defaults to `'location'`. - * `image_key` - The data attribute containing user image/picture. Defaults to `'image'`. - * `phone_key` - The data attribute containing user contact phone number. Defaults to `'phone'`. - -## Migrating from OmniAuth 0.3 - -Given the following OmniAuth 0.3 configuration: - -```ruby -provider :CAS, cas_server: 'https://cas.example.com/cas/' -``` - -Your new settings should look similar to this: - -```ruby -provider :cas3, - host: 'cas.example.com', - login_url: '/cas/login', - service_validate_url: '/cas/p3/serviceValidate' -``` - -If you encounter problems wih SSL certificates you may want to set the `ca_path` parameter or activate `disable_ssl_verification` (not recommended). - -## Contributing - -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Added some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request - -## Thanks - -Special thanks go out to the following people - - * @dlindahl For the original work in porting this from OmniAuth 0.3 - * Phillip Aldridge (@iterateNZ) and JB Barth (@jbbarth) for helping out with Issue #3 - * Elber Ribeiro (@dynaum) for Ubuntu SSL configuration support - * @rbq for README updates and OmniAuth 0.3 migration guide - -[old_omniauth_cas]: https://github.com/intridea/omniauth/blob/0-3-stable/oa-enterprise/lib/omniauth/strategies/cas.rb -[document_up]: http://tduehr.github.com/omniauth-cas3/ -[net_http]: http://ruby-doc.org/stdlib-1.9.3/libdoc/net/http/rdoc/Net/HTTP.html -[sso]: https://wiki.jasig.org/display/CASUM/Single+Sign+Out diff --git a/vendor/gems/omniauth-cas3/Rakefile b/vendor/gems/omniauth-cas3/Rakefile deleted file mode 100644 index af92638ba13..00000000000 --- a/vendor/gems/omniauth-cas3/Rakefile +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env rake -require 'bundler/gem_tasks' - -require 'rspec/core/rake_task' -desc 'Default: run specs.' -task default: :spec - -desc 'Run specs' -RSpec::Core::RakeTask.new(:spec) do |t| - t.rspec_opts = '--require spec_helper --color --order rand' -end - -task :test do - fail %q{This application uses RSpec. Try running "rake spec"} -end diff --git a/vendor/gems/omniauth-cas3/lib/omniauth-cas3.rb b/vendor/gems/omniauth-cas3/lib/omniauth-cas3.rb deleted file mode 100644 index 58509b933c8..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth-cas3.rb +++ /dev/null @@ -1 +0,0 @@ -require 'omniauth/cas3' diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/cas3.rb b/vendor/gems/omniauth-cas3/lib/omniauth/cas3.rb deleted file mode 100644 index 80460aa1f31..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth/cas3.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'omniauth/cas3/version' -require 'omniauth/strategies/cas3' \ No newline at end of file diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/cas3/version.rb b/vendor/gems/omniauth-cas3/lib/omniauth/cas3/version.rb deleted file mode 100644 index 9508dd69125..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth/cas3/version.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Omniauth - module Cas3 - VERSION = '1.1.4' - end -end diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb deleted file mode 100644 index 441529b67d8..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb +++ /dev/null @@ -1,227 +0,0 @@ -require 'omniauth' -require 'addressable/uri' - -module OmniAuth - module Strategies - class CAS3 - include OmniAuth::Strategy - - # Custom Exceptions - class MissingCASTicket < StandardError; end - class InvalidCASTicket < StandardError; end - - autoload :ServiceTicketValidator, 'omniauth/strategies/cas3/service_ticket_validator' - autoload :LogoutRequest, 'omniauth/strategies/cas3/logout_request' - - attr_accessor :raw_info - alias_method :user_info, :raw_info - - option :name, :cas3 # Required property by OmniAuth::Strategy - - option :host, nil - option :port, nil - option :path, nil - option :ssl, true - option :service_validate_url, '/p3/serviceValidate' - option :login_url, '/login' - option :logout_url, '/logout' - option :on_single_sign_out, Proc.new {} - # A Proc or lambda that returns a Hash of additional user info to be - # merged with the info returned by the CAS server. - # - # @param [Object] An instance of OmniAuth::Strategies::CAS for the current request - # @param [String] The user's Service Ticket value - # @param [Hash] The user info for the Service Ticket returned by the CAS server - # - # @return [Hash] Extra user info - option :fetch_raw_info, Proc.new { Hash.new } - # Make all the keys configurable with some defaults set here - option :uid_field, 'user' - option :name_key, 'name' - option :email_key, 'email' - option :nickname_key, 'user' - option :first_name_key, 'first_name' - option :last_name_key, 'last_name' - option :location_key, 'location' - option :image_key, 'image' - option :phone_key, 'phone' - - # As required by https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema - AuthHashSchemaKeys = %w{name email nickname first_name last_name location image phone} - info do - prune!({ - name: raw_info[options[:name_key].to_s], - email: raw_info[options[:email_key].to_s], - nickname: raw_info[options[:nickname_key].to_s], - first_name: raw_info[options[:first_name_key].to_s], - last_name: raw_info[options[:last_name_key].to_s], - location: raw_info[options[:location_key].to_s], - image: raw_info[options[:image_key].to_s], - phone: raw_info[options[:phone_key].to_s] - }) - end - - extra do - hash = {} - - unless skip_info? - hash = raw_info.dup - hash.delete_if { |k, _v| AuthHashSchemaKeys.include?(k) } - end - - prune! hash - end - - uid do - raw_info[options[:uid_field].to_s] - end - - credentials do - prune!({ ticket: @ticket }) - end - - def callback_phase - if on_sso_path? - single_sign_out_phase - else - @ticket = request.params['ticket'] - return fail!(:no_ticket, MissingCASTicket.new('No CAS Ticket')) unless @ticket - fetch_raw_info(@ticket) - return fail!(:invalid_ticket, InvalidCASTicket.new('Invalid CAS Ticket')) if raw_info.empty? - super - end - end - - def request_phase - service_url = append_params(callback_url, return_url) - - [ - 302, - { - 'Location' => login_url(service_url), - 'Content-Type' => 'text/plain' - }, - ["You are being redirected to CAS for sign-in."] - ] - end - - def on_sso_path? - request.post? && request.params.has_key?('logoutRequest') - end - - def single_sign_out_phase - logout_request_service.new(self, request).call(options) - end - - # Build a CAS host with protocol and port - # - # - def cas_url - extract_url if options['url'] - validate_cas_setup - @cas_url ||= begin - uri = Addressable::URI.new - uri.host = options.host - uri.scheme = options.ssl ? 'https' : 'http' - uri.port = options.port - uri.path = options.path - uri.to_s - end - end - - def extract_url - url = Addressable::URI.parse(options.delete('url')) - options.merge!( - 'host' => url.host, - 'port' => url.port, - 'path' => url.path, - 'ssl' => url.scheme == 'https' - ) - end - - def validate_cas_setup - if options.host.nil? || options.login_url.nil? - raise ArgumentError.new(":host and :login_url MUST be provided") - end - end - - # Build a service-validation URL from +service+ and +ticket+. - # If +service+ has a ticket param, first remove it. URL-encode - # +service+ and add it and the +ticket+ as paraemters to the - # CAS serviceValidate URL. - # - # @param [String] service the service (a.k.a. return-to) URL - # @param [String] ticket the ticket to validate - # - # @return [String] a URL like `http://cas.mycompany.com/serviceValidate?service=...&ticket=...` - def service_validate_url(service_url, ticket) - service_url = Addressable::URI.parse(service_url) - service_url.query_values = service_url.query_values.tap { |qs| qs.delete('ticket') } - cas_url + append_params(options.service_validate_url, { - service: service_url.to_s, - ticket: ticket - }) - end - - # Build a CAS login URL from +service+. - # - # @param [String] service the service (a.k.a. return-to) URL - # - # @return [String] a URL like `http://cas.mycompany.com/login?service=...` - def login_url(service) - cas_url + append_params(options.login_url, { service: service }) - end - - # Adds URL-escaped +parameters+ to +base+. - # - # @param [String] base the base URL - # @param [String] params the parameters to append to the URL - # - # @return [String] the new joined URL. - def append_params(base, params) - params = params.each { |k,v| v = Rack::Utils.escape(v) } - Addressable::URI.parse(base).tap do |base_uri| - base_uri.query_values = (base_uri.query_values || {}).merge(params) - end.to_s - end - - # Validate the Service Ticket - # @return [Object] the validated Service Ticket - def validate_service_ticket(ticket) - ServiceTicketValidator.new(self, options, callback_url, ticket).call - end - - private - - def fetch_raw_info(ticket) - ticket_user_info = validate_service_ticket(ticket).user_info - custom_user_info = options.fetch_raw_info.call(self, options, ticket, ticket_user_info) - self.raw_info = ticket_user_info.merge(custom_user_info) - end - - # Deletes Hash pairs with `nil` values. - # From https://github.com/mkdynamic/omniauth-facebook/blob/972ed5e3456bcaed7df1f55efd7c05c216c8f48e/lib/omniauth/strategies/facebook.rb#L122-127 - def prune!(hash) - hash.delete_if do |_, value| - prune!(value) if value.is_a?(Hash) - value.nil? || (value.respond_to?(:empty?) && value.empty?) - end - end - - def return_url - # If the request already has a `url` parameter, then it will already be appended to the callback URL. - if request.params && request.params['url'] - {} - else - { url: request.referer } - end - end - - def logout_request_service - LogoutRequest - end - end - end -end - -OmniAuth.config.add_camelization 'cas3', 'CAS3' diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/logout_request.rb b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/logout_request.rb deleted file mode 100644 index 72978227edb..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/logout_request.rb +++ /dev/null @@ -1,73 +0,0 @@ -module OmniAuth - module Strategies - class CAS3 - class LogoutRequest - def initialize(strategy, request) - @strategy, @request = strategy, request - end - - def call(options = {}) - @options = options - - begin - result = single_sign_out_callback.call(*logout_request) - rescue StandardError => err - return @strategy.fail! :logout_request, err - else - result = [200,{},'OK'] if result == true || result.nil? - ensure - return unless result - - # TODO: Why does ActionPack::Response return [status,headers,body] - # when Rack::Response#new wants [body,status,headers]? Additionally, - # why does Rack::Response differ in argument order from the usual - # Rack-like [status,headers,body] array? - return Rack::Response.new(result[2],result[0],result[1]).finish - end - end - - private - - def logout_request - @logout_request ||= begin - saml = parse_and_ensure_namespaces(@request.params['logoutRequest']) - ns = saml.collect_namespaces - name_id = saml.xpath('//saml:NameID', ns).text - sess_idx = saml.xpath('//samlp:SessionIndex', ns).text - inject_params(name_id:name_id, session_index:sess_idx) - @request - end - end - - def parse_and_ensure_namespaces(logout_request_xml) - doc = Nokogiri.parse(logout_request_xml) - ns = doc.collect_namespaces - if ns.include?('xmlns:samlp') && ns.include?('xmlns:saml') - doc - else - add_namespaces(doc) - end - end - - def add_namespaces(logout_request_doc) - root = logout_request_doc.root - root.add_namespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol') - root.add_namespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion\\') - - # In order to add namespaces properly we need to re-parse the document - Nokogiri.parse(logout_request_doc.to_s) - end - - def inject_params(new_params) - new_params.each do |key, val| - @request.update_param(key, val) - end - end - - def single_sign_out_callback - @options[:on_single_sign_out] - end - end - end - end -end diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/service_ticket_validator.rb b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/service_ticket_validator.rb deleted file mode 100644 index 4f9a61c5216..00000000000 --- a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3/service_ticket_validator.rb +++ /dev/null @@ -1,103 +0,0 @@ -require 'net/http' -require 'net/https' -require 'nokogiri' - -module OmniAuth - module Strategies - class CAS3 - class ServiceTicketValidator - VALIDATION_REQUEST_HEADERS = { 'Accept' => '*/*' } - - # Build a validator from a +configuration+, a - # +return_to+ URL, and a +ticket+. - # - # @param [Hash] options the OmniAuth Strategy options - # @param [String] return_to_url the URL of this CAS client service - # @param [String] ticket the service ticket to validate - def initialize(strategy, options, return_to_url, ticket) - @options = options - @uri = URI.parse(strategy.service_validate_url(return_to_url, ticket)) - end - - # Executes a network request to process the CAS Service Response - def call - @response_body = get_service_response_body - @success_body = find_authentication_success(@response_body) - self - end - - # Request validation of the ticket from the CAS server's - # serviceValidate (CAS 2.0) function. - # - # Swallows all XML parsing errors (and returns +nil+ in those cases). - # - # @return [Hash, nil] a user information hash if the response is valid; +nil+ otherwise. - # - # @raise any connection errors encountered. - def user_info - parse_user_info(@success_body) - end - - private - - # turns an `` node into a Hash; - # returns nil if given nil - def parse_user_info(node) - return nil if node.nil? - {}.tap do |hash| - node.children.each do |e| - node_name = e.name.sub(/^cas:/, '') - unless e.kind_of?(Nokogiri::XML::Text) || node_name == 'proxies' - # There are no child elements - if e.element_children.count == 0 - hash[node_name] = e.content - elsif e.element_children.count - # JASIG style extra attributes - if node_name == 'attributes' - hash.merge!(parse_user_info(e)) - else - hash[node_name] = [] if hash[node_name].nil? - hash[node_name].push(parse_user_info(e)) - end - end - end - end - end - end - - # finds an `` node in - # a `` body if present; returns nil - # if the passed body is nil or if there is no such node. - def find_authentication_success(body) - return nil if body.nil? || body == '' - begin - doc = Nokogiri::XML(body) - begin - doc.xpath('/cas:serviceResponse/cas:authenticationSuccess') - rescue Nokogiri::XML::XPath::SyntaxError - doc.xpath('/serviceResponse/authenticationSuccess') - end - rescue Nokogiri::XML::XPath::SyntaxError - nil - end - end - - # retrieves the `` XML from the CAS server - def get_service_response_body - result = '' - http = Net::HTTP.new(@uri.host, @uri.port) - http.use_ssl = @uri.port == 443 || @uri.instance_of?(URI::HTTPS) - if http.use_ssl? - http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @options.disable_ssl_verification? - http.ca_path = @options.ca_path - end - http.start do |c| - response = c.get "#{@uri.path}?#{@uri.query}", VALIDATION_REQUEST_HEADERS.dup - result = response.body - end - result - end - end - end - end -end diff --git a/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec b/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec deleted file mode 100644 index c976d85df99..00000000000 --- a/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec +++ /dev/null @@ -1,27 +0,0 @@ -# -*- encoding: utf-8 -*- -require File.expand_path('../lib/omniauth/cas3/version', __FILE__) - -Gem::Specification.new do |gem| - gem.authors = ["Derek Lindahl, tduehr"] - gem.email = ["td@matasano.com"] - gem.summary = %q{CAS 3.0 Strategy for OmniAuth} - gem.description = gem.summary - gem.homepage = "https://github.com/tduehr/omniauth-cas3" - - gem.files = Dir.glob("lib/**/*.*") - gem.test_files = Dir.glob("spec/**/**/*.*") - gem.name = "omniauth-cas3" - gem.require_paths = ["lib"] - gem.version = Omniauth::Cas3::VERSION - - gem.add_dependency 'omniauth', '~> 2.0' - gem.add_dependency 'nokogiri', '~> 1.7', '>= 1.7.1' - gem.add_dependency 'addressable', '~> 2.3' - - gem.add_development_dependency 'rake', '~> 10.0' - gem.add_development_dependency 'webmock' - gem.add_development_dependency 'rspec', '>= 3.4' - gem.add_development_dependency 'rack-test', '~> 0.6' - - gem.add_development_dependency 'awesome_print' -end diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml deleted file mode 100644 index f8238a18014..00000000000 --- a/vendor/gems/omniauth-cas3/spec/fixtures/cas_failure.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml deleted file mode 100644 index 18904f64b35..00000000000 --- a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - psegel - 54 - P. Segel - Peter - Segel - psegel@intridea.com - Washington, D.C. - /images/user.jpg - 555-555-5555 - 2004-07-13 - - diff --git a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml b/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml deleted file mode 100644 index 72f58edfb46..00000000000 --- a/vendor/gems/omniauth-cas3/spec/fixtures/cas_success_jasig.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - psegel - - 54 - P. Segel - Peter - Segel - psegel@intridea.com - Washington, D.C. - /images/user.jpg - 555-555-5555 - 2004-07-13 - - - diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb deleted file mode 100644 index 4834347fa03..00000000000 --- a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/logout_request_spec.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'spec_helper' - -describe OmniAuth::Strategies::CAS3::LogoutRequest do - let(:strategy) { double('strategy') } - let(:env) do - { 'rack.input' => StringIO.new('','r') } - end - let(:request) { double('request', params:params, env:env) } - let(:params) { { 'url' => url, 'logoutRequest' => logoutRequest } } - let(:url) { 'http://notes.dev/signed_in' } - let(:logoutRequest) do - %Q[ - - @NOT_USED@ - ST-123456-123abc456def - - ] - end - - subject { described_class.new(strategy, request).call(options) } - - describe 'SAML attributes' do - let(:callback) { Proc.new{} } - let(:options) do - { on_single_sign_out: callback } - end - - before do - @rack_input = nil - allow(callback).to receive(:call) do |req| - @rack_input = req.env['rack.input'].read - true - end - end - - it 'are parsed and injected into the Rack Request parameters', :skip => true do - subject - expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-123456-123abc456def' - end - - it 'are parsed and injected even if saml defined inside NameID', :skip => true do - request.params['logoutRequest'] = - %Q[ - - @NOT_USED@ - ST-foo-bar - - ] - subject - expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-foo-bar' - end - - it 'are parsed and injected even if saml and samlp namespaces not defined', :skip => true do - request.params['logoutRequest'] = - %Q[ - - @NOT_USED@ - ST-789000-456def789ghi - - ] - subject - expect(@rack_input).to eq 'name_id=%40NOT_USED%40&session_index=ST-789000-456def789ghi' - end - - context 'that raise when parsed' do - let(:env) { { 'rack.input' => nil } } - - before do - allow(strategy).to receive(:fail!) - subject - expect(strategy).to have_received(:fail!) - end - - it 'responds with an error', skip: true do - expect(strategy).to have_received(:fail!) - end - end - end - - describe 'with a configured callback' do - let(:options) do - { on_single_sign_out: callback } - end - - context 'that returns TRUE' do - let(:callback) { Proc.new{true} } - - it 'responds with OK', skip: true do - expect(subject[0]).to eq 200 - expect(subject[2].body).to eq ['OK'] - end - end - - context 'that returns Nil' do - let(:callback) { Proc.new{} } - - it 'responds with OK', skip: true do - expect(subject[0]).to eq 200 - expect(subject[2].body).to eq ['OK'] - end - end - - context 'that returns a tuple' do - let(:callback) { Proc.new{ [400,{},'Bad Request'] } } - - it 'responds with OK', skip: true do - expect(subject[0]).to eq 400 - expect(subject[2].body).to eq ['Bad Request'] - end - end - - context 'that raises an error' do - let(:exception) { RuntimeError.new('error' )} - let(:callback) { Proc.new{raise exception} } - - before do - allow(strategy).to receive(:fail!) - subject - end - - it 'responds with an error', skip: true do - expect(strategy).to have_received(:fail!) - .with(:logout_request, exception) - end - end - end -end diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb deleted file mode 100644 index b031d1d68fc..00000000000 --- a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3/service_ticket_validator_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'spec_helper' - -describe OmniAuth::Strategies::CAS3::ServiceTicketValidator do - let(:strategy) do - double('strategy', - service_validate_url: 'https://example.org/serviceValidate' - ) - end - let(:provider_options) do - double('provider_options', - disable_ssl_verification?: false, - ca_path: '/etc/ssl/certsZOMG' - ) - end - let(:validator) do - OmniAuth::Strategies::CAS3::ServiceTicketValidator.new( strategy, provider_options, '/foo', nil ) - end - - describe '#call' do - before do - stub_request(:get, 'https://example.org/serviceValidate?') - .to_return(status: 200, body: '') - end - - subject { validator.call } - - it 'returns itself' do - expect(subject).to eq validator - end - - it 'uses the configured CA path' do - subject - expect(provider_options).to have_received :ca_path - end - end - - describe '#user_info' do - let(:ok_fixture) do - File.expand_path(File.join(File.dirname(__FILE__), '../../../fixtures/cas_success.xml')) - end - let(:service_response) { File.read(ok_fixture) } - - before do - stub_request(:get, 'https://example.org/serviceValidate?') - .to_return(status: 200, body:service_response) - validator.call - end - - subject { validator.user_info } - - it 'parses user info from the response' do - expect(subject).to include 'user' => 'psegel' - end - end -end diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb deleted file mode 100644 index f434d711f02..00000000000 --- a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb +++ /dev/null @@ -1,266 +0,0 @@ -require 'spec_helper' -require 'securerandom' - -describe OmniAuth::Strategies::CAS3, type: :strategy do - include Rack::Test::Methods - - let(:my_cas_provider) { Class.new(OmniAuth::Strategies::CAS3) } - before do - stub_const 'MyCasProvider', my_cas_provider - end - let(:app) do - Rack::Builder.new { - use OmniAuth::Test::PhonySession - use MyCasProvider, name: :cas3, host: 'cas.example.org', ssl: false, port: 8080, uid_field: :employeeid - run lambda { |env| [404, {'Content-Type' => 'text/plain'}, [env.key?('omniauth.auth').to_s]] } - }.to_app - end - - let(:csrf_token) { SecureRandom.base64(32) } - let(:base_env) { { 'rack.session' => { csrf: csrf_token }, 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}") } } - let(:post_env) { make_env('/auth/cas3', base_env.merge(request_env)) } - let(:escaped_token) { URI.encode_www_form_component(csrf_token, Encoding::UTF_8) } - - def make_env(path = '/auth/cas3', props = {}) - { - 'REQUEST_METHOD' => 'POST', - 'PATH_INFO' => path, - 'rack.session' => {}, - 'rack.input' => StringIO.new('test=true') - }.merge(props) - end - - # TODO: Verify that these are even useful tests - shared_examples_for 'a CAS redirect response' do - let(:redirect_params) { 'service=' + Rack::Utils.escape("http://example.org/auth/cas3/callback?url=#{Rack::Utils.escape(return_url)}") } - - before { post url, nil, post_env } - - subject { last_response } - - it { should be_redirect } - - it 'redirects to the CAS server' do - expect(subject.status).to eq(302) - expect(subject.headers).to include 'Location' => "http://cas.example.org:8080/login?#{redirect_params}" - end - end - - describe '#cas_url' do - let(:params) { Hash.new } - let(:provider) { MyCasProvider.new(nil, params) } - - subject { provider.cas_url } - - it 'raises an ArgumentError' do - expect{subject}.to raise_error ArgumentError, %r{:host and :login_url MUST be provided} - end - - context 'with an explicit :url option' do - let(:url) { 'https://example.org:8080/my_cas' } - let(:params) { super().merge url:url } - - before { subject } - - it { should eq url } - - it 'parses the URL into it the appropriate strategy options' do - expect(provider.options).to include ssl:true - expect(provider.options).to include host:'example.org' - expect(provider.options).to include port:8080 - expect(provider.options).to include path:'/my_cas' - end - end - - context 'with explicit URL component' do - let(:params) { super().merge host:'example.org', port:1234, ssl:true, path:'/a/path' } - - before { subject } - - it { should eq 'https://example.org:1234/a/path' } - - it 'parses the URL into it the appropriate strategy options' do - expect(provider.options).to include ssl:true - expect(provider.options).to include host:'example.org' - expect(provider.options).to include port:1234 - expect(provider.options).to include path:'/a/path' - end - end - end - - describe 'defaults' do - subject { MyCasProvider.default_options.to_hash } - - it { should include('ssl' => true) } - end - - describe 'POST /auth/cas3' do - let(:return_url) { 'http://myapp.com/admin/foo' } - - context 'with a referer' do - let(:url) { '/auth/cas3' } - - let(:request_env) { { 'HTTP_REFERER' => return_url } } - - it_behaves_like 'a CAS redirect response' - end - - context 'with an explicit return URL' do - let(:url) { "/auth/cas3?url=#{return_url}" } - - let(:request_env) { {} } - - it_behaves_like 'a CAS redirect response' - end - end - - describe 'GET /auth/cas3/callback' do - context 'without a ticket' do - before { get '/auth/cas3/callback' } - - subject { last_response } - - it { should be_redirect } - - it 'redirects with a failure message' do - expect(subject.headers).to include 'Location' => '/auth/failure?message=no_ticket&strategy=cas3' - end - end - - context 'with an invalid ticket' do - before do - stub_request(:get, /^http:\/\/cas.example.org:8080?\/p3\/serviceValidate\?([^&]+&)?ticket=9391d/). - to_return( body: File.read('spec/fixtures/cas_failure.xml') ) - get '/auth/cas3/callback?ticket=9391d' - end - - subject { last_response } - - it { should be_redirect } - - it 'redirects with a failure message' do - expect(subject.headers).to include 'Location' => '/auth/failure?message=invalid_ticket&strategy=cas3' - end - end - - describe 'with a valid ticket' do - shared_examples :successful_validation do - before do - stub_request(:get, /^http:\/\/cas.example.org:8080?\/p3\/serviceValidate\?([^&]+&)?ticket=593af/) - .with { |request| @request_uri = request.uri.to_s } - .to_return( body: File.read("spec/fixtures/#{xml_file_name}") ) - - get "/auth/cas3/callback?ticket=593af&url=#{return_url}" - end - - it 'strips the ticket parameter from the callback URL' do - expect(@request_uri.scan('ticket=').size).to eq 1 - end - - it 'properly encodes the service URL' do - expect(WebMock).to have_requested(:get, 'http://cas.example.org:8080/p3/serviceValidate') - .with(query: { - ticket: '593af', - service: 'http://example.org/auth/cas3/callback?url=' + Rack::Utils.escape('http://127.0.0.10/?some=parameter') - }) - end - - context "request.env['omniauth.auth']" do - subject { last_request.env['omniauth.auth'] } - - it { should be_kind_of Hash } - - it 'identifes the provider' do - expect(subject.provider).to eq :cas3 - end - - it 'returns the UID of the user' do - expect(subject.uid).to eq '54' - end - - context 'the info hash' do - subject { last_request.env['omniauth.auth']['info'] } - - it 'includes user info attributes' do - expect(subject.name).to eq 'Peter Segel' - expect(subject.first_name).to eq 'Peter' - expect(subject.last_name).to eq 'Segel' - expect(subject.nickname).to eq 'psegel' - expect(subject.email).to eq 'psegel@intridea.com' - expect(subject.location).to eq 'Washington, D.C.' - expect(subject.image).to eq '/images/user.jpg' - expect(subject.phone).to eq '555-555-5555' - end - end - - context 'the extra hash' do - subject { last_request.env['omniauth.auth']['extra'] } - - it 'includes additional user attributes' do - expect(subject.user).to eq 'psegel' - expect(subject.employeeid).to eq '54' - expect(subject.hire_date).to eq '2004-07-13' - end - end - - context 'the credentials hash' do - subject { last_request.env['omniauth.auth']['credentials'] } - - it 'has a ticket value' do - expect(subject.ticket).to eq '593af' - end - end - end - - it 'calls through to the master app' do - expect(last_response.body).to eq 'true' - end - end - - let(:return_url) { 'http://127.0.0.10/?some=parameter' } - - context 'with JASIG flavored XML' do - let(:xml_file_name) { 'cas_success_jasig.xml' } - - it_behaves_like :successful_validation - end - - context 'with classic XML' do - let(:xml_file_name) { 'cas_success.xml' } - - it_behaves_like :successful_validation - end - end - end - - describe 'POST /auth/cas3/callback' do - describe 'with a Single Sign-Out logoutRequest' do - let(:logoutRequest) do - %Q[ - - @NOT_USED@ - ST-123456-123abc456def - - ] - end - - let(:logout_request) { double('logout_request', call:[200,{},'OK']) } - - subject do - post 'auth/cas3/callback', logoutRequest:logoutRequest - end - - before do - allow_any_instance_of(MyCasProvider) - .to receive(:logout_request_service) - .and_return double('LogoutRequest', new:logout_request) - - subject - end - - it 'initializes a LogoutRequest' do - expect(logout_request).to have_received :call - end - end - end -end diff --git a/vendor/gems/omniauth-cas3/spec/spec_helper.rb b/vendor/gems/omniauth-cas3/spec/spec_helper.rb deleted file mode 100644 index 75231268ff3..00000000000 --- a/vendor/gems/omniauth-cas3/spec/spec_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'bundler/setup' -require 'awesome_print' - -RSpec.configure do |c| - c.filter_run focus: true - c.run_all_when_everything_filtered = true -end - -require 'rack/test' -require 'webmock/rspec' -require 'omniauth-cas3' - -OmniAuth.config.logger = Logger.new( '/dev/null' )