diff --git a/.env_example b/.env_example
new file mode 100644
index 000000000..f0cfe5832
--- /dev/null
+++ b/.env_example
@@ -0,0 +1,7 @@
+PORT=1337
+NODE_ENV=production
+KONGA_HOOK_TIMEOUT=120000
+DB_ADAPTER=postgres
+DB_URI=postgresql://localhost:5432/konga
+KONGA_LOG_LEVEL=warn
+TOKEN_SECRET=some_secret_token
diff --git a/.gitignore b/.gitignore
index 922e80fcf..9422eeac7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,3 +121,6 @@ nbproject
/config/mssqlconfig.js
/www/
/kongadata/
+/certs/
+/.env
+.env
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9c83932c..6f73c9fb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
All notable changes to this project will be documented in this file.
+## [0.12.1](https://github.com/pantsel/konga/releases/tag/0.12.1) - 28-07-2018
+* **[Deprecation]** Deprecated consumer imports. This feature was not adopted and provided unnecessary
+complexity to maintenance as well as increased the overall project's size.
+* **[Fix]** Fixed the trailing slash issue. Konga is now able to communicate with
+Kong even if a trailing slash exists in the connection url.
+* Cleaned up unused dependencies.
+* When installing Konga from source, the `confing/local.js` file is deprecated
+in favor of a `.env` file. Check the README.md for details.
+
## [0.12.0](https://github.com/pantsel/konga/releases/tag/0.12.0) - 07-07-2018
* **[Fix]** Fix snapshots implementation. Use auto generated entity ids for proper relationships mapping.
* **[Compatibility]** Implement new Kong plugins properly.
diff --git a/README.md b/README.md
index a663e6c02..86b78f1fe 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ _Konga is not an official app. No affiliation with [Kong](https://www.konghq.com
- [**Used libraries**](#used-libraries)
- [**Installation**](#installation)
- [**Configuration**](#configuration)
+- [**Environment variables**](#environment-variables)
- [**Running Konga**](#running-konga)
- [**Upgrading**](#upgrading)
- [**FAQ**](#faq)
@@ -51,37 +52,58 @@ It also works with Kong 0.13.* yet without the ability to manage services and ro
## Prerequisites
- A running [Kong installation](https://getkong.org/)
-- Nodejs
+- Nodejs >= 8 (8.11.3 LTS is recommended)
- Npm
## Used libraries
* Sails.js, http://sailsjs.org/
* AngularJS, https://angularjs.org/
-* Bootstrap, http://getbootstrap.com/
## Installation
Install `npm` and `node.js`. Instructions can be found [here](http://sailsjs.org/#/getStarted?q=what-os-do-i-need).
-Install `bower`, `gulp` and `sails` packages.
+Install `bower`, ad `gulp` packages.
```
$ git clone https://github.com/pantsel/konga.git
$ cd konga
-$ npm install
+$ npm i
```
## Configuration
You can configure your application to use your environment specified
settings.
-There is an example configuration file on following path.
+There is an example configuration file on the root folder.
```
-config/local_example.js
+.env_example
```
-Just copy this to `config/local.js` and make necessary changes to it. Note that this
-`local.js` file is in .gitignore so it won't go to VCS at any point.
+Just copy this to `.env` and make necessary changes to it. Note that this
+`.env` file is in .gitignore so it won't go to VCS at any point.
+
+## Environment variables
+These are the general environment variables Konga uses.
+
+| VAR | DESCRIPTION | VALUES | DEFAULT |
+|--------------------|----------------------------------------------------------------------------------------------------------------------------|----------------------------------------|----------------------------------------------|
+| PORT | The port that will be used by Konga's server | - | 1337 |
+| NODE_ENV | The environment | `production`,`development` | `development` |
+| SSL_KEY_PATH | If you want to use SSL, this will be the absolute path to the .key file. Both `SSL_KEY_PATH` & `SSL_CRT_PATH` must be set. | - | null |
+| SSL_CRT_PATH | If you want to use SSL, this will be the absolute path to the .crt file. Both `SSL_KEY_PATH` & `SSL_CRT_PATH` must be set. | - | null |
+| KONGA_HOOK_TIMEOUT | The time in ms that Konga will wait for startup tasks to finish before exiting the process. | - | 60000 |
+| DB_ADAPTER | The database that Konga will use. If not set, the localDisk db will be used. | `mongo`,`mysql`,`postgres`,`sqlserver` | - |
+| DB_URI | The full db connection string. Depends on `DB_ADAPTER`. If this is set, no other DB related var is needed. | - | - |
+| DB_HOST | If `DB_URI` is not specified, this is the database host. Depends on `DB_ADAPTER`. | - | localhost |
+| DB_PORT | If `DB_URI` is not specified, this is the database port. Depends on `DB_ADAPTER`. | - | DB default. |
+| DB_USER | If `DB_URI` is not specified, this is the database user. Depends on `DB_ADAPTER`. | - | - |
+| DB_PASSWORD | If `DB_URI` is not specified, this is the database user's password. Depends on `DB_ADAPTER`. | - | - |
+| DB_DATABASE | If `DB_URI` is not specified, this is the name of Konga's db. Depends on `DB_ADAPTER`. | - | `konga_database` |
+| DB_PG_SCHEMA | If using postgres as a database, this is the schema that will be used. | - | `public` |
+| KONGA_LOG_LEVEL | The logging level | `silly`,`debug`,`info`,`warn`,`error` | `debug` on dev environment & `warn` on prod. |
+| TOKEN_SECRET | The secret that will be used to sign JWT tokens issued by Konga | - | - |
+
### Databases Integration
@@ -94,45 +116,42 @@ The application also supports some of the most popular databases out of the box:
1. MySQL
2. MongoDB
3. PostgresSQL
-4. SQL Server
-In order to use them, in your `/config/local.js` replace
-```
-models: {
- connection: process.env.DB_ADAPTER || 'localDiskDb',
-}
-```
-with
+In order to use them, set the appropriate env vars in your `.env` file.
+
+
+## Running Konga
+
+### Development
```
-models: {
- connection: process.env.DB_ADAPTER || 'the-name-of-adapter-you-wish-to-use', // 'mysql', 'mongo', 'sqlserver' or 'postgres'
-}
+$ npm start
```
+Konga GUI will be available at `http://localhost:1337`
-See [Sails adapters](http://sailsjs.com/documentation/concepts/extending-sails/adapters/available-adapters) for further configuration
+### Production
*****************************************************************************************
-##### Note :
-In case of `MySQL`, `PostgresSQL` or `SQL Server` adapters,
-you will need to prepare the database as explained on the next topic.
+In case of `MySQL` or `PostgresSQL` adapters, Konga will not perform db migrations when running in production mode.
-*****************************************************************************************
+You can manually perform the migrations by calling ```$ node ./bin/konga.js prepare```
+, passing the args needed for the database connectivity.
-## Running Konga
+For example:
-### Development
```
-$ npm start
+$ node ./bin/konga.js prepare --adapter postgres --uri postgresql://localhost:5432/konga
```
-Konga GUI will be available at `http://localhost:1337`
+The process will exit after all migrations are completed.
-### Production
+*****************************************************************************************
+Finally:
```
$ npm run production
```
Konga GUI will be available at `http://localhost:1337`
+
### Production Docker Image
The following instructions assume that you have a running Kong instance following the
@@ -143,6 +162,7 @@ $ docker run -p 1337:1337 \
--network {{kong-network}} \ // optional
--name konga \
-e "NODE_ENV=production" \ // or "development" | defaults to 'development'
+ -e "TOKEN_SECRET={{somerandomstring}}" \
pantsel/konga
```
@@ -151,20 +171,7 @@ $ docker run -p 1337:1337 \
1. ##### Prepare the database
> **Note**: You can skip this step if using the `mongo` adapter.
-Konga will not perform db migrations when running in production mode.
-
-You can manually perform the migrations by calling ```$ node ./bin/konga.js prepare```
-, passing the args needed for the database connectivity.
-
-The available adapters are ```'postgres', or 'mysql'```
-
-```
-$ node ./bin/konga.js prepare --adapter {adapter_name} --uri {full_connection_string}
-```
-The process will exit after all migrations are completed.
-
-If you're deploying Konga via the docker image, you can prepare the database using
-an ephemeral container running the prepare command.
+You can prepare the database using an ephemeral container that runs the prepare command.
**Args**
@@ -173,10 +180,9 @@ argument | description | default
-c | command | -
-a | adapter (can be `postgres` or `mysql`) | -
-u | full database connection url | -
--p | port | `1339`
```
-$ docker run --rm pantsel/konga:next -c prepare -a {{adapter}} -u {{connection-uri}} -p {{port}}
+$ docker run --rm pantsel/konga:next -c prepare -a {{adapter}} -u {{connection-uri}}
```
@@ -184,6 +190,7 @@ $ docker run --rm pantsel/konga:next -c prepare -a {{adapter}} -u {{connection-u
```
$ docker run -p 1337:1337
--network {{kong-network}} \ // optional
+ -e "TOKEN_SECRET={{somerandomstring}}" \
-e "DB_ADAPTER=the-name-of-the-adapter" \ // 'mongo','postgres','sqlserver' or 'mysql'
-e "DB_HOST=your-db-hostname" \
-e "DB_PORT=your-db-port" \ // Defaults to the default db port
@@ -199,6 +206,7 @@ $ docker run -p 1337:1337
// Alternatively you can use the full connection string to connect to a database
$ docker run -p 1337:1337
--network {{kong-network}} \ // optional
+ -e "TOKEN_SECRET={{somerandomstring}}" \
-e "DB_ADAPTER=the-name-of-the-adapter" \ // 'mongo','postgres','sqlserver' or 'mysql'
-e "DB_URI=full-conection-uri" \
-e "NODE_ENV=production" \ // or 'development' | defaults to 'development'
@@ -217,7 +225,10 @@ login: admin | password: adminadminadmin
*Demo user*
login: demo | password: demodemodemo
-This user data is populated to the database if there is not already any user data in it. [It is possible to alter the default user seed data.](DEFAULTUSERSEEDDATA.md)
+This user data is populated to the database if there is not already any user data in it. [It is possible to alter the default user seed data.](./docs/DEFAULTUSERSEEDDATA.md)
+
+You may also configure Konga to authenticate via [LDAP](./docs/LDAP.md).
+
## Upgrading
In some cases a newer version of Konga may introduce new db tables, collections or changes in schemas.
diff --git a/api/controllers/KongProxyController.js b/api/controllers/KongProxyController.js
index d83e0f10e..bc6e8985d 100644
--- a/api/controllers/KongProxyController.js
+++ b/api/controllers/KongProxyController.js
@@ -6,6 +6,7 @@ var unirest = require("unirest");
var KongService = require("../services/KongService");
var ProxyHooks = require("../services/KongProxyHooks");
var _ = require("lodash");
+var Utils = require('../services/Utils');
function getEntityFromRequest(req) {
@@ -44,6 +45,7 @@ var self = module.exports = {
});
}
+ sails.log("Kong admin url =>", req.connection.kong_admin_url);
var request = unirest[req.method.toLowerCase()](req.connection.kong_admin_url + req.url)
diff --git a/api/controllers/RemoteStorageController.js b/api/controllers/RemoteStorageController.js
deleted file mode 100644
index 09aa737e7..000000000
--- a/api/controllers/RemoteStorageController.js
+++ /dev/null
@@ -1,41 +0,0 @@
-'use strict';
-
-'use strict'
-
-var unirest = require('unirest');
-var RemoteStorageService = require('../services/remote/RemoteStorageService')
-var adapters = require('../services/remote/adapters')
-
-/**
- * RemoteStorageController
- */
-var RemoteStorageController = {
-
- loadAdapters : function(req,res) {
- return adapters.getSchemas(req,res)
- },
-
- testConnection : function(req,res) {
-
- //return new RemoteStorageService(req.adapter).loadConsumers(req,res)
- //var connection = mysql.createConnection({
- // host : req.query.host || '',
- // user : req.query.user || 'root',
- // password : req.query.password || '',
- // database : req.query.database || ''
- //});
- //connection.connect(function(err) {
- // if (err) return res.negotiate(err);
- // connection.query('SELECT 1 from ' + req.query.table, function(err, rows, fields) {
- // if (err) return res.negotiate(err);
- // return res.json(rows);
- // });
- //});
- },
-
- loadConsumers : function(req,res) {
- return RemoteStorageService.loadConsumers(req,res)
- },
-};
-
-module.exports = RemoteStorageController;
diff --git a/api/helpers/utils.js b/api/helpers/utils.js
index 57443eebc..f3dd2e8d2 100644
--- a/api/helpers/utils.js
+++ b/api/helpers/utils.js
@@ -72,6 +72,10 @@ module.exports = {
}
return version;
- }
+ },
+ withoutTrailingSlash(str) {
+ if(!str) return str;
+ return str.replace(/\/$/, "")
+ },
}
\ No newline at end of file
diff --git a/api/policies/authenticated.js b/api/policies/authenticated.js
index 54dc09421..2c21e0465 100644
--- a/api/policies/authenticated.js
+++ b/api/policies/authenticated.js
@@ -25,7 +25,7 @@ module.exports = function authenticated(request, response, next) {
*/
var verify = function verify(error, token) {
if (!(_.isEmpty(error) && token !== -1)) {
- return response.json(401, {message: 'Given authorization token is not valid'});
+ return response.json(401, {message: 'Given authorization token is not valid', logout: true});
} else {
// Store user id to request object
request.token = token;
@@ -42,6 +42,6 @@ module.exports = function authenticated(request, response, next) {
try {
sails.services.token.getToken(request, verify, true);
} catch (error) {
- return response.json(401, {message: error.message});
+ return response.json(401, {message: error.message, logout: true});
}
};
diff --git a/api/policies/dynamicNode.js b/api/policies/dynamicNode.js
index 3be590ad2..d35a92580 100644
--- a/api/policies/dynamicNode.js
+++ b/api/policies/dynamicNode.js
@@ -11,42 +11,46 @@ var _ = require('lodash');
*/
module.exports = function dynamicNode(request, response, next) {
- if(request.headers['connection-id'] || request.query.connection_id) {
-
- sails.log.debug("Policy:dynamicNode", "`connection-id` is defined.");
-
- sails.models.kongnode.findOne(request.headers['connection-id'] || request.query.connection_id)
- .exec(function(err,node) {
- if (err) return next(err);
- if (!node) return response.notFound({
- message: "connection not found"
- })
-
- request.connection = node;
-
- return next();
- });
-
- }else{
- // Get the default node from user
- sails.models.user.findOne({
- id:request.token
- }).populate('node').exec(function(err,user) {
- if(err) return next(err);
- if(!user) return response.notFound({
- message : "user not found"
- })
-
- if(user.node) {
- // sails.config.kong_admin_url = user.node.kong_admin_url
- request.connection = user.node;
- return next();
- }else{
- return response.badRequest({
- message : "No connection is selected. Please activate a connection in settings"
- });
- }
+ if (request.headers['connection-id'] || request.query.connection_id) {
+
+ sails.log.debug("Policy:dynamicNode", "`connection-id` is defined.");
+
+ sails.models.kongnode.findOne(request.headers['connection-id'] || request.query.connection_id)
+ .exec(function (err, node) {
+ if (err) return next(err);
+ if (!node) return response.notFound({
+ message: "connection not found"
+ })
+
+ // Remove trailing slash from kong_admin_url property
+ _.update(node, 'kong_admin_url', function(o) { return o.replace(/\/$/, ""); });
+
+ request.connection = node;
+
+ return next();
+ });
+
+ } else {
+ // Get the default node from user
+ sails.models.user.findOne({
+ id: request.token
+ }).populate('node').exec(function (err, user) {
+ if (err) return next(err);
+ if (!user) return response.notFound({
+ message: "user not found"
+ })
+
+ if (user.node) {
+ // Remove trailing slash from kong_admin_url property
+ _.update(user.node, 'kong_admin_url', function(o) { return o.replace(/\/$/, ""); });
+ request.connection = user.node;
+ return next();
+ } else {
+ return response.badRequest({
+ message: "No connection is selected. Please activate a connection in settings"
});
- }
+ }
+ });
+ }
};
diff --git a/api/services/KongService.js b/api/services/KongService.js
index e790f5924..393754def 100644
--- a/api/services/KongService.js
+++ b/api/services/KongService.js
@@ -3,6 +3,7 @@
var unirest = require("unirest")
var ApiHealthCheckService = require('../services/ApiHealthCheckService')
var JWT = require("./Token");
+var Utils = require('../helpers/utils');
var KongService = {
@@ -33,7 +34,7 @@ var KongService = {
create: function (req, res) {
- unirest.post(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.post(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.send(req.body)
.end(function (response) {
@@ -44,7 +45,7 @@ var KongService = {
createCb: function (req, res, cb) {
- unirest.post(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.post(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.send(req.body)
.end(function (response) {
@@ -55,7 +56,7 @@ var KongService = {
createFromEndpointCb: function (endpoint, data, req, cb) {
- unirest.post(req.connection.kong_admin_url + endpoint)
+ unirest.post(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + endpoint)
.headers(KongService.headers(req, true))
.send(data)
.end(function (response) {
@@ -65,8 +66,8 @@ var KongService = {
},
deleteFromEndpointCb: function (endpoint, req, cb) {
- sails.log('Deleting ' + req.connection.kong_admin_url + endpoint);
- unirest.delete(req.connection.kong_admin_url + endpoint)
+ sails.log('Deleting ' + Utils.withoutTrailingSlash(req.connection.kong_admin_url) + endpoint);
+ unirest.delete(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + endpoint)
.headers(KongService.headers(req, true))
.end(function (response) {
if (response.error) return cb(response)
@@ -76,7 +77,7 @@ var KongService = {
retrieve: function (req, res) {
- unirest.get(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.get(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.end(function (response) {
if (response.error) return res.kongError(response);
@@ -87,7 +88,7 @@ var KongService = {
fetch: (endpoint,req) => {
return new Promise((resolve, reject) => {
- unirest.get(req.connection.kong_admin_url + endpoint)
+ unirest.get(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + endpoint)
.header('Content-Type', 'application/json')
.end(function (response) {
if (response.error) return reject(response)
@@ -99,7 +100,7 @@ var KongService = {
nodeStatus: function (node, cb) {
- unirest.get(node.kong_admin_url + "/status")
+ unirest.get(Utils.withoutTrailingSlash(node.kong_admin_url) + "/status")
.headers(KongService.headers(node, true))
.end(function (response) {
if (response.error) return cb(response);
@@ -109,7 +110,7 @@ var KongService = {
nodeInfo: function (node, cb) {
- unirest.get(node.kong_admin_url)
+ unirest.get(Utils.withoutTrailingSlash(node.kong_admin_url))
.headers(KongService.headers(node, true))
.end(function (response) {
if (response.error) return cb(response);
@@ -133,7 +134,7 @@ var KongService = {
}
});
};
- getData([], (req.kong_admin_url || req.connection.kong_admin_url) + endpoint);
+ getData([], (Utils.withoutTrailingSlash(req.kong_admin_url) || Utils.withoutTrailingSlash(req.connection.kong_admin_url)) + endpoint);
},
@@ -153,11 +154,11 @@ var KongService = {
}
});
};
- getData([], (req.kong_admin_url || req.connection.kong_admin_url) + req.url.replace('/kong', ''));
+ getData([], (Utils.withoutTrailingSlash(req.kong_admin_url) || Utils.withoutTrailingSlash(req.connection.kong_admin_url)) + req.url.replace('/kong', ''));
},
update: function (req, res) {
- unirest.patch(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.patch(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.send(req.body)
.end(function (response) {
@@ -176,7 +177,7 @@ var KongService = {
},
updateCb: function (req, res, cb) {
- unirest.patch(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.patch(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.send(req.body)
.end(function (response) {
@@ -196,7 +197,7 @@ var KongService = {
},
updateOrCreate: function (req, res) {
- unirest.put(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.put(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.send(req.body)
.end(function (response) {
@@ -206,7 +207,7 @@ var KongService = {
},
delete: function (req, res) {
- unirest.delete(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.delete(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.end(function (response) {
if (response.error) return res.kongError(response);
@@ -227,7 +228,7 @@ var KongService = {
},
deleteCb: function (req, res, cb) {
- unirest.delete(req.connection.kong_admin_url + req.url.replace('/kong', ''))
+ unirest.delete(Utils.withoutTrailingSlash(req.connection.kong_admin_url) + req.url.replace('/kong', ''))
.header('Content-Type', 'application/json')
.end(function (response) {
if (response.error) return cb(response);
diff --git a/api/services/Passport.js b/api/services/Passport.js
index b0ea0a959..aed143258 100644
--- a/api/services/Passport.js
+++ b/api/services/Passport.js
@@ -25,6 +25,8 @@
*/
// Module dependencies
+var LdapStrategy = require('passport-ldapauth');
+var ldapConf = require("../../config/ldap");
var passport = require('passport');
var path = require('path');
var url = require('url');
@@ -198,10 +200,14 @@ passport.endpoint = function endpoint(request, response) {
passport.callback = function callback(request, response, next) {
sails.log.verbose(__filename + ':' + __line + ' [Service.Passport.callback() called]');
- var provider = request.param('provider', 'local');
+ var provider = request.param('provider', process.env.KONGA_AUTH_PROVIDER || 'local');
var action = request.param('action');
- if (provider === 'local' && action !== undefined) {
+ if (provider === 'ldap') {
+ passport.use(new LdapStrategy(ldapConf));
+ this.authenticate('ldapauth',
+ this.protocols.ldap.getResolver(next))(request, response, response.next);
+ } else if (provider === 'local' && action !== undefined) {
if (action === 'connect' && request.user) {
this.protocols.local.connect(request, response, next);
} else {
diff --git a/api/services/SnapshotsService.js b/api/services/SnapshotsService.js
index 117ab5e0c..8c0a697b0 100644
--- a/api/services/SnapshotsService.js
+++ b/api/services/SnapshotsService.js
@@ -215,7 +215,7 @@ module.exports = {
sails.models.snapshot.create({
name: name || "snap@" + Date.now(),
kong_node_name: node.name,
- kong_node_url: node.kong_admin_url,
+ kong_node_url: Utils.withoutTrailingSlash(node.kong_admin_url),
kong_version: status.version,
data: result
}).exec(function (err, created) {
diff --git a/api/services/Utils.js b/api/services/Utils.js
index b566f127a..ad9dfc3b3 100644
--- a/api/services/Utils.js
+++ b/api/services/Utils.js
@@ -1,14 +1,20 @@
module.exports = {
- Object : {
- clean : function clean(obj) {
- for(var key in obj) {
- if(JSON.stringify(obj[key])=="{}" || !obj[key]) {
- delete obj[key];
- } else if (typeof obj[key] == "object") {
- obj[key] = this.clean(obj[key]);
- }
- }
- return obj;
+ Object: {
+ clean: function clean(obj) {
+ for (var key in obj) {
+ if (JSON.stringify(obj[key]) == "{}" || !obj[key]) {
+ delete obj[key];
+ } else if (typeof obj[key] == "object") {
+ obj[key] = this.clean(obj[key]);
}
+ }
+ return obj;
}
+ },
+
+ isRuntimeVersionSupported() {
+ const minRequiredNodeVersion = '8.0.0';
+ const semver = require('semver');
+ return semver.gte(process.versions.node, minRequiredNodeVersion);
+ }
}
diff --git a/api/services/protocols/index.js b/api/services/protocols/index.js
index 4f807f05f..e2f2c05c4 100644
--- a/api/services/protocols/index.js
+++ b/api/services/protocols/index.js
@@ -16,6 +16,7 @@
module.exports = {
local: require('./local'),
oauth: require('./oauth'),
+ ldap: require('./ldap'),
oauth2: require('./oauth2'),
openid: require('./openid')
};
diff --git a/api/services/protocols/ldap.js b/api/services/protocols/ldap.js
new file mode 100644
index 000000000..2bf114da5
--- /dev/null
+++ b/api/services/protocols/ldap.js
@@ -0,0 +1,93 @@
+var _ = require('lodash');
+var adminGroup = new RegExp(process.env.KONGA_ADMIN_GROUP_REG || "^(admin|konga)$");
+var ldapAttrMap = {
+ username: process.env.KONGA_LDAP_ATTR_USERNAME || 'uid',
+ firstName: process.env.KONGA_LDAP_ATTR_FIRSTNAME || 'givenName',
+ lastName: process.env.KONGA_LDAP_ATTR_LASTNAME || 'sn',
+ email: process.env.KONGA_LDAP_ATTR_EMAIL || 'mail'
+};
+var commonName = /^cn=([^,]+),.*/;
+
+var ldapToUser = function (ldapUser, user, next) {
+ var data = _.clone(user || {});
+ data.active = true;
+
+ // copy attributes from the ldap user to the konga user using the ldapAttrMap
+ for (var userAttr in ldapAttrMap) {
+ if (ldapAttrMap.hasOwnProperty(userAttr)) {
+ data[userAttr] = ldapUser[ldapAttrMap[userAttr]];
+ }
+ }
+
+ if (data && data.id) {
+ sails.models.user.update({id: data.id}, data).exec(function(err) {
+ if (err) {
+ console.error("Failed to update user from ldap", err);
+ next(err);
+ } else {
+ setAdminStatus(ldapUser, data, next);
+ }
+ });
+ } else {
+ sails.models.user.create(data).exec(function (err, user) {
+ if (err) {
+ console.error("Failed to create user from ldap", err);
+ next(err);
+ } else {
+ setAdminStatus(ldapUser, user, next);
+ }
+ });
+ }
+}
+
+var group_test = function (group) {
+ return adminGroup.test(group.cn);
+}
+
+var member_test = function (group) {
+ return adminGroup.test(commonName.replace(group, "$1"));
+}
+
+var setAdminStatus = function (ldapUser, user, next) {
+ user.admin =
+ _.findIndex(ldapUser._groups, group_test) > -1 ||
+ _.findIndex(ldapUser.memberOf, member_test) > -1;
+ next(null, user);
+}
+
+/**
+ * Resolve LDAP user
+ *
+ * This function can be used to create a user in the local db to store
+ * users' roles locally
+ *
+ * @param {Request} request
+ * @param {Response} response
+ * @param {Function} next
+ */
+exports.getResolver = function getResolver(next) {
+ return function resolveUser(err, result, message) {
+ if (result === false || typeof result === 'undefined') {
+ console.error('failed to resolve user', err, message);
+ var error = message;
+ next(error);
+ } else {
+ var ldapUser = result;
+ sails.models.user
+ .findOne({ // UID is the default, but the LDAP provider could be ActiveDirectory
+ username: (ldapUser.uid || ldapUser.sAMAccountName)
+ })
+ .populate('node')
+ .exec(function onExec(error, user) {
+ if (error) {
+ // Dunno, something bad happened
+ console.error('failed to look up existing user', error);
+ next(error);
+ } else {
+ // sync the ldap user to konga user
+ ldapToUser(ldapUser, user, next);
+ }
+ })
+ }
+ };
+}
diff --git a/api/services/remote/RemoteStorageService.js b/api/services/remote/RemoteStorageService.js
deleted file mode 100644
index 4d87bf07f..000000000
--- a/api/services/remote/RemoteStorageService.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var RemoteStorageService = {
- loadConsumers : function(req,res) {
- return require('./adapters')[req.body.adapter].methods.loadConsumers(req,res);
- }
-}
-module.exports = RemoteStorageService
diff --git a/api/services/remote/adapters/api.js b/api/services/remote/adapters/api.js
deleted file mode 100644
index 9c64f82bf..000000000
--- a/api/services/remote/adapters/api.js
+++ /dev/null
@@ -1,73 +0,0 @@
-var unirest = require('unirest')
-
-module.exports = {
- enabled : true,
- schema : {
- "name": "API",
- "value": "api",
- "description": "Import Consumers by issuing a GET request to an API endpoint",
- "form_fields": {
- "connection": {
- "endpoint": {
- "name": "endpoint",
- "required" : true,
- "type": "text",
- "description": "The API endpoint. ex: http://myapi.io/users?limit=3000
"
- },
- "headers": {
- "name": "headers",
- "type": "text",
- "description": "A comma separated list of headers to send with the request. ex: Authorization:Bearer some-token-key,Content-type:application/json
"
- }
- },
- "consumer": {
- "data_property" : {
- "name": "Data property",
- "type": "text",
- "description": "If specified, the consumers will be extracted from this resulting object's property."
- },
- "username": {
- "name": "username field",
- "type": "text",
- "required": true,
- "description": "The property that will be used as the consumer username
."
- },
- "custom_id": {
- "name": "custom_id field",
- "type": "text",
- "required": true,
- "description": "The property will be used as the consumer custom_id
."
- }
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
- var headers = {}
- if(req.param('headers')) {
- var string = req.param('headers')
- var split = string.split(",")
- split.forEach(function(keyVal){
- var array = keyVal.split(":")
- headers[array[0]] = array[1]
- })
-
- sails.log("Headers =>",headers)
- }
-
- var request = unirest.get(req.param('endpoint'))
- request.headers(headers)
- request.end(function (response) {
- if (response.error) return res.negotiate(response)
-
- sails.log("response.body",response.body)
-
- var jsonRes = response.body
-
- return res.json(req.param('data_property') ? jsonRes[req.param('data_property')] : jsonRes)
- })
- }
- }
-
-}
diff --git a/api/services/remote/adapters/csv.js b/api/services/remote/adapters/csv.js
deleted file mode 100644
index 78a132150..000000000
--- a/api/services/remote/adapters/csv.js
+++ /dev/null
@@ -1,125 +0,0 @@
-var csv = require('csv-parser')
-var fs = require('fs')
-
-module.exports = {
- enabled : true,
- schema : {
- "name": "CSV",
- "value": "csv",
- "hasFiles" : true,
- "description": "Import Consumers from a .csv document",
- "form_fields": {
- "connection": {
- "file" : {
- "name" : "File",
- "type" : "file",
- "required": true,
- "description" : "Select the .csv document containing the consumers"
- },
- "raw" : {
- name : "raw",
- "type" : "boolean",
- "default" : false,
- description : "Whether or not to decode to utf-8 strings (optional)."
- },
- "separator" : {
- name : "separator",
- "type" : "text",
- description : "Specify optional cell separator. Defaults to ','
"
- },
- "quote" : {
- name : "quote",
- "type" : "text",
- description : "Specify optional quote character. Defaults to '\"'
"
- },
- "escape" : {
- name : "escape",
- "type" : "text",
- description : "Specify optional escape character. Defaults to quote value"
- },
- "newline" : {
- name : "newline",
- "type" : "text",
- description : "Specify a newline character. Defaults to '\\n'
"
- },
- "strict" : {
- name : "strict",
- "type" : "boolean",
- "default" : true,
- description : "Require column length match headers length (optional)."
- },
-
- "headers" : {
- name : "headers",
- "type" : "text",
- "required": true,
- "description" : "Specify the headers of each .csv row as a comma separated string." +
- " ex: 'id,name,email,updated_at,created_at...'
"
- }
- },
- "consumer": {
- "username": {
- "name": "username column",
- "type": "text",
- "required": true,
- "description": "The header of the cell that will be used as the consumer username
."
- },
- "custom_id": {
- "name": "custom_id field",
- "type": "text",
- "required": true,
- "description": "The header of the cell that will be used as the consumer custom_id
."
- }
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
- req.file('file').upload(function (err, uploadFiles) {
-
- if(err) return res.negotiate(err);
-
- if(!uploadFiles.length) return res.badRequest("No files uploaded")
-
- var result = [];
-
- fs.createReadStream(uploadFiles[0].fd)
- .pipe(csv({
- raw: req.body.raw || false, // do not decode to utf-8 strings
- separator: req.body.separator || ',', // specify optional cell separator
- quote: req.body.quote || '"', // specify optional quote character
- escape: req.body.escape || '"', // specify optional escape character (defaults to quote value)
- newline: req.body.newline || '\n', // specify a newline character
- strict: req.body.strict || true, // require column length match headers length
- headers: req.body.headers.split(",") // Specifing the headers
- }))
- .on('data', function (data) {
- var consumer = {}
- Object.keys(data).forEach(function(key){
-
- if(key === req.body.username) {
- consumer.username = data[key]
- }
-
- if(key === req.body.custom_id) {
- consumer.custom_id = data[key]
- }
- })
-
- if(Object.keys(data).length === 2)
- result.push(consumer);
- })
- .on('end', function () {
- if(!result.length)
- return res.notFound("No consumers found");
- return res.json(result)
- })
-
- });
-
-
- }
- }
-
-}
diff --git a/api/services/remote/adapters/google-spreadsheet.js b/api/services/remote/adapters/google-spreadsheet.js
deleted file mode 100644
index af8b72a1c..000000000
--- a/api/services/remote/adapters/google-spreadsheet.js
+++ /dev/null
@@ -1,143 +0,0 @@
-var fs = require('fs');
-var google = require('googleapis');
-var path = require('path')
-var mkdirp = require('mkdirp');
-
-module.exports = {
- enabled : true,
- schema : {
- "name": "Google Spreadsheet",
- "value": "google-spreadsheet",
- "hasFiles" : true,
- "description": "Import Consumers from Google spreadsheets. ",
- "info" : "Konga uses the JWT method to authenticate with Google spreadsheets API." +
- "Make sure you have created Service account JSON credentials for Konga " +
- "and that your spreadsheet is shared with that service account's email.",
- "form_fields": {
- "connection": {
- "file" : {
- "name" : "JSON credentials file",
- "type" : "file",
- "description" : "The Service account JSON credentials file." +
- "This file only needs to be uploaded once as long as the service account remains as is."
- },
- "spreadsheetId": {
- "name": "spreadsheetId",
- "type": "text",
- "required" : true,
- "description": "The long id in the sheets URL"
- },
- "range": {
- "name": "range",
- "type": "text",
- "required" : true,
- "description": "The range to read, in A1 notation. ex. A1:B15"
- },
- "ignore_first_row": {
- "name": "Ignore first row",
- "type": "boolean",
- "description": "Whether or not to ignore the first row of the spreadsheet." +
- " This can be useful in case it contains the column titles."
- },
- },
- "consumer": {
- "username": {
- "name": "username column",
- "type": "number",
- "required": true,
- "description": "The index of the spreadsheet column that will be used as the consumer username
. The first column is 0."
- },
- "custom_id": {
- "name": "custom_id column",
- "type": "number",
- "required": true,
- "description": "The index of the spreadsheet column that will be used as the consumer custom_id
. The first column is 0."
- }
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
- req.file('file').upload(function (err, uploadFiles) {
- if (err) return res.negotiate(err);
-
- var CREDENTIALS_PATH = path.join(__dirname,"..","credentials/google-spreadsheets");
-
- mkdirp(CREDENTIALS_PATH, function (err) {
- if (err) return res.negotiate(err)
-
- if(!uploadFiles.length && !fs.existsSync(CREDENTIALS_PATH + "/credentials.json")) {
- if (err) return res.notFound("Credentials JSON file not found. You will need to upload one.");
- }
-
- if(uploadFiles[0]) {
- fs.renameSync(uploadFiles[0].fd, CREDENTIALS_PATH + "/credentials.json");
- }
-
- var SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
-
- fs.readFile(CREDENTIALS_PATH + "/credentials.json", function(err, content) {
- if (err) if (err) return res.negotiate(err);
-
- var key = JSON.parse(content);
-
-
- var jwtClient = new google.auth.JWT(
- key.client_email,
- null,
- key.private_key,
- SCOPES,
- null
- );
-
- jwtClient.authorize(function (err, tokens) {
- if (err) return res.negotiate(err)
- listConsumers(jwtClient)
- });
-
- function listConsumers(auth) {
- var sheets = google.sheets('v4');
- sheets.spreadsheets.values.get({
- auth: auth,
- spreadsheetId: req.body.spreadsheetId,
- range : req.body.range,
- }, function(err, response) {
- if (err) return res.negotiate(err)
-
- //var rows = response.values;
- if (response.length == 0 || response.values.length == 0) {
- return res.notFound('No data found.')
- } else {
- //if(response.values.length > 200) return res.badRequest("Too many results! Maximum number of allowed results is 200")
- var consumers = []
- response.values.forEach(function(value,index){
- consumers.push({
- username : value[req.body.username],
- custom_id : value[req.body.custom_id],
- })
- })
-
- // Remove first item if specified
- if(req.body.ignore_first_row === 'true') {
- consumers.shift()
- }
-
- return res.json(consumers)
- }
- });
- }
- })
- });
-
- });
-
-
-
-
-
-
- }
- }
-
-}
diff --git a/api/services/remote/adapters/index.js b/api/services/remote/adapters/index.js
deleted file mode 100644
index 7acb34067..000000000
--- a/api/services/remote/adapters/index.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var fs = require('fs');
-
-// Methods
-module.exports = {
- getSchemas : function(req,res) {
- fs.readdir(__dirname, function(err, files) {
- if(err) return res.negotiate(err)
- var schemas = {}
- files.forEach(function(file) {
- if(file.indexOf('index') < 0) {
- var fileData = require('./' + file)
- if(fileData.enabled)
- schemas[file.replace(".js","")] = fileData.schema
- }
- });
- res.json(schemas)
- })
- }
-}
-
-// Export the actual adapters
-var files = fs.readdirSync(__dirname)
-files.forEach(function(file) {
- if(file.indexOf('index') < 0 && file.indexOf('credentials') < 0) {
- module.exports[file.replace(".js","")] = require('./' + file)
- }
-})
diff --git a/api/services/remote/adapters/kongajson.js b/api/services/remote/adapters/kongajson.js
deleted file mode 100644
index 0793bf05e..000000000
--- a/api/services/remote/adapters/kongajson.js
+++ /dev/null
@@ -1,56 +0,0 @@
-var unirest = require('unirest')
-
-module.exports = {
- enabled : false,
- schema : {
- "name": "Konga JSON export",
- "value": "kongajson",
- "description": "Import previously exported Consumers",
- "hasFiles" : true,
- "form_fields": {
- "connection": {
- "file": {
- "name": "File",
- "required" : true,
- "type": "file",
- "description": "The JSON file containing the exported consumers"
- },
- "keepids" : {
- name : "Keep Ids",
- "type" : "boolean",
- "default" : false,
- description : "Keep exported consumers Ids"
- },
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
-
- req.file('file').upload(function (err, uploadFiles) {
-
- if(err) return res.negotiate(err);
-
-
- if(!uploadFiles.length) return res.badRequest("No files uploaded")
-
-
- var jsonData = require(uploadFiles[0].fd)
-
- var keepIds = req.body.keepids || false
-
- if(!keepIds) {
- jsonData.forEach(function(item){
- delete item.id
- })
- }
-
-
- return res.json(jsonData.data)
-
- });
- }
- }
-
-}
diff --git a/api/services/remote/adapters/mongodb.js b/api/services/remote/adapters/mongodb.js
deleted file mode 100644
index ca717e549..000000000
--- a/api/services/remote/adapters/mongodb.js
+++ /dev/null
@@ -1,96 +0,0 @@
-var MongoClient = require('mongodb').MongoClient;
-
-module.exports = {
- enabled : true,
- schema : {
- "name": "MongoDB",
- "value": "mongodb",
- "description": "Import Consumers from a MongoDB collection",
- "form_fields": {
- "connection": {
- "host": {
- "name": "host",
- "type": "text",
- "description": "The database host. Defaults to localhost"
- },
- "port": {
- "name": "port",
- "type": "number",
- "description": "The database port. Defaults to 27017"
- },
- "user": {
- "name": "user",
- "type": "text",
- "description": "The database user."
- },
- "password": {
- "name": "password",
- "type": "text",
- "description": "The database user\"s password."
- },
- "database": {
- "name": "database",
- "type": "text",
- "required": true,
- "description": "The database name."
- }
- },
- "consumer": {
- "collection": {
- "name": "collection",
- "type": "text",
- "required": true,
- "description": "The mongodb collection containing the consumers that will be imported to Kong."
- },
- "username": {
- "name": "username field",
- "type": "text",
- "required": true,
- "description": "The collection property that will be used as the consumer username
."
- },
- "custom_id": {
- "name": "custom_id field",
- "type": "text",
- "required": true,
- "description": "The collection property that will be used as the consumer custom_id
."
- }
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
- var host = req.body.host || 'localhost'
- var port = req.body.port || 27017
- var user = req.body.user
- var password = req.body.password
- var database = req.body.database || 'sails'
-
- // Build connection string
- var m = 'mongodb://'
- var up = user ? user + ':' + password + '@' : ''
- var c = host + ':' + port + '/' + database;
- var url = m + up + c
-
- MongoClient.connect(url, function(err, db) {
- if (err) return res.negotiate(err);
- var collection = db.collection(req.body.collection || '');
- collection
- .aggregate([
- { "$group": {
- "_id": "$_id",
- "custom_id": { "$first": "$" + req.body.custom_id },
- "username": { "$first": "$" + req.body.username },
- }}
-
- ]).toArray(function(err, docs) {
- db.close(); // close the connection
- if (err) return res.negotiate(err);
- if(!docs.length) return res.notFound()
- return res.json(docs)
- });
- });
- }
- }
-
-}
diff --git a/api/services/remote/adapters/mysql.js b/api/services/remote/adapters/mysql.js
deleted file mode 100644
index 4bd8513b2..000000000
--- a/api/services/remote/adapters/mysql.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var mysql = require('mysql')
-
-module.exports = {
- enabled : true,
- schema : {
- "name": "MySQL",
- "value": "mysql",
- "description": "Import Consumers from a MySQL database table",
- "form_fields": {
- "connection": {
- "host": {
- "name": "host",
- "type": "text",
- "description": "The database host. Defaults to localhost"
- },
- "user": {
- "name": "user",
- "type": "text",
- "description": "The database user. Defaults to root"
- },
- "password": {
- "name": "password",
- "type": "text",
- "description": "The database user\"s password."
- },
- "database": {
- "name": "database",
- "type": "text",
- "required": true,
- "description": "The database to connect to."
- }
- },
- "consumer": {
- "table": {
- "name": "table",
- "type": "text",
- "required": true,
- "description": "The table containing the consumers that will be imported to Kong."
- },
- "username": {
- "name": "username field",
- "type": "text",
- "required": true,
- "description": "The table field that will be used as the consumers username
."
- },
- "custom_id": {
- "name": "custom_id field",
- "type": "text",
- "required": true,
- "description": "The table field that will be used as the consumers custom_id
."
- }
- }
- }
- },
- methods : {
- loadConsumers : function(req,res) {
-
- var connection = mysql.createConnection({
- host : req.body.host || 'localhost',
- user : req.body.user || 'root',
- password : req.body.password || '',
- database : req.body.database || ''
- });
-
- connection.connect(function(err) {
- if (err) return res.negotiate(err);
- connection.query('SELECT ' + req.body.username + ' as username,' + req.body.custom_id + ' as custom_id FROM ' + req.body.table, function(err, rows, fields) {
- if (err) return res.negotiate(err);
- return res.json(rows);
- });
- });
- }
- }
-
-}
diff --git a/api/services/remote/credentials/google-spreadsheets/credentials.json b/api/services/remote/credentials/google-spreadsheets/credentials.json
deleted file mode 100644
index 2af097dbf..000000000
--- a/api/services/remote/credentials/google-spreadsheets/credentials.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "type": "service_account",
- "project_id": "konga-148120",
- "private_key_id": "84413cd1c5f890600ed6d0c77beb0040f2880471",
- "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDB6r16wV0AGbG3\nHVY3hXc/g6r01UrYHQScNTAAA021zMKYJ8TLe3dJXm978avDcKkFeYL58dWVmTT9\nlICm8JsXGrLlx55K2MHs8NjrtEzI3c4xK2Y4pvFqDA+kKqQYgdxGlaaEyX8p0me3\nx2Zh8SgXRD+zUHTjZtL/oawU6FJkXY7D2ewmEhsYhvja+lqSv3GCOofhkfH/EtIX\nsdO9Jkg/UHHKjUW3UbS5U2HesiTGVtDMuP10TawT4d2CCGPdjRTUriTZU8jR1LcW\naHBA17QPMdWa9ObP3unUJebKv2p3shmNNfj46R6UcJSEKxhrviun3lo0aQYQLeAl\nVz4LTQIRAgMBAAECggEAHphOtON1LOLg6ycxxyjDm73GZ0KPwHEznQG3RQlAZYKc\n4SzFG+Wq+GRx6nhCxV8tC7QUOiMxs5ysg6W+dphXn9mSiDZqfxyb3CpTzzxvMPHt\n6kwSoLWWOUkV3qzrnwI+ItTRpPm1mn+b5Z8MRD+sN5+I/V2gU5CRkcuMPvA4r6Tc\nvLSv7x2Ilq8jWNiTNZS/ec39OcFObnL+gesgtw+PHlh+bITaXJKPqvEYM74TM5mh\nGth1EOm6YkOcoTvUSBYzmD1S+mrX5EcDiSxEL9je83banqbd631oAIjP8XmictjR\nYCznFvl7z9S4Bx0OvmvZeAR3eRZaoJgvbvHJ0THRAQKBgQDumQXw+WWWK8yEryCK\nfC1CxwKeKCig9VsrWeHD5llJK5kngEZbulJ7T/45MogMtfqbZ7DGzFVju5kSzs0v\n7NBRkJllgw0O2xh4hVjT+J2+QzA73IGDP1R3LnBfa5tUnzy8bNuKLh29qBffaTmx\nBOqFxDcb7akm9nfPC4rLz5XLCQKBgQDQD3Wvnge8YLPeCa+49crNnaubfp9I7F5M\nzAhyBocfz0s57LV2YbDIxhuK/c9n8STuRvyHoZAEtKHy4vQopxgtkRsa2Yx8yIOa\nKt0rSGX14zv6djgN18Mbc0f8kefgDwsx7GdTQ5mxs5ZojpYmDYiuzjbagwPqBSRK\nKk6Oe5nYyQKBgQC6d5jvFNnRnPU/FOanlBiDQajIFbZ65IWVwa7xPMq2pn4RIuzZ\nryna+U9DQDyXQnlSjpzXIMXzJZ+h1UECnV7I/+sCLIM+AviC6CPdtUUCdtvxTIlj\nG1FVu1NTH3PLlI8Q6zpAKX5QxFez4DaYI7FtTUWMbBZwPtqvUuPsGJWGkQKBgC7N\n+4SJQWZAPtZJSY2LRZchzBQL1RtbiQ8vqwqzqzwdrueV93BtI47W+iU3WawhVFSC\nBZchYjucwv3XzmNCy66cgQN2QyNjHC/XSq/M9prtBnYemBeNHSgUs/H+hLIj0Dnn\n24qgn3eZVdGpmd9vlfr1CuP3Ky8+/t3sTIXDCmX5AoGBAOirb1knm3jT7cbbtNfO\n5gEKw7GC9MPo/Sh9RDAhIEIhYkACgGSiWcQmOv17HJW55BCHlDVw4PDqv70XfnVT\nChbx7Rdcwy0Co+WK9wAmt8mQRM7njh4+wxVQWdDnfotAxrjFJtO7RPjpZ29oyvsK\n/iD5HA2q4XORpNgJ1nNZ/jGC\n-----END PRIVATE KEY-----\n",
- "client_email": "pantsel@konga-148120.iam.gserviceaccount.com",
- "client_id": "113335730171302572539",
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
- "token_uri": "https://accounts.google.com/o/oauth2/token",
- "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
- "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/pantsel%40konga-148120.iam.gserviceaccount.com"
-}
diff --git a/app.js b/app.js
index fc7da5b17..281e483bd 100644
--- a/app.js
+++ b/app.js
@@ -19,48 +19,57 @@
* The same command-line arguments are supported, e.g.:
* `node app.js --silent --port=80 --prod`
*/
+require('dotenv').config()
// Ensure a "sails" can be located:
-var sails;
+let sails;
try {
- sails = require('sails');
+ sails = require('sails');
} catch (e) {
- console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.');
- console.error('To do that, run `npm install sails`');
- console.error('');
- console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.');
- console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,');
- console.error('but if it doesn\'t, the app will run with the global sails instead!');
- return;
+ console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.');
+ console.error('To do that, run `npm install sails`');
+ console.error('');
+ console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.');
+ console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,');
+ console.error('but if it doesn\'t, the app will run with the global sails instead!');
+ return;
}
+// Validate node version
+const Utils = require('./api/services/Utils');
+if(!Utils.isRuntimeVersionSupported()) {
+ sails.log.error("Incompatible Node.js version. Please make sure that you have Node.js >= 8 installed.")
+ process.exit(1);
+}
+
+
// Try to get `rc` dependency
-var rc;
+let rc;
try {
- rc = require('rc');
+ rc = require('rc');
} catch (e0) {
- try {
- rc = require('sails/node_modules/rc');
- } catch (e1) {
- console.error('Could not find dependency: `rc`.');
- console.error('Your `.sailsrc` file(s) will be ignored.');
- console.error('To resolve this, run:');
- console.error('npm install rc --save');
- rc = function () {
- return {};
- };
- }
+ try {
+ rc = require('sails/node_modules/rc');
+ } catch (e1) {
+ console.error('Could not find dependency: `rc`.');
+ console.error('Your `.sailsrc` file(s) will be ignored.');
+ console.error('To resolve this, run:');
+ console.error('npm install rc --save');
+ rc = function () {
+ return {};
+ };
+ }
}
-require("./makedb")(function(err) {
- if(err) {
- process.exit();
- }
+require("./makedb")(function (err) {
+ if (err) {
+ process.exit();
+ }
- // Start server
- sails.lift(rc('sails'));
+ // Start server
+ sails.lift(rc('sails'));
});
diff --git a/assets/js/app/certificates/add-certificates-modal.html b/assets/js/app/certificates/add-certificates-modal.html
index 53d7ce879..8ee3a69b4 100644
--- a/assets/js/app/certificates/add-certificates-modal.html
+++ b/assets/js/app/certificates/add-certificates-modal.html
@@ -62,7 +62,7 @@
Define options to the consumer storage.
- -
- Loaded {{consumers.length}} unique consumers form the remote storage.
- Select the ones you want to import.
-
- | username | -custom_id | -
---|---|---|
- - | -{{item.username}} | -{{item.custom_id}} | -
- Loaded {{consumers.length}} consumers form the remote storage. -
-- {{result.imported.count}} consumers imported successfully -
-- {{result.failed.count}} imports failed -
-Select the source from which to import consumers
-