gitlab-org--gitlab-foss/app/validators/dynamic_path_validator.rb

137 lines
3.1 KiB
Ruby
Raw Normal View History

# DynamicPathValidator
2015-12-07 21:17:12 +00:00
#
# Custom validator for GitLab path values.
# These paths are assigned to `Namespace` (& `Group` as a subclass) & `Project`
2015-12-07 21:17:12 +00:00
#
# Values are checked for formatting and exclusion from a list of reserved path
# names.
class DynamicPathValidator < ActiveModel::EachValidator
# All routes that appear on the top level must be listed here.
# This will make sure that groups cannot be created with these names
# as these routes would be masked by the paths already in place.
#
# Example:
# /api/api-project
#
# the path `api` shouldn't be allowed because it would be masked by `api/*`
#
TOP_LEVEL_ROUTES = Set.new(%w[
.well-known
admin
all
assets
ci
dashboard
files
groups
help
hooks
issues
merge_requests
2016-01-08 09:19:22 +00:00
new
notes
profile
projects
public
repository
robots.txt
s
search
services
snippets
teams
u
unsubscribes
users
api
autocomplete
search
member
explore
uploads
import
notification_settings
abuse_reports
invites
help
koding
health_check
jwt
oauth
sent_notifications
2017-04-12 11:26:52 +00:00
-
]).freeze
# All project routes with wildcard argument must be listed here.
# Otherwise it can lead to routing issues when route considered as project name.
#
# Example:
# /group/project/tree/deploy_keys
#
# without tree as reserved name routing can match 'group/project' as group name,
# 'tree' as project name and 'deploy_keys' as route.
#
WILDCARD_ROUTES = Set.new(%w[tree commits wikis new edit create update logs_tree
preview blob blame raw files create_dir find_file
artifacts graphs refs badges objects folders file])
STRICT_RESERVED = (TOP_LEVEL_ROUTES | WILDCARD_ROUTES).freeze
def self.valid_full_path?(full_path)
path_segments = full_path.split('/')
root_segment = path_segments.shift
valid?(root_segment, type: :top_level) && valid_wildcard_segments?(path_segments)
end
def self.valid_wildcard_segments?(segments)
segments.all? { |segment| valid?(segment, type: :wildcard) }
end
def self.valid?(value, type: :strict)
!reserved?(value, type: type) && follow_format?(value)
end
def self.reserved?(value, type: :strict)
2017-04-14 18:06:20 +00:00
value = value.to_s.downcase
case type
when :wildcard
WILDCARD_ROUTES.include?(value)
when :top_level
TOP_LEVEL_ROUTES.include?(value)
else
STRICT_RESERVED.include?(value)
end
end
def self.follow_format?(value)
value =~ Gitlab::Regex.namespace_regex
end
delegate :reserved?, :follow_format?, to: :class
2015-12-07 21:17:12 +00:00
def validate_each(record, attribute, value)
unless follow_format?(value)
2015-12-07 21:17:12 +00:00
record.errors.add(attribute, Gitlab::Regex.namespace_regex_message)
end
if reserved?(value, type: validation_type(record))
2015-12-07 21:17:12 +00:00
record.errors.add(attribute, "#{value} is a reserved name")
end
end
def validation_type(record)
case record
when Namespace
record.has_parent? ? :wildcard : :top_level
when Project
:wildcard
when User
:top_level
else
:strict
end
end
2015-12-07 21:17:12 +00:00
end