Commit c853dd61 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Reuse Gitlab::Regex.full_namespace_regex in the DynamicPathValidator

parent 2e2a63c8
...@@ -15,7 +15,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -15,7 +15,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
# #
# the path `api` shouldn't be allowed because it would be masked by `api/*` # the path `api` shouldn't be allowed because it would be masked by `api/*`
# #
TOP_LEVEL_ROUTES = Set.new(%w[ TOP_LEVEL_ROUTES = %w[
- -
.well-known .well-known
abuse_reports abuse_reports
...@@ -59,7 +59,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -59,7 +59,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
unsubscribes unsubscribes
uploads uploads
users users
]).freeze ].freeze
# All project routes with wildcard argument must be listed here. # All project routes with wildcard argument must be listed here.
# Otherwise it can lead to routing issues when route considered as project name. # Otherwise it can lead to routing issues when route considered as project name.
...@@ -70,7 +70,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -70,7 +70,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
# without tree as reserved name routing can match 'group/project' as group name, # without tree as reserved name routing can match 'group/project' as group name,
# 'tree' as project name and 'deploy_keys' as route. # 'tree' as project name and 'deploy_keys' as route.
# #
WILDCARD_ROUTES = Set.new(%w[ WILDCARD_ROUTES = %w[
badges badges
blame blame
blob blob
...@@ -91,7 +91,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -91,7 +91,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
tree tree
update update
wikis wikis
]).freeze ].freeze
# These are all the paths that follow `/groups/*id/ or `/groups/*group_id` # These are all the paths that follow `/groups/*id/ or `/groups/*group_id`
# We need to reject these because we have a `/groups/*id` page that is the same # We need to reject these because we have a `/groups/*id` page that is the same
...@@ -100,7 +100,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -100,7 +100,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
# If we would allow a subgroup to be created with the name `activity` then # If we would allow a subgroup to be created with the name `activity` then
# this group would not be accessible through `/groups/parent/activity` since # this group would not be accessible through `/groups/parent/activity` since
# this would map to the activity-page of it's parent. # this would map to the activity-page of it's parent.
GROUP_ROUTES = Set.new(%w[ GROUP_ROUTES = %w[
activity activity
avatar avatar
edit edit
...@@ -111,16 +111,16 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -111,16 +111,16 @@ class DynamicPathValidator < ActiveModel::EachValidator
milestones milestones
projects projects
subgroups subgroups
]) ].freeze
CHILD_ROUTES = (WILDCARD_ROUTES | GROUP_ROUTES).freeze CHILD_ROUTES = (WILDCARD_ROUTES | GROUP_ROUTES).freeze
def self.without_reserved_wildcard_paths_regex def self.without_reserved_wildcard_paths_regex
@full_path_without_wildcard_regex ||= regex_excluding_child_paths(WILDCARD_ROUTES) @without_reserved_wildcard_paths_regex ||= regex_excluding_child_paths(WILDCARD_ROUTES)
end end
def self.without_reserved_child_paths_regex def self.without_reserved_child_paths_regex
@full_path_without_child_routes_regex ||= regex_excluding_child_paths(CHILD_ROUTES) @without_reserved_child_paths_regex ||= regex_excluding_child_paths(CHILD_ROUTES)
end end
# This is used to validate a full path. # This is used to validate a full path.
...@@ -128,22 +128,19 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -128,22 +128,19 @@ class DynamicPathValidator < ActiveModel::EachValidator
# - Starting with one of the top level words # - Starting with one of the top level words
# - Containing one of the child level words in the middle of a path # - Containing one of the child level words in the middle of a path
def self.regex_excluding_child_paths(child_routes) def self.regex_excluding_child_paths(child_routes)
reserved_top_level_words = Regexp.union(TOP_LEVEL_ROUTES.to_a) reserved_top_level_words = Regexp.union(TOP_LEVEL_ROUTES)
not_starting_in_reserved_word = %r{^(/?)(?!(#{reserved_top_level_words})(/|$))} not_starting_in_reserved_word = %r{\A/?(?!(#{reserved_top_level_words})(/|\z))}
reserved_child_level_words = Regexp.union(child_routes.to_a) reserved_child_level_words = Regexp.union(child_routes)
not_containing_reserved_child = %r{(?!(\S+)/(#{reserved_child_level_words})(/|$))} not_containing_reserved_child = %r{(?!\S+/(#{reserved_child_level_words})(/|\z))}
@full_path_regex = %r{ %r{#{not_starting_in_reserved_word}
#{not_starting_in_reserved_word} #{not_containing_reserved_child}
#{not_containing_reserved_child} #{Gitlab::Regex.full_namespace_regex}}x
#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}}x
end end
def self.valid?(path) def self.valid?(path)
path_segments = path.split('/') path =~ Gitlab::Regex.full_namespace_regex && !reserved?(path)
!reserved?(path) && path_segments.all? { |value| follow_format?(value) }
end end
def self.reserved?(path) def self.reserved?(path)
...@@ -165,13 +162,9 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -165,13 +162,9 @@ class DynamicPathValidator < ActiveModel::EachValidator
path !~ without_reserved_wildcard_paths_regex path !~ without_reserved_wildcard_paths_regex
end end
def self.follow_format?(value)
value =~ Gitlab::Regex.namespace_regex
end
delegate :reserved?, delegate :reserved?,
:any_reserved?, :any_reserved?,
:follow_format?, to: :class to: :class
def valid_full_path?(record, value) def valid_full_path?(record, value)
full_path = record.respond_to?(:full_path) ? record.full_path : value full_path = record.respond_to?(:full_path) ? record.full_path : value
...@@ -185,7 +178,7 @@ class DynamicPathValidator < ActiveModel::EachValidator ...@@ -185,7 +178,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
end end
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
unless follow_format?(value) unless value =~ Gitlab::Regex.namespace_regex
record.errors.add(attribute, Gitlab::Regex.namespace_regex_message) record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
end end
......
...@@ -22,6 +22,10 @@ module Gitlab ...@@ -22,6 +22,10 @@ module Gitlab
@namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze @namespace_regex ||= /\A#{NAMESPACE_REGEX_STR}\z/.freeze
end end
def full_namespace_regex
@full_namespace_regex ||= %r{\A#{FULL_NAMESPACE_REGEX_STR}\z}
end
def namespace_route_regex def namespace_route_regex
@namespace_route_regex ||= /#{NAMESPACE_REGEX_STR}/.freeze @namespace_route_regex ||= /#{NAMESPACE_REGEX_STR}/.freeze
end end
......
...@@ -45,8 +45,8 @@ describe Gitlab::Regex, lib: true do ...@@ -45,8 +45,8 @@ describe Gitlab::Regex, lib: true do
it { is_expected.not_to match('foo-') } it { is_expected.not_to match('foo-') }
end end
describe 'FULL_NAMESPACE_REGEX_STR' do describe '.full_namespace_regex' do
subject { %r{\A#{Gitlab::Regex::FULL_NAMESPACE_REGEX_STR}\z} } subject { described_class.full_namespace_regex }
it { is_expected.to match('gitlab.org') } it { is_expected.to match('gitlab.org') }
it { is_expected.to match('gitlab.org/gitlab-git') } it { is_expected.to match('gitlab.org/gitlab-git') }
......
...@@ -129,6 +129,10 @@ describe DynamicPathValidator do ...@@ -129,6 +129,10 @@ describe DynamicPathValidator do
expect(subject).not_to match('dashboard') expect(subject).not_to match('dashboard')
end end
it 'matches valid paths with a toplevel word in a different place' do
expect(subject).to match('parent/dashboard/project-path')
end
it 'rejects paths containing a wildcard reserved word' do it 'rejects paths containing a wildcard reserved word' do
expect(subject).not_to match('hello/edit') expect(subject).not_to match('hello/edit')
expect(subject).not_to match('hello/edit/in-the-middle') expect(subject).not_to match('hello/edit/in-the-middle')
...@@ -137,7 +141,6 @@ describe DynamicPathValidator do ...@@ -137,7 +141,6 @@ describe DynamicPathValidator do
it 'matches valid paths' do it 'matches valid paths' do
expect(subject).to match('parent/child/project-path') expect(subject).to match('parent/child/project-path')
expect(subject).to match('/parent/child/project-path')
end end
end end
...@@ -185,5 +188,11 @@ describe DynamicPathValidator do ...@@ -185,5 +188,11 @@ describe DynamicPathValidator do
expect(described_class.valid?(test_path)).to be_falsey expect(described_class.valid?(test_path)).to be_falsey
end end
it 'rejects paths that are in an incorrect format' do
test_path = 'incorrect/format.git'
expect(described_class.valid?(test_path)).to be_falsey
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment