Commit c84b60b1 authored by Tuomo Ala-Vannesluoma's avatar Tuomo Ala-Vannesluoma Committed by Nick Thomas

Make GitLab pages support access control

parent c972f2e4
...@@ -52,6 +52,21 @@ ...@@ -52,6 +52,21 @@
required: false, required: false,
default: '', default: '',
}, },
pagesAvailable: {
type: Boolean,
required: false,
default: false,
},
pagesAccessControlEnabled: {
type: Boolean,
required: false,
default: false,
},
pagesHelpPath: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
...@@ -64,6 +79,7 @@ ...@@ -64,6 +79,7 @@
buildsAccessLevel: 20, buildsAccessLevel: 20,
wikiAccessLevel: 20, wikiAccessLevel: 20,
snippetsAccessLevel: 20, snippetsAccessLevel: 20,
pagesAccessLevel: 20,
containerRegistryEnabled: true, containerRegistryEnabled: true,
lfsEnabled: true, lfsEnabled: true,
requestAccessEnabled: true, requestAccessEnabled: true,
...@@ -90,6 +106,13 @@ ...@@ -90,6 +106,13 @@
); );
}, },
pagesFeatureAccessLevelOptions() {
if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
return this.featureAccessLevelOptions.concat([[30, 'Everyone']]);
}
return this.featureAccessLevelOptions;
},
repositoryEnabled() { repositoryEnabled() {
return this.repositoryAccessLevel > 0; return this.repositoryAccessLevel > 0;
}, },
...@@ -109,6 +132,10 @@ ...@@ -109,6 +132,10 @@
this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel); this.buildsAccessLevel = Math.min(10, this.buildsAccessLevel);
this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel); this.wikiAccessLevel = Math.min(10, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(10, this.snippetsAccessLevel); 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(); this.highlightChanges();
} else if (oldValue === visibilityOptions.PRIVATE) { } else if (oldValue === visibilityOptions.PRIVATE) {
// if changing away from private, make enabled features more permissive // if changing away from private, make enabled features more permissive
...@@ -118,6 +145,7 @@ ...@@ -118,6 +145,7 @@
if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20; if (this.buildsAccessLevel > 0) this.buildsAccessLevel = 20;
if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20; if (this.wikiAccessLevel > 0) this.wikiAccessLevel = 20;
if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20; if (this.snippetsAccessLevel > 0) this.snippetsAccessLevel = 20;
if (this.pagesAccessLevel === 10) this.pagesAccessLevel = 20;
this.highlightChanges(); this.highlightChanges();
} }
}, },
...@@ -323,6 +351,18 @@ ...@@ -323,6 +351,18 @@
name="project[project_feature_attributes][snippets_access_level]" name="project[project_feature_attributes][snippets_access_level]"
/> />
</project-setting-row> </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>
</div> </div>
</template> </template>
...@@ -366,6 +366,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -366,6 +366,7 @@ class ProjectsController < Projects::ApplicationController
repository_access_level repository_access_level
snippets_access_level snippets_access_level
wiki_access_level wiki_access_level
pages_access_level
] ]
] ]
end end
......
...@@ -16,7 +16,7 @@ module Types ...@@ -16,7 +16,7 @@ module Types
:create_deployment, :push_to_delete_protected_branch, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki, :admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
:create_pages, :destroy_pages :create_pages, :destroy_pages, :read_pages_content
end end
end end
end end
...@@ -454,6 +454,7 @@ module ProjectsHelper ...@@ -454,6 +454,7 @@ module ProjectsHelper
buildsAccessLevel: feature.builds_access_level, buildsAccessLevel: feature.builds_access_level,
wikiAccessLevel: feature.wiki_access_level, wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level, snippetsAccessLevel: feature.snippets_access_level,
pagesAccessLevel: feature.pages_access_level,
containerRegistryEnabled: !!project.container_registry_enabled, containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled lfsEnabled: !!project.lfs_enabled
} }
...@@ -468,7 +469,10 @@ module ProjectsHelper ...@@ -468,7 +469,10 @@ module ProjectsHelper
registryAvailable: Gitlab.config.registry.enabled, registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'), registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled, 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 end
......
...@@ -55,8 +55,8 @@ class Project < ActiveRecord::Base ...@@ -55,8 +55,8 @@ class Project < ActiveRecord::Base
cache_markdown_field :description, pipeline: :description cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?, delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
:merge_requests_enabled?, :issues_enabled?, to: :project_feature, :merge_requests_enabled?, :issues_enabled?, :pages_enabled?, :public_pages?,
allow_nil: true to: :project_feature, allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage delegate :base_dir, :disk_path, :ensure_storage_path_exists, to: :storage
...@@ -356,7 +356,7 @@ class Project < ActiveRecord::Base ...@@ -356,7 +356,7 @@ class Project < ActiveRecord::Base
# "enabled" here means "not disabled". It includes private features! # "enabled" here means "not disabled". It includes private features!
scope :with_feature_enabled, ->(feature) { scope :with_feature_enabled, ->(feature) {
access_level_attribute = ProjectFeature.access_level_attribute(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. # Picks a feature where the level is exactly that given.
...@@ -418,15 +418,15 @@ class Project < ActiveRecord::Base ...@@ -418,15 +418,15 @@ class Project < ActiveRecord::Base
end end
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 # 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 # This method uses an optimised version of `with_feature_access_level` for
# logged in users to more efficiently get private projects with the given # logged in users to more efficiently get private projects with the given
# feature. # feature.
def self.with_feature_available_for_user(feature, user) def self.with_feature_available_for_user(feature, user)
visible = [nil, ProjectFeature::ENABLED] visible = [nil, ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin? if user&.admin?
with_feature_enabled(feature) with_feature_enabled(feature)
......
...@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base ...@@ -13,14 +13,16 @@ class ProjectFeature < ActiveRecord::Base
# Disabled: not enabled for anyone # Disabled: not enabled for anyone
# Private: enabled only for team members # Private: enabled only for team members
# Enabled: enabled for everyone able to access the project # Enabled: enabled for everyone able to access the project
# Public: enabled for everyone (only allowed for pages)
# #
# Permission levels # Permission levels
DISABLED = 0 DISABLED = 0
PRIVATE = 10 PRIVATE = 10
ENABLED = 20 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 class << self
def access_level_attribute(feature) def access_level_attribute(feature)
...@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base ...@@ -46,6 +48,7 @@ class ProjectFeature < ActiveRecord::Base
validates :project, presence: true validates :project, presence: true
validate :repository_children_level validate :repository_children_level
validate :allowed_access_levels
default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_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 ...@@ -81,6 +84,16 @@ class ProjectFeature < ActiveRecord::Base
issues_access_level > DISABLED issues_access_level > DISABLED
end 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 private
# Validates builds and merge requests access level # Validates builds and merge requests access level
...@@ -95,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base ...@@ -95,6 +108,17 @@ class ProjectFeature < ActiveRecord::Base
%i(merge_requests_access_level builds_access_level).each(&validator) %i(merge_requests_access_level builds_access_level).each(&validator)
end 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) def get_permission(user, level)
case level case level
when DISABLED when DISABLED
...@@ -103,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base ...@@ -103,6 +127,8 @@ class ProjectFeature < ActiveRecord::Base
user && (project.team.member?(user) || user.full_private_access?) user && (project.team.member?(user) || user.full_private_access?)
when ENABLED when ENABLED
true true
when PUBLIC
true
else else
true true
end end
......
...@@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy ...@@ -110,6 +110,7 @@ class ProjectPolicy < BasePolicy
snippets snippets
wiki wiki
builds builds
pages
] ]
features.each do |f| features.each do |f|
...@@ -167,6 +168,7 @@ class ProjectPolicy < BasePolicy ...@@ -167,6 +168,7 @@ class ProjectPolicy < BasePolicy
enable :upload_file enable :upload_file
enable :read_cycle_analytics enable :read_cycle_analytics
enable :award_emoji enable :award_emoji
enable :read_pages_content
end end
# These abilities are not allowed to admins that are not members of the project, # These abilities are not allowed to admins that are not members of the project,
...@@ -286,6 +288,8 @@ class ProjectPolicy < BasePolicy ...@@ -286,6 +288,8 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:merge_request)) prevent(*create_read_update_admin_destroy(:merge_request))
end end
rule { pages_disabled }.prevent :read_pages_content
rule { issues_disabled & merge_requests_disabled }.policy do rule { issues_disabled & merge_requests_disabled }.policy do
prevent(*create_read_update_admin_destroy(:label)) prevent(*create_read_update_admin_destroy(:label))
prevent(*create_read_update_admin_destroy(:milestone)) prevent(*create_read_update_admin_destroy(:milestone))
...@@ -345,6 +349,7 @@ class ProjectPolicy < BasePolicy ...@@ -345,6 +349,7 @@ class ProjectPolicy < BasePolicy
enable :download_code enable :download_code
enable :download_wiki_code enable :download_wiki_code
enable :read_cycle_analytics enable :read_cycle_analytics
enable :read_pages_content
# NOTE: may be overridden by IssuePolicy # NOTE: may be overridden by IssuePolicy
enable :read_issue enable :read_issue
......
...@@ -21,7 +21,9 @@ module Projects ...@@ -21,7 +21,9 @@ module Projects
def pages_config def pages_config
{ {
domains: pages_domains_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 end
...@@ -31,7 +33,9 @@ module Projects ...@@ -31,7 +33,9 @@ module Projects
domain: domain.domain, domain: domain.domain,
certificate: domain.certificate, certificate: domain.certificate,
key: domain.key, 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
end end
......
...@@ -72,7 +72,11 @@ module Projects ...@@ -72,7 +72,11 @@ module Projects
system_hook_service.execute_hooks_for(project, :update) system_hook_service.execute_hooks_for(project, :update)
end 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 end
def update_failed! def update_failed!
...@@ -102,6 +106,10 @@ module Projects ...@@ -102,6 +106,10 @@ module Projects
params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED params.dig(:project_feature_attributes, :wiki_access_level).to_i > ProjectFeature::DISABLED
end end
def changing_pages_access_level?
params.dig(:project_feature_attributes, :pages_access_level)
end
def ensure_wiki_exists def ensure_wiki_exists
ProjectWiki.new(project, project.owner).wiki ProjectWiki.new(project, project.owner).wiki
rescue ProjectWiki::CouldNotCreateWikiError rescue ProjectWiki::CouldNotCreateWikiError
......
---
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
...@@ -210,6 +210,7 @@ production: &base ...@@ -210,6 +210,7 @@ production: &base
## GitLab Pages ## GitLab Pages
pages: pages:
enabled: false enabled: false
access_control: false
# The location where pages are stored (default: shared/pages). # The location where pages are stored (default: shared/pages).
# path: shared/pages # path: shared/pages
......
...@@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path ...@@ -200,6 +200,7 @@ Settings.registry['path'] = Settings.absolute(Settings.registry['path
# #
Settings['pages'] ||= Settingslogic.new({}) Settings['pages'] ||= Settingslogic.new({})
Settings.pages['enabled'] = false if Settings.pages['enabled'].nil? 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['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil? Settings.pages['https'] = false if Settings.pages['https'].nil?
Settings.pages['host'] ||= "example.com" Settings.pages['host'] ||= "example.com"
......
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
...@@ -1578,6 +1578,7 @@ ActiveRecord::Schema.define(version: 20180924141949) do ...@@ -1578,6 +1578,7 @@ ActiveRecord::Schema.define(version: 20180924141949) do
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false t.integer "repository_access_level", default: 20, null: false
t.integer "pages_access_level", default: 20, null: false
end end
add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
......
...@@ -110,6 +110,7 @@ which visibility level you select on project settings. ...@@ -110,6 +110,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone - Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal - 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 with access: everyone can see depending on your project visibility level
- Everyone: enabled for everyone (only available for GitLab Pages)
### Protected branches ### Protected branches
...@@ -242,6 +243,7 @@ which visibility level you select on project settings. ...@@ -242,6 +243,7 @@ which visibility level you select on project settings.
- Disabled: disabled for everyone - Disabled: disabled for everyone
- Only team members: only team members will see even if your project is public or internal - 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 with access: everyone can see depending on your project visibility level
- Everyone: enabled for everyone (only available for GitLab Pages)
## GitLab CI/CD permissions ## GitLab CI/CD permissions
......
...@@ -287,6 +287,12 @@ module API ...@@ -287,6 +287,12 @@ module API
present_projects forks present_projects forks
end 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 desc 'Update an existing project' do
success Entities::Project success Entities::Project
end end
......
require_relative '../support/helpers/test_env' require_relative '../support/helpers/test_env'
FactoryBot.define do FactoryBot.define do
PAGES_ACCESS_LEVEL_SCHEMA_VERSION = 20180423204600
# Project without repository # Project without repository
# #
# Project does not have bare repository. # Project does not have bare repository.
...@@ -23,6 +25,7 @@ FactoryBot.define do ...@@ -23,6 +25,7 @@ FactoryBot.define do
issues_access_level ProjectFeature::ENABLED issues_access_level ProjectFeature::ENABLED
merge_requests_access_level ProjectFeature::ENABLED merge_requests_access_level ProjectFeature::ENABLED
repository_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 # 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
...@@ -34,13 +37,20 @@ FactoryBot.define do ...@@ -34,13 +37,20 @@ FactoryBot.define do
builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min 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 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, wiki_access_level: evaluator.wiki_access_level,
builds_access_level: builds_access_level, builds_access_level: builds_access_level,
snippets_access_level: evaluator.snippets_access_level, snippets_access_level: evaluator.snippets_access_level,
issues_access_level: evaluator.issues_access_level, issues_access_level: evaluator.issues_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
}
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 # Normally the class Projects::CreateService is used for creating
# projects, and this class takes care of making sure the owner and current # projects, and this class takes care of making sure the owner and current
...@@ -244,6 +254,10 @@ FactoryBot.define do ...@@ -244,6 +254,10 @@ FactoryBot.define do
trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED } trait(:repository_enabled) { repository_access_level ProjectFeature::ENABLED }
trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED } trait(:repository_disabled) { repository_access_level ProjectFeature::DISABLED }
trait(:repository_private) { repository_access_level ProjectFeature::PRIVATE } 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 trait :auto_devops do
association :auto_devops, factory: :project_auto_devops association :auto_devops, factory: :project_auto_devops
......
...@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do ...@@ -10,7 +10,7 @@ describe Types::PermissionTypes::Project do
:read_commit_status, :request_access, :create_pipeline, :create_pipeline_schedule, :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, :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, :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) expect(described_class).to have_graphql_fields(expected_permissions)
......
...@@ -492,6 +492,7 @@ ProjectFeature: ...@@ -492,6 +492,7 @@ ProjectFeature:
- snippets_access_level - snippets_access_level
- builds_access_level - builds_access_level
- repository_access_level - repository_access_level
- pages_access_level
- created_at - created_at
- updated_at - updated_at
ProtectedBranch::MergeAccessLevel: ProtectedBranch::MergeAccessLevel:
......
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
...@@ -65,7 +65,8 @@ describe InternalId do ...@@ -65,7 +65,8 @@ describe InternalId do
context 'with an insufficient schema version' do context 'with an insufficient schema version' do
before do before do
described_class.reset_column_information 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 end
let(:init) { double('block') } let(:init) { double('block') }
......
...@@ -17,7 +17,7 @@ describe ProjectFeature do ...@@ -17,7 +17,7 @@ describe ProjectFeature do
end end
describe '#feature_available?' do 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 context 'when features are disabled' do
it "returns false" do it "returns false" do
...@@ -112,6 +112,19 @@ describe ProjectFeature do ...@@ -112,6 +112,19 @@ describe ProjectFeature do
end end
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 describe '#*_enabled?' do
let(:features) { %w(wiki builds merge_requests) } let(:features) { %w(wiki builds merge_requests) }
......
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
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
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
...@@ -340,6 +340,27 @@ describe Projects::UpdateService do ...@@ -340,6 +340,27 @@ describe Projects::UpdateService do
call_service call_service
end end
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 end
describe '#run_auto_devops_pipeline?' do describe '#run_auto_devops_pipeline?' do
......
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