Commit 8d69436c authored by Douwe Maan's avatar Douwe Maan

Validate user namespace before saving so that errors persist on model

parent 2d5f10b2
...@@ -20,6 +20,9 @@ class Namespace < ActiveRecord::Base ...@@ -20,6 +20,9 @@ class Namespace < ActiveRecord::Base
has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :project_statistics has_many :project_statistics
# This should _not_ be `inverse_of: :namespace`, because that would also set
# `user.namespace` when this user creates a group with themselves as `owner`.
belongs_to :owner, class_name: "User" belongs_to :owner, class_name: "User"
belongs_to :parent, class_name: "Namespace" belongs_to :parent, class_name: "Namespace"
......
...@@ -77,7 +77,7 @@ class User < ActiveRecord::Base ...@@ -77,7 +77,7 @@ class User < ActiveRecord::Base
# #
# Namespace for personal projects # Namespace for personal projects
has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, autosave: true # rubocop:disable Cop/ActiveRecordDependent has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, autosave: true # rubocop:disable Cop/ActiveRecordDependent
# Profile # Profile
has_many :keys, -> do has_many :keys, -> do
...@@ -171,7 +171,7 @@ class User < ActiveRecord::Base ...@@ -171,7 +171,7 @@ class User < ActiveRecord::Base
before_save :ensure_user_rights_and_limits, if: ->(user) { user.new_record? || user.external_changed? } before_save :ensure_user_rights_and_limits, if: ->(user) { user.new_record? || user.external_changed? }
before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) } before_save :skip_reconfirmation!, if: ->(user) { user.email_changed? && user.read_only_attribute?(:email) }
before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? } before_save :check_for_verified_email, if: ->(user) { user.email_changed? && !user.new_record? }
after_save :ensure_namespace_correct before_validation :ensure_namespace_correct
after_update :username_changed_hook, if: :username_changed? after_update :username_changed_hook, if: :username_changed?
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
after_destroy :remove_key_cache after_destroy :remove_key_cache
...@@ -884,16 +884,10 @@ class User < ActiveRecord::Base ...@@ -884,16 +884,10 @@ class User < ActiveRecord::Base
end end
def ensure_namespace_correct def ensure_namespace_correct
# Ensure user has namespace if namespace
create_namespace!(path: username, name: username) unless namespace namespace.path = namespace.name = username if username_changed?
else
if username_changed? build_namespace(path: username, name: username)
unless namespace.update_attributes(path: username, name: username)
namespace.errors.each do |attribute, message|
self.errors.add(:"namespace_#{attribute}", message)
end
raise ActiveRecord::RecordInvalid.new(namespace)
end
end end
end end
......
---
title: Validate user namespace before saving so that errors persist on model
merge_request:
author:
type: fixed
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ClosingIssueExtractor do describe Gitlab::ClosingIssueExtractor do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:project2) { create(:project) } let(:project2) { create(:project) }
let(:forked_project) { Projects::ForkService.new(project, project.creator).execute } let(:forked_project) { Projects::ForkService.new(project, project2.creator).execute }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:issue2) { create(:issue, project: project2) } let(:issue2) { create(:issue, project: project2) }
let(:reference) { issue.to_reference } let(:reference) { issue.to_reference }
...@@ -14,6 +14,7 @@ describe Gitlab::ClosingIssueExtractor do ...@@ -14,6 +14,7 @@ describe Gitlab::ClosingIssueExtractor do
before do before do
project.add_developer(project.creator) project.add_developer(project.creator)
project.add_developer(project2.creator)
project2.add_master(project.creator) project2.add_master(project.creator)
end end
......
...@@ -101,7 +101,7 @@ describe User do ...@@ -101,7 +101,7 @@ describe User do
user = build(:user, username: 'dashboard') user = build(:user, username: 'dashboard')
expect(user).not_to be_valid expect(user).not_to be_valid
expect(user.errors.values).to eq [['dashboard is a reserved name']] expect(user.errors.messages[:username]).to eq ['dashboard is a reserved name']
end end
it 'allows child names' do it 'allows child names' do
...@@ -132,6 +132,23 @@ describe User do ...@@ -132,6 +132,23 @@ describe User do
expect(user.errors.messages[:username].first).to match('cannot be changed if a personal project has container registry tags') expect(user.errors.messages[:username].first).to match('cannot be changed if a personal project has container registry tags')
end end
end end
context 'when the username was used by another user before' do
let(:username) { 'foo' }
let!(:other_user) { create(:user, username: username) }
before do
other_user.username = 'bar'
other_user.save!
end
it 'is invalid' do
user = build(:user, username: username)
expect(user).not_to be_valid
expect(user.errors.messages[:"namespace.route.path"].first).to eq('foo has been taken before. Please use another one')
end
end
end end
it 'has a DB-level NOT NULL constraint on projects_limit' do it 'has a DB-level NOT NULL constraint on projects_limit' do
...@@ -2623,7 +2640,7 @@ describe User do ...@@ -2623,7 +2640,7 @@ describe User do
it 'should raise an ActiveRecord::RecordInvalid exception' do it 'should raise an ActiveRecord::RecordInvalid exception' do
user2 = build(:user, username: 'foo') user2 = build(:user, username: 'foo')
expect { user2.save! }.to raise_error(ActiveRecord::RecordInvalid, /Route path foo has been taken before. Please use another one, Route is invalid/) expect { user2.save! }.to raise_error(ActiveRecord::RecordInvalid, /Namespace route path foo has been taken before/)
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::GitlabProjectsImportService do describe Projects::GitlabProjectsImportService do
set(:namespace) { build(:namespace) } set(:namespace) { create(:namespace) }
let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') } let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
subject { described_class.new(namespace.owner, { namespace_id: namespace.id, path: path, file: file }) } subject { described_class.new(namespace.owner, { namespace_id: namespace.id, path: path, file: file }) }
......
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