Skip to content

Commit

Permalink
Reallow project paths ending in periods
Browse files Browse the repository at this point in the history
  • Loading branch information
DouweM committed Nov 6, 2017
1 parent 97b80fe commit a10925e
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 159 deletions.
2 changes: 1 addition & 1 deletion app/models/namespace.rb
Expand Up @@ -36,7 +36,7 @@ class Namespace < ActiveRecord::Base
validates :path,
presence: true,
length: { maximum: 255 },
dynamic_path: true
namespace_path: true

validate :nesting_level_allowed

Expand Down
4 changes: 1 addition & 3 deletions app/models/project.rb
Expand Up @@ -240,10 +240,8 @@ class Project < ActiveRecord::Base
message: Gitlab::Regex.project_name_regex_message }
validates :path,
presence: true,
dynamic_path: true,
project_path: true,
length: { maximum: 255 },
format: { with: Gitlab::PathRegex.project_path_format_regex,
message: Gitlab::PathRegex.project_path_format_message },
uniqueness: { scope: :namespace_id }

validates :namespace, presence: true
Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Expand Up @@ -146,7 +146,7 @@ def update_tracked_fields!(request)
presence: true,
numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
validates :username,
dynamic_path: true,
user_path: true,
presence: true,
uniqueness: { case_sensitive: false }

Expand Down
38 changes: 38 additions & 0 deletions app/validators/abstract_path_validator.rb
@@ -0,0 +1,38 @@
class AbstractPathValidator < ActiveModel::EachValidator
extend Gitlab::EncodingHelper

def self.path_regex
raise NotImplementedError
end

def self.format_regex
raise NotImplementedError
end

def self.format_error_message
raise NotImplementedError
end

def self.full_path(record, value)
value
end

def self.valid_path?(path)
encode!(path)
"#{path}/" =~ path_regex
end

def validate_each(record, attribute, value)
unless value =~ self.class.format_regex
record.errors.add(attribute, self.class.format_error_message)
return
end

full_path = self.class.full_path(record, value)
return unless full_path

unless self.class.valid_path?(full_path)
record.errors.add(attribute, "#{value} is a reserved name")
end
end
end
53 changes: 0 additions & 53 deletions app/validators/dynamic_path_validator.rb

This file was deleted.

19 changes: 19 additions & 0 deletions app/validators/namespace_path_validator.rb
@@ -0,0 +1,19 @@
class NamespacePathValidator < AbstractPathValidator
extend Gitlab::EncodingHelper

def self.path_regex
Gitlab::PathRegex.full_namespace_path_regex
end

def self.format_regex
Gitlab::PathRegex.namespace_format_regex
end

def self.format_error_message
Gitlab::PathRegex.namespace_format_message
end

def self.full_path(record, value)
record.build_full_path
end
end
19 changes: 19 additions & 0 deletions app/validators/project_path_validator.rb
@@ -0,0 +1,19 @@
class ProjectPathValidator < AbstractPathValidator
extend Gitlab::EncodingHelper

def self.path_regex
Gitlab::PathRegex.full_project_path_regex
end

def self.format_regex
Gitlab::PathRegex.project_path_format_regex
end

def self.format_error_message
Gitlab::PathRegex.project_path_format_message
end

def self.full_path(record, value)
record.build_full_path
end
end
15 changes: 15 additions & 0 deletions app/validators/user_path_validator.rb
@@ -0,0 +1,15 @@
class UserPathValidator < AbstractPathValidator
extend Gitlab::EncodingHelper

def self.path_regex
Gitlab::PathRegex.root_namespace_path_regex
end

def self.format_regex
Gitlab::PathRegex.namespace_format_regex
end

def self.format_error_message
Gitlab::PathRegex.namespace_format_message
end
end
@@ -0,0 +1,5 @@
---
title: Reallow project paths ending in periods
merge_request:
author:
type: fixed
2 changes: 1 addition & 1 deletion lib/constraints/group_url_constrainer.rb
Expand Up @@ -2,7 +2,7 @@ class GroupUrlConstrainer
def matches?(request)
full_path = request.params[:group_id] || request.params[:id]

return false unless DynamicPathValidator.valid_group_path?(full_path)
return false unless NamespacePathValidator.valid_path?(full_path)

Group.find_by_full_path(full_path, follow_redirects: request.get?).present?
end
Expand Down
2 changes: 1 addition & 1 deletion lib/constraints/project_url_constrainer.rb
Expand Up @@ -4,7 +4,7 @@ def matches?(request)
project_path = request.params[:project_id] || request.params[:id]
full_path = [namespace_path, project_path].join('/')

return false unless DynamicPathValidator.valid_project_path?(full_path)
return false unless ProjectPathValidator.valid_path?(full_path)

# We intentionally allow SELECT(*) here so result of this query can be used
# as cache for further Project.find_by_full_path calls within request
Expand Down
2 changes: 1 addition & 1 deletion lib/constraints/user_url_constrainer.rb
Expand Up @@ -2,7 +2,7 @@ class UserUrlConstrainer
def matches?(request)
full_path = request.params[:username]

return false unless DynamicPathValidator.valid_user_path?(full_path)
return false unless UserPathValidator.valid_path?(full_path)

User.find_by_full_path(full_path, follow_redirects: request.get?).present?
end
Expand Down
2 changes: 1 addition & 1 deletion lib/gitlab/o_auth/user.rb
Expand Up @@ -179,7 +179,7 @@ def user_attributes
valid_username = ::Namespace.clean_path(username)

uniquify = Uniquify.new
valid_username = uniquify.string(valid_username) { |s| !DynamicPathValidator.valid_user_path?(s) }
valid_username = uniquify.string(valid_username) { |s| !UserPathValidator.valid_path?(s) }

name = auth_hash.name
name = valid_username if name.strip.empty?
Expand Down
6 changes: 6 additions & 0 deletions spec/models/project_spec.rb
Expand Up @@ -276,6 +276,12 @@

expect(project).to be_valid
end

it 'allows a path ending in a period' do
project = build(:project, path: 'foo.')

expect(project).to be_valid
end
end
end

Expand Down
97 changes: 0 additions & 97 deletions spec/validators/dynamic_path_validator_spec.rb

This file was deleted.

38 changes: 38 additions & 0 deletions spec/validators/namespace_path_validator_spec.rb
@@ -0,0 +1,38 @@
require 'spec_helper'

describe NamespacePathValidator do
let(:validator) { described_class.new(attributes: [:path]) }

describe '.valid_path?' do
it 'handles invalid utf8' do
expect(described_class.valid_path?("a\0weird\255path")).to be_falsey
end
end

describe '#validates_each' do
it 'adds a message when the path is not in the correct format' do
group = build(:group)

validator.validate_each(group, :path, "Path with spaces, and comma's!")

expect(group.errors[:path]).to include(Gitlab::PathRegex.namespace_format_message)
end

it 'adds a message when the path is reserved when creating' do
group = build(:group, path: 'help')

validator.validate_each(group, :path, 'help')

expect(group.errors[:path]).to include('help is a reserved name')
end

it 'adds a message when the path is reserved when updating' do
group = create(:group)
group.path = 'help'

validator.validate_each(group, :path, 'help')

expect(group.errors[:path]).to include('help is a reserved name')
end
end
end

0 comments on commit a10925e

Please sign in to comment.