119 lines
3.9 KiB
TypeScript
119 lines
3.9 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { SMTPServer } from 'smtp-server';
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import postgres from 'postgres';
|
|
|
|
test('encrypted SMTP credentials, real TLS delivery and optional login alerts', async ({
|
|
request,
|
|
}) => {
|
|
const messages: string[] = [];
|
|
const recipients: string[] = [];
|
|
const server = new SMTPServer({
|
|
secure: true,
|
|
key: readFileSync(join(process.env.TEST_TLS_DIR!, 'key.pem')),
|
|
cert: readFileSync(join(process.env.TEST_TLS_DIR!, 'cert.pem')),
|
|
onAuth(auth, _session, done) {
|
|
if (auth.username === 'testmailer' && auth.password === 'smtp-test-password')
|
|
done(null, { user: 'testmailer' });
|
|
else done(new Error('Invalid credentials'));
|
|
},
|
|
onData(stream, session, done) {
|
|
let message = '';
|
|
stream.on('data', (chunk) => {
|
|
message += chunk.toString();
|
|
});
|
|
stream.on('end', () => {
|
|
messages.push(message);
|
|
recipients.push(...session.envelope.rcptTo.map((r) => r.address));
|
|
done();
|
|
});
|
|
},
|
|
});
|
|
await new Promise<void>((resolve, reject) => {
|
|
server.on('error', reject);
|
|
server.listen(15440, '127.0.0.1', resolve);
|
|
});
|
|
const db = postgres(process.env.TEST_DATABASE_URL!, { max: 1 });
|
|
const password = process.env.TEST_PASSWORD!;
|
|
const headers = {
|
|
origin: 'http://localhost:3100',
|
|
'x-forwarded-for': '1.2.3.4, 2001:db8::123',
|
|
'user-agent': 'HamHam-TestBrowser',
|
|
};
|
|
const mail = {
|
|
host: '127.0.0.1',
|
|
port: 15440,
|
|
secure: true,
|
|
username: 'testmailer',
|
|
password: 'smtp-test-password',
|
|
from: 'hamham@example.org',
|
|
};
|
|
try {
|
|
expect(
|
|
(
|
|
await request.post('/api/auth/login', {
|
|
headers,
|
|
data: { username: 'testadmin', password },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
expect(
|
|
(
|
|
await request.put('/api/settings/mail', {
|
|
headers,
|
|
data: { password, email: 'admin@example.org', loginNotifications: true, mail },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
expect((await request.post('/api/settings/mail/test', { headers })).status()).toBe(200);
|
|
expect(messages).toHaveLength(1);
|
|
expect(recipients).toEqual(['admin@example.org']);
|
|
await request.post('/api/auth/logout', { headers });
|
|
expect(
|
|
(
|
|
await request.post('/api/auth/login', {
|
|
headers,
|
|
data: { username: 'testadmin', password },
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
await expect
|
|
.poll(
|
|
async () => (await (await request.get('/api/auth/security')).json()).events[0].mail_status,
|
|
)
|
|
.toBe('sent');
|
|
expect(messages).toHaveLength(2);
|
|
expect(messages[1]).toContain('HamHam-TestBrowser');
|
|
expect(messages[1]).toContain('2001:db8::123');
|
|
expect(messages[1]).not.toContain('1.2.3.4');
|
|
expect(messages[1]).not.toContain(password);
|
|
expect(
|
|
(
|
|
await request.put('/api/settings/mail', {
|
|
headers,
|
|
data: {
|
|
password,
|
|
email: 'admin@example.org',
|
|
loginNotifications: false,
|
|
mail: { ...mail, password: '' },
|
|
},
|
|
})
|
|
).status(),
|
|
).toBe(200);
|
|
expect((await request.post('/api/settings/mail/test', { headers })).status()).toBe(200);
|
|
expect(messages).toHaveLength(3); // Blank password preserved the encrypted credential.
|
|
await request.post('/api/auth/logout', { headers });
|
|
await request.post('/api/auth/login', { headers, data: { username: 'testadmin', password } });
|
|
expect((await (await request.get('/api/auth/security')).json()).events[0].mail_status).toBe(
|
|
'disabled',
|
|
);
|
|
expect(messages).toHaveLength(3);
|
|
} finally {
|
|
await db`UPDATE admin_security SET login_notifications = false`;
|
|
await db`DELETE FROM mail_config`;
|
|
await db.end();
|
|
await new Promise<void>((resolve) => server.close(resolve));
|
|
}
|
|
});
|