kopia lustrzana https://github.com/OpenDroneMap/NodeODM
Added SimpleTokenAuth, NoTokenRequiredAuth for token authentication, API definition change
rodzic
02a9a316fc
commit
bf210e5f11
|
@ -42,3 +42,4 @@ jspm_packages
|
|||
|
||||
.vscode
|
||||
|
||||
package-lock.json
|
||||
|
|
|
@ -15,5 +15,6 @@
|
|||
"cleanupTasksAfter": 3,
|
||||
"test": false,
|
||||
"testSkipOrthophotos": false,
|
||||
"testSkipDems": false
|
||||
"testSkipDems": false,
|
||||
"token": ""
|
||||
}
|
|
@ -36,7 +36,8 @@ Options:
|
|||
--test Enable test mode. In test mode, no commands are sent to OpenDroneMap. This can be useful during development or testing (default: false)
|
||||
--test_skip_orthophotos If test mode is enabled, skip orthophoto results when generating assets. (default: false)
|
||||
--test_skip_dems If test mode is enabled, skip dems results when generating assets. (default: false)
|
||||
--powercycle When set, the application exits immediately after powering up. Useful for testing launch and compilation issues.
|
||||
--powercycle When set, the application exits immediately after powering up. Useful for testing launch and compilation issues.
|
||||
--token <token> Sets a token that needs to be passed for every request. This can be used to limit access to the node only to token holders. (default: none)
|
||||
Log Levels:
|
||||
error | debug | info | verbose | debug | silly
|
||||
`);
|
||||
|
@ -84,5 +85,6 @@ config.test = argv.test || fromConfigFile("test", false);
|
|||
config.testSkipOrthophotos = argv.test_skip_orthophotos || fromConfigFile("testSkipOrthophotos", false);
|
||||
config.testSkipDems = argv.test_skip_dems || fromConfigFile("testSkipDems", false);
|
||||
config.powercycle = argv.powercycle || fromConfigFile("powercycle", false);
|
||||
config.token = argv.token || fromConfigFile("token", "");
|
||||
|
||||
module.exports = config;
|
||||
|
|
|
@ -8,7 +8,7 @@ REST API to access OpenDroneMap
|
|||
|
||||
=== Version information
|
||||
[%hardbreaks]
|
||||
_Version_ : 1.0.4
|
||||
_Version_ : 1.1.0
|
||||
|
||||
|
||||
=== Contact information
|
||||
|
@ -127,6 +127,8 @@ Cancels a task (stops its execution, or prevents it from being executed)
|
|||
[options="header", cols=".^2,.^3,.^9,.^4,.^2"]
|
||||
|===
|
||||
|Type|Name|Description|Schema|Default
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|*Body*|*uuid* +
|
||||
_required_|UUID of the task|string|
|
||||
|===
|
||||
|
@ -153,6 +155,8 @@ Creates a new task and places it at the end of the processing queue
|
|||
[options="header", cols=".^2,.^3,.^9,.^4,.^2"]
|
||||
|===
|
||||
|Type|Name|Description|Schema|Default
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|*FormData*|*images* +
|
||||
_optional_|Images to process, plus an optional GPC file. If included, the GPC file should have .txt extension|file|
|
||||
|*FormData*|*name* +
|
||||
|
@ -206,6 +210,8 @@ Removes a task and deletes all of its assets
|
|||
[options="header", cols=".^2,.^3,.^9,.^4,.^2"]
|
||||
|===
|
||||
|Type|Name|Description|Schema|Default
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|*Body*|*uuid* +
|
||||
_required_|UUID of the task|string|
|
||||
|===
|
||||
|
@ -232,6 +238,8 @@ Restarts a task that was previously canceled, that had failed to process or that
|
|||
[options="header", cols=".^2,.^3,.^9,.^4,.^2"]
|
||||
|===
|
||||
|Type|Name|Description|Schema|Default
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|*Body*|*options* +
|
||||
_optional_|Serialized JSON string of the options to use for processing, as an array of the format: [{name: option1, value: value1}, {name: option2, value: value2}, …]. For example, [{"name":"cmvs-maxImages","value":"500"},{"name":"time","value":true}]. For a list of all options, call /options. Overrides the previous options set for this task.|string|
|
||||
|*Body*|*uuid* +
|
||||
|
@ -264,6 +272,8 @@ Retrieves an asset (the output of OpenDroneMap's processing) associated with a t
|
|||
_required_|Type of asset to download. Use "all.zip" for zip file containing all assets.|enum (all.zip, orthophoto.tif)|
|
||||
|*Path*|*uuid* +
|
||||
_required_|UUID of the task|string|
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|===
|
||||
|
||||
|
||||
|
@ -301,6 +311,10 @@ Gets information about this task, such as name, creation date, processing time,
|
|||
|Type|Name|Description|Schema|Default
|
||||
|*Path*|*uuid* +
|
||||
_required_|UUID of the task|string|
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|*FormData*|*options* +
|
||||
_optional_|Serialized JSON string of the options to use for processing, as an array of the format: [{name: option1, value: value1}, {name: option2, value: value2}, …]. For example, [{"name":"cmvs-maxImages","value":"500"},{"name":"time","value":true}]. For a list of all options, call /options|string|
|
||||
|===
|
||||
|
||||
|
||||
|
@ -369,6 +383,8 @@ Retrieves the console output of the OpenDroneMap's process. Useful for monitorin
|
|||
_required_|UUID of the task|string|
|
||||
|*Query*|*line* +
|
||||
_optional_|Optional line number that the console output should be truncated from. For example, passing a value of 100 will retrieve the console output starting from line 100. Defaults to 0 (retrieve all console output).|integer|`"0"`
|
||||
|*Query*|*token* +
|
||||
_optional_|Token required for authentication (when authentication is required).|string|
|
||||
|===
|
||||
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
71
index.js
71
index.js
|
@ -40,6 +40,9 @@ let odmOptions = require('./libs/odmOptions');
|
|||
let Directories = require('./libs/Directories');
|
||||
let unzip = require('node-unzip-2');
|
||||
|
||||
let auth = require('./libs/auth/factory').fromConfig(config);
|
||||
const authCheck = auth.getMiddleware();
|
||||
|
||||
// zip files
|
||||
let request = require('request');
|
||||
|
||||
|
@ -107,18 +110,24 @@ let server;
|
|||
* description: URL of the zip file containing the images to process, plus an optional GPC file. If included, the GPC file should have .txt extension
|
||||
* required: false
|
||||
* type: string
|
||||
* -
|
||||
* -
|
||||
* name: name
|
||||
* in: formData
|
||||
* description: An optional name to be associated with the task
|
||||
* required: false
|
||||
* type: string
|
||||
* -
|
||||
* -
|
||||
* name: options
|
||||
* in: formData
|
||||
* description: 'Serialized JSON string of the options to use for processing, as an array of the format: [{name: option1, value: value1}, {name: option2, value: value2}, ...]. For example, [{"name":"cmvs-maxImages","value":"500"},{"name":"time","value":true}]. For a list of all options, call /options'
|
||||
* required: false
|
||||
* type: string
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Success
|
||||
|
@ -134,7 +143,7 @@ let server;
|
|||
* schema:
|
||||
* $ref: '#/definitions/Error'
|
||||
*/
|
||||
app.post('/task/new', addRequestId, upload.array('images'), (req, res) => {
|
||||
app.post('/task/new', authCheck, addRequestId, upload.array('images'), (req, res) => {
|
||||
|
||||
if ((!req.files || req.files.length === 0) && !req.body.zipurl) res.json({ error: "Need at least 1 file or a zip file url." });
|
||||
|
||||
|
@ -270,6 +279,18 @@ let getTaskFromUuid = (req, res, next) => {
|
|||
* description: UUID of the task
|
||||
* required: true
|
||||
* type: string
|
||||
* -
|
||||
* name: options
|
||||
* in: formData
|
||||
* description: 'Serialized JSON string of the options to use for processing, as an array of the format: [{name: option1, value: value1}, {name: option2, value: value2}, ...]. For example, [{"name":"cmvs-maxImages","value":"500"},{"name":"time","value":true}]. For a list of all options, call /options'
|
||||
* required: false
|
||||
* type: string
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Task Information
|
||||
|
@ -315,7 +336,7 @@ let getTaskFromUuid = (req, res, next) => {
|
|||
* schema:
|
||||
* $ref: '#/definitions/Error'
|
||||
*/
|
||||
app.get('/task/:uuid/info', getTaskFromUuid, (req, res) => {
|
||||
app.get('/task/:uuid/info', authCheck, getTaskFromUuid, (req, res) => {
|
||||
res.json(req.task.getInfo());
|
||||
});
|
||||
|
||||
|
@ -338,6 +359,12 @@ app.get('/task/:uuid/info', getTaskFromUuid, (req, res) => {
|
|||
* default: 0
|
||||
* required: false
|
||||
* type: integer
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Console Output
|
||||
|
@ -348,7 +375,7 @@ app.get('/task/:uuid/info', getTaskFromUuid, (req, res) => {
|
|||
* schema:
|
||||
* $ref: '#/definitions/Error'
|
||||
*/
|
||||
app.get('/task/:uuid/output', getTaskFromUuid, (req, res) => {
|
||||
app.get('/task/:uuid/output', authCheck, getTaskFromUuid, (req, res) => {
|
||||
res.json(req.task.getOutput(req.query.line));
|
||||
});
|
||||
|
||||
|
@ -372,6 +399,12 @@ app.get('/task/:uuid/output', getTaskFromUuid, (req, res) => {
|
|||
* enum:
|
||||
* - all.zip
|
||||
* - orthophoto.tif
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Asset File
|
||||
|
@ -382,7 +415,7 @@ app.get('/task/:uuid/output', getTaskFromUuid, (req, res) => {
|
|||
* schema:
|
||||
* $ref: '#/definitions/Error'
|
||||
*/
|
||||
app.get('/task/:uuid/download/:asset', getTaskFromUuid, (req, res) => {
|
||||
app.get('/task/:uuid/download/:asset', authCheck, getTaskFromUuid, (req, res) => {
|
||||
let asset = req.params.asset !== undefined ? req.params.asset : "all.zip";
|
||||
let filePath = req.task.getAssetsArchivePath(asset);
|
||||
if (filePath) {
|
||||
|
@ -447,13 +480,19 @@ let successHandler = res => {
|
|||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Command Received
|
||||
* schema:
|
||||
* $ref: "#/definitions/Response"
|
||||
*/
|
||||
app.post('/task/cancel', uuidCheck, (req, res) => {
|
||||
app.post('/task/cancel', authCheck, uuidCheck, (req, res) => {
|
||||
taskManager.cancel(req.body.uuid, successHandler(res));
|
||||
});
|
||||
|
||||
|
@ -469,13 +508,19 @@ app.post('/task/cancel', uuidCheck, (req, res) => {
|
|||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Command Received
|
||||
* schema:
|
||||
* $ref: "#/definitions/Response"
|
||||
*/
|
||||
app.post('/task/remove', uuidCheck, (req, res) => {
|
||||
app.post('/task/remove', authCheck, uuidCheck, (req, res) => {
|
||||
taskManager.remove(req.body.uuid, successHandler(res));
|
||||
});
|
||||
|
||||
|
@ -498,13 +543,19 @@ app.post('/task/remove', uuidCheck, (req, res) => {
|
|||
* required: false
|
||||
* schema:
|
||||
* type: string
|
||||
* -
|
||||
* name: token
|
||||
* in: query
|
||||
* description: 'Token required for authentication (when authentication is required).'
|
||||
* required: false
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Command Received
|
||||
* schema:
|
||||
* $ref: "#/definitions/Response"
|
||||
*/
|
||||
app.post('/task/restart', uuidCheck, (req, res, next) => {
|
||||
app.post('/task/restart', authCheck, uuidCheck, (req, res, next) => {
|
||||
if (req.body.options){
|
||||
odmOptions.filterOptions(req.body.options, (err, options) => {
|
||||
if (err) res.json({ error: err.message });
|
||||
|
@ -590,6 +641,7 @@ app.get('/info', (req, res) => {
|
|||
let gracefulShutdown = done => {
|
||||
async.series([
|
||||
cb => taskManager.dumpTaskList(cb),
|
||||
cb => auth.cleanup(cb),
|
||||
cb => {
|
||||
logger.info("Closing server");
|
||||
server.close();
|
||||
|
@ -610,6 +662,7 @@ if (config.test) logger.info("Running in test mode");
|
|||
|
||||
let commands = [
|
||||
cb => odmOptions.initialize(cb),
|
||||
cb => auth.initialize(cb),
|
||||
cb => { taskManager = new TaskManager(cb); },
|
||||
cb => {
|
||||
server = app.listen(config.port, err => {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
Node-OpenDroneMap Node.js App and REST API to access OpenDroneMap.
|
||||
Copyright (C) 2018 Node-OpenDroneMap Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const TokenAuthBase = require('./TokenAuthBase');
|
||||
|
||||
module.exports = class NoTokenRequiredAuth extends TokenAuthBase{
|
||||
// Always return valid
|
||||
validateToken(token, cb){ cb(null, true); }
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Node-OpenDroneMap Node.js App and REST API to access OpenDroneMap.
|
||||
Copyright (C) 2018 Node-OpenDroneMap Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const TokenAuthBase = require('./TokenAuthBase');
|
||||
|
||||
module.exports = class SimpleTokenAuth extends TokenAuthBase{
|
||||
// @param token {String} token to use for authentication
|
||||
constructor(token){
|
||||
super(token);
|
||||
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
validateToken(token, cb){
|
||||
if (this.token === token){
|
||||
return cb(null, true);
|
||||
}else{
|
||||
cb(new Error("token does not match."), false);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Node-OpenDroneMap Node.js App and REST API to access OpenDroneMap.
|
||||
Copyright (C) 2018 Node-OpenDroneMap Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
let logger = require('../logger');
|
||||
|
||||
module.exports = /*abstract */ class TokenAuthBase{
|
||||
initialize(cb){
|
||||
logger.info(`Authentication using ${this.constructor.name.replace(/Auth$/, "")}`);
|
||||
cb();
|
||||
}
|
||||
|
||||
cleanup(cb){
|
||||
cb();
|
||||
}
|
||||
|
||||
validateToken(token, cb){ cb(new Error("Not implemented"), false); }
|
||||
|
||||
getMiddleware(){
|
||||
return (req, res, next) => {
|
||||
this.validateToken(req.query.token, (err, valid) => {
|
||||
if (valid) next();
|
||||
else{
|
||||
res.json({ error: "Invalid authentication token: " + err.message });
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
const NoTokenRequiredAuth = require('./NoTokenRequiredAuth');
|
||||
const SimpleTokenAuth = require('./SimpleTokenAuth');
|
||||
|
||||
module.exports = {
|
||||
fromConfig: function(config){
|
||||
if (config.token){
|
||||
return new SimpleTokenAuth(config.token);
|
||||
}else{
|
||||
return new NoTokenRequiredAuth();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "node-opendronemap",
|
||||
"version": "1.0.4",
|
||||
"version": "1.1.0",
|
||||
"description": "REST API to access OpenDroneMap",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
Ładowanie…
Reference in New Issue