From 8e3523281051490ff696bfd85bea1195c046c87c Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 2 Sep 2020 15:10:54 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .../issuable_filtered_search_token_keys.js | 5 ++ app/controllers/clusters/clusters_controller.rb | 5 +- .../concerns/authenticates_with_two_factor.rb | 24 ++++++++-- .../concerns/enforces_two_factor_authentication.rb | 18 +++---- app/controllers/omniauth_callbacks_controller.rb | 11 ++--- .../profiles/active_sessions_controller.rb | 1 + .../profiles/two_factor_auths_controller.rb | 4 +- app/controllers/sessions_controller.rb | 13 +++-- app/finders/ci/auth_job_finder.rb | 56 ++++++++++++++++++++++ app/finders/user_recent_events_finder.rb | 12 ++++- app/graphql/mutations/boards/lists/base.rb | 2 +- app/graphql/mutations/boards/lists/create.rb | 9 +++- app/graphql/mutations/snippets/base.rb | 2 +- app/models/active_session.rb | 13 +++++ app/models/aws/role.rb | 1 + app/models/clusters/applications/runner.rb | 2 +- app/models/concerns/discussion_on_diff.rb | 2 - app/models/concerns/resolvable_discussion.rb | 1 - app/models/diff_discussion.rb | 1 - app/models/discussion.rb | 1 - app/models/member.rb | 2 + app/models/user.rb | 7 +++ app/services/applications/create_service.rb | 11 ++++- .../container_registry_authentication_service.rb | 1 + app/services/branches/delete_service.rb | 2 +- app/services/ci/pipeline_trigger_service.rb | 7 +-- .../clusters/aws/authorize_role_service.rb | 16 +++---- app/services/members/destroy_service.rb | 33 +++++++++++-- .../projects/update_remote_mirror_service.rb | 4 ++ .../active_sessions/_active_session.html.haml | 2 +- app/views/projects/issues/service_desk.html.haml | 2 +- 31 files changed, 211 insertions(+), 59 deletions(-) create mode 100644 app/finders/ci/auth_job_finder.rb (limited to 'app') diff --git a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js index 9bea7aa7b04..d2ac80fa190 100644 --- a/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/issuable_filtered_search_token_keys.js @@ -85,6 +85,11 @@ export const conditions = flattenDeep( tokenKey: 'assignee', value: __('Any'), }, + { + url: 'author_username=support-bot', + tokenKey: 'author', + value: 'support-bot', + }, { url: 'milestone_title=None', tokenKey: 'milestone', diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index b6d5eb7e5c0..7006c23321c 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -38,8 +38,7 @@ class Clusters::ClustersController < Clusters::BaseController def new if params[:provider] == 'aws' - @aws_role = current_user.aws_role || Aws::Role.new - @aws_role.ensure_role_external_id! + @aws_role = Aws::Role.create_or_find_by!(user: current_user) @instance_types = load_instance_types.to_json elsif params[:provider] == 'gcp' @@ -274,7 +273,7 @@ class Clusters::ClustersController < Clusters::BaseController end def aws_role_params - params.require(:cluster).permit(:role_arn, :role_external_id) + params.require(:cluster).permit(:role_arn) end def generate_gcp_authorize_url diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 4b4bcc8d37e..2cc51c65c26 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -22,6 +22,8 @@ module AuthenticatesWithTwoFactor return handle_locked_user(user) unless user.can?(:log_in) session[:otp_user_id] = user.id + session[:user_updated_at] = user.updated_at + setup_u2f_authentication(user) render 'devise/sessions/two_factor' end @@ -39,6 +41,7 @@ module AuthenticatesWithTwoFactor def authenticate_with_two_factor user = self.resource = find_user return handle_locked_user(user) unless user.can?(:log_in) + return handle_changed_user(user) if user_changed?(user) if user_params[:otp_attempt].present? && session[:otp_user_id] authenticate_with_two_factor_via_otp(user) @@ -63,12 +66,14 @@ module AuthenticatesWithTwoFactor def clear_two_factor_attempt! session.delete(:otp_user_id) + session.delete(:user_updated_at) + session.delete(:challenge) end def authenticate_with_two_factor_via_otp(user) if valid_otp_attempt?(user) # Remove any lingering user data from login - session.delete(:otp_user_id) + clear_two_factor_attempt! remember_me(user) if user_params[:remember_me] == '1' user.save! @@ -85,8 +90,7 @@ module AuthenticatesWithTwoFactor def authenticate_with_two_factor_via_u2f(user) if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge]) # Remove any lingering user data from login - session.delete(:otp_user_id) - session.delete(:challenge) + clear_two_factor_attempt! remember_me(user) if user_params[:remember_me] == '1' sign_in(user, message: :two_factor_authenticated, event: :authentication) @@ -113,4 +117,18 @@ module AuthenticatesWithTwoFactor end end # rubocop: enable CodeReuse/ActiveRecord + + def handle_changed_user(user) + clear_two_factor_attempt! + + redirect_to new_user_session_path, alert: _('An error occurred. Please sign in again.') + end + + # If user has been updated since we validated the password, + # the password might have changed. + def user_changed?(user) + return false unless session[:user_updated_at] + + user.updated_at != session[:user_updated_at] + end end diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb index f1dd46648f1..02082f81598 100644 --- a/app/controllers/concerns/enforces_two_factor_authentication.rb +++ b/app/controllers/concerns/enforces_two_factor_authentication.rb @@ -29,12 +29,11 @@ module EnforcesTwoFactorAuthentication end def two_factor_authentication_required? - Gitlab::CurrentSettings.require_two_factor_authentication? || - current_user.try(:require_two_factor_authentication_from_group?) + two_factor_verifier.two_factor_authentication_required? end def current_user_requires_two_factor? - current_user && !current_user.temp_oauth_email? && !current_user.two_factor_enabled? && !skip_two_factor? + two_factor_verifier.current_user_needs_to_setup_two_factor? && !skip_two_factor? end # rubocop: disable CodeReuse/ActiveRecord @@ -43,7 +42,7 @@ module EnforcesTwoFactorAuthentication if Gitlab::CurrentSettings.require_two_factor_authentication? global.call else - groups = current_user.expanded_groups_requiring_two_factor_authentication.reorder(name: :asc) + groups = current_user.source_groups_of_two_factor_authentication_requirement.reorder(name: :asc) group.call(groups) end end @@ -51,14 +50,11 @@ module EnforcesTwoFactorAuthentication # rubocop: enable CodeReuse/ActiveRecord def two_factor_grace_period - periods = [Gitlab::CurrentSettings.two_factor_grace_period] - periods << current_user.two_factor_grace_period if current_user.try(:require_two_factor_authentication_from_group?) - periods.min + two_factor_verifier.two_factor_grace_period end def two_factor_grace_period_expired? - date = current_user.otp_grace_period_started_at - date && (date + two_factor_grace_period.hours) < Time.current + two_factor_verifier.two_factor_grace_period_expired? end def two_factor_skippable? @@ -70,6 +66,10 @@ module EnforcesTwoFactorAuthentication def skip_two_factor? session[:skip_two_factor] && session[:skip_two_factor] > Time.current end + + def two_factor_verifier + @two_factor_verifier ||= Gitlab::Auth::TwoFactorAuthVerifier.new(current_user) # rubocop:disable Gitlab/ModuleWithInstanceVariables + end end EnforcesTwoFactorAuthentication.prepend_if_ee('EE::EnforcesTwoFactorAuthentication') diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 6a393405e4d..a558b01f0c6 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -50,12 +50,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_unverified_saml_initiation end - def omniauth_error - @provider = params[:provider] - @error = params[:error] - render 'errors/omniauth_error', layout: "oauth_error", status: :unprocessable_entity - end - def cas3 ticket = params['ticket'] if ticket @@ -205,9 +199,10 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController def fail_login(user) log_failed_login(user.username, oauth['provider']) - error_message = user.errors.full_messages.to_sentence + @provider = Gitlab::Auth::OAuth::Provider.label_for(params[:action]) + @error = user.errors.full_messages.to_sentence - redirect_to omniauth_error_path(oauth['provider'], error: error_message) + render 'errors/omniauth_error', layout: "oauth_error", status: :unprocessable_entity end def fail_auth0_login diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb index f409193aefc..d9ec3195fd1 100644 --- a/app/controllers/profiles/active_sessions_controller.rb +++ b/app/controllers/profiles/active_sessions_controller.rb @@ -7,6 +7,7 @@ class Profiles::ActiveSessionsController < Profiles::ApplicationController def destroy ActiveSession.destroy_with_public_id(current_user, params[:id]) + current_user.forget_me! respond_to do |format| format.html { redirect_to profile_active_sessions_url, status: :found } diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index a88c5ca4fa1..f600c34ca75 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -4,7 +4,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController skip_before_action :check_two_factor_requirement def show - unless current_user.otp_secret + unless current_user.two_factor_enabled? current_user.otp_secret = User.generate_otp_secret(32) end @@ -38,6 +38,8 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def create if current_user.validate_and_consume_otp!(params[:pin_code]) + ActiveSession.destroy_all_but_current(current_user, session) + Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user| @codes = user.generate_otp_backup_codes! end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index baf7a05f8ba..5da2bafbcb3 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -8,6 +8,7 @@ class SessionsController < Devise::SessionsController include Recaptcha::Verify include RendersLdapServers include KnownSignIn + include Gitlab::Utils::StrongMemoize skip_before_action :check_two_factor_requirement, only: [:destroy] skip_before_action :check_password_expiration, only: [:destroy] @@ -199,10 +200,14 @@ class SessionsController < Devise::SessionsController end def find_user - if session[:otp_user_id] - User.find(session[:otp_user_id]) - elsif user_params[:login] - User.by_login(user_params[:login]) + strong_memoize(:find_user) do + if session[:otp_user_id] && user_params[:login] + User.by_id_and_login(session[:otp_user_id], user_params[:login]).first + elsif session[:otp_user_id] + User.find(session[:otp_user_id]) + elsif user_params[:login] + User.by_login(user_params[:login]) + end end end diff --git a/app/finders/ci/auth_job_finder.rb b/app/finders/ci/auth_job_finder.rb new file mode 100644 index 00000000000..aee7dd16341 --- /dev/null +++ b/app/finders/ci/auth_job_finder.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true +module Ci + class AuthJobFinder + AuthError = Class.new(StandardError) + NotRunningJobError = Class.new(AuthError) + ErasedJobError = Class.new(AuthError) + DeletedProjectError = Class.new(AuthError) + + def initialize(token:) + @token = token + end + + def execute! + find_job_by_token.tap do |job| + next unless job + + validate_job!(job) + end + end + + def execute + execute! + rescue AuthError + end + + private + + attr_reader :token, :require_running, :raise_on_missing + + def find_job_by_token + ::Ci::Build.find_by_token(token) + end + + def validate_job!(job) + validate_running_job!(job) + validate_job_not_erased!(job) + validate_project_presence!(job) + + true + end + + def validate_running_job!(job) + raise NotRunningJobError, 'Job is not running' unless job.running? + end + + def validate_job_not_erased!(job) + raise ErasedJobError, 'Job has been erased!' if job.erased? + end + + def validate_project_presence!(job) + if job.project.nil? || job.project.pending_delete? + raise DeletedProjectError, 'Project has been deleted!' + end + end + end +end diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb index 3f2e813d381..f376b26ab9c 100644 --- a/app/finders/user_recent_events_finder.rb +++ b/app/finders/user_recent_events_finder.rb @@ -6,6 +6,7 @@ # WARNING: does not consider project feature visibility! # - user: The user for which to load the events # - params: +# - limit: Number of items that to be returned. Defaults to 20 and limited to 100. # - offset: The page of events to return class UserRecentEventsFinder prepend FinderWithCrossProjectAccess @@ -16,7 +17,8 @@ class UserRecentEventsFinder attr_reader :current_user, :target_user, :params - LIMIT = 20 + DEFAULT_LIMIT = 20 + MAX_LIMIT = 100 def initialize(current_user, target_user, params = {}) @current_user = current_user @@ -31,7 +33,7 @@ class UserRecentEventsFinder recent_events(params[:offset] || 0) .joins(:project) .with_associations - .limit_recent(params[:limit].presence || LIMIT, params[:offset]) + .limit_recent(limit, params[:offset]) end # rubocop: enable CodeReuse/ActiveRecord @@ -59,4 +61,10 @@ class UserRecentEventsFinder def projects target_user.project_interactions.to_sql end + + def limit + return DEFAULT_LIMIT unless params[:limit].present? + + [params[:limit].to_i, MAX_LIMIT].min + end end diff --git a/app/graphql/mutations/boards/lists/base.rb b/app/graphql/mutations/boards/lists/base.rb index 34b271ba3b8..d244d6bf8dd 100644 --- a/app/graphql/mutations/boards/lists/base.rb +++ b/app/graphql/mutations/boards/lists/base.rb @@ -8,7 +8,7 @@ module Mutations argument :board_id, ::Types::GlobalIDType[::Board], required: true, - description: 'The Global ID of the issue board to mutate' + description: 'Global ID of the issue board to mutate' field :list, Types::BoardListType, diff --git a/app/graphql/mutations/boards/lists/create.rb b/app/graphql/mutations/boards/lists/create.rb index 4f545709ee9..3fe1052315f 100644 --- a/app/graphql/mutations/boards/lists/create.rb +++ b/app/graphql/mutations/boards/lists/create.rb @@ -12,7 +12,7 @@ module Mutations argument :label_id, ::Types::GlobalIDType[::Label], required: false, - description: 'ID of an existing label' + description: 'Global ID of an existing label' def ready?(**args) if args.slice(*mutually_exclusive_args).size != 1 @@ -39,6 +39,7 @@ module Mutations private + # Overridden in EE def authorize_list_type_resource!(board, params) return unless params[:label_id] @@ -57,13 +58,15 @@ module Mutations create_list_service.execute(board) end + # Overridden in EE def create_list_params(args) params = args.slice(*mutually_exclusive_args).with_indifferent_access - params[:label_id] = GitlabSchema.parse_gid(params[:label_id]).model_id if params[:label_id] + params[:label_id] &&= ::GitlabSchema.parse_gid(params[:label_id], expected_type: ::Label).model_id params end + # Overridden in EE def mutually_exclusive_args [:backlog, :label_id] end @@ -71,3 +74,5 @@ module Mutations end end end + +Mutations::Boards::Lists::Create.prepend_if_ee('::EE::Mutations::Boards::Lists::Create') diff --git a/app/graphql/mutations/snippets/base.rb b/app/graphql/mutations/snippets/base.rb index c8cc721b2e0..023f876d035 100644 --- a/app/graphql/mutations/snippets/base.rb +++ b/app/graphql/mutations/snippets/base.rb @@ -11,7 +11,7 @@ module Mutations private def find_object(id:) - GitlabSchema.object_from_id(id) + GitlabSchema.object_from_id(id, expected_type: ::Snippet) end def authorized_resource?(snippet) diff --git a/app/models/active_session.rb b/app/models/active_session.rb index be07c221f32..4908290e06b 100644 --- a/app/models/active_session.rb +++ b/app/models/active_session.rb @@ -105,6 +105,19 @@ class ActiveSession end end + def self.destroy_all_but_current(user, current_session) + session_ids = not_impersonated(user) + session_ids.reject! { |session| session.current?(current_session) } if current_session + + Gitlab::Redis::SharedState.with do |redis| + destroy_sessions(redis, user, session_ids.map(&:session_id)) if session_ids.any? + end + end + + def self.not_impersonated(user) + list(user).reject(&:is_impersonated) + end + def self.key_name(user_id, session_id = '*') "#{Gitlab::Redis::SharedState::USER_SESSIONS_NAMESPACE}:#{user_id}:#{session_id}" end diff --git a/app/models/aws/role.rb b/app/models/aws/role.rb index 54132be749d..7d34665082d 100644 --- a/app/models/aws/role.rb +++ b/app/models/aws/role.rb @@ -9,6 +9,7 @@ module Aws validates :role_external_id, uniqueness: true, length: { in: 1..64 } validates :role_arn, length: 1..2048, + allow_nil: true, format: { with: Gitlab::Regex.aws_arn_regex, message: Gitlab::Regex.aws_arn_regex_message diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index f33d54544ca..4983de83800 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -3,7 +3,7 @@ module Clusters module Applications class Runner < ApplicationRecord - VERSION = '0.20.0' + VERSION = '0.20.1' self.table_name = 'clusters_applications_runners' diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb index 8542c48f366..40891073738 100644 --- a/app/models/concerns/discussion_on_diff.rb +++ b/app/models/concerns/discussion_on_diff.rb @@ -13,14 +13,12 @@ module DiscussionOnDiff :diff_line, :active?, :created_at_diff?, - to: :first_note delegate :file_path, :blob, :highlighted_diff_lines, :diff_lines, - to: :diff_file, allow_nil: true end diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb index 5174ae05d15..3e1e5faee54 100644 --- a/app/models/concerns/resolvable_discussion.rb +++ b/app/models/concerns/resolvable_discussion.rb @@ -31,7 +31,6 @@ module ResolvableDiscussion delegate :resolved_at, :resolved_by, :resolved_by_push?, - to: :last_resolved_note, allow_nil: true end diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index f9e2f00b9f3..6806008d676 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -16,7 +16,6 @@ class DiffDiscussion < Discussion :diff_note_positions, :on_text?, :on_image?, - to: :first_note def legacy_diff_discussion? diff --git a/app/models/discussion.rb b/app/models/discussion.rb index adcb2217d85..793cdb5dece 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -24,7 +24,6 @@ class Discussion :system_note_with_references_visible_for?, :resource_parent, :save, - to: :first_note def declarative_policy_delegate diff --git a/app/models/member.rb b/app/models/member.rb index a27076b49d7..1913df61614 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -76,6 +76,8 @@ class Member < ApplicationRecord scope :request, -> { where.not(requested_at: nil) } scope :non_request, -> { where(requested_at: nil) } + scope :not_accepted_invitations_by_user, -> (user) { invite.where(invite_accepted_at: nil, created_by: user) } + scope :has_access, -> { active.where('access_level > 0') } scope :guests, -> { active.where(access_level: GUEST) } diff --git a/app/models/user.rb b/app/models/user.rb index 355a174ba9a..7574f0865a0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -364,6 +364,7 @@ class User < ApplicationRecord scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) } scope :order_recent_last_activity, -> { reorder(Gitlab::Database.nulls_last_order('last_activity_on', 'DESC')) } scope :order_oldest_last_activity, -> { reorder(Gitlab::Database.nulls_first_order('last_activity_on', 'ASC')) } + scope :by_id_and_login, ->(id, login) { where(id: id).where('username = LOWER(:login) OR email = LOWER(:login)', login: login) } def preferred_language read_attribute('preferred_language') || @@ -887,6 +888,12 @@ class User < ApplicationRecord all_expanded_groups.where(require_two_factor_authentication: true) end + def source_groups_of_two_factor_authentication_requirement + Gitlab::ObjectHierarchy.new(expanded_groups_requiring_two_factor_authentication) + .all_objects + .where(id: groups) + end + # rubocop: disable CodeReuse/ServiceClass def refresh_authorized_projects Users::RefreshAuthorizedProjectsService.new(self).execute diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb index 500db1e172a..92500fbc254 100644 --- a/app/services/applications/create_service.rb +++ b/app/services/applications/create_service.rb @@ -11,7 +11,16 @@ module Applications # EE would override and use `request` arg def execute(request) - Doorkeeper::Application.create(params) + @application = Doorkeeper::Application.new(params) + + unless params[:scopes].present? + @application.errors.add(:base, _("Scopes can't be blank")) + + return @application + end + + @application.save + @application end end end diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index 44a434f4402..831a25a637e 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -132,6 +132,7 @@ module Auth def can_access?(requested_project, requested_action) return false unless requested_project.container_registry_enabled? + return false if requested_project.repository_access_level == ::ProjectFeature::DISABLED case requested_action when 'pull' diff --git a/app/services/branches/delete_service.rb b/app/services/branches/delete_service.rb index 9bd5b343448..6efbdd161a1 100644 --- a/app/services/branches/delete_service.rb +++ b/app/services/branches/delete_service.rb @@ -26,7 +26,7 @@ module Branches message: 'Failed to remove branch', http_status: 400) end - rescue Gitlab::Git::PreReceiveError => ex + rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => ex ServiceResponse.error(message: ex.message, http_status: 400) end diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb index 37b9b4c362c..d9f41b7040e 100644 --- a/app/services/ci/pipeline_trigger_service.rb +++ b/app/services/ci/pipeline_trigger_service.rb @@ -10,6 +10,9 @@ module Ci elsif job_from_token create_pipeline_from_job(job_from_token) end + + rescue Ci::AuthJobFinder::AuthError => e + error(e.message, 401) end private @@ -41,8 +44,6 @@ module Ci # this check is to not leak the presence of the project if user cannot read it return unless can?(job.user, :read_project, project) - return error("400 Job has to be running", 400) unless job.running? - pipeline = Ci::CreatePipelineService.new(project, job.user, ref: params[:ref]) .execute(:pipeline, ignore_skip_ci: true) do |pipeline| source = job.sourced_pipelines.build( @@ -64,7 +65,7 @@ module Ci def job_from_token strong_memoize(:job) do - Ci::Build.find_by_token(params[:token].to_s) + Ci::AuthJobFinder.new(token: params[:token].to_s).execute! end end diff --git a/app/services/clusters/aws/authorize_role_service.rb b/app/services/clusters/aws/authorize_role_service.rb index fb620f77b9f..2712a4b05bb 100644 --- a/app/services/clusters/aws/authorize_role_service.rb +++ b/app/services/clusters/aws/authorize_role_service.rb @@ -9,6 +9,7 @@ module Clusters ERRORS = [ ActiveRecord::RecordInvalid, + ActiveRecord::RecordNotFound, Clusters::Aws::FetchCredentialsService::MissingRoleError, ::Aws::Errors::MissingCredentialsError, ::Aws::STS::Errors::ServiceError @@ -20,7 +21,8 @@ module Clusters end def execute - @role = create_or_update_role! + ensure_role_exists! + update_role_arn! Response.new(:ok, credentials) rescue *ERRORS => e @@ -33,14 +35,12 @@ module Clusters attr_reader :role, :params - def create_or_update_role! - if role = user.aws_role - role.update!(params) + def ensure_role_exists! + @role = ::Aws::Role.find_by_user_id!(user.id) + end - role - else - user.create_aws_role!(params) - end + def update_role_arn! + role.update!(params) end def credentials diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb index fdd43260521..8cad065e6cc 100644 --- a/app/services/members/destroy_service.rb +++ b/app/services/members/destroy_service.rb @@ -18,6 +18,7 @@ module Members end delete_subresources(member) unless skip_subresources + delete_project_invitations_by(member) unless skip_subresources enqueue_delete_todos(member) enqueue_unassign_issuables(member) if unassign_issuables @@ -39,24 +40,48 @@ module Members delete_project_members(member) delete_subgroup_members(member) + delete_invited_members(member) end def delete_project_members(member) groups = member.group.self_and_descendants - ProjectMember.in_namespaces(groups).with_user(member.user).each do |project_member| - self.class.new(current_user).execute(project_member, skip_authorization: @skip_auth) - end + destroy_project_members(ProjectMember.in_namespaces(groups).with_user(member.user)) end def delete_subgroup_members(member) groups = member.group.descendants - GroupMember.of_groups(groups).with_user(member.user).each do |group_member| + destroy_group_members(GroupMember.of_groups(groups).with_user(member.user)) + end + + def delete_invited_members(member) + groups = member.group.self_and_descendants + + destroy_group_members(GroupMember.of_groups(groups).not_accepted_invitations_by_user(member.user)) + + destroy_project_members(ProjectMember.in_namespaces(groups).not_accepted_invitations_by_user(member.user)) + end + + def destroy_project_members(members) + members.each do |project_member| + self.class.new(current_user).execute(project_member, skip_authorization: @skip_auth) + end + end + + def destroy_group_members(members) + members.each do |group_member| self.class.new(current_user).execute(group_member, skip_authorization: @skip_auth, skip_subresources: true) end end + def delete_project_invitations_by(member) + return unless member.is_a?(ProjectMember) && member.user && member.project + + members_to_delete = member.project.members.not_accepted_invitations_by_user(member.user) + destroy_project_members(members_to_delete) + end + def can_destroy_member?(member) can?(current_user, destroy_member_permission(member), member) end diff --git a/app/services/projects/update_remote_mirror_service.rb b/app/services/projects/update_remote_mirror_service.rb index fe2610f89fb..7961f689259 100644 --- a/app/services/projects/update_remote_mirror_service.rb +++ b/app/services/projects/update_remote_mirror_service.rb @@ -7,6 +7,10 @@ module Projects def execute(remote_mirror, tries) return success unless remote_mirror.enabled? + if Gitlab::UrlBlocker.blocked_url?(CGI.unescape(Gitlab::UrlSanitizer.sanitize(remote_mirror.url))) + return error("The remote mirror URL is invalid.") + end + update_mirror(remote_mirror) success diff --git a/app/views/profiles/active_sessions/_active_session.html.haml b/app/views/profiles/active_sessions/_active_session.html.haml index 9ae75fe6b8e..c4b3fa80d75 100644 --- a/app/views/profiles/active_sessions/_active_session.html.haml +++ b/app/views/profiles/active_sessions/_active_session.html.haml @@ -27,6 +27,6 @@ - unless is_current_session .float-right - = link_to profile_active_session_path(active_session.public_id), data: { confirm: _('Are you sure? The device will be signed out of GitLab.') }, method: :delete, class: "btn btn-danger gl-ml-3" do + = link_to profile_active_session_path(active_session.public_id), data: { confirm: _('Are you sure? The device will be signed out of GitLab and all remember me tokens revoked.') }, method: :delete, class: "btn btn-danger gl-ml-3" do %span.sr-only= _('Revoke') = _('Revoke') diff --git a/app/views/projects/issues/service_desk.html.haml b/app/views/projects/issues/service_desk.html.haml index bd260bdf143..65580a94cd0 100644 --- a/app/views/projects/issues/service_desk.html.haml +++ b/app/views/projects/issues/service_desk.html.haml @@ -7,7 +7,7 @@ - support_bot_attrs = { service_desk_enabled: @project.service_desk_enabled?, **UserSerializer.new.represent(User.support_bot) }.to_json -- data_endpoint = "#{expose_path(api_v4_projects_issues_path(id: @project.id))}?author_id=#{User.support_bot.id}" +- data_endpoint = "#{expose_path(api_v4_projects_issues_path(id: @project.id))}?author_username=#{User.support_bot.username}" %div{ class: "js-service-desk-issues service-desk-issues", data: { support_bot: support_bot_attrs, service_desk_meta: service_desk_meta(@project) } } .top-area -- cgit v1.2.3