wildduck/lib/api/domainaccess.js
Andris Reinman eb5707b41e doc updates
2020-09-28 13:52:22 +03:00

416 lines
13 KiB
JavaScript

'use strict';
const Joi = require('joi');
const ObjectID = require('mongodb').ObjectID;
const tools = require('../tools');
const roles = require('../roles');
const { sessSchema, sessIPSchema } = require('../schemas');
module.exports = (db, server) => {
/**
* @api {post} /domainaccess/:tag/allow Add domain to allowlist
* @apiDescription If an email is sent from a domain that is listed in the allowlist then it is never marked as spam. Lists apply for tagged users.
* @apiName PostDomainAccessAllow
* @apiGroup DomainAccess
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
* @apiHeaderExample {json} Header-Example:
* {
* "X-Access-Token": "59fc66a03e54454869460e45"
* }
*
* @apiParam {String} tag Tag to match
* @apiParam {String} domain Domain name to allowlist for users/addresses that include this tag
*
* @apiSuccess {Boolean} success Indicates successful response
* @apiSuccess {String} id ID for the created record
*
* @apiError error Description of the error
*
* @apiExample {curl} Example usage:
* curl -i -XPOST http://localhost:8080/domainaccess/account_12345/allow \
* -H 'Content-type: application/json' \
* -d '{
* "domain": "example.com"
* }'
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* "id": "5a1c0ee490a34c67e266931c"
* }
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 200 OK
* {
* "error": "Invalid domain"
* }
*/
/**
* @api {post} /domainaccess/:tag/block Add domain to blocklist
* @apiDescription If an email is sent from a domain that is listed in the blocklist then it is always marked as spam. Lists apply for tagged users.
* @apiName PostDomainAccessBlock
* @apiGroup DomainAccess
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
* @apiHeaderExample {json} Header-Example:
* {
* "X-Access-Token": "59fc66a03e54454869460e45"
* }
*
* @apiParam {String} tag Tag to match
* @apiParam {String} domain Domain name to blocklist for users/addresses that include this tag
*
* @apiSuccess {Boolean} success Indicates successful response
* @apiSuccess {String} id ID for the created record
*
* @apiError error Description of the error
*
* @apiExample {curl} Example usage:
* curl -i -XPOST http://localhost:8080/domainaccess/account_12345/block \
* -H 'Content-type: application/json' \
* -d '{
* "domain": "example.com"
* }'
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* "id": "5a1c0ee490a34c67e266931c"
* }
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 200 OK
* {
* "error": "Invalid domain"
* }
*/
server.post(
'/domainaccess/:tag/:action',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
tag: Joi.string().trim().max(128).required(),
domain: Joi.string()
.max(255)
//.hostname()
.required(),
action: Joi.string().valid('allow', 'block').required(),
sess: sessSchema,
ip: sessIPSchema
});
const result = schema.validate(req.params, {
abortEarly: false,
convert: true
});
if (result.error) {
res.status(400);
res.json({
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
});
return next();
}
// permissions check
req.validate(roles.can(req.role).createAny('domainaccess'));
let domain = tools.normalizeDomain(result.value.domain);
let tag = result.value.tag;
let tagview = tag.toLowerCase();
let action = result.value.action;
let r;
try {
r = await db.database.collection('domainaccess').findOneAndUpdate(
{
tagview,
domain
},
{
$setOnInsert: {
tag,
tagview,
domain
},
$set: {
action
}
},
{
upsert: true,
projection: { _id: true },
returnOriginal: false
}
);
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
res.json({
success: !!(r && r.value),
id: r && r.value && r.value._id
});
return next();
})
);
/**
* @api {get} /domainaccess/:tag/allow List allowlisted domains
* @apiName GetDomainAccessAllow
* @apiGroup DomainAccess
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
* @apiHeaderExample {json} Header-Example:
* {
* "X-Access-Token": "59fc66a03e54454869460e45"
* }
*
* @apiParam {String} tag Tag to look for
*
* @apiSuccess {Boolean} success Indicates successful response
* @apiSuccess {Object[]} results Domain list
* @apiSuccess {String} results.id Entry ID
* @apiSuccess {String} results.domain allowlisted domain name
* @apiError error Description of the error
*
* @apiExample {curl} Example usage:
* curl -i http://localhost:8080/domainaccess/account_12345/allow
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* "results": [
* {
* "id": "5a1c0ee490a34c67e266931c",
* "domain": "example.com",
* "action": "allow"
* }
* ]
* }
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 200 OK
* {
* "error": "Invalid ID"
* }
*/
/**
* @api {get} /domainaccess/:tag/block List blocklisted domains
* @apiName GetDomainAccessBlock
* @apiGroup DomainAccess
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
* @apiHeaderExample {json} Header-Example:
* {
* "X-Access-Token": "59fc66a03e54454869460e45"
* }
*
* @apiParam {String} tag Tag to look for
*
* @apiSuccess {Boolean} success Indicates successful response
* @apiSuccess {Object[]} results Domain list
* @apiSuccess {String} results.id Entry ID
* @apiSuccess {String} results.domain blocklisted domain name
* @apiError error Description of the error
*
* @apiExample {curl} Example usage:
* curl -i http://localhost:8080/domainaccess/account_12345/block
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true,
* "results": [
* {
* "id": "5a1c0ee490a34c67e266931c",
* "domain": "example.com",
* "action": "block"
* }
* ]
* }
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 200 OK
* {
* "error": "Invalid ID"
* }
*/
server.get(
'/domainaccess/:tag/:action',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
tag: Joi.string().trim().max(128).required(),
action: Joi.string().valid('allow', 'block').required(),
sess: sessSchema,
ip: sessIPSchema
});
const result = schema.validate(req.params, {
abortEarly: false,
convert: true
});
if (result.error) {
res.status(400);
res.json({
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
});
return next();
}
// permissions check
req.validate(roles.can(req.role).readAny('domainaccess'));
let tag = result.value.tag;
let tagview = tag.toLowerCase();
let action = result.value.action;
let domains;
try {
domains = await db.database
.collection('domainaccess')
.find({
tagview,
action
})
.sort({
domain: 1
})
.toArray();
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
if (!domains) {
domains = [];
}
res.json({
success: true,
results: domains.map(domainData => {
return {
id: domainData._id,
domain: domainData.domain,
action
};
})
});
return next();
})
);
/**
* @api {delete} /domainaccess/:domain Delete a Domain from listing
* @apiName DeleteDomainAccess
* @apiGroup DomainAccess
* @apiHeader {String} X-Access-Token Optional access token if authentication is enabled
* @apiHeaderExample {json} Header-Example:
* {
* "X-Access-Token": "59fc66a03e54454869460e45"
* }
*
* @apiParam {String} domain Listed domains unique ID
*
* @apiSuccess {Boolean} success Indicates successful response
*
* @apiError error Description of the error
*
* @apiExample {curl} Example usage:
* curl -i -XDELETE http://localhost:8080/domainaccess/59fc66a03e54454869460e45
*
* @apiSuccessExample {json} Success-Response:
* HTTP/1.1 200 OK
* {
* "success": true
* }
*
* @apiErrorExample {json} Error-Response:
* HTTP/1.1 200 OK
* {
* "error": "This domain does not exist"
* }
*/
server.del(
'/domainaccess/:domain',
tools.asyncifyJson(async (req, res, next) => {
res.charSet('utf-8');
const schema = Joi.object().keys({
domain: Joi.string().hex().lowercase().length(24).required(),
sess: sessSchema,
ip: sessIPSchema
});
const result = schema.validate(req.params, {
abortEarly: false,
convert: true
});
if (result.error) {
res.status(400);
res.json({
error: result.error.message,
code: 'InputValidationError',
details: tools.validationErrors(result)
});
return next();
}
// permissions check
req.validate(roles.can(req.role).deleteAny('domainaccess'));
let domain = new ObjectID(result.value.domain);
let r;
try {
r = await db.database.collection('domainaccess').deleteOne({
_id: domain
});
} catch (err) {
res.json({
error: 'MongoDB Error: ' + err.message,
code: 'InternalDatabaseError'
});
return next();
}
if (!r.deletedCount) {
res.status(404);
res.json({
error: 'Domain was not found',
code: 'DomainNotFound'
});
return next();
}
res.json({
success: true,
deleted: domain
});
return next();
})
);
};