diff --git a/server/server_test.go b/server/server_test.go index 384be7dc..dfad1fed 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1567,6 +1567,131 @@ func TestServer_PublishEmailAddressInvalid(t *testing.T) { }) } +func TestServer_PublishEmailVerify_VerifiedAddress(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + conf := newTestConfigWithAuthFile(t, databaseURL) + conf.SMTPSenderVerify = true + s := newTestServer(t, conf) + s.smtpSender = &testMailer{} + defer s.closeDatabases() + + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false)) + u, err := s.userManager.User("phil") + require.Nil(t, err) + require.Nil(t, s.userManager.AddEmail(u.ID, "phil@example.com")) + + // Verified address should succeed + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "phil@example.com", + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, response.Code) + + // Unverified address should fail + response = request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "other@example.com", + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 400, response.Code) + require.Equal(t, 40052, toHTTPError(t, response.Body.String()).Code) + }) +} + +func TestServer_PublishEmailVerify_BoolValue(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + conf := newTestConfigWithAuthFile(t, databaseURL) + conf.SMTPSenderVerify = true + s := newTestServer(t, conf) + s.smtpSender = &testMailer{} + defer s.closeDatabases() + + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false)) + u, err := s.userManager.User("phil") + require.Nil(t, err) + require.Nil(t, s.userManager.AddEmail(u.ID, "phil@example.com")) + + // "yes" should resolve to first verified email + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "yes", + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, response.Code) + + // "true" and "1" should also work + for _, val := range []string{"true", "1"} { + response = request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": val, + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 200, response.Code, "expected 200 for email: %s", val) + } + }) +} + +func TestServer_PublishEmailVerify_BoolValue_NoVerify(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + s := newTestServer(t, newTestConfig(t, databaseURL)) + s.smtpSender = &testMailer{} + + // "yes" without smtp-sender-verify should fail with invalid address + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "yes", + }) + require.Equal(t, 400, response.Code) + require.Equal(t, 40050, toHTTPError(t, response.Body.String()).Code) + }) +} + +func TestServer_PublishEmailVerify_Anonymous(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + conf := newTestConfigWithAuthFile(t, databaseURL) + conf.SMTPSenderVerify = true + s := newTestServer(t, conf) + s.smtpSender = &testMailer{} + defer s.closeDatabases() + + // Anonymous user should be rejected + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "test@example.com", + }) + require.Equal(t, 400, response.Code) + require.Equal(t, 40053, toHTTPError(t, response.Body.String()).Code) + }) +} + +func TestServer_PublishEmailVerify_NoVerifiedEmails(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + conf := newTestConfigWithAuthFile(t, databaseURL) + conf.SMTPSenderVerify = true + s := newTestServer(t, conf) + s.smtpSender = &testMailer{} + defer s.closeDatabases() + + require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleUser, false)) + + // Authenticated user with no verified emails should fail + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "phil@example.com", + "Authorization": util.BasicAuth("phil", "phil"), + }) + require.Equal(t, 400, response.Code) + require.Equal(t, 40052, toHTTPError(t, response.Body.String()).Code) + }) +} + +func TestServer_PublishEmailVerify_Disabled_Backwards_Compatible(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + s := newTestServer(t, newTestConfig(t, databaseURL)) + s.smtpSender = &testMailer{} + + // Without smtp-sender-verify, any email address should work (backwards compatible) + response := request(t, s, "PUT", "/mytopic", "hi", map[string]string{ + "Email": "anyone@example.com", + }) + require.Equal(t, 200, response.Code) + }) +} + func TestServer_PublishAndExpungeTopicAfter16Hours(t *testing.T) { forEachBackend(t, func(t *testing.T, databaseURL string) { t.Parallel() diff --git a/user/manager_test.go b/user/manager_test.go index c8e619cf..3bdb15b2 100644 --- a/user/manager_test.go +++ b/user/manager_test.go @@ -1137,6 +1137,60 @@ func TestUser_PhoneNumberAdd_Multiple_Users_Same_Number(t *testing.T) { }) } +func TestUser_EmailAddListRemove(t *testing.T) { + forEachBackend(t, func(t *testing.T, newManager newManagerFunc) { + a := newTestManager(t, newManager, PermissionDenyAll) + + require.Nil(t, a.AddUser("phil", "phil", RoleUser, false)) + phil, err := a.User("phil") + require.Nil(t, err) + require.Nil(t, a.AddEmail(phil.ID, "phil@example.com")) + + emails, err := a.Emails(phil.ID) + require.Nil(t, err) + require.Equal(t, 1, len(emails)) + require.Equal(t, "phil@example.com", emails[0]) + + require.Nil(t, a.RemoveEmail(phil.ID, "phil@example.com")) + emails, err = a.Emails(phil.ID) + require.Nil(t, err) + require.Equal(t, 0, len(emails)) + + // Paranoia check: We do NOT want to keep emails in there + rows, err := testDB(a).Query(`SELECT * FROM user_email`) + require.Nil(t, err) + require.False(t, rows.Next()) + require.Nil(t, rows.Close()) + }) +} + +func TestUser_EmailAdd_Multiple_Users_Same_Email(t *testing.T) { + forEachBackend(t, func(t *testing.T, newManager newManagerFunc) { + a := newTestManager(t, newManager, PermissionDenyAll) + + require.Nil(t, a.AddUser("phil", "phil", RoleUser, false)) + require.Nil(t, a.AddUser("ben", "ben", RoleUser, false)) + phil, err := a.User("phil") + require.Nil(t, err) + ben, err := a.User("ben") + require.Nil(t, err) + require.Nil(t, a.AddEmail(phil.ID, "shared@example.com")) + require.Nil(t, a.AddEmail(ben.ID, "shared@example.com")) + }) +} + +func TestUser_EmailAdd_Duplicate(t *testing.T) { + forEachBackend(t, func(t *testing.T, newManager newManagerFunc) { + a := newTestManager(t, newManager, PermissionDenyAll) + + require.Nil(t, a.AddUser("phil", "phil", RoleUser, false)) + phil, err := a.User("phil") + require.Nil(t, err) + require.Nil(t, a.AddEmail(phil.ID, "phil@example.com")) + require.ErrorIs(t, a.AddEmail(phil.ID, "phil@example.com"), ErrEmailExists) + }) +} + func TestManager_Topic_Wildcard_With_Asterisk_Underscore(t *testing.T) { forEachBackend(t, func(t *testing.T, newManager newManagerFunc) { a := newTestManager(t, newManager, PermissionDenyAll) @@ -2328,6 +2382,27 @@ func TestStorePhoneNumbers(t *testing.T) { }) } +func TestStoreEmails(t *testing.T) { + forEachStoreBackend(t, func(t *testing.T, manager *Manager) { + require.Nil(t, manager.AddUser("phil", "mypass", RoleUser, false)) + u, err := manager.User("phil") + require.Nil(t, err) + + require.Nil(t, manager.AddEmail(u.ID, "phil@example.com")) + require.Nil(t, manager.AddEmail(u.ID, "phil2@example.com")) + + emails, err := manager.Emails(u.ID) + require.Nil(t, err) + require.Len(t, emails, 2) + + require.Nil(t, manager.RemoveEmail(u.ID, "phil@example.com")) + emails, err = manager.Emails(u.ID) + require.Nil(t, err) + require.Len(t, emails, 1) + require.Equal(t, "phil2@example.com", emails[0]) + }) +} + func TestStoreChangeSettings(t *testing.T) { forEachStoreBackend(t, func(t *testing.T, manager *Manager) { require.Nil(t, manager.AddUser("phil", "mypass", RoleUser, false))