Commit 4d243f5c authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/security/gitlab@13-4-stable-ee

parent 516fba52
...@@ -5,6 +5,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -5,6 +5,7 @@ class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :new, :create] before_action :user, except: [:index, :new, :create]
before_action :check_impersonation_availability, only: :impersonate before_action :check_impersonation_availability, only: :impersonate
before_action :ensure_destroy_prerequisites_met, only: [:destroy]
def index def index
@users = User.filter_items(params[:filter]).order_name_asc @users = User.filter_items(params[:filter]).order_name_asc
...@@ -173,7 +174,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -173,7 +174,7 @@ class Admin::UsersController < Admin::ApplicationController
end end
def destroy def destroy
user.delete_async(deleted_by: current_user, params: params.permit(:hard_delete)) user.delete_async(deleted_by: current_user, params: destroy_params)
respond_to do |format| respond_to do |format|
format.html { redirect_to admin_users_path, status: :found, notice: _("The user is being deleted.") } format.html { redirect_to admin_users_path, status: :found, notice: _("The user is being deleted.") }
...@@ -202,6 +203,24 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -202,6 +203,24 @@ class Admin::UsersController < Admin::ApplicationController
user != current_user user != current_user
end end
def destroy_params
params.permit(:hard_delete)
end
def ensure_destroy_prerequisites_met
return if hard_delete?
if user.solo_owned_groups.present?
message = s_('AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account')
redirect_to admin_user_path(user), status: :see_other, alert: message
end
end
def hard_delete?
destroy_params[:hard_delete]
end
def user def user
@user ||= find_routable!(User, params[:id]) @user ||= find_routable!(User, params[:id])
end end
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
class Profiles::EmailsController < Profiles::ApplicationController class Profiles::EmailsController < Profiles::ApplicationController
before_action :find_email, only: [:destroy, :resend_confirmation_instructions] before_action :find_email, only: [:destroy, :resend_confirmation_instructions]
before_action -> { rate_limit!(:profile_add_new_email) }, only: [:create]
before_action -> { rate_limit!(:profile_resend_email_confirmation) }, only: [:resend_confirmation_instructions]
def index def index
@primary_email = current_user.email @primary_email = current_user.email
...@@ -38,6 +40,16 @@ class Profiles::EmailsController < Profiles::ApplicationController ...@@ -38,6 +40,16 @@ class Profiles::EmailsController < Profiles::ApplicationController
private private
def rate_limit!(action)
rate_limiter = ::Gitlab::ApplicationRateLimiter
if rate_limiter.throttled?(action, scope: current_user)
rate_limiter.log_request(request, action, current_user)
redirect_back_or_default(options: { alert: _('This action has been performed too many times. Try again later.') })
end
end
def email_params def email_params
params.require(:email).permit(:email) params.require(:email).permit(:email)
end end
......
...@@ -12,6 +12,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -12,6 +12,7 @@ class Projects::RawController < Projects::ApplicationController
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :show_rate_limit, only: [:show], unless: :external_storage_request? before_action :show_rate_limit, only: [:show], unless: :external_storage_request?
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :no_cache_headers, only: [:show]
before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled? before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled?
def show def show
......
...@@ -10,7 +10,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -10,7 +10,7 @@ class RegistrationsController < Devise::RegistrationsController
skip_before_action :required_signup_info, :check_two_factor_requirement, only: [:welcome, :update_registration] skip_before_action :required_signup_info, :check_two_factor_requirement, only: [:welcome, :update_registration]
prepend_before_action :check_captcha, only: :create prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, only: [:destroy] before_action :whitelist_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :ensure_terms_accepted, before_action :ensure_terms_accepted,
if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? } if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? }
before_action :load_recaptcha, only: :new before_action :load_recaptcha, only: :new
...@@ -124,6 +124,14 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -124,6 +124,14 @@ class RegistrationsController < Devise::RegistrationsController
private private
def ensure_destroy_prerequisites_met
if current_user.solo_owned_groups.present?
redirect_to profile_account_path,
status: :see_other,
alert: s_('Profiles|You must transfer ownership or delete groups you are an owner of before you can delete your account')
end
end
def user_created_message(confirmed: false) def user_created_message(confirmed: false)
"User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:#{confirmed}" "User Created: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip} confirmed:#{confirmed}"
end end
......
...@@ -5,7 +5,7 @@ module SafeParamsHelper ...@@ -5,7 +5,7 @@ module SafeParamsHelper
# Use this helper when generating links with `params.merge(...)` # Use this helper when generating links with `params.merge(...)`
def safe_params def safe_params
if params.respond_to?(:permit!) if params.respond_to?(:permit!)
params.except(:host, :port, :protocol).permit! params.except(*ActionDispatch::Routing::RouteSet::RESERVED_OPTIONS).permit!
else else
params params
end end
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Clusters module Clusters
module Applications module Applications
class Runner < ApplicationRecord class Runner < ApplicationRecord
VERSION = '0.20.1' VERSION = '0.20.2'
self.table_name = 'clusters_applications_runners' self.table_name = 'clusters_applications_runners'
......
---
title: Prevent SVG XSS via Web IDE
merge_request:
author:
type: security
---
title: Ensure user has no solo owned groups before triggering account deletion
merge_request:
author:
type: security
---
title: Security fix safe params helper
author:
type: security
---
title: Rate limit adding new email and re-sending email confirmation
merge_request:
author:
type: security
---
title: Update GitLab Runner Helm Chart to 0.20.2
merge_request:
author:
type: security
...@@ -15,7 +15,8 @@ templates are sourced. ...@@ -15,7 +15,8 @@ templates are sourced.
Every project directly under the group namespace will be Every project directly under the group namespace will be
available to the user if they have access to them. For example: available to the user if they have access to them. For example:
- Public project in the group will be available to every logged in user. - Public projects, in the group will be available to every signed-in user, if all enabled [project features](../project/settings/index.md#sharing-and-permissions)
are set to **Everyone With Access**.
- Private projects will be available only if the user is a member of the project. - Private projects will be available only if the user is a member of the project.
Repository and database information that are copied over to each new project are Repository and database information that are copied over to each new project are
......
...@@ -61,9 +61,8 @@ GitLab administrators can ...@@ -61,9 +61,8 @@ GitLab administrators can
[set project templates for an entire GitLab instance](../admin_area/custom_project_templates.md). [set project templates for an entire GitLab instance](../admin_area/custom_project_templates.md).
Within this section, you can configure the group where all the custom project Within this section, you can configure the group where all the custom project
templates are sourced. Every project directly under the group namespace will be templates are sourced. Every project _template_ directly under the group namespace is
available to the user if they have access to them. For example, every public available to every signed-in user, if all enabled [project features](../project/settings/index.md#sharing-and-permissions) are set to **Everyone With Access**.
project in the group will be available to every logged in user.
However, private projects will be available only if the user is a member of the project. However, private projects will be available only if the user is a member of the project.
......
...@@ -31,7 +31,9 @@ module Gitlab ...@@ -31,7 +31,9 @@ module Gitlab
group_export: { threshold: -> { application_settings.group_export_limit }, interval: 1.minute }, group_export: { threshold: -> { application_settings.group_export_limit }, interval: 1.minute },
group_download_export: { threshold: -> { application_settings.group_download_export_limit }, interval: 1.minute }, group_download_export: { threshold: -> { application_settings.group_download_export_limit }, interval: 1.minute },
group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute }, group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute },
group_testing_hook: { threshold: 5, interval: 1.minute } group_testing_hook: { threshold: 5, interval: 1.minute },
profile_add_new_email: { threshold: 5, interval: 1.minute },
profile_resend_email_confirmation: { threshold: 5, interval: 1.minute }
}.freeze }.freeze
end end
......
...@@ -2132,6 +2132,9 @@ msgstr "" ...@@ -2132,6 +2132,9 @@ msgstr ""
msgid "AdminUsers|You cannot remove your own admin rights." msgid "AdminUsers|You cannot remove your own admin rights."
msgstr "" msgstr ""
msgid "AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account"
msgstr ""
msgid "Administration" msgid "Administration"
msgstr "" msgstr ""
...@@ -19391,6 +19394,9 @@ msgstr "" ...@@ -19391,6 +19394,9 @@ msgstr ""
msgid "Profiles|You don't have access to delete this user." msgid "Profiles|You don't have access to delete this user."
msgstr "" msgstr ""
msgid "Profiles|You must transfer ownership or delete groups you are an owner of before you can delete your account"
msgstr ""
msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account." msgid "Profiles|You must transfer ownership or delete these groups before you can delete your account."
msgstr "" msgstr ""
...@@ -25734,6 +25740,9 @@ msgstr "" ...@@ -25734,6 +25740,9 @@ msgstr ""
msgid "This action cannot be undone. You will lose the project's repository and all content: issues, merge requests, etc." msgid "This action cannot be undone. You will lose the project's repository and all content: issues, merge requests, etc."
msgstr "" msgstr ""
msgid "This action has been performed too many times. Try again later."
msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all content: issues, merge requests, etc." msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all content: issues, merge requests, etc."
msgstr "" msgstr ""
......
...@@ -36,7 +36,7 @@ RSpec.describe Admin::UsersController do ...@@ -36,7 +36,7 @@ RSpec.describe Admin::UsersController do
end end
end end
describe 'DELETE #user with projects', :sidekiq_might_not_need_inline do describe 'DELETE #destroy', :sidekiq_might_not_need_inline do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
let!(:issue) { create(:issue, author: user) } let!(:issue) { create(:issue, author: user) }
...@@ -59,6 +59,41 @@ RSpec.describe Admin::UsersController do ...@@ -59,6 +59,41 @@ RSpec.describe Admin::UsersController do
expect(User.exists?(user.id)).to be_falsy expect(User.exists?(user.id)).to be_falsy
expect(Issue.exists?(issue.id)).to be_falsy expect(Issue.exists?(issue.id)).to be_falsy
end end
context 'prerequisites for account deletion' do
context 'solo-owned groups' do
let(:group) { create(:group) }
context 'if the user is the sole owner of at least one group' do
before do
create(:group_member, :owner, group: group, user: user)
end
context 'soft-delete' do
it 'fails' do
delete :destroy, params: { id: user.username }
message = s_('AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account')
expect(flash[:alert]).to eq(message)
expect(response).to have_gitlab_http_status(:see_other)
expect(response).to redirect_to admin_user_path(user)
expect(User.exists?(user.id)).to be_truthy
end
end
context 'hard-delete' do
it 'succeeds' do
delete :destroy, params: { id: user.username, hard_delete: true }
expect(response).to redirect_to(admin_users_path)
expect(flash[:notice]).to eq(_('The user is being deleted.'))
expect(User.exists?(user.id)).to be_falsy
end
end
end
end
end
end end
describe 'PUT #activate' do describe 'PUT #activate' do
......
...@@ -15,6 +15,29 @@ RSpec.describe Profiles::EmailsController do ...@@ -15,6 +15,29 @@ RSpec.describe Profiles::EmailsController do
end end
end end
shared_examples_for 'respects the rate limit' do
context 'after the rate limit is exceeded' do
before do
allowed_threshold = Gitlab::ApplicationRateLimiter.rate_limits[action][:threshold]
allow(Gitlab::ApplicationRateLimiter)
.to receive(:increment)
.and_return(allowed_threshold + 1)
end
it 'does not send any email' do
expect { subject }.not_to change { ActionMailer::Base.deliveries.size }
end
it 'displays an alert' do
subject
expect(response).to have_gitlab_http_status(:redirect)
expect(flash[:alert]).to eq(_('This action has been performed too many times. Try again later.'))
end
end
end
describe '#create' do describe '#create' do
let(:email) { 'add_email@example.com' } let(:email) { 'add_email@example.com' }
let(:params) { { email: { email: email } } } let(:params) { { email: { email: email } } }
...@@ -32,6 +55,10 @@ RSpec.describe Profiles::EmailsController do ...@@ -32,6 +55,10 @@ RSpec.describe Profiles::EmailsController do
expect { subject }.not_to change { ActionMailer::Base.deliveries.size } expect { subject }.not_to change { ActionMailer::Base.deliveries.size }
end end
end end
it_behaves_like 'respects the rate limit' do
let(:action) { :profile_add_new_email }
end
end end
describe '#resend_confirmation_instructions' do describe '#resend_confirmation_instructions' do
...@@ -54,5 +81,9 @@ RSpec.describe Profiles::EmailsController do ...@@ -54,5 +81,9 @@ RSpec.describe Profiles::EmailsController do
expect { subject }.not_to change { ActionMailer::Base.deliveries.size } expect { subject }.not_to change { ActionMailer::Base.deliveries.size }
end end
end end
it_behaves_like 'respects the rate limit' do
let(:action) { :profile_resend_email_confirmation }
end
end end
end end
...@@ -33,6 +33,11 @@ RSpec.describe Projects::RawController do ...@@ -33,6 +33,11 @@ RSpec.describe Projects::RawController do
it_behaves_like 'project cache control headers' it_behaves_like 'project cache control headers'
it_behaves_like 'content disposition headers' it_behaves_like 'content disposition headers'
it_behaves_like 'uncached response' do
before do
subject
end
end
end end
context 'image header' do context 'image header' do
......
...@@ -459,6 +459,24 @@ RSpec.describe RegistrationsController do ...@@ -459,6 +459,24 @@ RSpec.describe RegistrationsController do
expect_success expect_success
end end
end end
context 'prerequisites for account deletion' do
context 'solo-owned groups' do
let(:group) { create(:group) }
context 'if the user is the sole owner of at least one group' do
before do
create(:group_member, :owner, group: group, user: user)
end
it 'fails' do
delete :destroy, params: { password: '12345678' }
expect_failure(s_('Profiles|You must transfer ownership or delete groups you are an owner of before you can delete your account'))
end
end
end
end
end end
describe '#welcome' do describe '#welcome' do
......
...@@ -29,6 +29,7 @@ FactoryBot.define do ...@@ -29,6 +29,7 @@ FactoryBot.define do
pages_access_level do pages_access_level do
visibility_level == Gitlab::VisibilityLevel::PUBLIC ? ProjectFeature::ENABLED : ProjectFeature::PRIVATE visibility_level == Gitlab::VisibilityLevel::PUBLIC ? ProjectFeature::ENABLED : ProjectFeature::PRIVATE
end end
metrics_dashboard_access_level { ProjectFeature::PRIVATE }
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the # we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first # `#ci_cd_settings` relation needs to be created first
...@@ -53,7 +54,8 @@ FactoryBot.define do ...@@ -53,7 +54,8 @@ FactoryBot.define do
forking_access_level: evaluator.forking_access_level, forking_access_level: evaluator.forking_access_level,
merge_requests_access_level: merge_requests_access_level, merge_requests_access_level: merge_requests_access_level,
repository_access_level: evaluator.repository_access_level, repository_access_level: evaluator.repository_access_level,
pages_access_level: evaluator.pages_access_level pages_access_level: evaluator.pages_access_level,
metrics_dashboard_access_level: evaluator.metrics_dashboard_access_level
} }
project.build_project_feature(hash) project.build_project_feature(hash)
...@@ -309,6 +311,9 @@ FactoryBot.define do ...@@ -309,6 +311,9 @@ FactoryBot.define do
trait(:pages_enabled) { pages_access_level { ProjectFeature::ENABLED } } trait(:pages_enabled) { pages_access_level { ProjectFeature::ENABLED } }
trait(:pages_disabled) { pages_access_level { ProjectFeature::DISABLED } } trait(:pages_disabled) { pages_access_level { ProjectFeature::DISABLED } }
trait(:pages_private) { pages_access_level { ProjectFeature::PRIVATE } } trait(:pages_private) { pages_access_level { ProjectFeature::PRIVATE } }
trait(:metrics_dashboard_enabled) { metrics_dashboard_access_level { ProjectFeature::ENABLED } }
trait(:metrics_dashboard_disabled) { metrics_dashboard_access_level { ProjectFeature::DISABLED } }
trait(:metrics_dashboard_private) { metrics_dashboard_access_level { ProjectFeature::PRIVATE } }
trait :auto_devops do trait :auto_devops do
association :auto_devops, factory: :project_auto_devops association :auto_devops, factory: :project_auto_devops
......
...@@ -532,16 +532,13 @@ RSpec.describe API::Files do ...@@ -532,16 +532,13 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
end end
it 'sets no-cache headers' do it_behaves_like 'uncached response' do
before do
url = route('.gitignore') + "/raw" url = route('.gitignore') + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob) expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params: params get api(url, current_user), params: params
end
expect(response.headers["Cache-Control"]).to include("no-store")
expect(response.headers["Cache-Control"]).to include("no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end end
context 'when mandatory params are not given' do context 'when mandatory params are not given' do
......
...@@ -39,7 +39,9 @@ RSpec.describe 'Projects::MetricsDashboardController' do ...@@ -39,7 +39,9 @@ RSpec.describe 'Projects::MetricsDashboardController' do
context 'with anonymous user and public dashboard visibility' do context 'with anonymous user and public dashboard visibility' do
let(:anonymous_user) { create(:user) } let(:anonymous_user) { create(:user) }
let(:project) { create(:project, :public) } let(:project) do
create(:project, :public, :metrics_dashboard_enabled)
end
before do before do
project.update!(metrics_dashboard_access_level: 'enabled') project.update!(metrics_dashboard_access_level: 'enabled')
......
# frozen_string_literal: true
#
# Negates lib/gitlab/no_cache_headers.rb
#
RSpec.shared_examples 'cached response' do
it 'defines a cached header response' do
expect(response.headers["Cache-Control"]).not_to include("no-store", "no-cache")
expect(response.headers["Pragma"]).not_to eq("no-cache")
expect(response.headers["Expires"]).not_to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
end
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment