diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api/entities.rb | 1 | ||||
-rw-r--r-- | lib/api/helpers/projects_helpers.rb | 5 | ||||
-rw-r--r-- | lib/api/projects.rb | 4 | ||||
-rw-r--r-- | lib/api/users.rb | 8 | ||||
-rw-r--r-- | lib/banzai/filter/ascii_doc_sanitization_filter.rb | 33 | ||||
-rw-r--r-- | lib/banzai/filter/reference_redactor_filter.rb (renamed from lib/banzai/filter/redactor_filter.rb) | 4 | ||||
-rw-r--r-- | lib/banzai/object_renderer.rb | 2 | ||||
-rw-r--r-- | lib/banzai/pipeline/post_process_pipeline.rb | 2 | ||||
-rw-r--r-- | lib/banzai/reference_redactor.rb (renamed from lib/banzai/redactor.rb) | 2 | ||||
-rw-r--r-- | lib/banzai/renderer.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/background_migration/fix_pages_access_level.rb | 128 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml | 2 | ||||
-rw-r--r-- | lib/gitlab/git/repository.rb | 22 | ||||
-rw-r--r-- | lib/gitlab/graphql/representation/submodule_tree_entry.rb | 34 | ||||
-rw-r--r-- | lib/gitlab/submodule_links.rb | 26 | ||||
-rw-r--r-- | lib/gitlab/zoom_link_extractor.rb | 21 | ||||
-rw-r--r-- | lib/tasks/gitlab/features.rake | 14 |
17 files changed, 285 insertions, 25 deletions
diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0a9515f1dd2..494da770279 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -294,7 +294,6 @@ module API expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) } - expose :external_authorization_classification_label expose :auto_devops_enabled?, as: :auto_devops_enabled expose :auto_devops_deploy_strategy do |project, options| project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index 0e21a7a66fd..833e3b9ebaf 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -42,7 +42,6 @@ module API optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" - optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project' optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning' optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled' optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy' @@ -94,7 +93,6 @@ module API :visibility, :wiki_access_level, :avatar, - :external_authorization_classification_label, # TODO: remove in API v5, replaced by *_access_level :issues_enabled, @@ -105,6 +103,9 @@ module API :snippets_enabled ] end + + def filter_attributes_using_license!(attrs) + end end end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index a7d62014509..0923d31f5ff 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -145,6 +145,7 @@ module API post do attrs = declared_params(include_missing: false) attrs = translate_params_for_compatibility(attrs) + filter_attributes_using_license!(attrs) project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? @@ -179,6 +180,7 @@ module API attrs = declared_params(include_missing: false) attrs = translate_params_for_compatibility(attrs) + filter_attributes_using_license!(attrs) project = ::Projects::CreateService.new(user, attrs).execute if project.saved? @@ -292,7 +294,7 @@ module API authorize! :change_visibility_level, user_project if attrs[:visibility].present? attrs = translate_params_for_compatibility(attrs) - + filter_attributes_using_license!(attrs) verify_update_project_attrs!(user_project, attrs) result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute diff --git a/lib/api/users.rb b/lib/api/users.rb index 30a278fdff1..a4ac5b629b8 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -148,7 +148,7 @@ module API end desc 'Create a user. Available only for admins.' do - success Entities::UserPublic + success Entities::UserWithAdmin end params do requires :email, type: String, desc: 'The email of the user' @@ -168,7 +168,7 @@ module API user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true) if user.persisted? - present user, with: Entities::UserPublic, current_user: current_user + present user, with: Entities::UserWithAdmin, current_user: current_user else conflict!('Email has already been taken') if User .by_any_email(user.email.downcase) @@ -183,7 +183,7 @@ module API end desc 'Update a user. Available only for admins.' do - success Entities::UserPublic + success Entities::UserWithAdmin end params do requires :id, type: Integer, desc: 'The ID of the user' @@ -215,7 +215,7 @@ module API result = ::Users::UpdateService.new(current_user, user_params.merge(user: user)).execute if result[:status] == :success - present user, with: Entities::UserPublic, current_user: current_user + present user, with: Entities::UserWithAdmin, current_user: current_user else render_validation_error!(user) end diff --git a/lib/banzai/filter/ascii_doc_sanitization_filter.rb b/lib/banzai/filter/ascii_doc_sanitization_filter.rb index d8d63075752..9105e86ad04 100644 --- a/lib/banzai/filter/ascii_doc_sanitization_filter.rb +++ b/lib/banzai/filter/ascii_doc_sanitization_filter.rb @@ -8,12 +8,18 @@ module Banzai class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter # Section anchor link pattern SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze + SECTION_HEADINGS = %w(h2 h3 h4 h5 h6).freeze + + # Footnote link patterns + FOOTNOTE_LINK_ID_PATTERNS = { + a: /\A_footnoteref_\d+\z/, + div: /\A_footnotedef_\d+\z/ + }.freeze # Classes used by Asciidoctor to style components ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze CALLOUT_CLASSES = ['conum'].freeze CHECKLIST_CLASSES = %w(fa fa-check-square-o fa-square-o).freeze - LIST_CLASSES = %w(checklist none no-bullet unnumbered unstyled).freeze ELEMENT_CLASSES_WHITELIST = { @@ -26,8 +32,6 @@ module Banzai a: ['anchor'].freeze }.freeze - ALLOWED_HEADERS = %w(h2 h3 h4 h5 h6).freeze - def customize_whitelist(whitelist) # Allow marks whitelist[:elements].push('mark') @@ -44,20 +48,39 @@ module Banzai whitelist[:transformers].push(self.class.remove_element_classes) # Allow `id` in heading elements for section anchors - ALLOWED_HEADERS.each do |header| + SECTION_HEADINGS.each do |header| whitelist[:attributes][header] = %w(id) end whitelist[:transformers].push(self.class.remove_non_heading_ids) + # Allow `id` in footnote elements + FOOTNOTE_LINK_ID_PATTERNS.keys.each do |element| + whitelist[:attributes][element.to_s].push('id') + end + whitelist[:transformers].push(self.class.remove_non_footnote_ids) + whitelist end class << self + def remove_non_footnote_ids + lambda do |env| + node = env[:node] + + return unless (pattern = FOOTNOTE_LINK_ID_PATTERNS[node.name.to_sym]) + return unless node.has_attribute?('id') + + return if node['id'] =~ pattern + + node.remove_attribute('id') + end + end + def remove_non_heading_ids lambda do |env| node = env[:node] - return unless ALLOWED_HEADERS.any?(node.name) + return unless SECTION_HEADINGS.any?(node.name) return unless node.has_attribute?('id') return if node['id'] =~ SECTION_LINK_REF_PATTERN diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/reference_redactor_filter.rb index 1f091f594f8..485d3fd5fc7 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/reference_redactor_filter.rb @@ -7,12 +7,12 @@ module Banzai # # Expected to be run in its own post-processing pipeline. # - class RedactorFilter < HTML::Pipeline::Filter + class ReferenceRedactorFilter < HTML::Pipeline::Filter def call unless context[:skip_redaction] context = RenderContext.new(project, current_user) - Redactor.new(context).redact([doc]) + ReferenceRedactor.new(context).redact([doc]) end doc diff --git a/lib/banzai/object_renderer.rb b/lib/banzai/object_renderer.rb index 75661ffa233..d6d29f4bfab 100644 --- a/lib/banzai/object_renderer.rb +++ b/lib/banzai/object_renderer.rb @@ -72,7 +72,7 @@ module Banzai # # Returns an Array containing the redacted documents. def redact_documents(documents) - redactor = Redactor.new(context) + redactor = ReferenceRedactor.new(context) redactor.redact(documents) end diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb index 5c199453638..54af26b41be 100644 --- a/lib/banzai/pipeline/post_process_pipeline.rb +++ b/lib/banzai/pipeline/post_process_pipeline.rb @@ -12,7 +12,7 @@ module Banzai def self.internal_link_filters [ - Filter::RedactorFilter, + Filter::ReferenceRedactorFilter, Filter::InlineMetricsRedactorFilter, Filter::RelativeLinkFilter, Filter::IssuableStateFilter, diff --git a/lib/banzai/redactor.rb b/lib/banzai/reference_redactor.rb index c2da7fec7cc..eb5c35da375 100644 --- a/lib/banzai/redactor.rb +++ b/lib/banzai/reference_redactor.rb @@ -3,7 +3,7 @@ module Banzai # Class for removing Markdown references a certain user is not allowed to # view. - class Redactor + class ReferenceRedactor attr_reader :context # context - An instance of `Banzai::RenderContext`. diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb index 81f32ef5bcf..3cb9ec21e8f 100644 --- a/lib/banzai/renderer.rb +++ b/lib/banzai/renderer.rb @@ -134,7 +134,7 @@ module Banzai # # This method is used to perform state-dependent changes to a String of # HTML, such as removing references that the current user doesn't have - # permission to make (`RedactorFilter`). + # permission to make (`ReferenceRedactorFilter`). # # html - String to process # context - Hash of options to customize output diff --git a/lib/gitlab/background_migration/fix_pages_access_level.rb b/lib/gitlab/background_migration/fix_pages_access_level.rb new file mode 100644 index 00000000000..0d49f3dd8c5 --- /dev/null +++ b/lib/gitlab/background_migration/fix_pages_access_level.rb @@ -0,0 +1,128 @@ +# 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 < ApplicationRecord + 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 the have `private` pages_access_level + # if access control is disabled, these projects currently behave as if the 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 diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml index f176771775e..89eccce69f6 100644 --- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml @@ -41,6 +41,8 @@ dependency_scanning: DS_PULL_ANALYZER_IMAGE_TIMEOUT \ DS_RUN_ANALYZER_TIMEOUT \ DS_PYTHON_VERSION \ + PIP_INDEX_URL \ + PIP_EXTRA_INDEX_URL \ ) \ --volume "$PWD:/code" \ --volume /var/run/docker.sock:/var/run/docker.sock \ diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index b7b7578cef9..a7d9ba51277 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -464,6 +464,18 @@ module Gitlab end end + # Returns path to url mappings for submodules + # + # Ex. + # @repository.submodule_urls_for('master') + # # => { 'rack' => 'git@localhost:rack.git' } + # + def submodule_urls_for(ref) + wrapped_gitaly_errors do + gitaly_submodule_urls_for(ref) + end + end + # Return total commits count accessible from passed ref def commit_count(ref) wrapped_gitaly_errors do @@ -1059,12 +1071,16 @@ module Gitlab return unless commit_object && commit_object.type == :COMMIT + urls = gitaly_submodule_urls_for(ref) + urls && urls[path] + end + + def gitaly_submodule_urls_for(ref) gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) return unless gitmodules - found_module = GitmodulesParser.new(gitmodules.data).parse[path] - - found_module && found_module['url'] + submodules = GitmodulesParser.new(gitmodules.data).parse + submodules.transform_values { |submodule| submodule['url'] } end # Returns true if the given ref name exists diff --git a/lib/gitlab/graphql/representation/submodule_tree_entry.rb b/lib/gitlab/graphql/representation/submodule_tree_entry.rb new file mode 100644 index 00000000000..65716dff75d --- /dev/null +++ b/lib/gitlab/graphql/representation/submodule_tree_entry.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module Representation + class SubmoduleTreeEntry < SimpleDelegator + class << self + def decorate(submodules, tree) + repository = tree.repository + submodule_links = Gitlab::SubmoduleLinks.new(repository) + + submodules.map do |submodule| + self.new(submodule, submodule_links.for(submodule, tree.sha)) + end + end + end + + def initialize(submodule, submodule_links) + @submodule_links = submodule_links + + super(submodule) + end + + def web_url + @submodule_links.first + end + + def tree_url + @submodule_links.last + end + end + end + end +end diff --git a/lib/gitlab/submodule_links.rb b/lib/gitlab/submodule_links.rb new file mode 100644 index 00000000000..a6c0369d864 --- /dev/null +++ b/lib/gitlab/submodule_links.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Gitlab + class SubmoduleLinks + include Gitlab::Utils::StrongMemoize + + def initialize(repository) + @repository = repository + end + + def for(submodule, sha) + submodule_url = submodule_url_for(sha)[submodule.path] + SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository) + end + + private + + attr_reader :repository + + def submodule_url_for(sha) + strong_memoize(:"submodule_links_for_#{sha}") do + repository.submodule_urls_for(sha) + end + end + end +end diff --git a/lib/gitlab/zoom_link_extractor.rb b/lib/gitlab/zoom_link_extractor.rb new file mode 100644 index 00000000000..d9994898a08 --- /dev/null +++ b/lib/gitlab/zoom_link_extractor.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Detect links matching the following formats: +# Zoom Start links: https://zoom.us/s/<meeting-id> +# Zoom Join links: https://zoom.us/j/<meeting-id> +# Personal Zoom links: https://zoom.us/my/<meeting-id> +# Vanity Zoom links: https://gitlab.zoom.us/j/<meeting-id> (also /s and /my) + +module Gitlab + class ZoomLinkExtractor + ZOOM_REGEXP = %r{https://(?:[\w-]+\.)?zoom\.us/(?:s|j|my)/\S+}.freeze + + def initialize(text) + @text = text.to_s + end + + def links + @text.scan(ZOOM_REGEXP) + end + end +end diff --git a/lib/tasks/gitlab/features.rake b/lib/tasks/gitlab/features.rake index d88bcca0819..9cf568c07fe 100644 --- a/lib/tasks/gitlab/features.rake +++ b/lib/tasks/gitlab/features.rake @@ -10,14 +10,22 @@ namespace :gitlab do set_rugged_feature_flags(false) puts 'All Rugged feature flags were disabled.' end + + task unset_rugged: :environment do + set_rugged_feature_flags(nil) + puts 'All Rugged feature flags were unset.' + end end def set_rugged_feature_flags(status) Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag| - if status - Feature.enable(flag) - else + case status + when nil Feature.get(flag).remove + when true + Feature.enable(flag) + when false + Feature.disable(flag) end end end |