frist pass

This commit is contained in:
William Mantly 2018-02-13 19:58:47 -05:00
parent 45b0efaaec
commit b067b65a45
Signed by: wmantly
GPG Key ID: E1EEC7650BA97160
13 changed files with 1620 additions and 0 deletions

32
app.js Normal file
View File

@ -0,0 +1,32 @@
'use strict';
const express = require('express');
const app = express();
const middleware = require('./middleware/auth');
app.use(express.json());
app.use('/auth', require('./routes/auth'));
app.use('/users', middleware.auth, require('./routes/users'));
app.use('/api', middleware.auth, require('./routes/routes'));
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

90
bin/www Executable file
View File

@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('proxy-api:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

17
middleware/auth.js Normal file
View File

@ -0,0 +1,17 @@
const Users = require('../models/users');
async function auth(req, res, next){
if(req.header('auth-token')){
let user = await Users.checkToken({token: req.header('auth-token')});
if(user){
req.user = user;
return next();
}
}
return res.status(401).json({
message: 'Login failed'
});
}
module.exports = {auth};

0
models/auth.js Normal file
View File

86
models/users.js Normal file
View File

@ -0,0 +1,86 @@
const {promisify} = require('util');
const linuxUser = require('linux-user');
const pam = require('authenticate-pam');
const client = require('../redis');
const UUID = function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)};
/*
Invite
*/
async function makeInviteToken(data){
let token = UUID();
await client.HSET('users_tokens', token, JSON.stringify({
created_by: data.username,
isAdmin: data.isAdmin,
invited: false
}));
return token;
}
async function checkInviteToken(data){
let token = await client.HGET('users_tokens', data.token);
return JSON.parse(token);
}
async function useInviteToken(data){
let invite = await checkInviteToken(data);
invite.invited = data.username;
await client.HSET('users_tokens', data.token, JSON.stringify(invite));
}
/*
Auth/ Auth token
*/
async function login(data){
const authenticate = promisify(pam.authenticate);
const getUserGroups = promisify(linuxUser.getUserGroups);
try{
await authenticate(data.username, data.password);
let groups = await getUserGroups(data.username);
console.log('groups', groups)
return true;
}catch(error){
return false;
}
}
async function addToken(data){
let token = UUID();
await client.HSET('users_tokens', token, data.username);
return token;
}
async function checkToken(data){
let user = await client.HGET('users_tokens', data.token);
return user;
}
/*
Users
*/
async function add(data) {
const addUser = promisify(linuxUser.addUser);
const setPassword = promisify(linuxUser.setPassword);
let systemUser = await addUser(data.username);
let systemUserPassword = await setPassword(data.username, data.password);
}
async function ifUserExists(data){
const getUserInfo = promisify(linuxUser.getUserInfo);
return await getUserInfo(data.username);
}
module.exports = {login, add, addToken, checkToken, ifUserExists,
makeInviteToken, checkInviteToken, useInviteToken};

1181
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "proxy-api",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"authenticate-pam": "^1.0.2",
"bcrypt": "^1.0.3",
"express": "~4.16.1",
"linux-user": "github:wmantly/linux-user",
"pubsub-js": "^1.6.0",
"redis": "^2.8.0"
}
}

19
redis.js Normal file
View File

@ -0,0 +1,19 @@
const {createClient} = require('redis');
const {promisify} = require('util');
const config = {
prefix: 'proxy_'
}
function client() {
return createClient(config);
}
const _client = client();
module.exports = {
client: client,
HGET: promisify(_client.HGET).bind(_client),
SADD: promisify(_client.SADD).bind(_client),
HSET: promisify(_client.HSET).bind(_client),
};

83
routes/auth.js Normal file
View File

@ -0,0 +1,83 @@
'use strict';
const router = require('express').Router();
const linuxUser = require('linux-user');
const {promisify} = require('util');
const Users = require('../models/users');
router.post('/login', async function(req, res){
let username = req.body.username;
let password = req.body.password;
let groups = await Users.login({username, password})
if(groups){
return res.json({
login: true,
token: await Users.addToken({username}),
groups: groups,
});
}else{
return res.status(401).json({
login: false
});
}
});
router.post('/verifykey', async function(req, res){
let key = req.body.key;
const verifySSHKey = promisify(linuxUser.verifySSHKey);
let isValid;
try{
isValid = await verifySSHKey(key);
return res.json({
info: isValid
});
}catch(error){
return res.status(400).json({
message: 'Key is not a public key file!'
});
}
});
router.post('/:token', async function(req, res, next) {
let username = req.body.username;
let password = req.body.password;
let token = req.params.token;
let invite = await Users.checkInviteToken({token});
console.log('invite', invite)
if(!invite || invite.invited){
return res.status(401).json({
message: 'Token not valid'
});
}
if(!username || !password){
return res.status(400).json({
message: 'Missing fields'
});
}
if(await Users.ifUserExists({username})){
return res.json({
message: 'username taken'
});
}
await Users.add({username, password, isAdmin: invite.isAdmin});
await Users.useInviteToken({token, username});
return res.json({user:username});
});
module.exports = router;

9
routes/index.js Normal file
View File

@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;

74
routes/routes.js Normal file
View File

@ -0,0 +1,74 @@
const client = require('../redis');
const router = require('express').Router();
const app = require('../app');
router.get('/:host', async function(req, res){
client.HGETALL('host_' + req.params.host, function (error, results) {
res.json({
host: req.params.host,
results: results
});
});
});
router.get('/', async function(req, res){
client.SMEMBERS('hosts', function(error, results){
if(error){
return res.status(500).json({message: `ERROR ${error}`});
}
return res.json({hosts: results});
});
});
router.post('/', async function(req, res){
let ip = req.body.ip;
let host = req.body.host;
if(!host || !ip){
return res.status(400).json({
message: `Missing fields: ${!host ? 'host' : ''} ${!ip ? 'ip' : ''}`
});
}else{
try{
await client.SADD('hosts', host);
await client.HSET('host_' + host, 'ip', ip);
await client.HSET('host_' + host, 'updated', (new Date).getTime());
} catch (error){
return res.status(500).json({
message: `ERROR: ${error}`
});
}
return res.json({
message: `Host ${host} Added`
});
}
});
router.delete('/', async function(req, res){
let host = req.body.host;
if(!host){
return res.status(400).json({
message: `Missing fields: ${!host ? 'host' : ''}`
});
}else{
try{
await client.SREM('hosts', host);
await client.DEL('host_' + host);
}catch(error){
return res.status(500).json({
message: `ERROR: ${error}`
});
}
return res.json({
message: `Host ${host} deleted`
});
}
});
module.exports = router;

13
routes/users.js Normal file
View File

@ -0,0 +1,13 @@
const router = require('express').Router();
const Users = require('../models/users');
router.post('/invite', async function(req, res){
let token = await Users.makeInviteToken({
username: res.user
});
return res.json({token:token})
});
module.exports = router;

0
ssh-keygen Normal file
View File