Implemented issue #1251 - Config option to have uniq email addresses for users.
This commit is contained in:
parent
547f002f71
commit
866614122d
9 changed files with 207 additions and 53 deletions
|
@ -132,6 +132,10 @@ class UsersController < ApplicationController
|
||||||
raise Exceptions::UnprocessableEntity, 'Attribute \'email\' required!'
|
raise Exceptions::UnprocessableEntity, 'Attribute \'email\' required!'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# check if user already exists
|
||||||
|
exists = User.find_by(email: clean_params[:email].downcase.strip)
|
||||||
|
raise Exceptions::UnprocessableEntity, 'Email address is already used for other user.' if exists
|
||||||
|
|
||||||
user = User.new(clean_params)
|
user = User.new(clean_params)
|
||||||
user.associations_from_param(params)
|
user.associations_from_param(params)
|
||||||
user.updated_by_id = 1
|
user.updated_by_id = 1
|
||||||
|
@ -171,11 +175,6 @@ class UsersController < ApplicationController
|
||||||
user.associations_from_param(params)
|
user.associations_from_param(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
# check if user already exists
|
|
||||||
if user.email.present?
|
|
||||||
exists = User.where(email: user.email.downcase).first
|
|
||||||
raise Exceptions::UnprocessableEntity, 'User already exists!' if exists
|
|
||||||
end
|
|
||||||
user.save!
|
user.save!
|
||||||
|
|
||||||
# if first user was added, set system init done
|
# if first user was added, set system init done
|
||||||
|
|
|
@ -38,7 +38,7 @@ class User < ApplicationModel
|
||||||
load 'user/search_index.rb'
|
load 'user/search_index.rb'
|
||||||
include User::SearchIndex
|
include User::SearchIndex
|
||||||
|
|
||||||
before_validation :check_name, :check_email, :check_login, :ensure_password, :ensure_roles, :ensure_identifier
|
before_validation :check_name, :check_email, :check_login, :ensure_uniq_email, :ensure_password, :ensure_roles, :ensure_identifier
|
||||||
before_create :check_preferences_default, :validate_roles, :domain_based_assignment, :set_locale
|
before_create :check_preferences_default, :validate_roles, :domain_based_assignment, :set_locale
|
||||||
before_update :check_preferences_default, :validate_roles, :reset_login_failed
|
before_update :check_preferences_default, :validate_roles, :reset_login_failed
|
||||||
after_create :avatar_for_email_check
|
after_create :avatar_for_email_check
|
||||||
|
@ -845,7 +845,7 @@ returns
|
||||||
|
|
||||||
def check_email
|
def check_email
|
||||||
return true if Setting.get('import_mode')
|
return true if Setting.get('import_mode')
|
||||||
return true if email.empty?
|
return true if email.blank?
|
||||||
self.email = email.downcase.strip
|
self.email = email.downcase.strip
|
||||||
return true if id == 1
|
return true if id == 1
|
||||||
raise Exceptions::UnprocessableEntity, 'Invalid email' if email !~ /@/
|
raise Exceptions::UnprocessableEntity, 'Invalid email' if email !~ /@/
|
||||||
|
@ -897,6 +897,16 @@ returns
|
||||||
raise Exceptions::UnprocessableEntity, 'Minimum one identifier (login, firstname, lastname, phone or email) for user is required.'
|
raise Exceptions::UnprocessableEntity, 'Minimum one identifier (login, firstname, lastname, phone or email) for user is required.'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_uniq_email
|
||||||
|
return true if Setting.get('user_email_multiple_use')
|
||||||
|
return true if Setting.get('import_mode')
|
||||||
|
return true if email.blank?
|
||||||
|
return true if !changes
|
||||||
|
return true if !changes['email']
|
||||||
|
return true if !User.find_by(email: email.downcase.strip)
|
||||||
|
raise Exceptions::UnprocessableEntity, 'Email address is already used for other user.'
|
||||||
|
end
|
||||||
|
|
||||||
def validate_roles
|
def validate_roles
|
||||||
return true if !role_ids
|
return true if !role_ids
|
||||||
role_ids.each { |role_id|
|
role_ids.each { |role_id|
|
||||||
|
|
34
db/migrate/20170714000002_user_email_multiple_use.rb
Normal file
34
db/migrate/20170714000002_user_email_multiple_use.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
class UserEmailMultipleUse < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'User email for muliple users',
|
||||||
|
name: 'user_email_multiple_use',
|
||||||
|
area: 'Model::User',
|
||||||
|
description: 'Allow to use email address for muliple users.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'user_email_multiple_use',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
preferences: {
|
||||||
|
permission: ['admin'],
|
||||||
|
},
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -705,6 +705,31 @@ Setting.create_if_not_exists(
|
||||||
},
|
},
|
||||||
frontend: true
|
frontend: true
|
||||||
)
|
)
|
||||||
|
Setting.create_if_not_exists(
|
||||||
|
title: 'User email for muliple users',
|
||||||
|
name: 'user_email_multiple_use',
|
||||||
|
area: 'Model::User',
|
||||||
|
description: 'Allow to use email address for muliple users.',
|
||||||
|
options: {
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
display: '',
|
||||||
|
null: true,
|
||||||
|
name: 'user_email_multiple_use',
|
||||||
|
tag: 'boolean',
|
||||||
|
options: {
|
||||||
|
true => 'yes',
|
||||||
|
false => 'no',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
state: false,
|
||||||
|
preferences: {
|
||||||
|
permission: ['admin'],
|
||||||
|
},
|
||||||
|
frontend: false
|
||||||
|
)
|
||||||
Setting.create_if_not_exists(
|
Setting.create_if_not_exists(
|
||||||
title: 'Authentication via %s',
|
title: 'Authentication via %s',
|
||||||
name: 'auth_ldap',
|
name: 'auth_ldap',
|
||||||
|
|
|
@ -126,7 +126,15 @@ class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_response(422)
|
assert_response(422)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
assert(result['error'])
|
assert(result['error'])
|
||||||
assert_equal('User already exists!', result['error'])
|
assert_equal('Email address is already used for other user.', result['error'])
|
||||||
|
|
||||||
|
# email missing with enabled feature
|
||||||
|
params = { firstname: 'some firstname', signup: true }
|
||||||
|
post '/api/v1/users', params.to_json, headers
|
||||||
|
assert_response(422)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert(result['error'])
|
||||||
|
assert_equal('Attribute \'email\' required!', result['error'])
|
||||||
|
|
||||||
# email missing with enabled feature
|
# email missing with enabled feature
|
||||||
params = { firstname: 'some firstname', signup: true }
|
params = { firstname: 'some firstname', signup: true }
|
||||||
|
@ -330,7 +338,7 @@ class UserOrganizationControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_response(422)
|
assert_response(422)
|
||||||
result = JSON.parse(@response.body)
|
result = JSON.parse(@response.body)
|
||||||
assert(result)
|
assert(result)
|
||||||
assert_equal('User already exists!', result['error'])
|
assert_equal('Email address is already used for other user.', result['error'])
|
||||||
|
|
||||||
# missing required attributes
|
# missing required attributes
|
||||||
params = { note: 'some note' }
|
params = { note: 'some note' }
|
||||||
|
|
|
@ -51,7 +51,7 @@ class GeoLocationTest < ActiveSupport::TestCase
|
||||||
login: 'some_geo_login2',
|
login: 'some_geo_login2',
|
||||||
firstname: 'First',
|
firstname: 'First',
|
||||||
lastname: 'Last',
|
lastname: 'Last',
|
||||||
email: 'some_geo_login1@example.com',
|
email: 'some_geo_login2@example.com',
|
||||||
password: 'test',
|
password: 'test',
|
||||||
street: 'Marienstrasse 13',
|
street: 'Marienstrasse 13',
|
||||||
city: 'Berlin',
|
city: 'Berlin',
|
||||||
|
|
|
@ -10,7 +10,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
login: 'admin',
|
login: 'admin',
|
||||||
firstname: 'Bob',
|
firstname: 'Bob',
|
||||||
lastname: 'Smith',
|
lastname: 'Smith',
|
||||||
email: 'bob@example.com',
|
email: 'bob+active_stream@example.com',
|
||||||
password: 'some_pass',
|
password: 'some_pass',
|
||||||
active: true,
|
active: true,
|
||||||
roles: roles,
|
roles: roles,
|
||||||
|
@ -23,7 +23,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'ticket+user' do
|
test 'ticket+user' do
|
||||||
ticket = Ticket.create(
|
ticket = Ticket.create!(
|
||||||
group_id: Group.lookup(name: 'Users').id,
|
group_id: Group.lookup(name: 'Users').id,
|
||||||
customer_id: @current_user.id,
|
customer_id: @current_user.id,
|
||||||
owner_id: User.lookup(login: '-').id,
|
owner_id: User.lookup(login: '-').id,
|
||||||
|
@ -35,7 +35,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
)
|
)
|
||||||
travel 2.seconds
|
travel 2.seconds
|
||||||
|
|
||||||
article = Ticket::Article.create(
|
article = Ticket::Article.create!(
|
||||||
ticket_id: ticket.id,
|
ticket_id: ticket.id,
|
||||||
updated_by_id: @current_user.id,
|
updated_by_id: @current_user.id,
|
||||||
created_by_id: @current_user.id,
|
created_by_id: @current_user.id,
|
||||||
|
@ -86,12 +86,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
assert(stream.empty?)
|
assert(stream.empty?)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
ticket.destroy
|
ticket.destroy!
|
||||||
travel_back
|
travel_back
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'organization' do
|
test 'organization' do
|
||||||
organization = Organization.create(
|
organization = Organization.create!(
|
||||||
name: 'some name',
|
name: 'some name',
|
||||||
updated_by_id: @current_user.id,
|
updated_by_id: @current_user.id,
|
||||||
created_by_id: @current_user.id,
|
created_by_id: @current_user.id,
|
||||||
|
@ -125,12 +125,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
assert(stream.empty?)
|
assert(stream.empty?)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
organization.destroy
|
organization.destroy!
|
||||||
travel_back
|
travel_back
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'user with update check false' do
|
test 'user with update check false' do
|
||||||
user = User.create(
|
user = User.create!(
|
||||||
login: 'someemail@example.com',
|
login: 'someemail@example.com',
|
||||||
email: 'someemail@example.com',
|
email: 'someemail@example.com',
|
||||||
firstname: 'Bob Smith II',
|
firstname: 'Bob Smith II',
|
||||||
|
@ -157,12 +157,12 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
assert(stream.empty?)
|
assert(stream.empty?)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
user.destroy
|
user.destroy!
|
||||||
travel_back
|
travel_back
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'user with update check true' do
|
test 'user with update check true' do
|
||||||
user = User.create(
|
user = User.create!(
|
||||||
login: 'someemail@example.com',
|
login: 'someemail@example.com',
|
||||||
email: 'someemail@example.com',
|
email: 'someemail@example.com',
|
||||||
firstname: 'Bob Smith II',
|
firstname: 'Bob Smith II',
|
||||||
|
@ -204,7 +204,7 @@ class ActivityStreamTest < ActiveSupport::TestCase
|
||||||
assert(stream.empty?)
|
assert(stream.empty?)
|
||||||
|
|
||||||
# cleanup
|
# cleanup
|
||||||
user.destroy
|
user.destroy!
|
||||||
travel_back
|
travel_back
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -155,12 +155,12 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
# use transaction
|
# use transaction
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
ticket = Ticket.create(test[:ticket_create][:ticket])
|
ticket = Ticket.create!(test[:ticket_create][:ticket])
|
||||||
test[:ticket_create][:article][:ticket_id] = ticket.id
|
test[:ticket_create][:article][:ticket_id] = ticket.id
|
||||||
article = Ticket::Article.create(test[:ticket_create][:article])
|
article = Ticket::Article.create!(test[:ticket_create][:article])
|
||||||
|
|
||||||
assert_equal(ticket.class.to_s, 'Ticket')
|
assert_equal(ticket.class, Ticket)
|
||||||
assert_equal(article.class.to_s, 'Ticket::Article')
|
assert_equal(article.class, Ticket::Article)
|
||||||
|
|
||||||
# update ticket
|
# update ticket
|
||||||
if test[:ticket_update][:ticket]
|
if test[:ticket_update][:ticket]
|
||||||
|
@ -185,25 +185,21 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
# delete tickets
|
# delete tickets
|
||||||
tickets.each { |ticket|
|
tickets.each(&:destroy!)
|
||||||
ticket_id = ticket.id
|
|
||||||
ticket.destroy
|
|
||||||
found = Ticket.where(id: ticket_id).first
|
|
||||||
assert_not(found, 'Ticket destroyed')
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'user' do
|
test 'user' do
|
||||||
|
name = rand(999_999)
|
||||||
tests = [
|
tests = [
|
||||||
|
|
||||||
# test 1
|
# test 1
|
||||||
{
|
{
|
||||||
user_create: {
|
user_create: {
|
||||||
user: {
|
user: {
|
||||||
login: 'some_login_test',
|
login: "some_login_test-#{name}",
|
||||||
firstname: 'Bob',
|
firstname: 'Bob',
|
||||||
lastname: 'Smith',
|
lastname: 'Smith',
|
||||||
email: 'somebody@example.com',
|
email: "somebody-#{name}@example.com",
|
||||||
active: true,
|
active: true,
|
||||||
updated_by_id: current_user.id,
|
updated_by_id: current_user.id,
|
||||||
created_by_id: current_user.id,
|
created_by_id: current_user.id,
|
||||||
|
@ -213,7 +209,7 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
user: {
|
user: {
|
||||||
firstname: 'Bob',
|
firstname: 'Bob',
|
||||||
lastname: 'Master',
|
lastname: 'Master',
|
||||||
email: 'master@example.com',
|
email: "master-#{name}@example.com",
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -236,8 +232,8 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
history_object: 'User',
|
history_object: 'User',
|
||||||
history_type: 'updated',
|
history_type: 'updated',
|
||||||
history_attribute: 'email',
|
history_attribute: 'email',
|
||||||
value_from: 'somebody@example.com',
|
value_from: "somebody-#{name}@example.com",
|
||||||
value_to: 'master@example.com',
|
value_to: "master-#{name}@example.com",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
result: true,
|
result: true,
|
||||||
|
@ -258,9 +254,8 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
# user transaction
|
# user transaction
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
user = User.create(test[:user_create][:user])
|
user = User.create!(test[:user_create][:user])
|
||||||
|
assert_equal(user.class, User)
|
||||||
assert_equal(user.class.to_s, 'User')
|
|
||||||
|
|
||||||
# update user
|
# update user
|
||||||
if test[:user_update][:user]
|
if test[:user_update][:user]
|
||||||
|
@ -277,12 +272,7 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
# delete user
|
# delete user
|
||||||
users.each { |user|
|
users.each(&:destroy!)
|
||||||
user_id = user.id
|
|
||||||
user.destroy
|
|
||||||
found = User.where(id: user_id).first
|
|
||||||
assert_not(found, 'User destroyed')
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test 'organization' do
|
test 'organization' do
|
||||||
|
@ -328,9 +318,8 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
|
|
||||||
# user transaction
|
# user transaction
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
organization = Organization.create(test[:organization_create][:organization])
|
organization = Organization.create!(test[:organization_create][:organization])
|
||||||
|
assert_equal(organization.class, Organization)
|
||||||
assert_equal(organization.class.to_s, 'Organization')
|
|
||||||
|
|
||||||
# update organization
|
# update organization
|
||||||
if test[:organization_update][:organization]
|
if test[:organization_update][:organization]
|
||||||
|
@ -346,12 +335,7 @@ class HistoryTest < ActiveSupport::TestCase
|
||||||
}
|
}
|
||||||
|
|
||||||
# delete user
|
# delete user
|
||||||
organizations.each { |organization|
|
organizations.each(&:destroy!)
|
||||||
organization_id = organization.id
|
|
||||||
organization.destroy
|
|
||||||
found = Organization.where(id: organization_id).first
|
|
||||||
assert_not(found, 'Organization destroyed')
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def history_check(history_list, history_check)
|
def history_check(history_list, history_check)
|
||||||
|
|
|
@ -368,6 +368,100 @@ class UserTest < ActiveSupport::TestCase
|
||||||
admin.destroy!
|
admin.destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'uniq email' do
|
||||||
|
name = rand(999_999_999)
|
||||||
|
|
||||||
|
email1 = "admin1-role_without_email#{name}@example.com"
|
||||||
|
admin1 = User.create!(
|
||||||
|
login: email1,
|
||||||
|
firstname: 'Role',
|
||||||
|
lastname: "Admin1#{name}",
|
||||||
|
email: email1,
|
||||||
|
password: 'adminpw',
|
||||||
|
active: true,
|
||||||
|
roles: Role.where(name: %w(Admin Agent)),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(admin1.id)
|
||||||
|
assert_equal(admin1.email, email1)
|
||||||
|
|
||||||
|
assert_raises(Exceptions::UnprocessableEntity) {
|
||||||
|
User.create!(
|
||||||
|
login: "#{email1}-1",
|
||||||
|
firstname: 'Role',
|
||||||
|
lastname: "Admin1#{name}",
|
||||||
|
email: email1,
|
||||||
|
password: 'adminpw',
|
||||||
|
active: true,
|
||||||
|
roles: Role.where(name: %w(Admin Agent)),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
email2 = "admin2-role_without_email#{name}@example.com"
|
||||||
|
admin2 = User.create!(
|
||||||
|
firstname: 'Role',
|
||||||
|
lastname: "Admin2#{name}",
|
||||||
|
email: email2,
|
||||||
|
password: 'adminpw',
|
||||||
|
active: true,
|
||||||
|
roles: Role.where(name: %w(Admin Agent)),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_raises(Exceptions::UnprocessableEntity) {
|
||||||
|
admin2.email = email1
|
||||||
|
admin2.save!
|
||||||
|
}
|
||||||
|
|
||||||
|
admin1.email = admin1.email
|
||||||
|
admin1.save!
|
||||||
|
|
||||||
|
admin2.destroy!
|
||||||
|
admin1.destroy!
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'uniq email - multiple use' do
|
||||||
|
Setting.set('user_email_multiple_use', true)
|
||||||
|
name = rand(999_999_999)
|
||||||
|
|
||||||
|
email1 = "admin1-role_without_email#{name}@example.com"
|
||||||
|
admin1 = User.create!(
|
||||||
|
login: email1,
|
||||||
|
firstname: 'Role',
|
||||||
|
lastname: "Admin1#{name}",
|
||||||
|
email: email1,
|
||||||
|
password: 'adminpw',
|
||||||
|
active: true,
|
||||||
|
roles: Role.where(name: %w(Admin Agent)),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert(admin1.id)
|
||||||
|
assert_equal(admin1.email, email1)
|
||||||
|
|
||||||
|
admin2 = User.create!(
|
||||||
|
login: "#{email1}-1",
|
||||||
|
firstname: 'Role',
|
||||||
|
lastname: "Admin1#{name}",
|
||||||
|
email: email1,
|
||||||
|
password: 'adminpw',
|
||||||
|
active: true,
|
||||||
|
roles: Role.where(name: %w(Admin Agent)),
|
||||||
|
updated_by_id: 1,
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(admin2.email, email1)
|
||||||
|
admin2.destroy!
|
||||||
|
admin1.destroy!
|
||||||
|
Setting.set('user_email_multiple_use', false)
|
||||||
|
end
|
||||||
|
|
||||||
test 'ensure roles' do
|
test 'ensure roles' do
|
||||||
name = rand(999_999_999)
|
name = rand(999_999_999)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue