diff --git a/src/backend/internal/access-list.js b/src/backend/internal/access-list.js new file mode 100644 index 0000000..00f0120 --- /dev/null +++ b/src/backend/internal/access-list.js @@ -0,0 +1,183 @@ +'use strict'; + +const _ = require('lodash'); +const error = require('../lib/error'); +const accessListModel = require('../models/access_list'); + +function omissions () { + return ['is_deleted']; +} + +const internalAccessList = { + + /** + * @param {Access} access + * @param {Object} data + * @returns {Promise} + */ + create: (access, data) => { + return access.can('access_lists:create', data) + .then(access_data => { + // TODO + return {}; + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {String} [data.email] + * @param {String} [data.name] + * @return {Promise} + */ + update: (access, data) => { + return access.can('access_lists:update', data.id) + .then(access_data => { + // TODO + return {}; + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {Array} [data.expand] + * @param {Array} [data.omit] + * @return {Promise} + */ + get: (access, data) => { + if (typeof data === 'undefined') { + data = {}; + } + + if (typeof data.id === 'undefined' || !data.id) { + data.id = access.token.get('attrs').id; + } + + return access.can('access_lists:get', data.id) + .then(access_data => { + let query = accessListModel + .query() + .where('is_deleted', 0) + .andWhere('id', data.id) + .allowEager('[owner]') + .first(); + + if (access_data.permission_visibility !== 'all') { + query.andWhere('owner_user_id', access.token.get('attrs').id); + } + + // Custom omissions + if (typeof data.omit !== 'undefined' && data.omit !== null) { + query.omit(data.omit); + } + + if (typeof data.expand !== 'undefined' && data.expand !== null) { + query.eager('[' + data.expand.join(', ') + ']'); + } + + return query; + }) + .then(row => { + if (row) { + return _.omit(row, omissions()); + } else { + throw new error.ItemNotFoundError(data.id); + } + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {String} [data.reason] + * @returns {Promise} + */ + delete: (access, data) => { + return access.can('access_lists:delete', data.id) + .then(() => { + return internalAccessList.get(access, {id: data.id}); + }) + .then(row => { + if (!row) { + throw new error.ItemNotFoundError(data.id); + } + + return accessListModel + .query() + .where('id', row.id) + .patch({ + is_deleted: 1 + }); + }) + .then(() => { + return true; + }); + }, + + /** + * All Lists + * + * @param {Access} access + * @param {Array} [expand] + * @param {String} [search_query] + * @returns {Promise} + */ + getAll: (access, expand, search_query) => { + return access.can('access_lists:list') + .then(access_data => { + let query = accessListModel + .query() + .where('is_deleted', 0) + .groupBy('id') + .omit(['is_deleted']) + .allowEager('[owner]') + .orderBy('name', 'ASC'); + + if (access_data.permission_visibility !== 'all') { + query.andWhere('owner_user_id', access.token.get('attrs').id); + } + + // Query is used for searching + if (typeof search_query === 'string') { + query.where(function () { + this.where('name', 'like', '%' + search_query + '%'); + }); + } + + if (typeof expand !== 'undefined' && expand !== null) { + query.eager('[' + expand.join(', ') + ']'); + } + + return query; + }); + }, + + /** + * Report use + * + * @param {Integer} user_id + * @param {String} visibility + * @returns {Promise} + */ + getCount: (user_id, visibility) => { + let query = accessListModel + .query() + .count('id as count') + .where('is_deleted', 0); + + if (visibility !== 'all') { + query.andWhere('owner_user_id', user_id); + } + + return query.first() + .then(row => { + return parseInt(row.count, 10); + }); + } +}; + +module.exports = internalAccessList; diff --git a/src/backend/internal/proxy-host.js b/src/backend/internal/proxy-host.js index dbcb4d5..649a3df 100644 --- a/src/backend/internal/proxy-host.js +++ b/src/backend/internal/proxy-host.js @@ -125,7 +125,7 @@ const internalProxyHost = { .query() .where('is_deleted', 0) .andWhere('id', data.id) - .allowEager('[permissions]') + .allowEager('[owner,access_list]') .first(); if (access_data.permission_visibility !== 'all') { diff --git a/src/backend/internal/redirection-host.js b/src/backend/internal/redirection-host.js index 0ec8ab8..75e5447 100644 --- a/src/backend/internal/redirection-host.js +++ b/src/backend/internal/redirection-host.js @@ -3,12 +3,210 @@ const _ = require('lodash'); const error = require('../lib/error'); const redirectionHostModel = require('../models/redirection_host'); +const internalHost = require('./host'); function omissions () { return ['is_deleted']; } -const internalProxyHost = { +const internalRedirectionHost = { + + /** + * @param {Access} access + * @param {Object} data + * @returns {Promise} + */ + create: (access, data) => { + return access.can('redirection_hosts:create', data) + .then(access_data => { + // Get a list of the domain names and check each of them against existing records + let domain_name_check_promises = []; + + data.domain_names.map(function (domain_name) { + domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name)); + }); + + return Promise.all(domain_name_check_promises) + .then(check_results => { + check_results.map(function (result) { + if (result.is_taken) { + throw new error.ValidationError(result.hostname + ' is already in use'); + } + }); + }); + }) + .then(() => { + // At this point the domains should have been checked + data.owner_user_id = access.token.get('attrs').id; + + if (typeof data.meta === 'undefined') { + data.meta = {}; + } + + return redirectionHostModel + .query() + .omit(omissions()) + .insertAndFetch(data); + }) + .then(row => { + return _.omit(row, omissions()); + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {String} [data.email] + * @param {String} [data.name] + * @return {Promise} + */ + update: (access, data) => { + return access.can('redirection_hosts:update', data.id) + .then(access_data => { + // Get a list of the domain names and check each of them against existing records + let domain_name_check_promises = []; + + if (typeof data.domain_names !== 'undefined') { + data.domain_names.map(function (domain_name) { + domain_name_check_promises.push(internalHost.isHostnameTaken(domain_name, 'redirection', data.id)); + }); + + return Promise.all(domain_name_check_promises) + .then(check_results => { + check_results.map(function (result) { + if (result.is_taken) { + throw new error.ValidationError(result.hostname + ' is already in use'); + } + }); + }); + } + }) + .then(() => { + return internalRedirectionHost.get(access, {id: data.id}); + }) + .then(row => { + if (row.id !== data.id) { + // Sanity check that something crazy hasn't happened + throw new error.InternalValidationError('Redirection Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id); + } + + return redirectionHostModel + .query() + .omit(omissions()) + .patchAndFetchById(row.id, data) + .then(saved_row => { + saved_row.meta = internalHost.cleanMeta(saved_row.meta); + return _.omit(saved_row, omissions()); + }); + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {Array} [data.expand] + * @param {Array} [data.omit] + * @return {Promise} + */ + get: (access, data) => { + if (typeof data === 'undefined') { + data = {}; + } + + if (typeof data.id === 'undefined' || !data.id) { + data.id = access.token.get('attrs').id; + } + + return access.can('redirection_hosts:get', data.id) + .then(access_data => { + let query = redirectionHostModel + .query() + .where('is_deleted', 0) + .andWhere('id', data.id) + .allowEager('[owner]') + .first(); + + if (access_data.permission_visibility !== 'all') { + query.andWhere('owner_user_id', access.token.get('attrs').id); + } + + // Custom omissions + if (typeof data.omit !== 'undefined' && data.omit !== null) { + query.omit(data.omit); + } + + if (typeof data.expand !== 'undefined' && data.expand !== null) { + query.eager('[' + data.expand.join(', ') + ']'); + } + + return query; + }) + .then(row => { + if (row) { + row.meta = internalHost.cleanMeta(row.meta); + return _.omit(row, omissions()); + } else { + throw new error.ItemNotFoundError(data.id); + } + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {String} [data.reason] + * @returns {Promise} + */ + delete: (access, data) => { + return access.can('redirection_hosts:delete', data.id) + .then(() => { + return internalRedirectionHost.get(access, {id: data.id}); + }) + .then(row => { + if (!row) { + throw new error.ItemNotFoundError(data.id); + } + + return redirectionHostModel + .query() + .where('id', row.id) + .patch({ + is_deleted: 1 + }); + }) + .then(() => { + return true; + }); + }, + + /** + * @param {Access} access + * @param {Object} data + * @param {Integer} data.id + * @param {Object} data.files + * @returns {Promise} + */ + setCerts: (access, data) => { + return internalRedirectionHost.get(access, {id: data.id}) + .then(row => { + _.map(data.files, (file, name) => { + if (internalHost.allowed_ssl_files.indexOf(name) !== -1) { + row.meta[name] = file.data.toString(); + } + }); + + return internalRedirectionHost.update(access, { + id: data.id, + meta: row.meta + }); + }) + .then(row => { + return _.pick(row.meta, internalHost.allowed_ssl_files); + }); + }, /** * All Hosts @@ -26,6 +224,7 @@ const internalProxyHost = { .where('is_deleted', 0) .groupBy('id') .omit(['is_deleted']) + .allowEager('[owner]') .orderBy('domain_names', 'ASC'); if (access_data.permission_visibility !== 'all') { @@ -44,6 +243,13 @@ const internalProxyHost = { } return query; + }) + .then(rows => { + rows.map(row => { + row.meta = internalHost.cleanMeta(row.meta); + }); + + return rows; }); }, @@ -71,4 +277,4 @@ const internalProxyHost = { } }; -module.exports = internalProxyHost; +module.exports = internalRedirectionHost; diff --git a/src/backend/lib/access/access_lists-create.json b/src/backend/lib/access/access_lists-create.json new file mode 100644 index 0000000..f2a91ff --- /dev/null +++ b/src/backend/lib/access/access_lists-create.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_access_lists", "roles"], + "properties": { + "permission_access_lists": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/access_lists-delete.json b/src/backend/lib/access/access_lists-delete.json new file mode 100644 index 0000000..f2a91ff --- /dev/null +++ b/src/backend/lib/access/access_lists-delete.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_access_lists", "roles"], + "properties": { + "permission_access_lists": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/access_lists-get.json b/src/backend/lib/access/access_lists-get.json new file mode 100644 index 0000000..12203b3 --- /dev/null +++ b/src/backend/lib/access/access_lists-get.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_access_lists", "roles"], + "properties": { + "permission_access_lists": { + "$ref": "perms#/definitions/view" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/access_lists-list.json b/src/backend/lib/access/access_lists-list.json new file mode 100644 index 0000000..12203b3 --- /dev/null +++ b/src/backend/lib/access/access_lists-list.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_access_lists", "roles"], + "properties": { + "permission_access_lists": { + "$ref": "perms#/definitions/view" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/access_lists-update.json b/src/backend/lib/access/access_lists-update.json new file mode 100644 index 0000000..f2a91ff --- /dev/null +++ b/src/backend/lib/access/access_lists-update.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_access_lists", "roles"], + "properties": { + "permission_access_lists": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/redirection_hosts-create.json b/src/backend/lib/access/redirection_hosts-create.json new file mode 100644 index 0000000..b27c1f4 --- /dev/null +++ b/src/backend/lib/access/redirection_hosts-create.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_redirection_hosts", "roles"], + "properties": { + "permission_redirection_hosts": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/redirection_hosts-delete.json b/src/backend/lib/access/redirection_hosts-delete.json new file mode 100644 index 0000000..b27c1f4 --- /dev/null +++ b/src/backend/lib/access/redirection_hosts-delete.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_redirection_hosts", "roles"], + "properties": { + "permission_redirection_hosts": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/redirection_hosts-get.json b/src/backend/lib/access/redirection_hosts-get.json new file mode 100644 index 0000000..227fc54 --- /dev/null +++ b/src/backend/lib/access/redirection_hosts-get.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_redirection_hosts", "roles"], + "properties": { + "permission_redirection_hosts": { + "$ref": "perms#/definitions/view" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/lib/access/redirection_hosts-update.json b/src/backend/lib/access/redirection_hosts-update.json new file mode 100644 index 0000000..b27c1f4 --- /dev/null +++ b/src/backend/lib/access/redirection_hosts-update.json @@ -0,0 +1,23 @@ +{ + "anyOf": [ + { + "$ref": "roles#/definitions/admin" + }, + { + "type": "object", + "required": ["permission_redirection_hosts", "roles"], + "properties": { + "permission_redirection_hosts": { + "$ref": "perms#/definitions/manage" + }, + "roles": { + "type": "array", + "items": { + "type": "string", + "enum": ["user"] + } + } + } + } + ] +} diff --git a/src/backend/routes/api/main.js b/src/backend/routes/api/main.js index 1f6fb92..c06c243 100644 --- a/src/backend/routes/api/main.js +++ b/src/backend/routes/api/main.js @@ -35,6 +35,7 @@ router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts')); router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts')); router.use('/nginx/dead-hosts', require('./nginx/dead_hosts')); router.use('/nginx/streams', require('./nginx/streams')); +router.use('/nginx/access-lists', require('./nginx/access_lists')); /** * API 404 for all other routes diff --git a/src/backend/routes/api/nginx/access_lists.js b/src/backend/routes/api/nginx/access_lists.js new file mode 100644 index 0000000..b514403 --- /dev/null +++ b/src/backend/routes/api/nginx/access_lists.js @@ -0,0 +1,150 @@ +'use strict'; + +const express = require('express'); +const validator = require('../../../lib/validator'); +const jwtdecode = require('../../../lib/express/jwt-decode'); +const internalAccessList = require('../../../internal/access-list'); +const apiValidator = require('../../../lib/validator/api'); + +let router = express.Router({ + caseSensitive: true, + strict: true, + mergeParams: true +}); + +/** + * /api/nginx/access-lists + */ +router + .route('/') + .options((req, res) => { + res.sendStatus(204); + }) + .all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes + + /** + * GET /api/nginx/access-lists + * + * Retrieve all access-lists + */ + .get((req, res, next) => { + validator({ + additionalProperties: false, + properties: { + expand: { + $ref: 'definitions#/definitions/expand' + }, + query: { + $ref: 'definitions#/definitions/query' + } + } + }, { + expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null), + query: (typeof req.query.query === 'string' ? req.query.query : null) + }) + .then(data => { + return internalAccessList.getAll(res.locals.access, data.expand, data.query); + }) + .then(rows => { + res.status(200) + .send(rows); + }) + .catch(next); + }) + + /** + * POST /api/nginx/access-lists + * + * Create a new access-list + */ + .post((req, res, next) => { + apiValidator({$ref: 'endpoints/access-lists#/links/1/schema'}, req.body) + .then(payload => { + return internalAccessList.create(res.locals.access, payload); + }) + .then(result => { + res.status(201) + .send(result); + }) + .catch(next); + }); + +/** + * Specific access-list + * + * /api/nginx/access-lists/123 + */ +router + .route('/:host_id') + .options((req, res) => { + res.sendStatus(204); + }) + .all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes + + /** + * GET /api/nginx/access-lists/123 + * + * Retrieve a specific access-list + */ + .get((req, res, next) => { + validator({ + required: ['host_id'], + additionalProperties: false, + properties: { + host_id: { + $ref: 'definitions#/definitions/id' + }, + expand: { + $ref: 'definitions#/definitions/expand' + } + } + }, { + host_id: req.params.host_id, + expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null) + }) + .then(data => { + return internalAccessList.get(res.locals.access, { + id: parseInt(data.host_id, 10), + expand: data.expand + }); + }) + .then(row => { + res.status(200) + .send(row); + }) + .catch(next); + }) + + /** + * PUT /api/nginx/access-lists/123 + * + * Update and existing access-list + */ + .put((req, res, next) => { + apiValidator({$ref: 'endpoints/access-lists#/links/2/schema'}, req.body) + .then(payload => { + payload.id = parseInt(req.params.host_id, 10); + return internalAccessList.update(res.locals.access, payload); + }) + .then(result => { + res.status(200) + .send(result); + }) + .catch(next); + }) + + /** + * DELETE /api/nginx/access-lists/123 + * + * Update and existing access-list + */ + .delete((req, res, next) => { + internalAccessList.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)}) + .then(result => { + res.status(200) + .send(result); + }) + .catch(next); + }); + +module.exports = router; diff --git a/src/backend/routes/api/nginx/redirection_hosts.js b/src/backend/routes/api/nginx/redirection_hosts.js index dbaf333..d2ec0d3 100644 --- a/src/backend/routes/api/nginx/redirection_hosts.js +++ b/src/backend/routes/api/nginx/redirection_hosts.js @@ -104,7 +104,7 @@ router }) .then(data => { return internalRedirectionHost.get(res.locals.access, { - id: data.host_id, + id: parseInt(data.host_id, 10), expand: data.expand }); }) @@ -123,7 +123,7 @@ router .put((req, res, next) => { apiValidator({$ref: 'endpoints/redirection-hosts#/links/2/schema'}, req.body) .then(payload => { - payload.id = req.params.host_id; + payload.id = parseInt(req.params.host_id, 10); return internalRedirectionHost.update(res.locals.access, payload); }) .then(result => { @@ -139,7 +139,7 @@ router * Update and existing redirection-host */ .delete((req, res, next) => { - internalRedirectionHost.delete(res.locals.access, {id: req.params.host_id}) + internalRedirectionHost.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)}) .then(result => { res.status(200) .send(result); @@ -147,4 +147,38 @@ router .catch(next); }); +/** + * Specific redirection-host Certificates + * + * /api/nginx/redirection-hosts/123/certificates + */ +router + .route('/:host_id/certificates') + .options((req, res) => { + res.sendStatus(204); + }) + .all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes + + /** + * POST /api/nginx/redirection-hosts/123/certificates + * + * Upload certifications + */ + .post((req, res, next) => { + if (!req.files) { + res.status(400) + .send({error: 'No files were uploaded'}); + } else { + internalRedirectionHost.setCerts(res.locals.access, { + id: parseInt(req.params.host_id, 10), + files: req.files + }) + .then(result => { + res.status(200) + .send(result); + }) + .catch(next); + } + }); + module.exports = router; diff --git a/src/backend/schema/definitions.json b/src/backend/schema/definitions.json index e064b4d..75c148b 100644 --- a/src/backend/schema/definitions.json +++ b/src/backend/schema/definitions.json @@ -116,6 +116,12 @@ "type": "integer", "minimum": 1 }, + "access_list_id": { + "description": "Access List ID", + "example": 1234, + "type": "integer", + "minimum": 0 + }, "name": { "type": "string", "minLength": 1, @@ -135,6 +141,12 @@ "minLength": 8, "maxLength": 255 }, + "domain_name": { + "description": "Domain Name", + "example": "jc21.com", + "type": "string", + "pattern": "^(?:[^.*]+\\.?)+[^.]$" + }, "domain_names": { "description": "Domain Names separated by a comma", "example": "*.jc21.com,blog.jc21.com", @@ -159,6 +171,16 @@ "ssl_provider": { "type": "string", "pattern": "^(letsencrypt|other)$" + }, + "block_exploits": { + "description": "Should we block common exploits", + "example": true, + "type": "boolean" + }, + "caching_enabled": { + "description": "Should we cache assets", + "example": true, + "type": "boolean" } } } diff --git a/src/backend/schema/endpoints/proxy-hosts.json b/src/backend/schema/endpoints/proxy-hosts.json index e73ec0b..2e0a9d4 100644 --- a/src/backend/schema/endpoints/proxy-hosts.json +++ b/src/backend/schema/endpoints/proxy-hosts.json @@ -36,6 +36,15 @@ "ssl_provider": { "$ref": "../definitions.json#/definitions/ssl_provider" }, + "block_exploits": { + "$ref": "../definitions.json#/definitions/block_exploits" + }, + "caching_enabled": { + "$ref": "../definitions.json#/definitions/caching_enabled" + }, + "access_list_id": { + "$ref": "../definitions.json#/definitions/access_list_id" + }, "meta": { "type": "object", "additionalProperties": false, @@ -78,6 +87,15 @@ "ssl_provider": { "$ref": "#/definitions/ssl_provider" }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "caching_enabled": { + "$ref": "#/definitions/caching_enabled" + }, + "access_list_id": { + "$ref": "#/definitions/access_list_id" + }, "meta": { "$ref": "#/definitions/meta" } @@ -136,6 +154,15 @@ "ssl_provider": { "$ref": "#/definitions/ssl_provider" }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "caching_enabled": { + "$ref": "#/definitions/caching_enabled" + }, + "access_list_id": { + "$ref": "#/definitions/access_list_id" + }, "meta": { "$ref": "#/definitions/meta" } @@ -178,6 +205,15 @@ "ssl_provider": { "$ref": "#/definitions/ssl_provider" }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "caching_enabled": { + "$ref": "#/definitions/caching_enabled" + }, + "access_list_id": { + "$ref": "#/definitions/access_list_id" + }, "meta": { "$ref": "#/definitions/meta" } diff --git a/src/backend/schema/endpoints/redirection-hosts.json b/src/backend/schema/endpoints/redirection-hosts.json index 706d2ee..a66b50b 100644 --- a/src/backend/schema/endpoints/redirection-hosts.json +++ b/src/backend/schema/endpoints/redirection-hosts.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "endpoints/redirection-hosts", - "title": "Users", + "title": "Redirection Hosts", "description": "Endpoints relating to Redirection Hosts", "stability": "stable", "type": "object", @@ -15,49 +15,83 @@ "modified_on": { "$ref": "../definitions.json#/definitions/modified_on" }, - "name": { - "description": "Name", - "example": "Jamie Curnow", - "type": "string", - "minLength": 2, - "maxLength": 100 + "domain_names": { + "$ref": "../definitions.json#/definitions/domain_names" }, - "nickname": { - "description": "Nickname", - "example": "Jamie", - "type": "string", - "minLength": 2, - "maxLength": 50 + "forward_domain_name": { + "$ref": "../definitions.json#/definitions/domain_name" }, - "email": { - "$ref": "../definitions.json#/definitions/email" - }, - "avatar": { - "description": "Avatar", - "example": "http://somewhere.jpg", - "type": "string", - "minLength": 2, - "maxLength": 150, - "readOnly": true - }, - "roles": { - "description": "Roles", - "example": [ - "admin" - ], - "type": "array" - }, - "is_disabled": { - "description": "Is Disabled", - "example": false, + "preserve_path": { + "description": "Should the path be preserved", + "example": true, "type": "boolean" + }, + "ssl_enabled": { + "$ref": "../definitions.json#/definitions/ssl_enabled" + }, + "ssl_forced": { + "$ref": "../definitions.json#/definitions/ssl_forced" + }, + "ssl_provider": { + "$ref": "../definitions.json#/definitions/ssl_provider" + }, + "block_exploits": { + "$ref": "../definitions.json#/definitions/block_exploits" + }, + "meta": { + "type": "object", + "additionalProperties": false, + "properties": { + "letsencrypt_email": { + "type": "string", + "format": "email" + }, + "letsencrypt_agree": { + "type": "boolean" + } + } + } + }, + "properties": { + "id": { + "$ref": "#/definitions/id" + }, + "created_on": { + "$ref": "#/definitions/created_on" + }, + "modified_on": { + "$ref": "#/definitions/modified_on" + }, + "domain_names": { + "$ref": "#/definitions/domain_names" + }, + "forward_domain_name": { + "$ref": "#/definitions/forward_domain_name" + }, + "preserve_path": { + "$ref": "#/definitions/preserve_path" + }, + "ssl_enabled": { + "$ref": "#/definitions/ssl_enabled" + }, + "ssl_forced": { + "$ref": "#/definitions/ssl_forced" + }, + "ssl_provider": { + "$ref": "#/definitions/ssl_provider" + }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "meta": { + "$ref": "#/definitions/meta" } }, "links": [ { "title": "List", - "description": "Returns a list of Users", - "href": "/users", + "description": "Returns a list of Redirection Hosts", + "href": "/nginx/redirection-hosts", "access": "private", "method": "GET", "rel": "self", @@ -73,8 +107,8 @@ }, { "title": "Create", - "description": "Creates a new User", - "href": "/users", + "description": "Creates a new Redirection Host", + "href": "/nginx/redirection-hosts", "access": "private", "method": "POST", "rel": "create", @@ -84,33 +118,33 @@ "schema": { "type": "object", "required": [ - "name", - "nickname", - "email" + "domain_names", + "forward_domain_name" ], "properties": { - "name": { - "$ref": "#/definitions/name" + "domain_names": { + "$ref": "#/definitions/domain_names" }, - "nickname": { - "$ref": "#/definitions/nickname" + "forward_domain_name": { + "$ref": "#/definitions/forward_domain_name" }, - "email": { - "$ref": "#/definitions/email" + "preserve_path": { + "$ref": "#/definitions/preserve_path" }, - "roles": { - "$ref": "#/definitions/roles" + "ssl_enabled": { + "$ref": "#/definitions/ssl_enabled" }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" + "ssl_forced": { + "$ref": "#/definitions/ssl_forced" }, - "auth": { - "type": "object", - "description": "Auth Credentials", - "example": { - "type": "password", - "secret": "bigredhorsebanana" - } + "ssl_provider": { + "$ref": "#/definitions/ssl_provider" + }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "meta": { + "$ref": "#/definitions/meta" } } }, @@ -122,8 +156,8 @@ }, { "title": "Update", - "description": "Updates a existing User", - "href": "/users/{definitions.identity.example}", + "description": "Updates a existing Redirection Host", + "href": "/nginx/redirection-hosts/{definitions.identity.example}", "access": "private", "method": "PUT", "rel": "update", @@ -133,20 +167,29 @@ "schema": { "type": "object", "properties": { - "name": { - "$ref": "#/definitions/name" + "domain_names": { + "$ref": "#/definitions/domain_names" }, - "nickname": { - "$ref": "#/definitions/nickname" + "forward_domain_name": { + "$ref": "#/definitions/forward_domain_name" }, - "email": { - "$ref": "#/definitions/email" + "preserve_path": { + "$ref": "#/definitions/preserve_path" }, - "roles": { - "$ref": "#/definitions/roles" + "ssl_enabled": { + "$ref": "#/definitions/ssl_enabled" }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" + "ssl_forced": { + "$ref": "#/definitions/ssl_forced" + }, + "ssl_provider": { + "$ref": "#/definitions/ssl_provider" + }, + "block_exploits": { + "$ref": "#/definitions/block_exploits" + }, + "meta": { + "$ref": "#/definitions/meta" } } }, @@ -158,8 +201,8 @@ }, { "title": "Delete", - "description": "Deletes a existing User", - "href": "/users/{definitions.identity.example}", + "description": "Deletes a existing Redirection Host", + "href": "/nginx/redirection-hosts/{definitions.identity.example}", "access": "private", "method": "DELETE", "rel": "delete", @@ -170,34 +213,5 @@ "type": "boolean" } } - ], - "properties": { - "id": { - "$ref": "#/definitions/id" - }, - "created_on": { - "$ref": "#/definitions/created_on" - }, - "modified_on": { - "$ref": "#/definitions/modified_on" - }, - "name": { - "$ref": "#/definitions/name" - }, - "nickname": { - "$ref": "#/definitions/nickname" - }, - "email": { - "$ref": "#/definitions/email" - }, - "avatar": { - "$ref": "#/definitions/avatar" - }, - "roles": { - "$ref": "#/definitions/roles" - }, - "is_disabled": { - "$ref": "#/definitions/is_disabled" - } - } + ] } diff --git a/src/frontend/js/app/api.js b/src/frontend/js/app/api.js index 0a80b29..162e358 100644 --- a/src/frontend/js/app/api.js +++ b/src/frontend/js/app/api.js @@ -377,7 +377,7 @@ module.exports = { * @params {Promise} */ setCerts: function (id, form_data) { - return upload('nginx/redirection-hosts/' + id + '/certificates', form_data); + return FileUpload('nginx/redirection-hosts/' + id + '/certificates', form_data); } }, @@ -460,46 +460,46 @@ module.exports = { * @params {Promise} */ setCerts: function (id, form_data) { - return upload('nginx/dead-hosts/' + id + '/certificates', form_data); + return FileUpload('nginx/dead-hosts/' + id + '/certificates', form_data); } - } - }, - - AccessLists: { - /** - * @param {Array} [expand] - * @param {String} [query] - * @returns {Promise} - */ - getAll: function (expand, query) { - return getAllObjects('access-lists', expand, query); }, - /** - * @param {Object} data - */ - create: function (data) { - return fetch('post', 'access-lists', data); - }, + AccessLists: { + /** + * @param {Array} [expand] + * @param {String} [query] + * @returns {Promise} + */ + getAll: function (expand, query) { + return getAllObjects('nginx/access-lists', expand, query); + }, - /** - * @param {Object} data - * @param {Integer} data.id - * @returns {Promise} - */ - update: function (data) { - let id = data.id; - delete data.id; - return fetch('put', 'access-lists/' + id, data); - }, + /** + * @param {Object} data + */ + create: function (data) { + return fetch('post', 'nginx/access-lists', data); + }, - /** - * @param {Integer} id - * @returns {Promise} - */ - delete: function (id) { - return fetch('delete', 'access-lists/' + id); - } + /** + * @param {Object} data + * @param {Integer} data.id + * @returns {Promise} + */ + update: function (data) { + let id = data.id; + delete data.id; + return fetch('put', 'nginx/access-lists/' + id, data); + }, + + /** + * @param {Integer} id + * @returns {Promise} + */ + delete: function (id) { + return fetch('delete', 'nginx/access-lists/' + id); + } + }, }, AuditLog: { diff --git a/src/frontend/js/app/audit-log/main.ejs b/src/frontend/js/app/audit-log/main.ejs index b5440e6..acaa8b4 100644 --- a/src/frontend/js/app/audit-log/main.ejs +++ b/src/frontend/js/app/audit-log/main.ejs @@ -1,4 +1,5 @@