Commit de614b0e authored by Douwe Maan's avatar Douwe Maan

Merge branch 'bvl-free-paths' into 'master'

Free some reserved group routes

See merge request gitlab-org/gitlab-ce!15052
parents cb606c5a 9b0899cb
---
title: Free up some reserved group names
merge_request: 15052
author:
type: other
...@@ -8,24 +8,33 @@ constraints(GroupUrlConstrainer.new) do ...@@ -8,24 +8,33 @@ constraints(GroupUrlConstrainer.new) do
scope(path: 'groups/*id', scope(path: 'groups/*id',
controller: :groups, controller: :groups,
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ }) do constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ }) do
scope(path: '-') do
get :edit, as: :edit_group get :edit, as: :edit_group
get :issues, as: :issues_group get :issues, as: :issues_group
get :merge_requests, as: :merge_requests_group get :merge_requests, as: :merge_requests_group
get :projects, as: :projects_group get :projects, as: :projects_group
get :activity, as: :activity_group get :activity, as: :activity_group
end
get '/', action: :show, as: :group_canonical get '/', action: :show, as: :group_canonical
end end
scope(path: 'groups/*group_id', scope(path: 'groups/*group_id/-',
module: :groups, module: :groups,
as: :group, as: :group,
constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do constraints: { group_id: Gitlab::PathRegex.full_namespace_route_regex }) do
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do namespace :settings do
post :resend_invite, on: :member resource :ci_cd, only: [:show], controller: 'ci_cd'
delete :leave, on: :collection end
resources :variables, only: [:index, :show, :update, :create, :destroy]
resources :children, only: [:index]
resources :labels, except: [:show] do
post :toggle_subscription, on: :member
end end
resource :avatar, only: [:destroy]
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :edit, :update, :new, :create] do resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :edit, :update, :new, :create] do
member do member do
get :merge_requests get :merge_requests
...@@ -34,18 +43,11 @@ constraints(GroupUrlConstrainer.new) do ...@@ -34,18 +43,11 @@ constraints(GroupUrlConstrainer.new) do
end end
end end
resources :labels, except: [:show] do resource :avatar, only: [:destroy]
post :toggle_subscription, on: :member
end
scope path: '-' do
namespace :settings do
resource :ci_cd, only: [:show], controller: 'ci_cd'
end
resources :variables, only: [:index, :show, :update, :create, :destroy]
resources :children, only: [:index] resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
end end
end end
...@@ -58,4 +60,12 @@ constraints(GroupUrlConstrainer.new) do ...@@ -58,4 +60,12 @@ constraints(GroupUrlConstrainer.new) do
put '/', action: :update put '/', action: :update
delete '/', action: :destroy delete '/', action: :destroy
end end
# Legacy paths should be defined last, so they would be ignored if routes with
# one of the previously reserved words exist.
scope(path: 'groups/*group_id') do
Gitlab::Routing.redirect_legacy_paths(self, :labels, :milestones, :group_members,
:edit, :issues, :merge_requests, :projects,
:activity)
end
end end
...@@ -112,22 +112,6 @@ module Gitlab ...@@ -112,22 +112,6 @@ module Gitlab
# this would map to the activity-page of its parent. # this would map to the activity-page of its parent.
GROUP_ROUTES = %w[ GROUP_ROUTES = %w[
- -
activity
analytics
audit_events
avatar
edit
group_members
hooks
issues
labels
ldap
ldap_group_links
merge_requests
milestones
notification_setting
pipeline_quota
projects
].freeze ].freeze
ILLEGAL_PROJECT_PATH_WORDS = PROJECT_WILDCARD_ROUTES ILLEGAL_PROJECT_PATH_WORDS = PROJECT_WILDCARD_ROUTES
......
...@@ -40,5 +40,24 @@ module Gitlab ...@@ -40,5 +40,24 @@ module Gitlab
def self.url_helpers def self.url_helpers
@url_helpers ||= Gitlab::Application.routes.url_helpers @url_helpers ||= Gitlab::Application.routes.url_helpers
end end
def self.redirect_legacy_paths(router, *paths)
build_redirect_path = lambda do |request, _params, path|
# Only replace the last occurence of `path`.
#
# `request.fullpath` includes the querystring
path = request.path.sub(%r{/#{path}/*(?!.*#{path})}, "/-/#{path}/")
path << "?#{request.query_string}" if request.query_string.present?
path
end
paths.each do |path|
router.match "/#{path}(/*rest)",
via: [:get, :post, :patch, :delete],
to: router.redirect { |params, request| build_redirect_path.call(request, params, path) },
as: "legacy_#{path}_redirect"
end
end
end end
end end
...@@ -24,7 +24,7 @@ describe LabelsHelper do ...@@ -24,7 +24,7 @@ describe LabelsHelper do
let(:group) { build(:group, name: 'bar') } let(:group) { build(:group, name: 'bar') }
it 'links to group issues page' do it 'links to group issues page' do
expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/issues\?label_name%5B%5D=#{label.name}">.*</a>} expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end end
end end
......
...@@ -45,21 +45,16 @@ describe Gitlab::PathRegex do ...@@ -45,21 +45,16 @@ describe Gitlab::PathRegex do
Found new routes that could cause conflicts with existing namespaced routes Found new routes that could cause conflicts with existing namespaced routes
for groups or projects. for groups or projects.
Add <#{missing_words.join(', ')}> to `Gitlab::PathRegex::#{constant_name} Nest <#{missing_words.join(', ')}> in a route containing `-`, that way
to make sure no projects or namespaces can be created with those paths. we know there will be no conflicts with groups or projects created with those
paths.
To rename any existing records with those paths you can use the
`Gitlab::Database::RenameReservedpathsMigration::<VERSION>.#{migration_helper}`
migration helper.
Make sure to make a note of the renamed records in the release blog post.
MISSING MISSING
end end
if additional_words.any? if additional_words.any?
message += <<-ADDITIONAL message += <<-ADDITIONAL
Why are <#{additional_words.join(', ')}> in `#{constant_name}`? Is <#{additional_words.join(', ')}> in `#{constant_name}` required?
If they are really required, update these specs to reflect that. If they are really required, update these specs to reflect that.
ADDITIONAL ADDITIONAL
...@@ -157,16 +152,7 @@ describe Gitlab::PathRegex do ...@@ -157,16 +152,7 @@ describe Gitlab::PathRegex do
let(:paths_after_group_id) do let(:paths_after_group_id) do
group_routes.map do |route| group_routes.map do |route|
route.gsub(STARTING_WITH_GROUP, '').split('/').first route.gsub(STARTING_WITH_GROUP, '').split('/').first
end.uniq + ee_paths_after_group_id end.uniq
end
let(:ee_paths_after_group_id) do
%w(analytics
ldap
ldap_group_links
notification_setting
audit_events
pipeline_quota hooks)
end end
describe 'TOP_LEVEL_ROUTES' do describe 'TOP_LEVEL_ROUTES' do
...@@ -225,8 +211,6 @@ describe Gitlab::PathRegex do ...@@ -225,8 +211,6 @@ describe Gitlab::PathRegex do
it 'accepts group routes' do it 'accepts group routes' do
expect(subject).to match('activity/') expect(subject).to match('activity/')
expect(subject).to match('group_members/')
expect(subject).to match('labels/')
end end
it 'is not case sensitive' do it 'is not case sensitive' do
...@@ -258,8 +242,6 @@ describe Gitlab::PathRegex do ...@@ -258,8 +242,6 @@ describe Gitlab::PathRegex do
it 'accepts group routes' do it 'accepts group routes' do
expect(subject).to match('activity/') expect(subject).to match('activity/')
expect(subject).to match('group_members/')
expect(subject).to match('labels/')
end end
end end
...@@ -280,8 +262,6 @@ describe Gitlab::PathRegex do ...@@ -280,8 +262,6 @@ describe Gitlab::PathRegex do
it 'accepts group routes' do it 'accepts group routes' do
expect(subject).to match('activity/more/') expect(subject).to match('activity/more/')
expect(subject).to match('group_members/more/')
expect(subject).to match('labels/more/')
end end
end end
end end
...@@ -303,9 +283,7 @@ describe Gitlab::PathRegex do ...@@ -303,9 +283,7 @@ describe Gitlab::PathRegex do
end end
it 'rejects group routes' do it 'rejects group routes' do
expect(subject).not_to match('root/activity/') expect(subject).not_to match('root/-/')
expect(subject).not_to match('root/group_members/')
expect(subject).not_to match('root/labels/')
end end
end end
...@@ -325,9 +303,7 @@ describe Gitlab::PathRegex do ...@@ -325,9 +303,7 @@ describe Gitlab::PathRegex do
end end
it 'rejects group routes' do it 'rejects group routes' do
expect(subject).not_to match('root/activity/more/') expect(subject).not_to match('root/-/')
expect(subject).not_to match('root/group_members/more/')
expect(subject).not_to match('root/labels/more/')
end end
end end
end end
...@@ -360,9 +336,7 @@ describe Gitlab::PathRegex do ...@@ -360,9 +336,7 @@ describe Gitlab::PathRegex do
end end
it 'accepts group routes' do it 'accepts group routes' do
expect(subject).to match('activity/') expect(subject).to match('analytics/')
expect(subject).to match('group_members/')
expect(subject).to match('labels/')
end end
it 'is not case sensitive' do it 'is not case sensitive' do
...@@ -393,9 +367,7 @@ describe Gitlab::PathRegex do ...@@ -393,9 +367,7 @@ describe Gitlab::PathRegex do
end end
it 'accepts group routes' do it 'accepts group routes' do
expect(subject).to match('root/activity/') expect(subject).to match('root/analytics/')
expect(subject).to match('root/group_members/')
expect(subject).to match('root/labels/')
end end
it 'is not case sensitive' do it 'is not case sensitive' do
......
...@@ -65,12 +65,6 @@ describe Group do ...@@ -65,12 +65,6 @@ describe Group do
expect(group).not_to be_valid expect(group).not_to be_valid
end end
it 'rejects reserved group paths' do
group = build(:group, path: 'activity', parent: create(:group))
expect(group).not_to be_valid
end
end end
describe '#visibility_level_allowed_by_parent' do describe '#visibility_level_allowed_by_parent' do
......
require 'spec_helper'
describe "Groups", "routing" do
let(:group_path) { 'complex.group-namegit' }
let!(:group) { create(:group, path: group_path) }
it "to #show" do
expect(get("/groups/#{group_path}")).to route_to('groups#show', id: group_path)
end
it "also supports nested groups" do
nested_group = create(:group, parent: group)
expect(get("/#{group_path}/#{nested_group.path}")).to route_to('groups#show', id: "#{group_path}/#{nested_group.path}")
end
it "also display group#show on the short path" do
expect(get("/#{group_path}")).to route_to('groups#show', id: group_path)
end
it "to #activity" do
expect(get("/groups/#{group_path}/-/activity")).to route_to('groups#activity', id: group_path)
end
it "to #issues" do
expect(get("/groups/#{group_path}/-/issues")).to route_to('groups#issues', id: group_path)
end
it "to #members" do
expect(get("/groups/#{group_path}/-/group_members")).to route_to('groups/group_members#index', group_id: group_path)
end
it "to #labels" do
expect(get("/groups/#{group_path}/-/labels")).to route_to('groups/labels#index', group_id: group_path)
end
it "to #milestones" do
expect(get("/groups/#{group_path}/-/milestones")).to route_to('groups/milestones#index', group_id: group_path)
end
describe 'legacy redirection' do
describe 'labels' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/labels", "/groups/complex.group-namegit/-/labels/" do
let(:resource) { create(:group, parent: group, path: 'labels') }
end
end
describe 'group_members' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/group_members", "/groups/complex.group-namegit/-/group_members/" do
let(:resource) { create(:group, parent: group, path: 'group_members') }
end
end
describe 'avatar' do
it 'routes to the avatars controller' do
expect(delete("/groups/#{group_path}/-/avatar"))
.to route_to(group_id: group_path,
controller: 'groups/avatars',
action: 'destroy')
end
end
describe 'milestones' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/milestones", "/groups/complex.group-namegit/-/milestones/" do
let(:resource) { create(:group, parent: group, path: 'milestones') }
end
context 'nested routes' do
include RSpec::Rails::RequestExampleGroup
let(:milestone) { create(:milestone, group: group) }
it 'redirects the nested routes' do
request = get("/groups/#{group_path}/milestones/#{milestone.id}/merge_requests")
expect(request).to redirect_to("/groups/#{group_path}/-/milestones/#{milestone.id}/merge_requests")
end
end
context 'with a query string' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/milestones?hello=world", "/groups/complex.group-namegit/-/milestones/?hello=world" do
let(:resource) { create(:group, parent: group, path: 'milestones') }
end
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/milestones?milestones=/milestones", "/groups/complex.group-namegit/-/milestones/?milestones=/milestones" do
let(:resource) { create(:group, parent: group, path: 'milestones') }
end
end
end
describe 'edit' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/edit", "/groups/complex.group-namegit/-/edit/" do
let(:resource) do
pending('still rejected because of the wildcard reserved word')
create(:group, parent: group, path: 'edit')
end
end
end
describe 'issues' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/issues", "/groups/complex.group-namegit/-/issues/" do
let(:resource) { create(:group, parent: group, path: 'issues') }
end
end
describe 'merge_requests' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/merge_requests", "/groups/complex.group-namegit/-/merge_requests/" do
let(:resource) { create(:group, parent: group, path: 'merge_requests') }
end
end
describe 'projects' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/projects", "/groups/complex.group-namegit/-/projects/" do
let(:resource) { create(:group, parent: group, path: 'projects') }
end
end
describe 'activity' do
it_behaves_like 'redirecting a legacy path', "/groups/complex.group-namegit/activity", "/groups/complex.group-namegit/-/activity/" do
let(:resource) { create(:group, parent: group, path: 'activity') }
end
it_behaves_like 'redirecting a legacy path', "/groups/activity/activity", "/groups/activity/-/activity/" do
let!(:parent) { create(:group, path: 'activity') }
let(:resource) { create(:group, parent: parent, path: 'activity') }
end
end
end
end
...@@ -278,36 +278,6 @@ describe "Authentication", "routing" do ...@@ -278,36 +278,6 @@ describe "Authentication", "routing" do
end end
end end
describe "Groups", "routing" do
let(:name) { 'complex.group-namegit' }
let!(:group) { create(:group, name: name) }
it "to #show" do
expect(get("/groups/#{name}")).to route_to('groups#show', id: name)
end
it "also supports nested groups" do
nested_group = create(:group, parent: group)
expect(get("/#{name}/#{nested_group.name}")).to route_to('groups#show', id: "#{name}/#{nested_group.name}")
end
it "also display group#show on the short path" do
expect(get("/#{name}")).to route_to('groups#show', id: name)
end
it "to #activity" do
expect(get("/groups/#{name}/activity")).to route_to('groups#activity', id: name)
end
it "to #issues" do
expect(get("/groups/#{name}/issues")).to route_to('groups#issues', id: name)
end
it "to #members" do
expect(get("/groups/#{name}/group_members")).to route_to('groups/group_members#index', group_id: name)
end
end
describe HealthCheckController, 'routing' do describe HealthCheckController, 'routing' do
it 'to #index' do it 'to #index' do
expect(get('/health_check')).to route_to('health_check#index') expect(get('/health_check')).to route_to('health_check#index')
......
shared_examples 'redirecting a legacy path' do |source, target|
include RSpec::Rails::RequestExampleGroup
it "redirects #{source} to #{target} when the resource does not exist" do
expect(get(source)).to redirect_to(target)
end
it "does not redirect #{source} to #{target} when the resource exists" do
resource
expect(get(source)).not_to redirect_to(target)
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