Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6732a1e1df |
@@ -237,4 +237,49 @@ describe('DC-085: link-first admin invites', () => {
|
||||
.send({ email: 'a@x.com', ttlHours: 1 });
|
||||
expect(res.body.shareText).toContain('expires in 1h');
|
||||
});
|
||||
});
|
||||
|
||||
test('DC-089: SMTP-unconfigured warn log masks the invite email (no raw PII)', async () => {
|
||||
mockEmailSender.isConfigured.mockReturnValueOnce(false);
|
||||
|
||||
const res = await request(app)
|
||||
.post('/api/v1/auth/admin/invites')
|
||||
.send({ email: 'friend@example.com', role: 'operator', sendEmail: true });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.body.deliveredVia).toBe('failed');
|
||||
const warn = logCalls.find(c =>
|
||||
c.level === 'warn' && c.topic === 'auth-invite-send'
|
||||
);
|
||||
expect(warn).toBeDefined();
|
||||
// The raw address must not appear; the masked form must.
|
||||
expect(JSON.stringify(warn.meta)).not.toContain('friend@example.com');
|
||||
expect(warn.meta.email).toBe('fr****@example.com');
|
||||
});
|
||||
|
||||
test('DC-089: invite-accepted info log masks the created user email (no raw PII)', async () => {
|
||||
// Pre-authorize the email (POST /admin/users) so userStore.login doesn't
|
||||
// reject with not_authorized — bootstrap already happened in beforeEach.
|
||||
const preauth = await request(app)
|
||||
.post('/api/v1/auth/admin/users')
|
||||
.send({ email: 'newfriend@example.com' });
|
||||
expect(preauth.status).toBe(200);
|
||||
|
||||
const issue = await request(app)
|
||||
.post('/api/v1/auth/admin/invites')
|
||||
.send({ email: 'newfriend@example.com', role: 'viewer' });
|
||||
expect(issue.status).toBe(200);
|
||||
const token = issue.body.acceptUrl.match(/invites\/([^/]+)\/accept/)[1];
|
||||
|
||||
const res = await request(app)
|
||||
.post(`/api/v1/auth/invites/${token}/accept`)
|
||||
.send({});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const info = logCalls.find(c =>
|
||||
c.level === 'info' && c.msg === 'invite accepted, user created'
|
||||
);
|
||||
expect(info).toBeDefined();
|
||||
expect(JSON.stringify(info.meta)).not.toContain('newfriend@example.com');
|
||||
expect(info.meta.email).toBe('ne****@example.com');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,7 @@ const platformPaths = require('../../platform-paths');
|
||||
const { createUserStore } = require('../../src/security/user-store');
|
||||
const { createInviteStore } = require('../../src/security/invite-store');
|
||||
const emailSender = require('../../src/auth/providers/email-sender');
|
||||
const AuthProvider = require('../../src/auth/providers/base');
|
||||
const { ValidationError, NotFoundError, ForbiddenError, PaymentRequiredError } = require('../../src/utilities/errors');
|
||||
const { ok, successMessage } = require('../../src/utils/responses');
|
||||
|
||||
@@ -254,7 +255,7 @@ module.exports = function({ asyncHandler, errorResponse, log, session, dataDir }
|
||||
// server log on every unconfigured-install invite.
|
||||
log.warn && log.warn('auth-invite-send',
|
||||
'invite send skipped: SMTP not configured (operator opted in)',
|
||||
{ inviteId: issued.id, email: issued.email });
|
||||
{ inviteId: issued.id, email: AuthProvider.maskEmail(issued.email) || '[unmaskable-email]' });
|
||||
deliveredVia = 'failed';
|
||||
}
|
||||
} catch (sendErr) {
|
||||
@@ -369,7 +370,7 @@ module.exports = function({ asyncHandler, errorResponse, log, session, dataDir }
|
||||
|
||||
log.info && log.info('auth', 'invite accepted, user created', {
|
||||
userId: userResult.user.id,
|
||||
email: userResult.user.email,
|
||||
email: AuthProvider.maskEmail(userResult.user.email) || '[unmaskable-email]',
|
||||
role: userResult.user.role,
|
||||
inviteId: invite.id,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user