/*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; }); });