mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-07-09 05:16:02 -05:00
Merge pull request #9 from PretendoNetwork/dev
admin login working + code cleanup
This commit is contained in:
commit
2950bb9bb7
|
|
@ -15,6 +15,6 @@
|
|||
"url": "mongodb://localhost:27017/pretendo_website"
|
||||
},
|
||||
"secrets": {
|
||||
"session": "abcdef"
|
||||
"session": "session secret here"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,9 @@ blog post helper functionality
|
|||
|
||||
*/
|
||||
|
||||
//import filesystem
|
||||
// imports
|
||||
const fs = require('fs');
|
||||
|
||||
//import author list
|
||||
const authors = require('../config.json').blog.authors;
|
||||
|
||||
//import markdown converter
|
||||
const showdown = require('showdown');
|
||||
const converter = new showdown.Converter({metadata: true});
|
||||
|
||||
|
|
@ -54,7 +50,6 @@ function writeMarkdownToFile(text) {
|
|||
fs.writeFileSync(`posts/${length}.md`, text);
|
||||
}
|
||||
|
||||
// export the router
|
||||
module.exports = {
|
||||
getBlogPostAsHtml,
|
||||
getBlogPostAsMarkdown,
|
||||
|
|
|
|||
|
|
@ -5,48 +5,67 @@ common page functionality.
|
|||
|
||||
*/
|
||||
|
||||
function sendDefault404(req, res) {
|
||||
// shows 404 template.
|
||||
function sendDefault404(res) {
|
||||
res.status(404).send('404');
|
||||
}
|
||||
|
||||
function sendApi404(req, res) {
|
||||
// use for any api return. it has basic layout used for every return.
|
||||
function sendApiReturn(res, data, errors) {
|
||||
res.status(200).json(
|
||||
// combine 2 objects
|
||||
Object.assign({
|
||||
code: 200,
|
||||
success: true,
|
||||
errors: [] + (errors ? errors : [])
|
||||
}, data)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// use if api endpoint doesnt exist
|
||||
function sendApi404(res) {
|
||||
res.status(404).json({
|
||||
error: {
|
||||
code: 404,
|
||||
message: 'Endpoint not in use'
|
||||
}
|
||||
code: 404,
|
||||
errors: [
|
||||
'Endpoint not in use'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function sendApiAuthError(req, res) {
|
||||
// use if not logged in and is required (handled with middleware)
|
||||
function sendApiAuthError(res) {
|
||||
res.status(401).json({
|
||||
error: {
|
||||
code: 401,
|
||||
message: 'Not authenticated'
|
||||
}
|
||||
code: 401,
|
||||
errors: [
|
||||
'Not authenticated'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function sendApiGenericError(req, res) {
|
||||
// use for completely broken requests
|
||||
function sendApiGenericError(res) {
|
||||
res.status(400).json({
|
||||
error: {
|
||||
code: 400,
|
||||
message: 'Bad request'
|
||||
}
|
||||
code: 400,
|
||||
success: false,
|
||||
errors: [
|
||||
'Bad request'
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function sendApiError(res, code, message) {
|
||||
// use for any api not successfull
|
||||
function sendApiError(res, code, errors) {
|
||||
res.status(code).json({
|
||||
error: {
|
||||
code,
|
||||
message
|
||||
}
|
||||
code,
|
||||
success: false,
|
||||
errors
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendDefault404,
|
||||
sendApiReturn,
|
||||
sendApi404,
|
||||
sendApiGenericError,
|
||||
sendApiError,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,28 @@
|
|||
/*
|
||||
|
||||
admin-authentication.js -
|
||||
Middleware file for authentication checking
|
||||
|
||||
*/
|
||||
|
||||
// imports
|
||||
const common = require('../helpers/common');
|
||||
|
||||
function adminUserAuthenticationMiddleware(req, res, next) {
|
||||
if (req.isAuthenticated()) {
|
||||
// middleware to use if admin authentication is required
|
||||
function adminAuthenticationRequired(req, res, next) {
|
||||
if (req.isAuthenticated() && req.user.role && req.user.role === 'admin') {
|
||||
return next();
|
||||
} else {
|
||||
common.sendApiAuthError(res);
|
||||
}
|
||||
|
||||
common.sendApiAuthError(req, res);
|
||||
}
|
||||
|
||||
module.exports = adminUserAuthenticationMiddleware;
|
||||
// middleware to use if authentication
|
||||
function authenticationOptional(req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
adminAuthenticationRequired,
|
||||
authenticationOptional
|
||||
};
|
||||
|
|
@ -5,10 +5,12 @@ file containing the model file for admin user
|
|||
|
||||
*/
|
||||
|
||||
// imports
|
||||
const mongoose = require('mongoose');
|
||||
const uniqueValidator = require('mongoose-unique-validator');
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
// admin user database layout
|
||||
const adminUserSchema = new mongoose.Schema({
|
||||
username: {
|
||||
type: String,
|
||||
|
|
@ -16,26 +18,32 @@ const adminUserSchema = new mongoose.Schema({
|
|||
minlength: [1, 'Username must be at-least 1 character.'],
|
||||
maxlength: [42, 'Username cannot be more than 42 characters long.'],
|
||||
unique: true,
|
||||
trim: true,
|
||||
trim: true
|
||||
},
|
||||
password: {
|
||||
type: String,
|
||||
required: [true, 'Password is required.'],
|
||||
minlength: [7, 'Password must be at-least 7 characters.'],
|
||||
maxlength: [256, 'Password cannot be more than 256 characters long.'],
|
||||
trim: true,
|
||||
trim: true
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'admin'
|
||||
}
|
||||
});
|
||||
|
||||
adminUserSchema.plugin(uniqueValidator, {message: '{PATH} already in use.'});
|
||||
|
||||
// hashing password
|
||||
adminUserSchema.pre('save', function(next) {
|
||||
// only hash the password if it has been modified (or is new)
|
||||
// only if modified
|
||||
if (!this.isModified('password')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Hash the password with 10 rounds.
|
||||
// hashing
|
||||
bcrypt.hash(this.get('password'), 10, (err, hash) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
|
|
|
|||
877
package-lock.json
generated
877
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -18,6 +18,7 @@
|
|||
"bcrypt": "^3.0.1",
|
||||
"body-parser": "^1.18.3",
|
||||
"colors": "^1.1.2",
|
||||
"connect-mongo": "^2.0.1",
|
||||
"express": "^4.16.2",
|
||||
"express-handlebars": "^3.0.0",
|
||||
"express-session": "^1.15.6",
|
||||
|
|
|
|||
55
passport.config.js
Normal file
55
passport.config.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
|
||||
passport.config.js -
|
||||
Passport setup file. handles authentication strategy and session setup
|
||||
|
||||
*/
|
||||
|
||||
// imports
|
||||
const LocalStrategy = require('passport-local').Strategy;
|
||||
const passport = require('passport');
|
||||
const bcrypt = require('bcrypt');
|
||||
const adminUserModel = require('./models/admin-user').adminUserModel;
|
||||
|
||||
// setup authentication
|
||||
module.exports = (app) => {
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
passport.use('adminUserStrategy', new LocalStrategy(
|
||||
(username, password, done) => {
|
||||
// find user in database
|
||||
adminUserModel.findByUsername(username).then((user) => {
|
||||
if (!user) {
|
||||
// user doesnt exist
|
||||
return done(null, false, {message: 'Incorrect user'});
|
||||
}
|
||||
|
||||
bcrypt.compare(password, user.password, (err, res) => {
|
||||
if (err || !res) {
|
||||
// error comparing hashes
|
||||
return done(null, false, {message: 'Incorrect password'});
|
||||
}
|
||||
|
||||
// password is correct, return user
|
||||
return done(null, user);
|
||||
|
||||
});
|
||||
}).catch((err) => {
|
||||
if (err) {
|
||||
// error finding in database
|
||||
return done(null, false, {code: 500});
|
||||
}
|
||||
});
|
||||
}
|
||||
));
|
||||
//Configuring app to have sessions
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user.id);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(id, done) {
|
||||
adminUserModel.findById(id, function(err, user) {
|
||||
done(err, user);
|
||||
});
|
||||
});
|
||||
};
|
||||
106
routes/admin.js
106
routes/admin.js
|
|
@ -5,10 +5,9 @@ file for handling admin panel routes
|
|||
|
||||
*/
|
||||
|
||||
// import express' router
|
||||
// imports
|
||||
const router = require('express').Router();
|
||||
|
||||
// import dependencies
|
||||
const passport = require('passport');
|
||||
const common = require('../helpers/common');
|
||||
const adminUserMiddleware = require('../middleware/admin-authentication');
|
||||
const adminUser = require('../models/admin-user');
|
||||
|
|
@ -18,22 +17,57 @@ router.get('/admin', (req, res) => {
|
|||
res.render('admin');
|
||||
});
|
||||
|
||||
// admin login
|
||||
router.post('/admin/login', (req, res) => {
|
||||
common.sendApi404(req, res);
|
||||
/*
|
||||
* /admin/api/v1/login
|
||||
*
|
||||
* signs admin user in
|
||||
*
|
||||
* post {
|
||||
* username - username of admin account
|
||||
* password - password of admin account
|
||||
* }
|
||||
* return {
|
||||
* code: http code
|
||||
* success: boolean - true if login succesfull
|
||||
* username: undefined | string - username if login was successfull
|
||||
* role: undefined | string - role of user if login was successfull
|
||||
* errors: Strings[messages] - not yet :(
|
||||
* }
|
||||
*/
|
||||
// TODO make login somehow display errors in correct format.
|
||||
router.post('/admin/api/v1/login', passport.authenticate('adminUserStrategy'), function (req, res) {
|
||||
common.sendApiReturn(res, {
|
||||
username: req.user.username,
|
||||
role: req.user.role ? req.user.role : undefined
|
||||
});
|
||||
});
|
||||
|
||||
// endpoints
|
||||
router.use('/admin/api/v1', adminUserMiddleware);
|
||||
|
||||
router.post('/admin/register', (req, res) => {
|
||||
if (!req.body || !req.body.username || !req.body.password) {
|
||||
// no/wrong post body
|
||||
common.sendApiGenericError(req, res);
|
||||
/*
|
||||
* /admin/api/v1/register
|
||||
* - requires admin auth
|
||||
*
|
||||
* registers a new admin user
|
||||
*
|
||||
* post {
|
||||
* username - username of new admin account
|
||||
* password - password of new admin account
|
||||
* }
|
||||
* return {
|
||||
* code: httpcode
|
||||
* success: boolean - true if register was successull
|
||||
* username: undefined | string - username if register was successfull
|
||||
* role: undefined | string - role of user if register was successfull
|
||||
* errors: Strings[messages]
|
||||
* }
|
||||
*/
|
||||
router.post('/admin/api/v1/register', adminUserMiddleware.adminAuthenticationRequired, (req, res) => {
|
||||
if (!req.body) {
|
||||
// no post body
|
||||
common.sendApiGenericError(res);
|
||||
return;
|
||||
}
|
||||
|
||||
const { username, password } = req.body;
|
||||
|
||||
const newUser = new adminUser.adminUserModel({
|
||||
username,
|
||||
password
|
||||
|
|
@ -41,23 +75,53 @@ router.post('/admin/register', (req, res) => {
|
|||
|
||||
newUser.save().then(() => {
|
||||
// successfull
|
||||
res.status(201).json({
|
||||
success: true
|
||||
common.sendApiReturn(res, {
|
||||
username: req.user.username,
|
||||
role: req.user.role ? req.user.role : undefined
|
||||
});
|
||||
return;
|
||||
}).catch((rejection) => {
|
||||
res.status(500).json(rejection);
|
||||
// TODO format exception so it doesnt have a huge list of errors
|
||||
common.sendApiError(res, 500, [rejection]);
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/admin/api/v1/ping', (req, res) => {
|
||||
res.json({
|
||||
success: true,
|
||||
messsage: 'pong!'
|
||||
/*
|
||||
* /admin/api/v1/check
|
||||
*
|
||||
* checks if admin logged in
|
||||
*
|
||||
* return {
|
||||
* code: httpcode
|
||||
* success: boolean - true if request was without errors
|
||||
* isAuthed: boolean - true if logged in
|
||||
* role: undefined | string - returns user role
|
||||
* errors: Strings[messages]
|
||||
* }
|
||||
*/
|
||||
router.get('/admin/api/v1/check', adminUserMiddleware.authenticationOptional, (req, res) => {
|
||||
common.sendApiReturn(res, {
|
||||
isAuthed: req.user ? true : false,
|
||||
role: req.user ? (req.user.role ? req.user.role : undefined) : undefined
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* /admin/api/v1/logout
|
||||
*
|
||||
* logs out admin user
|
||||
*
|
||||
* return {
|
||||
* code: httpcode
|
||||
* success: boolean - true if logout is successfull
|
||||
* errors: Strings[messages]
|
||||
* }
|
||||
*/
|
||||
router.get('/admin/api/v1/logout', adminUserMiddleware.adminAuthenticationRequired, (req, res) => {
|
||||
req.logout();
|
||||
common.sendApiReturn(res, {});
|
||||
});
|
||||
|
||||
// export the router
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ file for handling routes regarding blog posts.
|
|||
|
||||
*/
|
||||
|
||||
// import express' router
|
||||
// imports
|
||||
const router = require('express').Router();
|
||||
const blogHelper = require('../helpers/blog-helper.js');
|
||||
|
||||
|
|
@ -28,5 +28,5 @@ router.get('/news/:id', async (req, res) => {
|
|||
res.render('post', hbsObject);
|
||||
});
|
||||
|
||||
// export the router
|
||||
// export router
|
||||
module.exports = router;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ for routes on the root path
|
|||
|
||||
*/
|
||||
|
||||
// import express' router
|
||||
// imports
|
||||
const router = require('express').Router();
|
||||
|
||||
// display home page
|
||||
|
|
|
|||
128
server.js
128
server.js
|
|
@ -1,110 +1,78 @@
|
|||
/*
|
||||
|
||||
server.js -
|
||||
the file that contains the code to run the server
|
||||
the file that contains the startup code
|
||||
|
||||
*/
|
||||
|
||||
// module imports, and
|
||||
// some important variable defs
|
||||
// imports
|
||||
const express = require('express');
|
||||
const handlebars = require('express-handlebars');
|
||||
const session = require('express-session');
|
||||
const passport = require('passport');
|
||||
const LocalStrategy = require('passport').Strategy;
|
||||
const bcrypt = require('bcrypt');
|
||||
const mongoStore = require('connect-mongo')(session);
|
||||
const mongoose = require('mongoose');
|
||||
const bodyParser = require('body-parser');
|
||||
const config = require('./config.json');
|
||||
const app = express();
|
||||
const common = require('./helpers/common');
|
||||
const adminUserModel = require('./models/admin-user').adminUserModel;
|
||||
const passportconfig = require('./passport.config.js');
|
||||
|
||||
// import the colors module
|
||||
// setup console colors
|
||||
require('colors');
|
||||
|
||||
// locations for some files
|
||||
// fix database deprecation warnings
|
||||
mongoose.set('useCreateIndex', true);
|
||||
mongoose.set('useNewUrlParser', true);
|
||||
// setup database connection
|
||||
mongoose.connect(config.database.url);
|
||||
const connection = mongoose.connection;
|
||||
connection.on('error', console.error.bind(console, 'connection error:'));
|
||||
|
||||
// setup express
|
||||
const app = express();
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(session({
|
||||
secret: config.secrets.session,
|
||||
saveUninitialized: true,
|
||||
resave: true,
|
||||
cookie: {
|
||||
maxAge: 3600000,
|
||||
httpOnly: true//, // remove on production
|
||||
/*secure: true*/ // uncomment on production
|
||||
},
|
||||
// permanent session storing for MongoDB
|
||||
store: new mongoStore({
|
||||
mongooseConnection: connection,
|
||||
collection: 'sessions'
|
||||
})
|
||||
}));
|
||||
passportconfig(app);
|
||||
|
||||
// handlebars templating setup
|
||||
app.engine('.hbs', handlebars({
|
||||
extname: '.hbs',
|
||||
layoutsDir: 'views',
|
||||
partialsDir: 'views/partials'
|
||||
}));
|
||||
app.set('view engine', '.hbs');
|
||||
|
||||
// locations and routes setup
|
||||
const locations = {
|
||||
home: require('./routes/home'),
|
||||
posts: require('./routes/blog'),
|
||||
admin: require('./routes/admin')
|
||||
};
|
||||
|
||||
// setup body parser
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
// setup database
|
||||
mongoose.connect(config.database.url);
|
||||
const connection = mongoose.connection;
|
||||
connection.on('error', console.error.bind(console, 'connection error:'));
|
||||
|
||||
// setup session
|
||||
app.use(session({
|
||||
secret: config.secrets.session,
|
||||
//cookie: { secure: true } // use when https is enabled
|
||||
}));
|
||||
|
||||
// setup authentication
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
passport.use('adminUserStrategy', new LocalStrategy(
|
||||
(username, password, done) => {
|
||||
// find user in database
|
||||
adminUserModel.findByUsername(username).then((user) => {
|
||||
if (!user) {
|
||||
// user doesnt exist
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
bcrypt.compare(password, user.password, (err, res) => {
|
||||
if (err || !res) {
|
||||
// error comparing hashes
|
||||
return done(null, false);
|
||||
}
|
||||
|
||||
// password is correct, return user
|
||||
return done(null, user);
|
||||
|
||||
});
|
||||
}).catch((err) => {
|
||||
if (err) {
|
||||
// error finding in database
|
||||
return done(null, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
// load the handlebars module
|
||||
app.engine('.hbs', handlebars({
|
||||
extname: '.hbs',
|
||||
layoutsDir: 'views',
|
||||
partialsDir: 'views/partials'
|
||||
}));
|
||||
|
||||
// set express to use handlebars
|
||||
// as the templating engine
|
||||
app.set('view engine', '.hbs');
|
||||
|
||||
// set some routes on the server
|
||||
|
||||
// assets folder serving
|
||||
// static files
|
||||
app.use('/assets', express.static('assets'));
|
||||
|
||||
// website root
|
||||
// page map
|
||||
app.use('/', locations.home);
|
||||
|
||||
// blog posts
|
||||
app.use('/', locations.posts);
|
||||
|
||||
// admin panel
|
||||
app.use('/', locations.admin);
|
||||
app.use((req, res) => {
|
||||
common.sendDefault404(res);
|
||||
});
|
||||
|
||||
// send a 404 on a file not
|
||||
// being found
|
||||
app.use(common.sendDefault404);
|
||||
|
||||
// start the server
|
||||
// startup
|
||||
app.listen(config.http.port, () => {
|
||||
console.log(`started the server on port: ${new String(config.http.port).green}`);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,19 +7,21 @@
|
|||
|
||||
<h1>Admin panel here</h1>
|
||||
<h2>register</h2>
|
||||
<form action="/admin/register" method="POST">
|
||||
<form action="/admin/api/v1/register" method="POST">
|
||||
<input type="text" name="username">
|
||||
<input type="text" name="password">
|
||||
<button>submit register</button>
|
||||
</form>
|
||||
|
||||
<h2>login</h2>
|
||||
<form action="/admin/login" method="POST">
|
||||
<form action="/admin/api/v1/login" method="POST">
|
||||
<input type="text" name="username">
|
||||
<input type="text" name="password">
|
||||
<button>submit login</button>
|
||||
</form>
|
||||
|
||||
<a href="/admin/api/v1/check">Check if logged in</a>
|
||||
|
||||
{{> footer }}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user