diff options
author | Michael Kozono <mkozono@gmail.com> | 2017-05-18 12:56:39 -0700 |
---|---|---|
committer | Michael Kozono <mkozono@gmail.com> | 2017-05-18 16:24:10 -0700 |
commit | f9785dcec34c4205732871523f95b9743db00965 (patch) | |
tree | 4fccd9b776c98371e2e5e434d85ea1bfe7f3c1ac /spec/controllers | |
parent | 11f82de1efc087ee812764764e31161347e593cb (diff) | |
download | gitlab-ce-f9785dcec34c4205732871523f95b9743db00965.tar.gz |
Fix ensure_canonical_path for top level routes
Don’t replace a substring of the path if it is part of the top level route.
E.g. When redirecting from `/groups/ups` to `/groups/foo`, be careful not to do `/grofoo/ups`.
Projects are unaffected by this issue, but I am grouping the `#ensure_canonical_path` tests similar to the group and user tests.
Diffstat (limited to 'spec/controllers')
-rw-r--r-- | spec/controllers/groups_controller_spec.rb | 229 | ||||
-rw-r--r-- | spec/controllers/projects_controller_spec.rb | 199 | ||||
-rw-r--r-- | spec/controllers/users_controller_spec.rb | 253 |
3 files changed, 369 insertions, 312 deletions
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 0392315b62f..993654fddaa 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -84,26 +84,6 @@ describe GroupsController do expect(assigns(:issues)).to eq [issue_2, issue_1] end end - - context 'when requesting the canonical path with different casing' do - it 'redirects to the correct casing' do - get :issues, id: group.to_param.upcase - - expect(response).to redirect_to(issues_group_path(group.to_param)) - expect(controller).not_to set_flash[:notice] - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - - it 'redirects to the canonical path' do - get :issues, id: redirect_route.path - - expect(response).to redirect_to(issues_group_path(group.to_param)) - expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) - end - end end describe 'GET #merge_requests' do @@ -129,26 +109,6 @@ describe GroupsController do expect(assigns(:merge_requests)).to eq [merge_request_2, merge_request_1] end end - - context 'when requesting the canonical path with different casing' do - it 'redirects to the correct casing' do - get :merge_requests, id: group.to_param.upcase - - expect(response).to redirect_to(merge_requests_group_path(group.to_param)) - expect(controller).not_to set_flash[:notice] - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - - it 'redirects to the canonical path' do - get :merge_requests, id: redirect_route.path - - expect(response).to redirect_to(merge_requests_group_path(group.to_param)) - expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) - end - end end describe 'DELETE #destroy' do @@ -178,30 +138,6 @@ describe GroupsController do expect(response).to redirect_to(root_path) end - - context 'when requesting the canonical path with different casing' do - it 'does not 404' do - delete :destroy, id: group.to_param.upcase - - expect(response).not_to have_http_status(404) - end - - it 'does not redirect to the correct casing' do - delete :destroy, id: group.to_param.upcase - - expect(response).not_to redirect_to(group_path(group.to_param)) - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - - it 'returns not found' do - delete :destroy, id: redirect_route.path - - expect(response).to have_http_status(404) - end - end end end @@ -224,41 +160,166 @@ describe GroupsController do expect(assigns(:group).errors).not_to be_empty expect(assigns(:group).path).not_to eq('new_path') end + end + + describe '#ensure_canonical_path' do + before do + sign_in(user) + end + + context 'for a GET request' do + context 'when requesting groups at the root path' do + before do + allow(request).to receive(:original_fullpath).and_return("/#{group_full_path}") + get :show, id: group_full_path + end - context 'when requesting the canonical path with different casing' do - it 'does not 404' do - post :update, id: group.to_param.upcase, group: { path: 'new_path' } + context 'when requesting the canonical path with different casing' do + let(:group_full_path) { group.to_param.upcase } - expect(response).not_to have_http_status(404) + it 'redirects to the correct casing' do + expect(response).to redirect_to(group) + expect(controller).not_to set_flash[:notice] + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + let(:group_full_path) { redirect_route.path } + + it 'redirects to the canonical path' do + expect(response).to redirect_to(group) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + + context 'when the old group path is a substring of the scheme or host' do + let(:redirect_route) { group.redirect_routes.create(path: 'http') } + + it 'does not modify the requested host' do + expect(response).to redirect_to(group) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + end + + context 'when the old group path is substring of groups' do + # I.e. /groups/oups should not become /grfoo/oups + let(:redirect_route) { group.redirect_routes.create(path: 'oups') } + + it 'does not modify the /groups part of the path' do + expect(response).to redirect_to(group) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + end + end end - it 'does not redirect to the correct casing' do - post :update, id: group.to_param.upcase, group: { path: 'new_path' } + context 'when requesting groups under the /groups path' do + context 'when requesting the canonical path with different casing' do + it 'redirects to the correct casing' do + get :issues, id: group.to_param.upcase - expect(response).not_to redirect_to(group_path(group.to_param)) + expect(response).to redirect_to(issues_group_path(group.to_param)) + expect(controller).not_to set_flash[:notice] + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + + it 'redirects to the canonical path' do + get :issues, id: redirect_route.path + + expect(response).to redirect_to(issues_group_path(group.to_param)) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + + context 'when the old group path is a substring of the scheme or host' do + let(:redirect_route) { group.redirect_routes.create(path: 'http') } + + it 'does not modify the requested host' do + get :issues, id: redirect_route.path + + expect(response).to redirect_to(issues_group_path(group.to_param)) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + end + + context 'when the old group path is substring of groups' do + # I.e. /groups/oups should not become /grfoo/oups + let(:redirect_route) { group.redirect_routes.create(path: 'oups') } + + it 'does not modify the /groups part of the path' do + get :issues, id: redirect_route.path + + expect(response).to redirect_to(issues_group_path(group.to_param)) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + end + + context 'when the old group path is substring of groups plus the new path' do + # I.e. /groups/oups/oup should not become /grfoos + let(:redirect_route) { group.redirect_routes.create(path: 'oups/oup') } + + it 'does not modify the /groups part of the path' do + get :issues, id: redirect_route.path + + expect(response).to redirect_to(issues_group_path(group.to_param)) + expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + end + end + end end end - context 'when requesting a redirected path' do - let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + context 'for a POST request' do + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + post :update, id: group.to_param.upcase, group: { path: 'new_path' } + + expect(response).not_to have_http_status(404) + end + + it 'does not redirect to the correct casing' do + post :update, id: group.to_param.upcase, group: { path: 'new_path' } + + expect(response).not_to have_http_status(301) + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } - it 'returns not found' do - post :update, id: redirect_route.path, group: { path: 'new_path' } + it 'returns not found' do + post :update, id: redirect_route.path, group: { path: 'new_path' } - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end end - end - describe 'ensure_canonical_path' do - context 'when the old group path is a substring of the scheme or host' do - let(:redirect_route) { group.redirect_routes.create(path: 'http') } + context 'for a DELETE request' do + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + delete :destroy, id: group.to_param.upcase - it 'does not modify the requested host' do - get :issues, id: redirect_route.path + expect(response).not_to have_http_status(404) + end - expect(response).to redirect_to(issues_group_path(group.to_param)) - expect(controller).to set_flash[:notice].to(group_moved_message(redirect_route, group)) + it 'does not redirect to the correct casing' do + delete :destroy, id: group.to_param.upcase + + expect(response).not_to have_http_status(301) + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { group.redirect_routes.create(path: 'old-path') } + + it 'returns not found' do + delete :destroy, id: redirect_route.path + + expect(response).to have_http_status(404) + end end end end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index e230944d52e..a8be6768a47 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -169,27 +169,6 @@ describe ProjectsController do end end - context "when requested with case sensitive namespace and project path" do - context "when there is a match with the same casing" do - it "loads the project" do - get :show, namespace_id: public_project.namespace, id: public_project - - expect(assigns(:project)).to eq(public_project) - expect(response).to have_http_status(200) - end - end - - context "when there is a match with different casing" do - it "redirects to the normalized path" do - get :show, namespace_id: public_project.namespace, id: public_project.path.upcase - - expect(assigns(:project)).to eq(public_project) - expect(response).to redirect_to("/#{public_project.full_path}") - expect(controller).not_to set_flash[:notice] - end - end - end - context "when the url contains .atom" do let(:public_project_with_dot_atom) { build(:empty_project, :public, name: 'my.atom', path: 'my.atom') } @@ -219,17 +198,6 @@ describe ProjectsController do expect(response).to redirect_to(namespace_project_path) end end - - context 'when requesting a redirected path' do - let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } - - it 'redirects to the canonical path' do - get :show, namespace_id: 'foo', id: 'bar' - - expect(response).to redirect_to(public_project) - expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) - end - end end describe "#update" do @@ -256,34 +224,6 @@ describe ProjectsController do expect(assigns(:repository).path).to eq(project.repository.path) expect(response).to have_http_status(302) end - - context 'when requesting the canonical path' do - it "is case-insensitive" do - controller.instance_variable_set(:@project, project) - - put :update, - namespace_id: 'FOo', - id: 'baR', - project: project_params - - expect(project.repository.path).to include(new_path) - expect(assigns(:repository).path).to eq(project.repository.path) - expect(response).to have_http_status(302) - end - end - - context 'when requesting a redirected path' do - let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") } - - it 'returns not found' do - put :update, - namespace_id: 'foo', - id: 'bar', - project: project_params - - expect(response).to have_http_status(404) - end - end end describe "#destroy" do @@ -319,31 +259,6 @@ describe ProjectsController do expect(merge_request.reload.state).to eq('closed') end end - - context 'when requesting the canonical path' do - it "is case-insensitive" do - controller.instance_variable_set(:@project, project) - sign_in(admin) - - orig_id = project.id - delete :destroy, namespace_id: project.namespace, id: project.path.upcase - - expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) - expect(response).to have_http_status(302) - expect(response).to redirect_to(dashboard_projects_path) - end - end - - context 'when requesting a redirected path' do - let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") } - - it 'returns not found' do - sign_in(admin) - delete :destroy, namespace_id: 'foo', id: 'bar' - - expect(response).to have_http_status(404) - end - end end describe 'PUT #new_issue_address' do @@ -465,17 +380,6 @@ describe ProjectsController do expect(parsed_body["Tags"]).to include("v1.0.0") expect(parsed_body["Commits"]).to include("123456") end - - context 'when requesting a redirected path' do - let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } - - it 'redirects to the canonical path' do - get :refs, namespace_id: 'foo', id: 'bar' - - expect(response).to redirect_to(refs_namespace_project_path(namespace_id: public_project.namespace, id: public_project)) - expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) - end - end end describe 'POST #preview_markdown' do @@ -488,6 +392,109 @@ describe ProjectsController do end end + describe '#ensure_canonical_path' do + before do + sign_in(user) + end + + context 'for a GET request' do + context 'when requesting the canonical path' do + context "with exactly matching casing" do + it "loads the project" do + get :show, namespace_id: public_project.namespace, id: public_project + + expect(assigns(:project)).to eq(public_project) + expect(response).to have_http_status(200) + end + end + + context "with different casing" do + it "redirects to the normalized path" do + get :show, namespace_id: public_project.namespace, id: public_project.path.upcase + + expect(assigns(:project)).to eq(public_project) + expect(response).to redirect_to("/#{public_project.full_path}") + expect(controller).not_to set_flash[:notice] + end + end + end + + context 'when requesting a redirected path' do + let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } + + it 'redirects to the canonical path' do + get :show, namespace_id: 'foo', id: 'bar' + + expect(response).to redirect_to(public_project) + expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) + end + + it 'redirects to the canonical path (testing non-show action)' do + get :refs, namespace_id: 'foo', id: 'bar' + + expect(response).to redirect_to(refs_namespace_project_path(namespace_id: public_project.namespace, id: public_project)) + expect(controller).to set_flash[:notice].to(project_moved_message(redirect_route, public_project)) + end + end + end + + context 'for a POST request' do + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + post :toggle_star, namespace_id: public_project.namespace, id: public_project.path.upcase + + expect(response).not_to have_http_status(404) + end + + it 'does not redirect to the correct casing' do + post :toggle_star, namespace_id: public_project.namespace, id: public_project.path.upcase + + expect(response).not_to have_http_status(301) + end + end + + context 'when requesting a redirected path' do + let!(:redirect_route) { public_project.redirect_routes.create!(path: "foo/bar") } + + it 'returns not found' do + post :toggle_star, namespace_id: 'foo', id: 'bar' + + expect(response).to have_http_status(404) + end + end + end + + context 'for a DELETE request' do + before do + sign_in(create(:admin)) + end + + context 'when requesting the canonical path with different casing' do + it 'does not 404' do + delete :destroy, namespace_id: project.namespace, id: project.path.upcase + + expect(response).not_to have_http_status(404) + end + + it 'does not redirect to the correct casing' do + delete :destroy, namespace_id: project.namespace, id: project.path.upcase + + expect(response).not_to have_http_status(301) + end + end + + context 'when requesting a redirected path' do + let!(:redirect_route) { project.redirect_routes.create!(path: "foo/bar") } + + it 'returns not found' do + delete :destroy, namespace_id: 'foo', id: 'bar' + + expect(response).to have_http_status(404) + end + end + end + end + def project_moved_message(redirect_route, project) "Project '#{redirect_route.path}' was moved to '#{project.full_path}'. Please update any links and bookmarks that may still have the old path." end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 1d61719f1d0..d33e2ba1e53 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -53,40 +53,6 @@ describe UsersController do end end - context 'when requesting the canonical path' do - let(:user) { create(:user, username: 'CamelCaseUser') } - - before { sign_in(user) } - - context 'with exactly matching casing' do - it 'responds with success' do - get :show, username: user.username - - expect(response).to be_success - end - end - - context 'with different casing' do - it 'redirects to the correct casing' do - get :show, username: user.username.downcase - - expect(response).to redirect_to(user) - expect(controller).not_to set_flash[:notice] - end - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') } - - it 'redirects to the canonical path' do - get :show, username: redirect_route.path - - expect(response).to redirect_to(user) - expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) - end - end - context 'when a user by that username does not exist' do context 'when logged out' do it 'redirects to login page' do @@ -131,40 +97,6 @@ describe UsersController do expect(assigns(:contributions_calendar).projects.count).to eq(2) end end - - context 'when requesting the canonical path' do - let(:user) { create(:user, username: 'CamelCaseUser') } - - before { sign_in(user) } - - context 'with exactly matching casing' do - it 'responds with success' do - get :calendar, username: user.username - - expect(response).to be_success - end - end - - context 'with different casing' do - it 'redirects to the correct casing' do - get :calendar, username: user.username.downcase - - expect(response).to redirect_to(user_calendar_path(user)) - expect(controller).not_to set_flash[:notice] - end - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') } - - it 'redirects to the canonical path' do - get :calendar, username: redirect_route.path - - expect(response).to redirect_to(user_calendar_path(user)) - expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) - end - end end describe 'GET #calendar_activities' do @@ -187,38 +119,6 @@ describe UsersController do get :calendar_activities, username: user.username expect(response).to render_template('calendar_activities') end - - context 'when requesting the canonical path' do - let(:user) { create(:user, username: 'CamelCaseUser') } - - context 'with exactly matching casing' do - it 'responds with success' do - get :calendar_activities, username: user.username - - expect(response).to be_success - end - end - - context 'with different casing' do - it 'redirects to the correct casing' do - get :calendar_activities, username: user.username.downcase - - expect(response).to redirect_to(user_calendar_activities_path(user)) - expect(controller).not_to set_flash[:notice] - end - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') } - - it 'redirects to the canonical path' do - get :calendar_activities, username: redirect_route.path - - expect(response).to redirect_to(user_calendar_activities_path(user)) - expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) - end - end end describe 'GET #snippets' do @@ -241,38 +141,6 @@ describe UsersController do expect(JSON.parse(response.body)).to have_key('html') end end - - context 'when requesting the canonical path' do - let(:user) { create(:user, username: 'CamelCaseUser') } - - context 'with exactly matching casing' do - it 'responds with success' do - get :snippets, username: user.username - - expect(response).to be_success - end - end - - context 'with different casing' do - it 'redirects to the correct casing' do - get :snippets, username: user.username.downcase - - expect(response).to redirect_to(user_snippets_path(user)) - expect(controller).not_to set_flash[:notice] - end - end - end - - context 'when requesting a redirected path' do - let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') } - - it 'redirects to the canonical path' do - get :snippets, username: redirect_route.path - - expect(response).to redirect_to(user_snippets_path(user)) - expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) - end - end end describe 'GET #exists' do @@ -321,6 +189,127 @@ describe UsersController do end end + describe '#ensure_canonical_path' do + before do + sign_in(user) + end + + context 'for a GET request' do + context 'when requesting users at the root path' do + context 'when requesting the canonical path' do + let(:user) { create(:user, username: 'CamelCaseUser') } + + context 'with exactly matching casing' do + it 'responds with success' do + get :show, username: user.username + + expect(response).to be_success + end + end + + context 'with different casing' do + it 'redirects to the correct casing' do + get :show, username: user.username.downcase + + expect(response).to redirect_to(user) + expect(controller).not_to set_flash[:notice] + end + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-path') } + + it 'redirects to the canonical path' do + get :show, username: redirect_route.path + + expect(response).to redirect_to(user) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + + context 'when the old path is a substring of the scheme or host' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'http') } + + it 'does not modify the requested host' do + get :show, username: redirect_route.path + + expect(response).to redirect_to(user) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + end + + context 'when the old path is substring of users' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'ser') } + + it 'redirects to the canonical path' do + get :show, username: redirect_route.path + + expect(response).to redirect_to(user) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + end + end + end + + context 'when requesting users under the /users path' do + context 'when requesting the canonical path' do + let(:user) { create(:user, username: 'CamelCaseUser') } + + context 'with exactly matching casing' do + it 'responds with success' do + get :projects, username: user.username + + expect(response).to be_success + end + end + + context 'with different casing' do + it 'redirects to the correct casing' do + get :projects, username: user.username.downcase + + expect(response).to redirect_to(user_projects_path(user)) + expect(controller).not_to set_flash[:notice] + end + end + end + + context 'when requesting a redirected path' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-path') } + + it 'redirects to the canonical path' do + get :projects, username: redirect_route.path + + expect(response).to redirect_to(user_projects_path(user)) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + + context 'when the old path is a substring of the scheme or host' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'http') } + + it 'does not modify the requested host' do + get :projects, username: redirect_route.path + + expect(response).to redirect_to(user_projects_path(user)) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + end + + context 'when the old path is substring of users' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'ser') } + + # I.e. /users/ser should not become /ufoos/ser + it 'does not modify the /users part of the path' do + get :projects, username: redirect_route.path + + expect(response).to redirect_to(user_projects_path(user)) + expect(controller).to set_flash[:notice].to(user_moved_message(redirect_route, user)) + end + end + end + end + end + end + def user_moved_message(redirect_route, user) "User '#{redirect_route.path}' was moved to '#{user.full_path}'. Please update any links and bookmarks that may still have the old path." end |