WIP: User auth

This commit is contained in:
mike.rode
2019-02-27 13:42:18 -06:00
parent cfadb7432d
commit b82cb7f8eb
12 changed files with 399 additions and 38 deletions

19
client/views/signin.hbs Normal file
View File

@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form id="signin" name="signin" method="post" action="signin">
<label for="email">Email Address</label>
<input class="text" name="email" type="text" />
<label for="password">Password</label>
<input name="password" type="password" />
<input class="btn" type="submit" value="Sign In" />
</form>
</body>
</html>

23
client/views/signup.hbs Normal file
View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<form id="signup" name="signup" method="post" action="/signup">
<label for="email">Email Address</label>
<input class="text" name="email" type="email" />
<label for="firstname">Firstname</label>
<input name="firstname" type="text" />
<label for="lastname">Lastname</label>
<input name="lastname" type="text" />
<label for="password">Password</label>
<input name="password" type="password" />
<input class="btn" type="submit" value="Sign Up" />
</form>
</body>
</html>

173
package-lock.json generated
View File

@@ -484,6 +484,11 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
@@ -514,6 +519,14 @@
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
"dev": true
},
"async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"requires": {
"lodash": "^4.17.11"
}
},
"async-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
@@ -1242,6 +1255,11 @@
"safe-buffer": "5.1.2"
}
},
"bcrypt-nodejs": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz",
"integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
@@ -1895,6 +1913,11 @@
}
}
},
"crc": {
"version": "3.4.4",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz",
"integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms="
},
"create-error-class": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
@@ -2016,7 +2039,6 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
@@ -2672,6 +2694,34 @@
}
}
},
"express-handlebars": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/express-handlebars/-/express-handlebars-3.0.1.tgz",
"integrity": "sha512-ZrfVjnGHS8liArTuVpWFckWII6Wwf1Z9BLqgDIYNjzH/yYxkRPeYvA4mv/o+7f7mJTJAX9oXys0ZZRGqkUyTog==",
"requires": {
"glob": "^7.1.3",
"graceful-fs": "^4.1.2",
"handlebars": "^4.0.13",
"object.assign": "^4.1.0",
"promise": "^8.0.2"
}
},
"express-session": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz",
"integrity": "sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA==",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"crc": "3.4.4",
"debug": "2.6.9",
"depd": "~1.1.1",
"on-headers": "~1.0.1",
"parseurl": "~1.3.2",
"uid-safe": "~2.1.5",
"utils-merge": "1.0.1"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -3474,8 +3524,7 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
},
"functional-red-black-tree": {
"version": "1.0.1",
@@ -3687,6 +3736,24 @@
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
"handlebars": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
"integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
"requires": {
"async": "^2.5.0",
"optimist": "^0.6.1",
"source-map": "^0.6.1",
"uglify-js": "^3.1.4"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -3735,8 +3802,7 @@
"has-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
"dev": true
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
},
"has-value": {
"version": "1.0.0",
@@ -9962,8 +10028,7 @@
"object-keys": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
"integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
"dev": true
"integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
},
"object-visit": {
"version": "1.0.1",
@@ -9977,7 +10042,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
"integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
@@ -10074,6 +10138,27 @@
"mimic-fn": "^1.0.0"
}
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
},
"dependencies": {
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
}
}
},
"optionator": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
@@ -10249,6 +10334,28 @@
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
},
"passport": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz",
"integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
}
},
"passport-local": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
"integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
"requires": {
"passport-strategy": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
@@ -10308,6 +10415,11 @@
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
"dev": true
},
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -10534,6 +10646,14 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"promise": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.0.2.tgz",
"integrity": "sha512-EIyzM39FpVOMbqgzEHhxdrEhtOSDOtjMZQ0M6iVfCE+kWNgCkAyOdnuCWqfmflylftfadU6FkiMgHZA2kUzwRw==",
"requires": {
"asap": "~2.0.6"
}
},
"promise-polyfill": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.1.0.tgz",
@@ -10610,6 +10730,11 @@
"strict-uri-encode": "^1.0.0"
}
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"randomatic": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
@@ -11987,6 +12112,38 @@
"mime-types": "~2.1.18"
}
},
"uglify-js": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
"optional": true,
"requires": {
"commander": "~2.17.1",
"source-map": "~0.6.1"
},
"dependencies": {
"commander": {
"version": "2.17.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
"optional": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"optional": true
}
}
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"umzug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/umzug/-/umzug-2.2.0.tgz",

View File

@@ -27,6 +27,7 @@
"license": "ISC",
"dependencies": {
"axios": "^0.18.0",
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.18.3",
"btoa": "^1.2.1",
"build-url": "^1.3.2",
@@ -36,9 +37,13 @@
"custom-env": "^1.0.0",
"dotenv": "^6.2.0",
"express": "^4.16.4",
"express-handlebars": "^3.0.1",
"express-session": "^1.15.6",
"lodash": "^4.17.11",
"morgan": "^1.9.1",
"onchange": "^5.2.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0",
"pg": "^7.8.0",
"plex-api": "^5.2.1",
"request-promise": "^4.2.4",

View File

@@ -0,0 +1,10 @@
// eslint-disable-next-line import/prefer-default-export
const signup = (req, res) => {
res.render('signup');
};
const signin = (req, res) => {
res.render('signin');
};
export default {signup, signin};

View File

@@ -17,6 +17,20 @@ module.exports = {
type: Sequelize.STRING,
unique: true,
},
username: Sequelize.STRING,
password: {
type: Sequelize.STRING,
allowNull: false,
},
last_login: {
type: Sequelize.DATE,
},
status: {
type: Sequelize.ENUM('active', 'inactive'),
defaultValue: 'active',
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,

View File

@@ -5,6 +5,20 @@ module.exports = (sequelize, DataTypes) => {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: {type: DataTypes.STRING, unique: true},
username: DataTypes.STRING,
password: {
type: DataTypes.STRING,
allowNull: false,
},
last_login: {
type: DataTypes.DATE,
},
status: {
type: DataTypes.ENUM('active', 'inactive'),
defaultValue: 'active',
},
},
{},
);

View File

@@ -4,6 +4,7 @@ export default {
firstName: 'Mike',
lastName: 'Rode',
email: 'mike@email.com',
password: 'password123',
},
],
};

