mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-01-06 08:02:27 +08:00
443 lines
16 KiB
JavaScript
443 lines
16 KiB
JavaScript
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0, no-console:0 */
|
|
|
|
'use strict';
|
|
|
|
const supertest = require('supertest');
|
|
const chai = require('chai');
|
|
|
|
const expect = chai.expect;
|
|
chai.config.includeStack = true;
|
|
const config = require('wild-config');
|
|
|
|
const server = supertest.agent(`http://127.0.0.1:${config.api.port}`);
|
|
|
|
describe('API Users', function () {
|
|
this.timeout(10000); // eslint-disable-line no-invalid-this
|
|
|
|
let user, user2, token;
|
|
|
|
it('should POST /users', async () => {
|
|
const response = await server
|
|
.post('/users')
|
|
.send({
|
|
username: 'myuser2',
|
|
name: 'John Smith',
|
|
address: 'john@example.com',
|
|
password: 'secretvalue',
|
|
hashedPassword: false,
|
|
emptyAddress: false,
|
|
language: 'et',
|
|
retention: 0,
|
|
targets: ['user@example.com', 'https://example.com/upload/email'],
|
|
spamLevel: 50,
|
|
quota: 1073741824,
|
|
recipients: 2000,
|
|
forwards: 2000,
|
|
requirePasswordChange: false,
|
|
imapMaxUpload: 5368709120,
|
|
imapMaxDownload: 21474836480,
|
|
pop3MaxDownload: 21474836480,
|
|
pop3MaxMessages: 300,
|
|
imapMaxConnections: 15,
|
|
receivedMax: 60,
|
|
fromWhitelist: ['user@alternative.domain', '*@example.com'],
|
|
tags: ['status:user', 'account:example.com'],
|
|
addTagsToAddress: false,
|
|
uploadSentMessages: false,
|
|
mailboxes: {
|
|
sent: 'Saadetud kirjad',
|
|
trash: 'Prügikast',
|
|
junk: 'Praht',
|
|
drafts: 'Mustandid'
|
|
},
|
|
disabledScopes: ['imap', 'pop3', 'smtp'],
|
|
metaData: {
|
|
accountIcon: 'avatar.png'
|
|
},
|
|
internalData: {
|
|
inTrial: true
|
|
},
|
|
pubKey: '-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: Keybase OpenPGP v1.0.0\nComment: https://keybase.io/crypto\n\nxo0EYb0PqAEEANJtI/ivwudfCMmxm+a77Fll5YwSzaaI2nqhcp6pMRJ4l0aafsX3\nBcXUQpsyyELelt2xFtwTNygR4RFWVTn4OoXmO5zFtWCSegAwSyUNK7R/GXi2GTKk\nkYtxUwGcNKBkfY7yAn5KsaeuZL1feDXUGt0YHUmBds5i+6ylI+i4tNbRABEBAAHN\nH1dpbGQgRHVjayA8dGVzdEB3aWxkZHVjay5lbWFpbD7CrQQTAQoAFwUCYb0PqAIb\nLwMLCQcDFQoIAh4BAheAAAoJEJVLs8wf5gSCzBoD/3gz32OfJM1D4IrmKVwyLKxC\n1P81kL7E6ICWD2A0JF9EkojsMHl+/zagwoJejBQhmzTNkFmui5zwmdLGforKl303\ntB0l9vCTb5+eDDHOTUatJrvlw76Fz2ZjIhQTqD4xEM7MWx4xwTGY8bC5roIpdZJD\n9+vr81MXxiq9LZJDBXIyzo0EYb0PqAEEAL/uCTOrAncTRC/3cOQz+kLIzF4A9OTe\n6yxdNWWmx+uo9yJxnBv59Xz9qt8OT8Ih7SD/A4kFCuQqlyd0OFVhyd3KTAQ3CEml\nYOgL5jOE11YrEQjr36xPqO646JZuZIorKDf9PoIyipAMG89BlAoAjSXB1oeQADYn\n5fFLFVm1S7pLABEBAAHCwIMEGAEKAA8FAmG9D6gFCQ8JnAACGy4AqAkQlUuzzB/m\nBIKdIAQZAQoABgUCYb0PqAAKCRBhR/oKY9pg/YqnA/0Szmy4q4TnTBby+j57oXtn\nX/7H/xiaqlCd6bA3lbj3cPK4ybn/gnI4ECsfZfmSFG3T5C9EcZU0e9ByzimH6sxi\nOwPgKFWeJzpl5o8toR7m4wQVhv2NZRUukHe+2JH7nITS0gKeIBHMq2TbufcH6do1\n8s2G7XyLSd5Kkljxx7YmNiKoA/9CQ4l2WkARAFByyEJT9BEE4NBO0m0bI8sg0HRK\nGuP3FKcUu0Pz9R8AExEecofh8s4kaxofa2sbrTcK+L0p0hdR/39JWNuTJbxwEU3C\nA0mZKthjzL7seiRTG7Eny5gGenejRp2x0ziyMEaTgkvf44LPi06XiuE6FGnhElOc\nC7JoIc6NBGG9D6gBBADzW30GOysnqYkexL+bY9o+ai1mL+X58GPLilXJ5WXgEEdf\n8Pg/9jlEOzOnWTTgJAQDGHtwm0duKmK7EJGozLEY94QGOzRjAir6tMF2OYDQIDgj\nAoXavPAc5chFABEVUS12hUPPLoW6YgvaIb3AAZbIM8603BLXTaLGbtZ0z7eYxwAR\nAQABwsCDBBgBCgAPBQJhvQ+oBQkPCZwAAhsuAKgJEJVLs8wf5gSCnSAEGQEKAAYF\nAmG9D6gACgkQ58zrS0TNGbAiVAP/UIxYiSdoHDnBW5qB7onEiUVL5ZFk1Xk+NB0z\n7jOm1oAV0RH8I5NRQBtZ+75xar0vPTX122IdkgpaiNT0wy5Kd/2vz4LKVK9apyJI\neaZ+D7dt5Ipu1p0lWtglqL0xtjOSWuwHFwHuiRYg6eyhGN1RylFpuiKi5KykhrBS\nuBL/BHrk6AP/boRA+KIlb6s19KHNt54Kl8n8G4ZApCwZbUc2jzvbP5DZL5rcjlHd\ns4i4XE+uIJxsiX3iJZtVXzhTKuQlaoEljlhPs/TZYUmxeJ3TdV4o7emWiZ4gE8EQ\nhfxV37ew/GoYm6yME3tAZLIXbv2+bj6HZ4eE8bAMmPvpcQ+UwNJXvnk=\n=dR+x\n-----END PGP PUBLIC KEY BLOCK-----',
|
|
encryptMessages: false,
|
|
encryptForwarded: false
|
|
})
|
|
.expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
expect(/^[0-9a-f]{24}$/.test(response.body.id)).to.be.true;
|
|
|
|
user = response.body.id;
|
|
});
|
|
|
|
it('should POST /authenticate', async () => {
|
|
const authResponse = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'secretvalue'
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse.body.success).to.be.true;
|
|
expect(authResponse.body).to.deep.equal({
|
|
success: true,
|
|
id: user,
|
|
username: 'myuser2',
|
|
scope: 'master',
|
|
require2fa: false,
|
|
requirePasswordChange: false
|
|
});
|
|
});
|
|
|
|
it('should POST /authenticate and fail', async () => {
|
|
const authResponse = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'invalidpass'
|
|
})
|
|
.expect(403);
|
|
expect(authResponse.body.code).to.equal('AuthFailed');
|
|
});
|
|
|
|
it('should POST /users and fail - invalid username', async () => {
|
|
const response = await server
|
|
.post('/users')
|
|
.send({
|
|
username: 'ömyuser2',
|
|
name: 'John Smith',
|
|
password: 'secretvalue'
|
|
})
|
|
.expect(400);
|
|
|
|
expect(response.body.details.username).to.exist;
|
|
});
|
|
|
|
it('should POST /authenticate and request a token', async () => {
|
|
const authResponse = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'secretvalue',
|
|
token: true
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse.body.success).to.be.true;
|
|
expect(authResponse.body.token).to.exist;
|
|
|
|
token = authResponse.body.token;
|
|
});
|
|
|
|
it('should POST /users with hashed password', async () => {
|
|
const response = await server
|
|
.post('/users')
|
|
.send({
|
|
username: 'myuser2hash',
|
|
name: 'John Smith',
|
|
// password: 'test',
|
|
password: '$argon2i$v=19$m=16,t=2,p=1$SFpGczI1bWV1RVRpYjNYaw$EBE/WnOGeWint3eQ+SQ7Sg',
|
|
hashedPassword: true
|
|
})
|
|
.expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
user2 = response.body.id;
|
|
|
|
const authResponse = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2hash',
|
|
password: 'test'
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse.body.success).to.be.true;
|
|
expect(authResponse.body).to.deep.equal({
|
|
success: true,
|
|
id: user2,
|
|
username: 'myuser2hash',
|
|
scope: 'master',
|
|
require2fa: false,
|
|
requirePasswordChange: false
|
|
});
|
|
});
|
|
|
|
it('should GET /users/resolve/{username}', async () => {
|
|
const response = await server.get('/users/resolve/myuser2').expect(200);
|
|
|
|
expect(response.body).to.deep.equal({
|
|
success: true,
|
|
id: user
|
|
});
|
|
});
|
|
|
|
it('should GET /users/resolve/{username} and fail', async () => {
|
|
const response = await server.get('/users/resolve/myuser2invalid').expect(404);
|
|
expect(response.body.code).to.equal('UserNotFound');
|
|
});
|
|
|
|
it('should GET /users', async () => {
|
|
const response = await server.get('/users?query=myuser2').expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
expect(response.body.results.find(entry => entry.id === user)).to.exist;
|
|
});
|
|
|
|
it('should GET /users/{user}', async () => {
|
|
let response = await server.get(`/users/${user}`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
expect(response.body.id).to.equal(user);
|
|
});
|
|
|
|
it('should GET /users/{user} using a token', async () => {
|
|
let response = await server.get(`/users/${user}?accessToken=${token}`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
expect(response.body.id).to.equal(user);
|
|
});
|
|
|
|
it('should GET /users/me using a token', async () => {
|
|
let response = await server.get(`/users/me?accessToken=${token}`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
expect(response.body.id).to.equal(user);
|
|
});
|
|
|
|
it('should GET /users/{user} using a token and fail against other user', async () => {
|
|
let response = await server.get(`/users/${user2}?accessToken=${token}`);
|
|
expect(response.body.code).to.equal('MissingPrivileges');
|
|
});
|
|
|
|
it('should DELETE /authenticate', async () => {
|
|
let response = await server.delete(`/authenticate?accessToken=${token}`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
});
|
|
|
|
it('should DELETE /authenticate with false', async () => {
|
|
// token is not valid anymore
|
|
await server.delete(`/authenticate?accessToken=${token}`).expect(403);
|
|
});
|
|
|
|
it('should PUT /users/{user}', async () => {
|
|
const name = 'John Smith 2';
|
|
|
|
// update user data
|
|
const response = await server
|
|
.put(`/users/${user}`)
|
|
.send({
|
|
name
|
|
})
|
|
.expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
|
|
// request and verify
|
|
let getResponse = await server.get(`/users/${user}`);
|
|
expect(getResponse.body.success).to.be.true;
|
|
expect(getResponse.body.id).to.equal(user);
|
|
expect(getResponse.body.name).to.equal(name);
|
|
});
|
|
|
|
it('should PUT /users/{user} and renew a token', async () => {
|
|
const authResponse1 = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'secretvalue',
|
|
token: true
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse1.body.success).to.be.true;
|
|
expect(authResponse1.body.token).to.exist;
|
|
|
|
let token1 = authResponse1.body.token;
|
|
|
|
const authResponse2 = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'secretvalue',
|
|
token: true
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse2.body.success).to.be.true;
|
|
expect(authResponse2.body.token).to.exist;
|
|
|
|
let token2 = authResponse2.body.token;
|
|
|
|
// try out token 1
|
|
let getResponse1 = await server.get(`/users/me?accessToken=${token1}`).expect(200);
|
|
expect(getResponse1.body.success).to.be.true;
|
|
expect(getResponse1.body.id).to.equal(user);
|
|
|
|
// try out token 2
|
|
let getResponse2 = await server.get(`/users/me?accessToken=${token2}`).expect(200);
|
|
expect(getResponse2.body.success).to.be.true;
|
|
expect(getResponse2.body.id).to.equal(user);
|
|
|
|
// update password using a token
|
|
const response = await server
|
|
.put(`/users/me?accessToken=${token1}`)
|
|
.send({
|
|
password: 'secretvalue'
|
|
})
|
|
.expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
|
|
// try out token 1, should have been renewed
|
|
let getResponse3 = await server.get(`/users/me?accessToken=${token1}`).expect(200);
|
|
expect(getResponse3.body.success).to.be.true;
|
|
expect(getResponse3.body.id).to.equal(user);
|
|
|
|
// try out token 2, should fail as it was not renewed
|
|
await server.get(`/users/me?accessToken=${token2}`).expect(403);
|
|
});
|
|
|
|
it('should PUT /users/{user}/logout', async () => {
|
|
// request logout
|
|
const response = await server.put(`/users/${user}/logout`).send({ reason: 'Just because' }).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
});
|
|
|
|
it('should POST /users/{user}/quota/reset', async () => {
|
|
const response = await server.post(`/users/${user}/quota/reset`).send({}).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.storageUsed).to.exist;
|
|
expect(response.body.previousStorageUsed).to.exist;
|
|
});
|
|
|
|
it('should POST /quota/reset', async () => {
|
|
const response = await server.post(`/quota/reset`).send({}).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
expect(response.body.task).to.exist;
|
|
});
|
|
|
|
it('should POST /users/{user}/password/reset', async () => {
|
|
const response = await server.post(`/users/${user}/password/reset`).send({}).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.password).to.exist;
|
|
|
|
const authResponse = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: response.body.password
|
|
})
|
|
.expect(200);
|
|
|
|
expect(authResponse.body.success).to.be.true;
|
|
expect(authResponse.body).to.deep.equal({
|
|
success: true,
|
|
id: user,
|
|
username: 'myuser2',
|
|
scope: 'master',
|
|
require2fa: false,
|
|
// using a temporary password requires a password change
|
|
requirePasswordChange: true
|
|
});
|
|
});
|
|
|
|
it('should POST /users/{user}/password/reset using a future date', async () => {
|
|
const response = await server
|
|
.post(`/users/${user}/password/reset`)
|
|
.send({
|
|
validAfter: new Date(Date.now() + 1 * 3600 * 1000).toISOString()
|
|
})
|
|
.expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.password).to.exist;
|
|
|
|
// password not yet valid
|
|
await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: response.body.password
|
|
})
|
|
.expect(403);
|
|
});
|
|
|
|
it('should DELETE /users/{user}', async () => {
|
|
// first set the user password
|
|
const passwordUpdateResponse = await server
|
|
.put(`/users/${user}`)
|
|
.send({
|
|
password: 'secretvalue',
|
|
ip: '1.2.3.5'
|
|
})
|
|
.expect(200);
|
|
|
|
expect(passwordUpdateResponse.body.success).to.be.true;
|
|
|
|
// Delete user
|
|
const response = await server.delete(`/users/${user}?deleteAfter=${encodeURIComponent(new Date(Date.now() + 3600 * 1000).toISOString())}`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.addresses.deleted).to.gte(1);
|
|
expect(response.body.task).to.exist;
|
|
|
|
// Try to authenticate, should fail
|
|
await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'myuser2',
|
|
password: 'secretvalue'
|
|
})
|
|
.expect(403);
|
|
});
|
|
|
|
it('should GET /users/{user}/restore', async () => {
|
|
const response = await server.get(`/users/${user}/restore`).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.username).to.equal('myuser2');
|
|
expect(response.body.recoverableAddresses).to.deep.equal(['john@example.com']);
|
|
});
|
|
|
|
it('should POST /users/{user}/restore', async () => {
|
|
const response = await server.post(`/users/${user}/restore`).send({}).expect(200);
|
|
expect(response.body.success).to.be.true;
|
|
|
|
expect(response.body.addresses.recovered).to.gte(1);
|
|
expect(response.body.addresses.main).to.equal('john@example.com');
|
|
});
|
|
|
|
it('should POST /users with DES hash', async () => {
|
|
const response = await server
|
|
.post('/users')
|
|
.send({
|
|
username: 'desuser',
|
|
name: 'Crypt Des',
|
|
address: 'des@example.com',
|
|
password: 'sBk81TlWxyZlc',
|
|
hashedPassword: true
|
|
})
|
|
.expect(200);
|
|
|
|
expect(response.body.success).to.be.true;
|
|
expect(/^[0-9a-f]{24}$/.test(response.body.id)).to.be.true;
|
|
|
|
const authResponseSuccess = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'desuser',
|
|
password: '12Mina34Ise56P.'
|
|
})
|
|
.expect(200);
|
|
expect(authResponseSuccess.body.success).to.be.true;
|
|
|
|
const authResponseFail = await server
|
|
.post('/authenticate')
|
|
.send({
|
|
username: 'desuser',
|
|
password: 'wrongpass'
|
|
})
|
|
.expect(403);
|
|
expect(authResponseFail.body.error).to.exist;
|
|
});
|
|
});
|