Commit 7d216055 authored by Vladimir Shushlin's avatar Vladimir Shushlin Committed by Nick Thomas

Allow admins for force pages access control

Add force_pages_access_control to application_settings
Add force_pages_access_control to settings form
Make pages private if it's forced on an instance level
Pass forced pages access control param to frontend
Show proper options for pages access when control is forced
Refactor and fix false-negative specs for project_feature
Move private_pages? specs to a separate describe block
Make pages private to public projects on creation if forced
Add specs for forced pages access control
parent b3a5416b
......@@ -82,6 +82,11 @@ export default {
required: false,
default: false,
},
pagesAccessControlForced: {
type: Boolean,
required: false,
default: false,
},
pagesHelpPath: {
type: String,
required: false,
......@@ -130,10 +135,22 @@ export default {
},
pagesFeatureAccessLevelOptions() {
if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
return this.featureAccessLevelOptions.concat([[30, PAGE_FEATURE_ACCESS_LEVEL]]);
const options = [featureAccessLevelMembers];
if (this.pagesAccessControlForced) {
if (this.visibilityLevel === visibilityOptions.INTERNAL) {
options.push(featureAccessLevelEveryone);
}
} else {
if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
options.push(featureAccessLevelEveryone);
}
if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
options.push([30, PAGE_FEATURE_ACCESS_LEVEL]);
}
}
return this.featureAccessLevelOptions;
return options;
},
repositoryEnabled() {
......
......@@ -202,6 +202,7 @@ module ApplicationSettingsHelper
:enabled_git_access_protocol,
:enforce_terms,
:first_day_of_week,
:force_pages_access_control,
:gitaly_timeout_default,
:gitaly_timeout_medium,
:gitaly_timeout_fast,
......
......@@ -587,6 +587,7 @@ module ProjectsHelper
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
pagesAvailable: Gitlab.config.pages.enabled,
pagesAccessControlEnabled: Gitlab.config.pages.access_control,
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control-core')
}
end
......
......@@ -97,7 +97,13 @@ class ProjectFeature < ApplicationRecord
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
default_value_for(:pages_access_level, allows_nil: false) { |feature| feature.project&.public? ? ENABLED : PRIVATE }
default_value_for(:pages_access_level, allows_nil: false) do |feature|
if ::Gitlab::Pages.access_control_is_forced?
PRIVATE
else
feature.project&.public? ? ENABLED : PRIVATE
end
end
def feature_available?(feature, user)
# This feature might not be behind a feature flag at all, so default to true
......@@ -137,6 +143,8 @@ class ProjectFeature < ApplicationRecord
def public_pages?
return true unless Gitlab.config.pages.access_control
return false if ::Gitlab::Pages.access_control_is_forced?
pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
end
......
......@@ -15,6 +15,15 @@
.form-text.text-muted
= _("Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled")
= link_to icon('question-circle'), help_page_path('user/project/pages/custom_domains_ssl_tls_certification/index.md', anchor: '4-verify-the-domains-ownership')
- if Gitlab.config.pages.access_control
.form-group
.form-check
= f.check_box :force_pages_access_control, class: 'form-check-input'
= f.label :force_pages_access_control, class: 'form-check-label' do
= _("Disable public access to Pages sites")
.form-text.text-muted
= _("Access to Pages websites are controlled based on the user's membership to a given project. By checking this box, users will be required to be logged in to have access to all Pages websites in your instance.")
= link_to icon('question-circle'), help_page_path('administration/pages/index.md', anchor: 'disabling-public-access-to-all-pages-websites')
%h5
= _("Configure Let's Encrypt")
%p
......
---
title: Allow administrators to enforce access control for all pages web-sites
merge_request: 22003
author:
type: added
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddForcePagesAccessControlToApplicationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :force_pages_access_control, :boolean, null: false, default: false
end
end
......@@ -364,6 +364,7 @@ ActiveRecord::Schema.define(version: 2020_01_08_233040) do
t.string "encrypted_slack_app_secret_iv", limit: 255
t.text "encrypted_slack_app_verification_token"
t.string "encrypted_slack_app_verification_token_iv", limit: 255
t.boolean "force_pages_access_control", default: false, null: false
t.boolean "updating_name_disabled_for_users", default: false, null: false
t.integer "instance_administrators_group_id"
t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id"
......
......@@ -307,6 +307,27 @@ Pages access control is disabled by default. To enable it:
1. [Reconfigure GitLab][reconfigure].
1. Users can now configure it in their [projects' settings](../../user/project/pages/pages_access_control.md).
#### Disabling public access to all Pages websites
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/32095) in GitLab 12.7.
You can enforce [Access Control](#access-control) for all GitLab Pages websites hosted
on your GitLab instance. By doing so, only logged-in users will have access to them.
This setting overrides Access Control set by users in individual projects.
This can be useful to preserve information published with Pages websites to the users
of your instance only.
To do that:
1. Navigate to your instance's **Admin Area > Settings > Preferences** and expand **Pages** settings.
1. Check the **Disable public access to Pages sites** checkbox.
1. Click **Save changes**.
CAUTION: **Warning:**
This action will not make all currently public web-sites private until they redeployed.
This issue among others will be resolved by
[changing GitLab Pages configuration mechanism](https://gitlab.com/gitlab-org/gitlab-pages/issues/282).
### Running behind a proxy
Like the rest of GitLab, Pages can be used in those environments where external
......
......@@ -18,6 +18,11 @@ module Gitlab
def secret_path
Gitlab.config.pages.secret_file
end
def access_control_is_forced?
::Gitlab.config.pages.access_control &&
::Gitlab::CurrentSettings.current_application_settings.force_pages_access_control
end
end
end
end
......@@ -847,6 +847,9 @@ msgstr ""
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
msgid "Access to Pages websites are controlled based on the user's membership to a given project. By checking this box, users will be required to be logged in to have access to all Pages websites in your instance."
msgstr ""
msgid "AccessDropdown|Groups"
msgstr ""
......@@ -6324,6 +6327,9 @@ msgstr ""
msgid "Disable group Runners"
msgstr ""
msgid "Disable public access to Pages sites"
msgstr ""
msgid "Disable shared Runners"
msgstr ""
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
describe Gitlab::Pages do
using RSpec::Parameterized::TableSyntax
let(:pages_secret) { SecureRandom.random_bytes(Gitlab::Pages::SECRET_LENGTH) }
before do
......@@ -26,4 +28,24 @@ describe Gitlab::Pages do
expect(described_class.verify_api_request(headers)).to eq([{ "iss" => "gitlab-pages" }, { "alg" => "HS256" }])
end
end
describe '.access_control_is_forced?' do
subject { described_class.access_control_is_forced? }
where(:access_control_is_enabled, :access_control_is_forced, :result) do
false | false | false
false | true | false
true | false | false
true | true | true
end
with_them do
before do
stub_pages_setting(access_control: access_control_is_enabled)
stub_application_setting(force_pages_access_control: access_control_is_forced)
end
it { is_expected.to eq(result) }
end
end
end
......@@ -3,6 +3,8 @@
require 'spec_helper'
describe ProjectFeature do
using RSpec::Parameterized::TableSyntax
let(:project) { create(:project) }
let(:user) { create(:user) }
......@@ -121,13 +123,14 @@ describe ProjectFeature do
end
context 'public features' do
it "does not allow public for other than pages" do
features = %w(issues wiki builds merge_requests snippets repository)
project_feature = project.project_feature
features = %w(issues wiki builds merge_requests snippets repository)
features.each do |feature|
features.each do |feature|
it "does not allow public access level for #{feature}" do
project_feature = project.project_feature
field = "#{feature}_access_level".to_sym
project_feature.update_attribute(field, ProjectFeature::PUBLIC)
expect(project_feature.valid?).to be_falsy
end
end
......@@ -158,12 +161,13 @@ describe ProjectFeature do
end
describe 'default pages access level' do
subject { project.project_feature.pages_access_level }
subject { project_feature.pages_access_level }
before do
let(:project_feature) do
# project factory overrides all values in project_feature after creation
project.project_feature.destroy!
project.build_project_feature.save!
project.project_feature
end
context 'when new project is private' do
......@@ -182,6 +186,14 @@ describe ProjectFeature do
let(:project) { create(:project, :public) }
it { is_expected.to eq(ProjectFeature::ENABLED) }
context 'when access control is forced on the admin level' do
before do
allow(::Gitlab::Pages).to receive(:access_control_is_forced?).and_return(true)
end
it { is_expected.to eq(ProjectFeature::PRIVATE) }
end
end
end
......@@ -189,53 +201,59 @@ describe ProjectFeature do
it 'returns true if Pages access controll is not enabled' do
stub_config(pages: { access_control: false })
project_feature = described_class.new
project_feature = described_class.new(pages_access_level: described_class::PRIVATE)
expect(project_feature.public_pages?).to eq(true)
end
context 'Pages access control is enabled' do
context 'when Pages access control is enabled' do
before do
stub_config(pages: { access_control: true })
end
it 'returns true if Pages access level is public' do
project_feature = described_class.new(pages_access_level: described_class::PUBLIC)
expect(project_feature.public_pages?).to eq(true)
where(:project_visibility, :pages_access_level, :result) do
:private | ProjectFeature::PUBLIC | true
:internal | ProjectFeature::PUBLIC | true
:internal | ProjectFeature::ENABLED | false
:public | ProjectFeature::ENABLED | true
:private | ProjectFeature::PRIVATE | false
:public | ProjectFeature::PRIVATE | false
end
it 'returns true if Pages access level is enabled and the project is public' do
project = build(:project, :public)
project_feature = described_class.new(project: project, pages_access_level: described_class::ENABLED)
expect(project_feature.public_pages?).to eq(true)
end
with_them do
let(:project_feature) do
project = build(:project, project_visibility)
project_feature = project.project_feature
project_feature.update!(pages_access_level: pages_access_level)
project_feature
end
it 'returns false if pages or the project are not public' do
project = build(:project, :private)
it 'properly handles project and Pages visibility settings' do
expect(project_feature.public_pages?).to eq(result)
end
project_feature = described_class.new(project: project, pages_access_level: described_class::ENABLED)
it 'returns false if access_control is forced on the admin level' do
stub_application_setting(force_pages_access_control: true)
expect(project_feature.public_pages?).to eq(false)
expect(project_feature.public_pages?).to eq(false)
end
end
end
end
describe '#private_pages?' do
subject(:project_feature) { described_class.new }
describe '#private_pages?' do
subject(:project_feature) { described_class.new }
it 'returns false if public_pages? is true' do
expect(project_feature).to receive(:public_pages?).and_return(true)
it 'returns false if public_pages? is true' do
expect(project_feature).to receive(:public_pages?).and_return(true)
expect(project_feature.private_pages?).to eq(false)
end
expect(project_feature.private_pages?).to eq(false)
end
it 'returns true if public_pages? is false' do
expect(project_feature).to receive(:public_pages?).and_return(false)
it 'returns true if public_pages? is false' do
expect(project_feature).to receive(:public_pages?).and_return(false)
expect(project_feature.private_pages?).to eq(true)
end
expect(project_feature.private_pages?).to eq(true)
end
end
......
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