signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
184 lines (183 loc) • 7.19 kB
JavaScript
;
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
* Copyright 2017 Teppo Kurki <teppo.kurki@iki.fi>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidTokenError = void 0;
exports.startSecurity = startSecurity;
exports.getSecurityConfig = getSecurityConfig;
exports.pathForSecurityConfig = pathForSecurityConfig;
exports.saveSecurityConfig = saveSecurityConfig;
exports.getCertificateOptions = getCertificateOptions;
exports.getCAChainArray = getCAChainArray;
exports.createCertificateOptions = createCertificateOptions;
exports.requestAccess = requestAccess;
const fs_1 = require("fs");
const lodash_1 = __importDefault(require("lodash"));
const path_1 = __importDefault(require("path"));
const selfsigned_1 = require("selfsigned");
const stat_mode_1 = require("stat-mode");
const debug_1 = require("./debug");
const dummysecurity_1 = __importDefault(require("./dummysecurity"));
const debug = (0, debug_1.createDebug)('signalk-server:security');
class InvalidTokenError extends Error {
constructor(...args) {
super(...args);
Error.captureStackTrace(this, InvalidTokenError);
}
}
exports.InvalidTokenError = InvalidTokenError;
function startSecurity(app, securityConfig) {
let securityStrategyModuleName = process.env.SECURITYSTRATEGY ||
lodash_1.default.get(app, 'config.settings.security.strategy');
if (securityStrategyModuleName) {
if (securityStrategyModuleName === 'sk-simple-token-security') {
console.log('The sk-simple-token-security security strategy is depricated, please update to @signalk/sk-simple-token-security');
process.exit(1);
}
else if (securityStrategyModuleName === '@signalk/sk-simple-token-security') {
securityStrategyModuleName = './tokensecurity';
}
const config = securityConfig || getSecurityConfig(app, true);
// eslint-disable-next-line @typescript-eslint/no-require-imports
app.securityStrategy = require(securityStrategyModuleName)(app, config);
if (securityConfig) {
app.securityStrategy.configFromArguments = true;
app.securityStrategy.securityConfig = securityConfig;
}
}
else {
app.securityStrategy = (0, dummysecurity_1.default)();
}
}
function getSecurityConfig(app, forceRead = false) {
if (!forceRead && app.securityStrategy?.configFromArguments) {
return app.securityStrategy.securityConfig;
}
else {
try {
const optionsAsString = (0, fs_1.readFileSync)(pathForSecurityConfig(app), 'utf8');
return JSON.parse(optionsAsString);
}
catch (e) {
console.error('Could not parse security config');
console.error(e.message);
return {};
}
}
}
function pathForSecurityConfig(app) {
return path_1.default.join(app.config.configPath, 'security.json');
}
function saveSecurityConfig(app, data, callback) {
if (app.securityStrategy.configFromArguments) {
app.securityStrategy.securityConfig = data;
if (callback) {
callback(null);
}
}
else {
//const config = JSON.parse(JSON.stringify(data))
const configPath = pathForSecurityConfig(app);
(0, fs_1.writeFile)(configPath, JSON.stringify(data, null, 2), (err) => {
if (!err) {
(0, fs_1.chmodSync)(configPath, '600');
}
if (callback) {
callback(err);
}
});
}
}
function getCertificateOptions(app, cb) {
let certLocation;
if (!app.config.configPath || (0, fs_1.existsSync)('./settings/ssl-cert.pem')) {
certLocation = './settings';
}
else {
certLocation = app.config.configPath;
}
const certFile = path_1.default.join(certLocation, 'ssl-cert.pem');
const keyFile = path_1.default.join(certLocation, 'ssl-key.pem');
const chainFile = path_1.default.join(certLocation, 'ssl-chain.pem');
if ((0, fs_1.existsSync)(certFile) && (0, fs_1.existsSync)(keyFile)) {
if (!hasStrictPermissions((0, fs_1.statSync)(keyFile))) {
cb(new Error(`${keyFile} must be accessible only by the user that is running the server, refusing to start`));
return;
}
if (!hasStrictPermissions((0, fs_1.statSync)(certFile))) {
cb(new Error(`${certFile} must be accessible only by the user that is running the server, refusing to start`));
return;
}
let ca;
if ((0, fs_1.existsSync)(chainFile)) {
debug('Found ssl-chain.pem');
ca = getCAChainArray(chainFile);
debug(JSON.stringify(ca, null, 2));
}
debug(`Using certificate ssl-key.pem and ssl-cert.pem in ${certLocation}`);
cb(null, {
key: (0, fs_1.readFileSync)(keyFile),
cert: (0, fs_1.readFileSync)(certFile),
ca
});
}
else {
createCertificateOptions(app, certFile, keyFile, cb);
}
}
function hasStrictPermissions(stat) {
if (process.platform === 'win32') {
return true;
}
else {
return /^-r[-w][-x]------$/.test(new stat_mode_1.Mode(stat).toString());
}
}
function getCAChainArray(filename) {
let chainCert = new Array();
return (0, fs_1.readFileSync)(filename, 'utf8')
.split('\n')
.reduce((ca, line) => {
chainCert.push(line);
if (line.match(/-END CERTIFICATE-/)) {
ca.push(chainCert.join('\n'));
chainCert = [];
}
return ca;
}, new Array());
}
function createCertificateOptions(app, certFile, keyFile, cb) {
const location = app.config.configPath ? app.config.configPath : './settings';
debug(`Creating certificate files in ${location}`);
(0, selfsigned_1.generate)([{ name: 'commonName', value: 'localhost' }], { days: 3650 }, function (err, pems) {
(0, fs_1.writeFileSync)(keyFile, pems.private);
(0, fs_1.chmodSync)(keyFile, '600');
(0, fs_1.writeFileSync)(certFile, pems.cert);
(0, fs_1.chmodSync)(certFile, '600');
cb(null, {
key: pems.private,
cert: pems.cert
});
});
}
function requestAccess(app, request, ip, updateCb) {
const config = getSecurityConfig(app);
return app.securityStrategy.requestAccess(config, request, ip, updateCb);
}
//# sourceMappingURL=security.js.map