summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuomo Ala-Vannesluoma <tuomoav@gmail.com>2018-10-05 13:41:11 +0000
committerNick Thomas <nick@gitlab.com>2018-10-05 13:41:11 +0000
commitc84b60b1645950a30fdbc37c9065a200dc750d90 (patch)
tree76d523f37481fa1422f63e96e2a1777d48060b9c
parentc972f2e459a6b45852a3d4e76566cdf772a6764a (diff)
downloadgitlab-ce-c84b60b1645950a30fdbc37c9065a200dc750d90.tar.gz
Make GitLab pages support access control
-rw-r--r--GITLAB_PAGES_VERSION2
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue40
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/graphql/types/permission_types/project.rb2
-rw-r--r--app/helpers/projects_helper.rb6
-rw-r--r--app/models/project.rb12
-rw-r--r--app/models/project_feature.rb28
-rw-r--r--app/policies/project_policy.rb5
-rw-r--r--app/services/projects/update_pages_configuration_service.rb8
-rw-r--r--app/services/projects/update_service.rb10
-rw-r--r--changelogs/unreleased/auth.yml5
-rw-r--r--config/gitlab.yml.example1
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb16
-rw-r--r--db/schema.rb1
-rw-r--r--doc/user/permissions.md2
-rw-r--r--lib/api/projects.rb6
-rw-r--r--spec/factories/projects.rb18
-rw-r--r--spec/graphql/types/permission_types/project_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/migrations/add_pages_access_level_to_project_feature_spec.rb30
-rw-r--r--spec/models/internal_id_spec.rb3
-rw-r--r--spec/models/project_feature_spec.rb15
-rw-r--r--spec/requests/api/pages/internal_access_spec.rb88
-rw-r--r--spec/requests/api/pages/private_access_spec.rb88
-rw-r--r--spec/requests/api/pages/public_access_spec.rb88
-rw-r--r--spec/services/projects/update_service_spec.rb21
27 files changed, 482 insertions, 18 deletions
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 9084fa2f716..26aaba0e866 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.1.0
+1.2.0
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 875f6928bed..a16f7e6b77c 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -52,6 +52,21 @@
required: false,
default: '',
},
+ pagesAvailable: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesAccessControlEnabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pagesHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
@@ -64,6 +79,7 @@
buildsAccessLevel: 20,
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
+ pagesAccessLevel: 20,
containerRegistryEnabled: true,
lfsEnabled: true,
requestAccessEnabled: true,
@@ -90,6 +106,13 @@
);
},
+ pagesFeatureAccessLevelOptions() {
+ if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
+ return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
+ }
+ return this.featureAccessLevelOptions;
+ },
+
repositoryEnabled() {
return this.repositoryAccessLevel > 0;
},
@@ -109,6 +132,10 @@
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel);
+ if (this.pagesAccessLevel === 20) {
+ // When from Internal->Private narrow access for only members
+ this.pagesAccessLevel = 10;
+ }
this.highlightChanges();
} else if (oldValue === visibilityOptions.PRIVATE) {
// if changing away from private, make enabled features more permissive
@@ -118,6 +145,7 @@
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
+ if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
this.highlightChanges();
}
},
@@ -323,6 +351,18 @@
name="project[project_feature_attributes][snippets_access_level]"
/>
</project-setting-row>
+ <project-setting-row
+ v-if="pagesAvailable && pagesAccessControlEnabled"
+ :help-path="pagesHelpPath"
+ label="Pages"
+ help-text="Static website for the project."
+ >
+ <project-feature-setting
+ v-model="pagesAccessLevel"
+ :options="pagesFeatureAccessLevelOptions"
+ name="project[project_feature_attributes][pages_access_level]"
+ />
+ </project-setting-row>
</div>
</div>
</template>
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a9417369ca2..ee438e160f2 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -366,6 +366,7 @@ class ProjectsController < Projects::ApplicationController
repository_access_level
snippets_access_level
wiki_access_level
+ pages_access_level
]
]
end
diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb
index 066ce64a254..ab37c282fe5 100644
--- a/app/graphql/types/permission_types/project.rb
+++ b/app/graphql/types/permission_types/project.rb
@@ -16,7 +16,7 @@ module Types
:create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
- :create_pages, :destroy_pages
+ :create_pages, :destroy_pages, :read_pages_content
end
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 8b17e6ef75d..0016f89db5c 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -454,6 +454,7 @@ module ProjectsHelper
buildsAccessLevel: feature.builds_access_level,
wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level,
+ pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled
}
@@ -468,7 +469,10 @@ module ProjectsHelper
registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled,
- lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
+ pagesAvailable: Gitlab.config.pages.enabled,
+ pagesAccessControlEnabled: Gitlab.config.pages.access_control,
+ pagesHelpPath: help_page_path('user/project/pages/index.md')
}
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 59f088156c7..dc2732cc6c2 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -55,8 +55,8 @@ class Project < ActiveRecord::Base
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
- :merge_requests_enabled?, :issues_enabled?, to: :project_feature,
- allow_nil: true
+ :merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
+ to: :project_feature, allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
@@ -356,7 +356,7 @@ class Project < ActiveRecord::Base
# "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) {
access_level_attribute = ProjectFeature.access_level_attribute(feature)
- with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED] })
+ with_project_feature.where(project_features: { access_level_attribute => [nil, ProjectFeature::PRIVATE, ProjectFeature::ENABLED, ProjectFeature::PUBLIC] })
}
# Picks a feature where the level is exactly that given.
@@ -418,15 +418,15 @@ class Project < ActiveRecord::Base
end
end
- # project features may be "disabled", "internal" or "enabled". If "internal",
+ # project features may be "disabled", "internal", "enabled" or "public". If "internal",
# they are only available to team members. This scope returns projects where
- # the feature is either enabled, or internal with permission for the user.
+ # the feature is either public, enabled, or internal with permission for the user.
#
# This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given
# feature.
def self.with_feature_available_for_user(feature, user)
- visible = [nil, ProjectFeature::ENABLED]
+ visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin?
with_feature_enabled(feature)
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 754c2461d23..39f2b8fe0de 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base
# Disabled: not enabled for anyone
# Private: enabled only for team members
# Enabled: enabled for everyone able to access the project
+ # Public: enabled for everyone (only allowed for pages)
#
# Permission levels
DISABLED = 0
PRIVATE = 10
ENABLED = 20
+ PUBLIC = 30
- FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze
+ FEATURES = %i(issues merge_requests wiki snippets builds repository pages).freeze
class << self
def access_level_attribute(feature)
@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
validates :project, presence: true
validate :repository_children_level
+ validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false
@@ -81,6 +84,16 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED
end
+ def pages_enabled?
+ pages_access_level > DISABLED
+ end
+
+ def public_pages?
+ return true unless Gitlab.config.pages.access_control
+
+ pages_access_level == PUBLIC || pages_access_level == ENABLED && project.public?
+ end
+
private
# Validates builds and merge requests access level
@@ -95,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base
%i(merge_requests_access_level builds_access_level).each(&validator)
end
+ # Validates access level for other than pages cannot be PUBLIC
+ def allowed_access_levels
+ validator = lambda do |field|
+ level = public_send(field) || ProjectFeature::ENABLED # rubocop:disable GitlabSecurity/PublicSend
+ not_allowed = level > ProjectFeature::ENABLED
+ self.errors.add(field, "cannot have public visibility level") if not_allowed
+ end
+
+ (FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
+ end
+
def get_permission(user, level)
case level
when DISABLED
@@ -103,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base
user && (project.team.member?(user) || user.full_private_access?)
when ENABLED
true
+ when PUBLIC
+ true
else
true
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index f2c246cd969..a76a083bceb 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy
snippets
wiki
builds
+ pages
]
features.each do |f|
@@ -167,6 +168,7 @@ class ProjectPolicy < BasePolicy
enable :upload_file
enable :read_cycle_analytics
enable :award_emoji
+ enable :read_pages_content
end
# These abilities are not allowed to admins that are not members of the project,
@@ -286,6 +288,8 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:merge_request))
end
+ rule { pages_disabled }.prevent :read_pages_content
+
rule { issues_disabled & merge_requests_disabled }.policy do
prevent(*create_read_update_admin_destroy(:label))
prevent(*create_read_update_admin_destroy(:milestone))
@@ -345,6 +349,7 @@ class ProjectPolicy < BasePolicy
enable :download_code
enable :download_wiki_code
enable :read_cycle_analytics
+ enable :read_pages_content
# NOTE: may be overridden by IssuePolicy
enable :read_issue
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index efbd4c7b323..abf40b3ad7a 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -21,7 +21,9 @@ module Projects
def pages_config
{
domains: pages_domains_config,
- https_only: project.pages_https_only?
+ https_only: project.pages_https_only?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
@@ -31,7 +33,9 @@ module Projects
domain: domain.domain,
certificate: domain.certificate,
key: domain.key,
- https_only: project.pages_https_only? && domain.https?
+ https_only: project.pages_https_only? && domain.https?,
+ id: project.project_id,
+ access_control: !project.public_pages?
}
end
end
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index d6d9bacf232..f25a4e30938 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -72,7 +72,11 @@ module Projects
system_hook_service.execute_hooks_for(project, :update)
end
- update_pages_config if changing_pages_https_only?
+ update_pages_config if changing_pages_related_config?
+ end
+
+ def changing_pages_related_config?
+ changing_pages_https_only? || changing_pages_access_level?
end
def update_failed!
@@ -102,6 +106,10 @@ module Projects
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end
+ def changing_pages_access_level?
+ params.dig(:project_feature_attributes, :pages_access_level)
+ end
+
def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError
diff --git a/changelogs/unreleased/auth.yml b/changelogs/unreleased/auth.yml
new file mode 100644
index 00000000000..cd4bbf0059e
--- /dev/null
+++ b/changelogs/unreleased/auth.yml
@@ -0,0 +1,5 @@
+---
+title: Add access control to GitLab pages and make it possible to enable/disable it in project settings
+merge_request: 18589
+author: Tuomo Ala-Vannesluoma
+type: added
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 67337f4b82f..749cdd0f869 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -210,6 +210,7 @@ production: &base
## GitLab Pages
pages:
enabled: false
+ access_control: false
# The location where pages are stored (default: shared/pages).
# path: shared/pages
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 0caa4962128..bd02b85c7ce 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
#
Settings['pages'] ||= Settingslogic.new({})
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
+Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com"
diff --git a/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
new file mode 100644
index 00000000000..1d2f8cf9c76
--- /dev/null
+++ b/db/migrate/20180423204600_add_pages_access_level_to_project_feature.rb
@@ -0,0 +1,16 @@
+class AddPagesAccessLevelToProjectFeature < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:project_features, :pages_access_level, :integer, default: ProjectFeature::PUBLIC, allow_null: false)
+
+ change_column_default(:project_features, :pages_access_level, ProjectFeature::ENABLED)
+ end
+
+ def down
+ remove_column :project_features, :pages_access_level
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b3d4badaf82..7d78756c16f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1578,6 +1578,7 @@ ActiveRecord::Schema.define(version: 20180924141949) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false
+ t.integer "pages_access_level", default: 20, null: false
end
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 8369cff2386..4a1ca41ab97 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -110,6 +110,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
### Protected branches
@@ -242,6 +243,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal
- Everyone with access: everyone can see depending on your project visibility level
+- Everyone: enabled for everyone (only available for GitLab Pages)
## GitLab CI/CD permissions
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 00bad49ebdc..ae2d327e45b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -287,6 +287,12 @@ module API
present_projects forks
end
+ desc 'Check pages access of this project'
+ get ':id/pages_access' do
+ authorize! :read_pages_content, user_project unless user_project.public_pages?
+ status 200
+ end
+
desc 'Update an existing project' do
success Entities::Project
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 80801eb1082..e4823a5adf1 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,6 +1,8 @@
require_relative '../support/helpers/test_env'
FactoryBot.define do
+ PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
+
# Project without repository
#
# Project does not have bare repository.
@@ -23,6 +25,7 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED
repository_access_level ProjectFeature::ENABLED
+ pages_access_level ProjectFeature::ENABLED
# we can't assign the delegated `#ci_cd_settings` attributes directly, as the
# `#ci_cd_settings` relation needs to be created first
@@ -34,13 +37,20 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
- project.project_feature.update(
+ hash = {
wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level,
issues_access_level: evaluator.issues_access_level,
merge_requests_access_level: merge_requests_access_level,
- repository_access_level: evaluator.repository_access_level)
+ repository_access_level: evaluator.repository_access_level
+ }
+
+ if ActiveRecord::Migrator.current_version >= PAGES_ACCESS_LEVEL_SCHEMA_VERSION
+ hash.store("pages_access_level", evaluator.pages_access_level)
+ end
+
+ project.project_feature.update(hash)
# Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current
@@ -244,6 +254,10 @@ FactoryBot.define do
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE }
+ trait(:pages_public) { pages_access_level ProjectFeature::PUBLIC }
+ trait(:pages_enabled) { pages_access_level ProjectFeature::ENABLED }
+ trait(:pages_disabled) { pages_access_level ProjectFeature::DISABLED }
+ trait(:pages_private) { pages_access_level ProjectFeature::PRIVATE }
trait :auto_devops do
association :auto_devops, factory: :project_auto_devops
diff --git a/spec/graphql/types/permission_types/project_spec.rb b/spec/graphql/types/permission_types/project_spec.rb
index 89eecef096e..927153adc5b 100644
--- a/spec/graphql/types/permission_types/project_spec.rb
+++ b/spec/graphql/types/permission_types/project_spec.rb
@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule,
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
- :update_wiki, :destroy_wiki, :create_pages, :destroy_pages
+ :update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content
]
expect(described_class).to have_graphql_fields(expected_permissions)
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index e9f1be172b0..7be1bf6e0bf 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -492,6 +492,7 @@ ProjectFeature:
- snippets_access_level
- builds_access_level
- repository_access_level
+- pages_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
diff --git a/spec/migrations/add_pages_access_level_to_project_feature_spec.rb b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
new file mode 100644
index 00000000000..3946602c5be
--- /dev/null
+++ b/spec/migrations/add_pages_access_level_to_project_feature_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180423204600_add_pages_access_level_to_project_feature.rb')
+
+describe AddPagesAccessLevelToProjectFeature, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:features) { table(:project_features) }
+ let!(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab') }
+ let!(:first_project) { projects.create(name: 'gitlab1', path: 'gitlab1', namespace_id: namespace.id) }
+ let!(:first_project_features) { features.create(project_id: first_project.id) }
+ let!(:second_project) { projects.create(name: 'gitlab2', path: 'gitlab2', namespace_id: namespace.id) }
+ let!(:second_project_features) { features.create(project_id: second_project.id) }
+
+ it 'correctly migrate pages for old projects to be public' do
+ migrate!
+
+ # For old projects pages should be public
+ expect(first_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ expect(second_project_features.reload.pages_access_level).to eq ProjectFeature::PUBLIC
+ end
+
+ it 'after migration pages are enabled as default' do
+ migrate!
+
+ # For new project default is enabled
+ third_project = projects.create(name: 'gitlab3', path: 'gitlab3', namespace_id: namespace.id)
+ third_project_features = features.create(project_id: third_project.id)
+ expect(third_project_features.reload.pages_access_level).to eq ProjectFeature::ENABLED
+ end
+end
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index f2aad455d5f..52c00a74b4b 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -65,7 +65,8 @@ describe InternalId do
context 'with an insufficient schema version' do
before do
described_class.reset_column_information
- expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ # Project factory will also call the current_version
+ expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 2a193864e46..fee7d65c217 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -17,7 +17,7 @@ describe ProjectFeature do
end
describe '#feature_available?' do
- let(:features) { %w(issues wiki builds merge_requests snippets repository) }
+ let(:features) { %w(issues wiki builds merge_requests snippets repository pages) }
context 'when features are disabled' do
it "returns false" do
@@ -112,6 +112,19 @@ describe ProjectFeature do
end
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.each do |feature|
+ field = "#{feature}_access_level".to_sym
+ project_feature.update_attribute(field, ProjectFeature::PUBLIC)
+ expect(project_feature.valid?).to be_falsy
+ end
+ end
+ end
+
describe '#*_enabled?' do
let(:features) { %w(wiki builds merge_requests) }
diff --git a/spec/requests/api/pages/internal_access_spec.rb b/spec/requests/api/pages/internal_access_spec.rb
new file mode 100644
index 00000000000..c41eabe0a48
--- /dev/null
+++ b/spec/requests/api/pages/internal_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Internal Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :internal, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be internal" do
+ describe '#internal?' do
+ subject { project.internal? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/private_access_spec.rb b/spec/requests/api/pages/private_access_spec.rb
new file mode 100644
index 00000000000..d69c15b0477
--- /dev/null
+++ b/spec/requests/api/pages/private_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Private Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :private, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be private" do
+ describe '#private?' do
+ subject { project.private? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 404
+ ProjectFeature::DISABLED | nil | 404
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 404
+ ProjectFeature::PUBLIC | nil | 404
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 404
+ ProjectFeature::ENABLED | nil | 404
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 404
+ ProjectFeature::PRIVATE | nil | 404
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/pages/public_access_spec.rb b/spec/requests/api/pages/public_access_spec.rb
new file mode 100644
index 00000000000..882ca26ac51
--- /dev/null
+++ b/spec/requests/api/pages/public_access_spec.rb
@@ -0,0 +1,88 @@
+require 'spec_helper'
+
+describe "Public Project Pages Access" do
+ using RSpec::Parameterized::TableSyntax
+ include AccessMatchers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, :public, pages_access_level: ProjectFeature::ENABLED, namespace: group) }
+
+ set(:admin) { create(:admin) }
+ set(:owner) { create(:user) }
+ set(:master) { create(:user) }
+ set(:developer) { create(:user) }
+ set(:reporter) { create(:user) }
+ set(:guest) { create(:user) }
+ set(:user) { create(:user) }
+
+ before do
+ allow(Gitlab.config.pages).to receive(:access_control).and_return(true)
+ group.add_owner(owner)
+ project.add_master(master)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ describe "Project should be public" do
+ describe '#public?' do
+ subject { project.public? }
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ describe "GET /projects/:id/pages_access" do
+ context 'access depends on the level' do
+ where(:pages_access_level, :with_user, :expected_result) do
+ ProjectFeature::DISABLED | "admin" | 403
+ ProjectFeature::DISABLED | "owner" | 403
+ ProjectFeature::DISABLED | "master" | 403
+ ProjectFeature::DISABLED | "developer" | 403
+ ProjectFeature::DISABLED | "reporter" | 403
+ ProjectFeature::DISABLED | "guest" | 403
+ ProjectFeature::DISABLED | "user" | 403
+ ProjectFeature::DISABLED | nil | 403
+ ProjectFeature::PUBLIC | "admin" | 200
+ ProjectFeature::PUBLIC | "owner" | 200
+ ProjectFeature::PUBLIC | "master" | 200
+ ProjectFeature::PUBLIC | "developer" | 200
+ ProjectFeature::PUBLIC | "reporter" | 200
+ ProjectFeature::PUBLIC | "guest" | 200
+ ProjectFeature::PUBLIC | "user" | 200
+ ProjectFeature::PUBLIC | nil | 200
+ ProjectFeature::ENABLED | "admin" | 200
+ ProjectFeature::ENABLED | "owner" | 200
+ ProjectFeature::ENABLED | "master" | 200
+ ProjectFeature::ENABLED | "developer" | 200
+ ProjectFeature::ENABLED | "reporter" | 200
+ ProjectFeature::ENABLED | "guest" | 200
+ ProjectFeature::ENABLED | "user" | 200
+ ProjectFeature::ENABLED | nil | 200
+ ProjectFeature::PRIVATE | "admin" | 200
+ ProjectFeature::PRIVATE | "owner" | 200
+ ProjectFeature::PRIVATE | "master" | 200
+ ProjectFeature::PRIVATE | "developer" | 200
+ ProjectFeature::PRIVATE | "reporter" | 200
+ ProjectFeature::PRIVATE | "guest" | 200
+ ProjectFeature::PRIVATE | "user" | 403
+ ProjectFeature::PRIVATE | nil | 403
+ end
+
+ with_them do
+ before do
+ project.project_feature.update(pages_access_level: pages_access_level)
+ end
+ it "correct return value" do
+ if !with_user.nil?
+ user = public_send(with_user)
+ get api("/projects/#{project.id}/pages_access", user)
+ else
+ get api("/projects/#{project.id}/pages_access")
+ end
+
+ expect(response).to have_gitlab_http_status(expected_result)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 695b9980548..d58ff2cedc0 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -340,6 +340,27 @@ describe Projects::UpdateService do
call_service
end
end
+
+ context 'when updating #pages_access_level' do
+ subject(:call_service) do
+ update_project(project, admin, project_feature_attributes: { pages_access_level: ProjectFeature::PRIVATE })
+ end
+
+ it 'updates the attribute' do
+ expect { call_service }
+ .to change { project.project_feature.pages_access_level }
+ .to(ProjectFeature::PRIVATE)
+ end
+
+ it 'calls Projects::UpdatePagesConfigurationService' do
+ expect(Projects::UpdatePagesConfigurationService)
+ .to receive(:new)
+ .with(project)
+ .and_call_original
+
+ call_service
+ end
+ end
end
describe '#run_auto_devops_pipeline?' do