Skip to content

Commit 9a31752

Browse files
committed
feat(user): prevent destroying primary email
1 parent 013b167 commit 9a31752

File tree

7 files changed

+38
-19
lines changed

7 files changed

+38
-19
lines changed

app/controllers/user/emails_controller.rb

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ def create
1414
end
1515

1616
def destroy
17+
if @email.primary?
18+
@email.errors.add(:base, I18n.t('user.emails.index.cannot_delete_primary'))
19+
render json: { errors: @email.errors.full_messages.to_sentence }, status: :bad_request
20+
21+
return
22+
end
23+
1724
if @email.destroy
1825
render_emails
1926
else

authentication/import/coursemology_realm.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -1800,22 +1800,22 @@
18001800
"false"
18011801
],
18021802
"findBySearchTerm": [
1803-
"SELECT users.id AS \"id\", users.id AS \"user_id\", pe.email AS \"username\", pe.email AS \"email\", users.name AS \"firstName\", CASE WHEN MAX(CASE WHEN pe.confirmed_at IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'TRUE' ELSE NULL END AS \"EMAIL_VERIFIED\" FROM user_emails ue LEFT JOIN users ON ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE WHERE LOWER(pe.email) LIKE LOWER(CONCAT('%', ?, '%')) OR LOWER(users.name) LIKE LOWER(CONCAT('%', ?, '%')) OR (ue.primary = FALSE AND ue.confirmed_at IS NOT NULL AND LOWER(ue.email) LIKE LOWER(CONCAT('%', ?, '%'))) GROUP BY pe.id, users.id, pe.email, users.name"
1803+
"SELECT users.id AS \"id\", users.id AS \"user_id\", pe.email AS \"username\", pe.email AS \"email\", users.name AS \"firstName\", CASE WHEN MAX(CASE WHEN pe.confirmed_at IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'TRUE' ELSE 'FALSE' END AS \"EMAIL_VERIFIED\" FROM user_emails ue LEFT JOIN users ON ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE WHERE LOWER(pe.email) LIKE LOWER(CONCAT('%', ?, '%')) OR LOWER(users.name) LIKE LOWER(CONCAT('%', ?, '%')) OR (ue.primary = FALSE AND ue.confirmed_at IS NOT NULL AND LOWER(ue.email) LIKE LOWER(CONCAT('%', ?, '%'))) GROUP BY pe.id, users.id, pe.email, users.name"
18041804
],
18051805
"password": [
18061806
"password"
18071807
],
18081808
"findByUsername": [
1809-
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as \"EMAIL_VERIFIED\" from user_emails ue left join users on ue.user_id = users.id left join user_emails pe on ue.user_id = pe.user_id AND pe.primary = TRUE where LOWER(ue.email) = LOWER(?) AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at"
1809+
"SELECT users.id as \"id\", users.id as \"user_id\", ue.email as \"username\", ue.email as \"email\", users.name as \"firstName\", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as \"EMAIL_VERIFIED\" from user_emails ue left join users on ue.user_id = users.id where LOWER(ue.email) = LOWER(?) GROUP BY users.id, ue.email, users.name, ue.confirmed_at"
18101810
],
18111811
"findById": [
1812-
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as \"EMAIL_VERIFIED\" from user_emails ue left join users on ue.user_id = users.id left join user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE where cast(users.id as character varying) = ? AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at"
1812+
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\" from user_emails pe left join users on pe.user_id = users.id where cast(users.id as character varying) = ? AND pe.primary = TRUE AND pe.confirmed_at IS NOT NULL GROUP BY users.id, pe.email, users.name"
18131813
],
18141814
"listAll": [
1815-
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\", CASE WHEN pe.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as \"EMAIL_VERIFIED\" from user_emails pe left join users on pe.user_id = users.id where pe.primary = TRUE"
1815+
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\", CASE WHEN pe.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as \"EMAIL_VERIFIED\" from user_emails pe left join users on pe.user_id = users.id where pe.primary = TRUE"
18161816
],
18171817
"findByEmail": [
1818-
"SELECT users.id as \"id\", users.id as \"user_id\", pe.email as \"username\", pe.email as \"email\", users.name as \"firstName\", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as \"EMAIL_VERIFIED\" from user_emails ue left join users on ue.user_id = users.id left join user_emails pe on ue.user_id = pe.user_id AND pe.primary = TRUE where LOWER(ue.email) = LOWER(?) AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at"
1818+
"SELECT users.id as \"id\", users.id as \"user_id\", ue.email as \"username\", ue.email as \"email\", users.name as \"firstName\", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as \"EMAIL_VERIFIED\" from user_emails ue left join users on ue.user_id = users.id where LOWER(ue.email) = LOWER(?) GROUP BY users.id, ue.email, users.name, ue.confirmed_at"
18191819
],
18201820
"user": [
18211821
"postgres"
@@ -4180,7 +4180,7 @@
41804180
"select count(*) from users"
41814181
],
41824182
"cachePolicy": [
4183-
"DEFAULT"
4183+
"NO_CACHE"
41844184
],
41854185
"url": [
41864186
"jdbc:postgresql://host.docker.internal:5432/coursemology_test"

authentication/script/cm_db_federation.sql

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
select count(*) from users;
33

44
-- List All Users SQL query
5-
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName", CASE WHEN pe.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as "EMAIL_VERIFIED" from user_emails pe left join users on pe.user_id = users.id where pe.primary = TRUE
5+
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName", CASE WHEN pe.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as "EMAIL_VERIFIED" from user_emails pe left join users on pe.user_id = users.id where pe.primary = TRUE
66

77
-- Find user by id SQL query
8-
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as "EMAIL_VERIFIED" from user_emails ue left join users on ue.user_id = users.id left join user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE where cast(users.id as character varying) = ? AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at
8+
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName" from user_emails pe left join users on pe.user_id = users.id where cast(users.id as character varying) = ? AND pe.primary = TRUE and pe.confirmed_at IS NOT NULL GROUP BY users.id, pe.email, users.name
99

1010
-- Find user by username SQL query
11-
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as "EMAIL_VERIFIED" from user_emails ue left join users on ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE where LOWER(ue.email) = LOWER(?) AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at
11+
select users.id as "id", users.id as "user_id", ue.email as "username", ue.email as "email", users.name as "firstName", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as "EMAIL_VERIFIED" from user_emails ue left join users on ue.user_id = users.id where LOWER(ue.email) = LOWER(?) GROUP BY users.id, ue.email, users.name, ue.confirmed_at
1212

1313
-- Find user by email SQL query
14-
select users.id as "id", users.id as "user_id", pe.email as "username", pe.email as "email", users.name as "firstName", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE NULL END as "EMAIL_VERIFIED" from user_emails ue left join users on ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE where LOWER(ue.email) = LOWER(?) AND ue.confirmed_at IS NOT NULL GROUP BY pe.id, users.id, pe.email, users.name, ue.confirmed_at
14+
select users.id as "id", users.id as "user_id", ue.email as "username", ue.email as "email", users.name as "firstName", CASE WHEN ue.confirmed_at IS NOT NULL THEN 'TRUE' ELSE 'FALSE' END as "EMAIL_VERIFIED" from user_emails ue left join users on ue.user_id = users.id where LOWER(ue.email) = LOWER(?) GROUP BY users.id, ue.email, users.name, ue.confirmed_at
1515

1616
-- Find user by search term SQL query
17-
SELECT users.id AS "id", users.id AS "user_id", pe.email AS "username", pe.email AS "email", users.name AS "firstName", CASE WHEN MAX(CASE WHEN pe.confirmed_at IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'TRUE' ELSE NULL END AS "EMAIL_VERIFIED" FROM user_emails ue LEFT JOIN users ON ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE WHERE LOWER(pe.email) LIKE LOWER(CONCAT('%', ?, '%')) OR LOWER(users.name) LIKE LOWER(CONCAT('%', ?, '%')) OR (ue.primary = FALSE AND ue.confirmed_at IS NOT NULL AND LOWER(ue.email) LIKE LOWER(CONCAT('%', ?, '%'))) GROUP BY pe.id, users.id, pe.email, users.name
17+
SELECT users.id AS "id", users.id AS "user_id", pe.email AS "username", pe.email AS "email", users.name AS "firstName", CASE WHEN MAX(CASE WHEN pe.confirmed_at IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'TRUE' ELSE 'FALSE' END AS "EMAIL_VERIFIED" FROM user_emails ue LEFT JOIN users ON ue.user_id = users.id LEFT JOIN user_emails pe ON ue.user_id = pe.user_id AND pe.primary = TRUE WHERE LOWER(pe.email) LIKE LOWER(CONCAT('%', ?, '%')) OR LOWER(users.name) LIKE LOWER(CONCAT('%', ?, '%')) OR (ue.primary = FALSE AND ue.confirmed_at IS NOT NULL AND LOWER(ue.email) LIKE LOWER(CONCAT('%', ?, '%'))) GROUP BY pe.id, users.id, pe.email, users.name
1818

1919
-- Find password hash (blowfish or hash digest hex) SQL query
2020
select encrypted_password as hash_pwd from users right join user_emails ue on users.id = ue.user_id where ue.email = ?

config/locales/en/user/emails.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ en:
33
emails:
44
index:
55
header: 'Emails'
6+
cannot_delete_primary: 'You cannot delete the primary email.'
67
set_primary:
78
no_confirmed_emails: 'There are no confirmed emails to set as the primary email.'
89
send_confirmation:

config/locales/ko/user/emails.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ko:
33
emails:
44
index:
55
header: '이메일'
6+
cannot_delete_primary: '기본 이메일을 삭제할 수 없습니다.'
67
set_primary:
78
no_confirmed_emails: '기본 이메일로 설정할 수 있는 확인된 이메일 주소가 없습니다.'
89
send_confirmation:

config/locales/zh/user/emails.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ zh:
33
emails:
44
index:
55
header: '邮箱'
6+
cannot_delete_primary: '不能删除主要邮箱。'
67
set_primary:
78
no_confirmed_emails: '没有已验证的邮箱地址可以设置为主要邮箱。'
89
send_confirmation:

spec/controllers/user/emails_controller_spec.rb

+17-8
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66

77
with_tenant(:instance) do
88
let!(:user) { create(:administrator) }
9+
let!(:primary_email) { user.send(:default_email_record) }
10+
let!(:non_primary_email) { create(:user_email, user: user, primary: false) }
911
before { controller_sign_in(controller, user) }
1012

11-
describe '#destroy' do
12-
subject { delete :destroy, as: :json, params: { id: user.send(:default_email_record) } }
13+
describe '#destroy_primary_email' do
14+
subject { delete :destroy, as: :json, params: { id: primary_email.id } }
1315

1416
context 'when the user only has one email address' do
1517
it 'cannot be deleted' do
@@ -19,16 +21,23 @@
1921
end
2022

2123
context 'when destroying a primary email' do
22-
let!(:non_primary_email) { create(:user_email, user: user, primary: false) }
2324
before { controller_sign_in(controller, user) }
2425

25-
it 'deletes the primary email' do
26-
expect { subject }.to change { user.emails.count }.by(-1)
26+
it 'cannot be deleted' do
27+
expect { subject }.to change { user.emails.count }.by(0)
2728
end
29+
it { is_expected.to have_http_status(:bad_request) }
30+
end
31+
end
2832

29-
it 'sets another email as primary' do
30-
subject
31-
expect(non_primary_email.reload).to be_primary
33+
describe '#destroy_non_primary_email' do
34+
subject { delete :destroy, as: :json, params: { id: non_primary_email.id } }
35+
36+
context 'when destroying a non-primary email' do
37+
before { controller_sign_in(controller, user) }
38+
39+
it 'deletes that email' do
40+
expect { subject }.to change { user.emails.count }.by(-1)
3241
end
3342
end
3443
end

0 commit comments

Comments
 (0)