feat(quick-260907-let): Verbindungstest fuer Postfach-Endpunkt im API
- TenderEmailConfigService.testConnection(userId, dto) mit Rueckfall auf
gespeicherte, entschluesselte Zugangsdaten bei leeren Feldern
- TendersController: POST email-config/test, userId aus Auth-Kontext,
deklariert vor @Get(':id')
- Beide Provider (ImapProvider/ExchangeInboxProvider) optional angehaengt,
bestehende 2-Arg-Konstruktoraufrufe bleiben typkorrekt
- Reihenfolge-Waechter und IDOR-Testfall ergaenzt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
@@ -230,4 +230,92 @@ describe('TenderEmailConfigService', () => {
|
||||
expect(configG2!.host).toBe('imap.user-g2.test');
|
||||
expect(prisma.__store.size).toBe(2);
|
||||
});
|
||||
|
||||
describe('testConnection (Quick 260907-let, WINDOWS #16)', () => {
|
||||
function makeFakeProviders() {
|
||||
return {
|
||||
imapProvider: { testConnection: vi.fn(async () => ({ success: true })) },
|
||||
exchangeProvider: { testConnection: vi.fn(async () => ({ success: true })) },
|
||||
};
|
||||
}
|
||||
|
||||
it('a typed-in password is passed through to the provider unchanged, without reading the database', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const crypto = makeFakeCrypto();
|
||||
const { imapProvider, exchangeProvider } = makeFakeProviders();
|
||||
const service = new TenderEmailConfigService(
|
||||
prisma as any,
|
||||
crypto as any,
|
||||
imapProvider as any,
|
||||
exchangeProvider as any,
|
||||
);
|
||||
|
||||
const result = await service.testConnection('user-h', {
|
||||
protocol: 'imap',
|
||||
encryption: 'ssl-tls',
|
||||
host: 'imap.example.test',
|
||||
username: 'typed-user',
|
||||
password: 'typed-secret',
|
||||
} as any);
|
||||
|
||||
expect(prisma.tenderEmailConfig.findUnique).not.toHaveBeenCalled();
|
||||
expect(imapProvider.testConnection).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ username: 'typed-user', password: 'typed-secret' }),
|
||||
);
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('an empty password falls back to this same user\'s stored, decrypted credentials', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const crypto = makeFakeCrypto();
|
||||
const { imapProvider, exchangeProvider } = makeFakeProviders();
|
||||
const service = new TenderEmailConfigService(
|
||||
prisma as any,
|
||||
crypto as any,
|
||||
imapProvider as any,
|
||||
exchangeProvider as any,
|
||||
);
|
||||
|
||||
await service.saveConfig(
|
||||
{ userId: 'user-i', tenantId: 'tenant-i' },
|
||||
{
|
||||
protocol: 'imap',
|
||||
encryption: 'ssl-tls',
|
||||
username: 'stored-user',
|
||||
password: 'stored-secret',
|
||||
} as any,
|
||||
);
|
||||
|
||||
await service.testConnection('user-i', {
|
||||
protocol: 'imap',
|
||||
encryption: 'ssl-tls',
|
||||
} as any);
|
||||
|
||||
expect(imapProvider.testConnection).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ username: 'stored-user', password: 'stored-secret' }),
|
||||
);
|
||||
});
|
||||
|
||||
it("dto.protocol 'exchange' selects the Exchange provider, not IMAP", async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const crypto = makeFakeCrypto();
|
||||
const { imapProvider, exchangeProvider } = makeFakeProviders();
|
||||
const service = new TenderEmailConfigService(
|
||||
prisma as any,
|
||||
crypto as any,
|
||||
imapProvider as any,
|
||||
exchangeProvider as any,
|
||||
);
|
||||
|
||||
await service.testConnection('user-j', {
|
||||
protocol: 'exchange',
|
||||
encryption: 'ssl-tls',
|
||||
username: 'ews-user',
|
||||
password: 'ews-secret',
|
||||
} as any);
|
||||
|
||||
expect(exchangeProvider.testConnection).toHaveBeenCalledTimes(1);
|
||||
expect(imapProvider.testConnection).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user