View File

@@ -1,7 +1,12 @@
/* eslint-disable global-require */
import express from 'express';
import {json, urlencoded} from 'body-parser';
import passport from 'passport';
import session from 'express-session';
import exphbs from 'express-handlebars';
import {sequelize} from './db/models';
import plex from './routes/plex.route';
import auth from './routes/auth';
export default () => {
const server = express();
@@ -16,8 +21,26 @@ export default () => {
server.use(json());
server.use(urlencoded({extended: true}));
// Passport
server.use(
session({secret: 'keyboard cat', resave: true, saveUninitialized: true}),
);
server.use(passport.initialize());
server.use(passport.session()); // persistent login sessions
// Set up routes
server.use('/plex', plex);
auth(server);
// Set up views
server.set('views', './client/views');
server.engine(
'hbs',
exphbs({
extname: '.hbs',
}),
);
server.set('view engine', '.hbs');
return server;
};

View File

@@ -0,0 +1,60 @@
import Strategy from 'passport-local';
import bCrypt from 'bcrypt-nodejs';
module.exports = function(passport, user) {
const User = user;
const LocalStrategy = Strategy;
passport.use(
'local-signup',
new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true, // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
const generateHash = function(password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
email,
},
}).then(function(user) {
if (user) {
return done(null, false, {
message: 'That email is already taken',
});
}
const userPassword = generateHash(password);
const data = {
email,
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname,
};
User.create(data).then(function(newUser, created) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
});
},
),
);
};

6
server/routes/auth.js Normal file
View File

@@ -0,0 +1,6 @@
import authController from '../controllers/auth.controller';
module.exports = function(app) {
app.get('/signup', authController.signup);
app.get('/signin', authController.signin);
};

View File

@@ -1,37 +1,66 @@
// import chai from 'chai';
// import nock from 'nock';
// import app from '../../../../index';
// import importData from '../../../../server/services/plex/importData';
// import { PlexSection, User, PlexLibrary } from '../../../../server/db/models';
// import { seed, truncate } from '../../../../server/db/scripts';
// import * as nocks from '../../../nocks';
import chai from 'chai';
import nock from 'nock';
import app from '../../../../index';
import importData from '../../../../server/services/plex/importData';
import models from '../../../../server/db/models';
import { seed, truncate } from '../../../../server/db/scripts';
import * as nocks from '../../../nocks';
// // before(() => truncate('PlexSection'));
// describe('ImportData', () => {
// before(() => seed('User'));
// after(() => {
// truncate('User');
// truncate('PlexLibrary');
// truncate('PlexSection');
describe('ImportData', () => {
before(() => {
nocks.plexSections();
seed('User');
});
after(() => {
truncate('User');
truncate('PlexLibrary');
truncate('PlexSection');
});
describe('GET /plex/import/sections', () => {
it('should find and store sections in the database', async () => {
const response = await importData.importSections();
console.log('import sec response', response);
const sections = await models.PlexSection.findAll();
console.log('Sections response', sections);
sections.should.be.length(2);
});
});
});
// describe('createRecord', () => {
// const name = 'testCreateIM';
// const im = { name, description: 'new interface method' };
// let jwt;
// before((done) => {
// createToken('test')
// .then((token) => {
// jwt = token;
// done();
// });
// });
// describe('GET /plex/import/sections', async () => {
// it('should find and store sections in the database first', async () => {
// nocks.plexSections();
// const response = await importData.importSections().then
// console.log('import sec response', response);
// const sections = await PlexSection.findAll();
// sections.should.be.length(2);
// });
// });
// after(() => Interfacemethod.destroy({ where: { name } }));
// describe('Get /plex/import/libraries', async () => {
// it('should import sections', async () => {
// nocks.plexLibrary();
// nocks.plexSections();
// await chai.request(app).get('/plex/import/libraries');
// const media = await PlexLibrary.findAll();
// media.should.be.length(56);
// it('creates record', (done) => {
// let responseRecord;
// chai.request(app)
// .post('/v1/interfacemethods')
// .set('Authorization', `Bearer ${jwt}`)
// .send(im)
// .then((res) => {
// expect(res).to.have.status(200);
// expect(res.body).to.have.property('record');
// responseRecord = res.body.record;
// return Interfacemethod.findByPk(responseRecord.id);
// })
// .then((dbRecord) => {
// expect(responseRecord).to.deep.equal(JSON.parse(JSON.stringify(dbRecord)));
// expect(im.name).to.equal(responseRecord.name);
// expect(im.description).to.equal(responseRecord.description);
// done();
// });
// });
// });
// });