# frozen_string_literal: true module Gitlab module BackgroundMigration # corrects stored pages access level on db depending on project visibility class FixPagesAccessLevel # Copy routable here to avoid relying on application logic module Routable def build_full_path if parent && path parent.build_full_path + '/' + path else path end end end # Namespace class Namespace < ActiveRecord::Base self.table_name = 'namespaces' self.inheritance_column = :_type_disabled include Routable belongs_to :parent, class_name: "Namespace" end # Project class Project < ActiveRecord::Base self.table_name = 'projects' self.inheritance_column = :_type_disabled include Routable belongs_to :namespace alias_method :parent, :namespace alias_attribute :parent_id, :namespace_id PRIVATE = 0 INTERNAL = 10 PUBLIC = 20 def pages_deployed? Dir.exist?(public_pages_path) end def public_pages_path File.join(pages_path, 'public') end def pages_path # TODO: when we migrate Pages to work with new storage types, change here to use disk_path File.join(Settings.pages.path, build_full_path) end end # ProjectFeature class ProjectFeature < ActiveRecord::Base include ::EachBatch self.table_name = 'project_features' belongs_to :project PRIVATE = 10 ENABLED = 20 PUBLIC = 30 end def perform(start_id, stop_id) fix_public_access_level(start_id, stop_id) make_internal_projects_public(start_id, stop_id) fix_private_access_level(start_id, stop_id) end private def access_control_is_enabled @access_control_is_enabled = Gitlab.config.pages.access_control end # Public projects are allowed to have only enabled pages_access_level # which is equivalent to public def fix_public_access_level(start_id, stop_id) project_features(start_id, stop_id, ProjectFeature::PUBLIC, Project::PUBLIC).each_batch do |features| features.update_all(pages_access_level: ProjectFeature::ENABLED) end end # If access control is disabled and project has pages deployed # project will become unavailable when access control will become enabled # we make these projects public to avoid negative surprise to user def make_internal_projects_public(start_id, stop_id) return if access_control_is_enabled project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::INTERNAL).find_each do |project_feature| next unless project_feature.project.pages_deployed? project_feature.update(pages_access_level: ProjectFeature::PUBLIC) end end # Private projects are not allowed to have enabled access level, only `private` and `public` # If access control is enabled, these projects currently behave as if they have `private` pages_access_level # if access control is disabled, these projects currently behave as if they have `public` pages_access_level # so we preserve this behaviour for projects with pages already deployed # for project without pages we always set `private` access_level def fix_private_access_level(start_id, stop_id) project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::PRIVATE).find_each do |project_feature| if access_control_is_enabled project_feature.update!(pages_access_level: ProjectFeature::PRIVATE) else fixed_access_level = project_feature.project.pages_deployed? ? ProjectFeature::PUBLIC : ProjectFeature::PRIVATE project_feature.update!(pages_access_level: fixed_access_level) end end end def project_features(start_id, stop_id, pages_access_level, project_visibility_level) ProjectFeature.where(id: start_id..stop_id).joins(:project) .where(pages_access_level: pages_access_level) .where(projects: { visibility_level: project_visibility_level }) end end end end