Commit 0cd54219 authored by Girish Ramakrishnan's avatar Girish Ramakrishnan

sync coding style

parent 23785041
......@@ -102,7 +102,7 @@
<a :href="'/api/key/' + scope.row.name + '?format=ovpn&zip=false'">.ovpn - embedded certs</a>
</el-dropdown-item>
<el-dropdown-item>
<a :href="'/api/key/' + scope.row.name + '?format=tblk'">.tblk</a>
<a :href="'/api/key/' + scope.row.name + '?format=tblk'">.tblk (For Tunnelblick)</a>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
......
#!/usr/bin/env node
'use strict'
const express = require('express')
const morgan = require('morgan')
const path = require('path')
const compression = require('compression')
const ini = require('ini')
const session = require('express-session')
const lastMile = require('connect-lastmile')
const {HttpSuccess, HttpError} = require('connect-lastmile')
const ldap = require('./src/ldap')
const bodyParser = require('body-parser')
const fs = require('fs')
const openvpn = require('./src/openvpn')
const LokiStore = require('connect-loki')(session)
'use strict';
const express = require('express');
const morgan = require('morgan');
const path = require('path');
const compression = require('compression');
const ini = require('ini');
const session = require('express-session');
const lastMile = require('connect-lastmile');
const {HttpSuccess, HttpError} = require('connect-lastmile');
const ldap = require('./src/ldap');
const bodyParser = require('body-parser');
const fs = require('fs');
const openvpn = require('./src/openvpn');
const LokiStore = require('connect-loki')(session);
const execSync = require('child_process').execSync;
const app = express()
const router = new express.Router()
const app = express();
const router = new express.Router();
const urlEncodedParser = bodyParser.urlencoded({extended: true})
const jsonParser = bodyParser.json({strict: true})
const urlEncodedParser = bodyParser.urlencoded({extended: true});
const jsonParser = bodyParser.json({strict: true});
const baseDir = process.env.CLOUDRON ? '/app/data' : path.join(__dirname, '.dev/data')
const baseDir = process.env.CLOUDRON ? '/app/data' : path.join(__dirname, '.dev/data');
// config is for app configs, settings is for openvpn settings
const CONFIG_FILE_PATH = path.join(baseDir, 'config.ini')
const OPENVPN_SETTINGS_FILE_PATH = path.join(baseDir, 'openvpn.conf')
const CONFIG_FILE_PATH = path.join(baseDir, 'config.ini');
const OPENVPN_SETTINGS_FILE_PATH = path.join(baseDir, 'openvpn.conf');
console.log(`Using app config file at ${CONFIG_FILE_PATH}`)
console.log(`Using OpenVPN settings file at ${OPENVPN_SETTINGS_FILE_PATH}`)
console.log(`Using app config file at ${CONFIG_FILE_PATH}`);
console.log(`Using OpenVPN settings file at ${OPENVPN_SETTINGS_FILE_PATH}`);
function reloadConfig() {
let config = {}
try {
config = ini.parse(fs.readFileSync(CONFIG_FILE_PATH, 'utf-8'))
} catch (e) {
console.log('No config file found, creating empty one')
fs.writeFileSync(CONFIG_FILE_PATH, '[roles]\n#admins=username1,username2\n')
}
config.roles = config.roles || {}
config.roles.admins = config.roles.admins || ''
config.roles.admins = config.roles.admins.split(',')
return config
let config = {};
try {
config = ini.parse(fs.readFileSync(CONFIG_FILE_PATH, 'utf-8'));
} catch (e) {
console.log('No config file found, creating empty one');
fs.writeFileSync(CONFIG_FILE_PATH, '[roles]\n#admins=username1,username2\n');
}
config.roles = config.roles || {};
config.roles.admins = config.roles.admins || '';
config.roles.admins = config.roles.admins.split(',');
return config;
}
let config = reloadConfig()
let config = reloadConfig();
// This only fetches the settings supported by the UI
function getOpenVPNSetting() {
let settings = {}
// this crashes the app intentionally to restart it and let it create a default
let tmp = ''
try {
tmp = fs.readFileSync(OPENVPN_SETTINGS_FILE_PATH, 'utf-8')
} catch (e) {
console.error(e)
process.exit(1)
}
// ignore comments and empty lines
tmp = tmp.split('\n').map((l) => { return l.trim() })
tmp = tmp.filter((l) => { return l && l[0] !== '#' })
function findItem(prefix) {
let idx = tmp.findIndex((l) => { return l.indexOf(prefix) === 0 })
if (idx === -1) return null
return tmp[idx].slice(prefix.length).trim().replace(/["]+$/g, '')
}
settings.address = findItem('server').split(' ')[0];
settings.netmask = findItem('server').split(' ')[1];
settings.dnsServer = findItem('push "dhcp-option DNS')
settings.allowICC = findItem('client-to-client') !== null
return settings
let settings = {};
// this crashes the app intentionally to restart it and let it create a default
let tmp = '';
try {
tmp = fs.readFileSync(OPENVPN_SETTINGS_FILE_PATH, 'utf-8');
} catch (e) {
console.error(e);
process.exit(1);
}
// ignore comments and empty lines
tmp = tmp.split('\n').map((l) => { return l.trim(); });
tmp = tmp.filter((l) => { return l && l[0] !== '#'; });
function findItem(prefix) {
let idx = tmp.findIndex((l) => { return l.indexOf(prefix) === 0; });
if (idx === -1) return null;
return tmp[idx].slice(prefix.length).trim().replace(/["]+$/g, '');
}
settings.address = findItem('server').split(' ')[0];
settings.netmask = findItem('server').split(' ')[1];
settings.dnsServer = findItem('push "dhcp-option DNS');
settings.allowICC = findItem('client-to-client') !== null;
return settings;
}
// This only fetches the settings supported by the UI
function setOpenVPNSetting(settings) {
// this crashes the app intentionally to restart it and let it create a default
let tmp = ''
try {
tmp = fs.readFileSync(OPENVPN_SETTINGS_FILE_PATH, 'utf-8')
} catch (e) {
console.error(e)
process.exit(1)
}
tmp = tmp.split('\n')
// allowICC
let idx = tmp.findIndex((l) => { return l.indexOf('client-to-client') !== -1 })
let value = (settings.allowICC ? '' : '# ') + 'client-to-client'
if (idx === -1) tmp.push(value)
else tmp[idx] = value
// server
idx = tmp.findIndex((l) => { return l.indexOf('server ') !== -1 })
value = `server ${settings.address} ${settings.netmask}`
if (idx === -1) tmp.push(value)
else tmp[idx] = value
// dnsServer
idx = tmp.findIndex((l) => { return l.indexOf('push "dhcp-option DNS') !== -1 })
value = `push "dhcp-option DNS ${settings.dnsServer}"`
if (idx === -1) tmp.push(value)
else tmp[idx] = value
fs.writeFileSync(OPENVPN_SETTINGS_FILE_PATH, tmp.join('\n'), 'utf-8')
// this crashes the app intentionally to restart it and let it create a default
let tmp = '';
try {
tmp = fs.readFileSync(OPENVPN_SETTINGS_FILE_PATH, 'utf-8');
} catch (e) {
console.error(e);
process.exit(1);
}
tmp = tmp.split('\n');
// allowICC
let idx = tmp.findIndex((l) => { return l.indexOf('client-to-client') !== -1; });
let value = (settings.allowICC ? '' : '# ') + 'client-to-client';
if (idx === -1) tmp.push(value);
else tmp[idx] = value;
// server
idx = tmp.findIndex((l) => { return l.indexOf('server ') !== -1; });
value = `server ${settings.address} ${settings.netmask}`;
if (idx === -1) tmp.push(value);
else tmp[idx] = value;
// dnsServer
idx = tmp.findIndex((l) => { return l.indexOf('push "dhcp-option DNS') !== -1; });
value = `push "dhcp-option DNS ${settings.dnsServer}"`;
if (idx === -1) tmp.push(value);
else tmp[idx] = value;
fs.writeFileSync(OPENVPN_SETTINGS_FILE_PATH, tmp.join('\n'), 'utf-8');
}
function restartOpenVPN() {
......@@ -124,60 +124,60 @@ function restartOpenVPN() {
return null;
}
const isAuthenticated = (req, res, next) => (req.session && req.session.user) ? next() : res.status(401).send({})
const isAuthenticated = (req, res, next) => (req.session && req.session.user) ? next() : res.status(401).send({});
app.use('/api/healthcheck', (req, res) => openvpn.isRunning()
.then(running => running ? res.status(200).send() : res.status(500).send())
)
.then(running => running ? res.status(200).send() : res.status(500).send())
);
app.set('trust proxy', 1)
app.use(morgan('dev'))
app.use(compression())
app.set('trust proxy', 1);
app.use(morgan('dev'));
app.use(compression());
app.use(session({
secret: fs.readFileSync(path.resolve(__dirname, `${baseDir}/session.secret`), 'utf8'),
store: new LokiStore({
path: path.resolve(__dirname, `${baseDir}/session.db`),
logErrors: true
}),
resave: false,
saveUninitialized: false,
name: 'openvpn.sid',
cookie: {
path: '/',
httpOnly: true,
secure: !!process.env.CLOUDRON,
maxAge: /* 1 week: */ 7 /* d */ * 24 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */
}
}))
secret: fs.readFileSync(path.resolve(__dirname, `${baseDir}/session.secret`), 'utf8'),
store: new LokiStore({
path: path.resolve(__dirname, `${baseDir}/session.db`),
logErrors: true
}),
resave: false,
saveUninitialized: false,
name: 'openvpn.sid',
cookie: {
path: '/',
httpOnly: true,
secure: !!process.env.CLOUDRON,
maxAge: /* 1 week: */ 7 /* d */ * 24 /* h */ * 60 /* min */ * 60 /* s */ * 1000 /* ms */
}
}));
router.post('/api/login', jsonParser, (req, res, next) => {
if (!req.body.username || !req.body.password) {
req.session.user = null
next(new HttpError(401, 'Unauthorized'))
} else {
ldap.auth(req.body.username, req.body.password)
.then(profile => {
// on login check for new roles
config = reloadConfig()
profile.isAdmin = config.roles.admins.indexOf(profile.username) !== -1
req.session.user = profile
next(new HttpSuccess(200, {user: req.session.user}))
})
.catch(() => next(new HttpError(401, 'Unauthorized')))
}
})
if (!req.body.username || !req.body.password) {
req.session.user = null;
next(new HttpError(401, 'Unauthorized'));
} else {
ldap.auth(req.body.username, req.body.password)
.then(profile => {
// on login check for new roles
config = reloadConfig();
profile.isAdmin = config.roles.admins.indexOf(profile.username) !== -1;
req.session.user = profile;
next(new HttpSuccess(200, {user: req.session.user}));
})
.catch(() => next(new HttpError(401, 'Unauthorized')));
}
});
router.get('/api/logout', (req, res) => {
req.session.user = null
res.redirect('/')
})
req.session.user = null;
res.redirect('/');
});
router.get('/api/profile', isAuthenticated, (req, res, next) => {
next(new HttpSuccess(200, {user: req.session.user}))
})
next(new HttpSuccess(200, {user: req.session.user}));
});
router.get('/api/settings', isAuthenticated, (req, res, next) => {
next(new HttpSuccess(200, { settings: getOpenVPNSetting() }))
})
next(new HttpSuccess(200, { settings: getOpenVPNSetting() }));
});
router.post('/api/settings', isAuthenticated, jsonParser, (req, res, next) => {
setOpenVPNSetting(req.body.settings);
......@@ -189,19 +189,19 @@ router.post('/api/settings', isAuthenticated, jsonParser, (req, res, next) => {
next(new HttpSuccess(201, {}));
});
router.post('/api/onConnect/', urlEncodedParser, openvpn.onClientConnect)
router.post('/api/onDisconnect/', urlEncodedParser, openvpn.onClientDisconnect)
router.post('/api/onLearnAddress/', urlEncodedParser, openvpn.onLearnAddress)
router.get('/api/list/', isAuthenticated, openvpn.list)
router.put('/api/key/*', isAuthenticated, openvpn.createKey)
router.get('/api/key/*', isAuthenticated, openvpn.getKey)
router.delete('/api/key/*', isAuthenticated, openvpn.revokeKey)
app.use(router)
app.use('/', express.static(path.resolve(__dirname, 'frontend')))
app.use(lastMile())
router.post('/api/onConnect/', urlEncodedParser, openvpn.onClientConnect);
router.post('/api/onDisconnect/', urlEncodedParser, openvpn.onClientDisconnect);
router.post('/api/onLearnAddress/', urlEncodedParser, openvpn.onLearnAddress);
router.get('/api/list/', isAuthenticated, openvpn.list);
router.put('/api/key/*', isAuthenticated, openvpn.createKey);
router.get('/api/key/*', isAuthenticated, openvpn.getKey);
router.delete('/api/key/*', isAuthenticated, openvpn.revokeKey);
app.use(router);
app.use('/', express.static(path.resolve(__dirname, 'frontend')));
app.use(lastMile());
const server = app.listen(3000, '0.0.0.0', () => {
const {address, port} = server.address()
const {address, port} = server.address();
console.log(`OpenVPN web-config interface listening at http://${address}:${port}`)
})
console.log(`OpenVPN web-config interface listening at http://${address}:${port}`);
});
'use strict'
'use strict';
const ldap = require('ldapjs')
const ldap = require('ldapjs');
if (!process.env.CLOUDRON_LDAP_URL) {
console.error('CLOUDRON_LDAP_URL not set. Cannot continue')
process.exit(1)
console.error('CLOUDRON_LDAP_URL not set. Cannot continue');
process.exit(1);
}
const client = ldap.createClient({
url: process.env.CLOUDRON_LDAP_URL,
timeout: 10000, /* 10 seconds */
reconnect: true /* undocumented option to automatically reconnect on connection failure : https://github.com/joyent/node-ldapjs/issues/318#issuecomment-165769581 */
})
url: process.env.CLOUDRON_LDAP_URL,
timeout: 10000, /* 10 seconds */
reconnect: true /* undocumented option to automatically reconnect on connection failure : https://github.com/joyent/node-ldapjs/issues/318#issuecomment-165769581 */
});
const bind = (dn, password) => new Promise((resolve, reject) =>
client.bind(dn, password, err => err ? reject(err) : resolve())
)
client.bind(dn, password, err => err ? reject(err) : resolve())
);
const search = (username) => new Promise((resolve, reject) => {
const filter = username
? {filter: `(|(uid=${username})(mail=${username})(username=${username})(sAMAccountName=${username}))`}
: {}
client.search(process.env.CLOUDRON_LDAP_USERS_BASE_DN, filter, (err, res) => {
if (err) return reject(err)
const filter = username
? {filter: `(|(uid=${username})(mail=${username})(username=${username})(sAMAccountName=${username}))`}
: {};
client.search(process.env.CLOUDRON_LDAP_USERS_BASE_DN, filter, (err, res) => {
if (err) return reject(err);
const entries = []
let done = false
res.on('searchEntry', entry => {
if (done) return
entries.push(entry.object)
})
res.on('error', err => {
if (done) return
done = true
reject(err)
})
res.on('end', result => {
if (done) return
done = true
if (result.status === 0) {
resolve(entries)
} else {
reject(new Error('Unexpected error while retrieving users from LDAP. Status: ' + result.status))
}
})
})
})
const entries = [];
let done = false;
res.on('searchEntry', entry => {
if (done) return;
entries.push(entry.object);
});
res.on('error', err => {
if (done) return;
done = true;
reject(err);
});
res.on('end', result => {
if (done) return;
done = true;
if (result.status === 0) {
resolve(entries);
} else {
reject(new Error('Unexpected error while retrieving users from LDAP. Status: ' + result.status));
}
});
});
});
const auth = (username, password) => search(username)
.then(entries => {
if (entries.length !== 1) {
throw new Error('Unknown user')
} else {
return bind(`cn=${entries[0].username},${process.env.CLOUDRON_LDAP_USERS_BASE_DN}`, password)
.then(() => ({
username: entries[0].username,
displayName: entries[0].displayname,
email: entries[0].email
}))
.catch(() => { throw new Error('Wrong password') })
}
})
.then(entries => {
if (entries.length !== 1) {
throw new Error('Unknown user');
} else {
return bind(`cn=${entries[0].username},${process.env.CLOUDRON_LDAP_USERS_BASE_DN}`, password)
.then(() => ({
username: entries[0].username,
displayName: entries[0].displayname,
email: entries[0].email
}))
.catch(() => { throw new Error('Wrong password'); });
}
});
const cache = {
data: null,
duration: /* 1 min: */ 60 /* seconds */ * 1000 /* milliseconds */, // User can change this value to modify duration of caching or disable with 0
time: Date.now()
}
data: null,
duration: /* 1 min: */ 60 /* seconds */ * 1000 /* milliseconds */, // User can change this value to modify duration of caching or disable with 0
time: Date.now()
};
const listUsers = (useCache = true) => {
if (useCache && cache.data && Date.now() - cache.time < cache.duration) return Promise.resolve(cache.data)
return search()
.then(entries => {
cache.time = Date.now() // remember the cache
cache.data = entries
return entries
})
}
if (useCache && cache.data && Date.now() - cache.time < cache.duration) return Promise.resolve(cache.data);
return search()
.then(entries => {
cache.time = Date.now(); // remember the cache
cache.data = entries;
return entries;
});
};
const doesUserExist = (username, useCache = true) => listUsers(useCache)
.then(users => users.some(user => user.username === username))
.then(users => users.some(user => user.username === username));
module.exports = {
auth,
doesUserExist
}
auth,
doesUserExist
};
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment