|
|
@@ -1012,6 +1012,7 @@ test('registration and verification resend use configured system email', async (
|
|
|
const registerBody = await register.json();
|
|
|
assert.equal(registerBody.user.status, 'pending_email');
|
|
|
assert.equal(registerBody.verificationEmailSent, true);
|
|
|
+ assert.equal(registerBody.message, '注册成功,验证邮件已发送,请先验证邮箱。');
|
|
|
assert.equal(countAccountTokensForUser(dataDir, sessionSecret, 'mailuser', 'email_verification'), 1);
|
|
|
|
|
|
const publicResend = await fetch(`${baseUrl}/api/auth/resend-verification`, {
|
|
|
@@ -1420,7 +1421,7 @@ test('self registration creates a pending email user and verification token with
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-test('email verification route consumes token and moves user to admin review', async () => {
|
|
|
+test('email verification activates users by default and redirects browsers with an exact success message', async () => {
|
|
|
const { child, baseUrl, dataDir, sessionSecret } = await startTestServer();
|
|
|
|
|
|
try {
|
|
|
@@ -1458,25 +1459,169 @@ test('email verification route consumes token and moves user to admin review', a
|
|
|
headers: { Accept: 'text/html,application/xhtml+xml' }
|
|
|
});
|
|
|
assert.equal(browserResponse.status, 303);
|
|
|
- assert.match(browserResponse.headers.get('location') || '', /^\/login\?message=/);
|
|
|
+ assert.equal(
|
|
|
+ browserResponse.headers.get('location'),
|
|
|
+ `/login?message=${encodeURIComponent('邮箱验证成功,现在可以登录。')}`
|
|
|
+ );
|
|
|
assert.equal(sessionCookieFrom(browserResponse), '');
|
|
|
+ const browserLogin = await loginResponse(baseUrl, 'verifybrowser', 'password123');
|
|
|
+ assert.equal(browserLogin.status, 200);
|
|
|
+ assert.ok(sessionCookieFrom(browserLogin));
|
|
|
|
|
|
const response = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(created.token)}`);
|
|
|
assert.equal(response.status, 200);
|
|
|
assert.equal(sessionCookieFrom(response), '');
|
|
|
const body = await response.json();
|
|
|
assert.equal(body.user.id, created.user.id);
|
|
|
- assert.equal(body.user.status, 'pending_review');
|
|
|
- assert.match(body.message, /管理员审核/);
|
|
|
+ assert.equal(body.user.status, 'active');
|
|
|
+ assert.equal(body.message, '邮箱验证成功,现在可以登录。');
|
|
|
|
|
|
const reused = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(created.token)}`);
|
|
|
assert.equal(reused.status, 400);
|
|
|
assert.equal(sessionCookieFrom(reused), '');
|
|
|
|
|
|
- const login = await loginResponse(baseUrl, 'verifyme', 'password123');
|
|
|
- assert.equal(login.status, 403);
|
|
|
- assert.equal(sessionCookieFrom(login), '');
|
|
|
- assert.equal((await login.json()).error, '账号正在等待管理员审核。');
|
|
|
+ const verifiedLogin = await loginResponse(baseUrl, 'verifyme', 'password123');
|
|
|
+ assert.equal(verifiedLogin.status, 200);
|
|
|
+ assert.ok(sessionCookieFrom(verifiedLogin));
|
|
|
+
|
|
|
+ const alreadyPendingReview = createPendingEmailUserWithVerificationToken(dataDir, sessionSecret, {
|
|
|
+ username: 'alreadyreview',
|
|
|
+ email: 'alreadyreview@example.com',
|
|
|
+ password: 'password123',
|
|
|
+ status: 'pending_review'
|
|
|
+ });
|
|
|
+ const pendingReviewResponse = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(alreadyPendingReview.token)}`);
|
|
|
+ assert.equal(pendingReviewResponse.status, 200);
|
|
|
+ const pendingReviewBody = await pendingReviewResponse.json();
|
|
|
+ assert.equal(pendingReviewBody.user.status, 'pending_review');
|
|
|
+ assert.equal(pendingReviewBody.message, '邮箱验证成功,请等待管理员审核。');
|
|
|
+
|
|
|
+ const disabled = createPendingEmailUserWithVerificationToken(dataDir, sessionSecret, {
|
|
|
+ username: 'disabledverify',
|
|
|
+ email: 'disabledverify@example.com',
|
|
|
+ password: 'password123',
|
|
|
+ status: 'disabled'
|
|
|
+ });
|
|
|
+ const disabledResponse = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(disabled.token)}`);
|
|
|
+ assert.equal(disabledResponse.status, 400);
|
|
|
+ assert.equal(countUnusedAccountTokensForUser(dataDir, sessionSecret, 'disabledverify', 'email_verification'), 1);
|
|
|
+ } finally {
|
|
|
+ child.kill('SIGTERM');
|
|
|
+ await waitForExit(child, 1000);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+test('admin registration approval setting persists and keeps verified users pending review', async () => {
|
|
|
+ const { child, baseUrl, dataDir, sessionSecret } = await startTestServer();
|
|
|
+
|
|
|
+ try {
|
|
|
+ seedUsers(dataDir, sessionSecret, [
|
|
|
+ { username: 'settingsmember', email: 'settingsmember@example.com', password: 'password123', status: 'active' }
|
|
|
+ ]);
|
|
|
+ const adminCookie = await login(baseUrl, 'admin', 'password123');
|
|
|
+ const memberCookie = await login(baseUrl, 'settingsmember', 'password123');
|
|
|
+
|
|
|
+ const defaultsResponse = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ headers: { Cookie: adminCookie }
|
|
|
+ });
|
|
|
+ assert.equal(defaultsResponse.status, 200);
|
|
|
+ assert.equal((await defaultsResponse.json()).settings.registrationRequiresApproval, false);
|
|
|
+
|
|
|
+ const forbiddenRead = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ headers: { Cookie: memberCookie }
|
|
|
+ });
|
|
|
+ assert.equal(forbiddenRead.status, 403);
|
|
|
+
|
|
|
+ const forbidden = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ method: 'PATCH',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Cookie: memberCookie
|
|
|
+ },
|
|
|
+ body: JSON.stringify({ registrationRequiresApproval: true })
|
|
|
+ });
|
|
|
+ assert.equal(forbidden.status, 403);
|
|
|
+
|
|
|
+ const unchangedResponse = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ headers: { Cookie: adminCookie }
|
|
|
+ });
|
|
|
+ assert.equal((await unchangedResponse.json()).settings.registrationRequiresApproval, false);
|
|
|
+
|
|
|
+ const enabledResponse = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ method: 'PATCH',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Cookie: adminCookie
|
|
|
+ },
|
|
|
+ body: JSON.stringify({ registrationRequiresApproval: true })
|
|
|
+ });
|
|
|
+ assert.equal(enabledResponse.status, 200);
|
|
|
+ assert.equal((await enabledResponse.json()).settings.registrationRequiresApproval, true);
|
|
|
+
|
|
|
+ const persistedResponse = await fetch(`${baseUrl}/api/admin/settings`, {
|
|
|
+ headers: { Cookie: adminCookie }
|
|
|
+ });
|
|
|
+ assert.equal(persistedResponse.status, 200);
|
|
|
+ assert.equal((await persistedResponse.json()).settings.registrationRequiresApproval, true);
|
|
|
+
|
|
|
+ const register = await fetch(`${baseUrl}/api/register`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
+ body: JSON.stringify({
|
|
|
+ username: 'reviewregistration',
|
|
|
+ email: 'reviewregistration@example.com',
|
|
|
+ password: 'password123'
|
|
|
+ })
|
|
|
+ });
|
|
|
+ assert.equal(register.status, 201);
|
|
|
+ const registerBody = await register.json();
|
|
|
+ assert.equal(registerBody.user.status, 'pending_email');
|
|
|
+ assert.doesNotMatch(registerBody.message, /管理员审核|即可登录/);
|
|
|
+
|
|
|
+ const created = createPendingEmailUserWithVerificationToken(dataDir, sessionSecret, {
|
|
|
+ username: 'reviewrequired',
|
|
|
+ email: 'reviewrequired@example.com',
|
|
|
+ password: 'password123',
|
|
|
+ status: 'pending_email'
|
|
|
+ });
|
|
|
+ const response = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(created.token)}`);
|
|
|
+ assert.equal(response.status, 200);
|
|
|
+ assert.equal(sessionCookieFrom(response), '');
|
|
|
+ const body = await response.json();
|
|
|
+ assert.equal(body.user.status, 'pending_review');
|
|
|
+ assert.equal(body.message, '邮箱验证成功,请等待管理员审核。');
|
|
|
+
|
|
|
+ const browserCreated = createPendingEmailUserWithVerificationToken(dataDir, sessionSecret, {
|
|
|
+ username: 'reviewbrowser',
|
|
|
+ email: 'reviewbrowser@example.com',
|
|
|
+ password: 'password123',
|
|
|
+ status: 'pending_email'
|
|
|
+ });
|
|
|
+ const browserResponse = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(browserCreated.token)}`, {
|
|
|
+ redirect: 'manual',
|
|
|
+ headers: { Accept: 'text/html,application/xhtml+xml' }
|
|
|
+ });
|
|
|
+ assert.equal(browserResponse.status, 303);
|
|
|
+ assert.equal(
|
|
|
+ browserResponse.headers.get('location'),
|
|
|
+ `/login?message=${encodeURIComponent('邮箱验证成功,请等待管理员审核。')}`
|
|
|
+ );
|
|
|
+ assert.equal(sessionCookieFrom(browserResponse), '');
|
|
|
+
|
|
|
+ const alreadyActive = createPendingEmailUserWithVerificationToken(dataDir, sessionSecret, {
|
|
|
+ username: 'alreadyactive',
|
|
|
+ email: 'alreadyactive@example.com',
|
|
|
+ password: 'password123',
|
|
|
+ status: 'active'
|
|
|
+ });
|
|
|
+ const activeResponse = await fetch(`${baseUrl}/api/auth/verify-email?token=${encodeURIComponent(alreadyActive.token)}`);
|
|
|
+ assert.equal(activeResponse.status, 200);
|
|
|
+ const activeBody = await activeResponse.json();
|
|
|
+ assert.equal(activeBody.user.status, 'active');
|
|
|
+ assert.equal(activeBody.message, '邮箱验证成功,现在可以登录。');
|
|
|
+
|
|
|
+ await assertLoginDeniedByStatus(baseUrl, 'reviewrequired', '账号正在等待管理员审核。');
|
|
|
+ await assertLoginDeniedByStatus(baseUrl, 'reviewbrowser', '账号正在等待管理员审核。');
|
|
|
} finally {
|
|
|
child.kill('SIGTERM');
|
|
|
await waitForExit(child, 1000);
|
|
|
@@ -1640,6 +1785,7 @@ async function startTestServer() {
|
|
|
DATA_DIR: dataDir,
|
|
|
ADMIN_PASSWORD: 'password123',
|
|
|
SESSION_SECRET: sessionSecret,
|
|
|
+ REGISTRATION_REQUIRES_APPROVAL: 'false',
|
|
|
DNS_AUTO_CHECK_ENABLED: 'false',
|
|
|
SUBMISSION_ENABLED: 'false',
|
|
|
IMAP_ENABLED: 'false',
|