Register, Login, and Logout
This commit is contained in:
+2
-1
@@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
ndm.db
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
body {
|
||||||
|
width: 1200px;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Logo {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Header {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Header-Left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#Header-Right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
#PageContent {
|
||||||
|
display: block;
|
||||||
|
width: 900px;
|
||||||
|
min-height: 600px;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: #e5f1fd;
|
||||||
|
border-left: gray 2px solid;
|
||||||
|
border-right: gray 2px solid;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CenteredFocusHeader {
|
||||||
|
width: 400px;
|
||||||
|
margin: auto;
|
||||||
|
background-color: #4682b4;
|
||||||
|
color: white;
|
||||||
|
padding: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CenteredFocusHeader h1,
|
||||||
|
.CenteredFocusHeader h2,
|
||||||
|
.CenteredFocusHeader h3,
|
||||||
|
.CenteredFocusHeader h4,
|
||||||
|
.CenteredFocusHeader h5,
|
||||||
|
.CenteredFocusHeader h6 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CenteredFocusContent {
|
||||||
|
width: 400px;
|
||||||
|
background-color: white;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 2px solid #4682b4;
|
||||||
|
margin: auto;
|
||||||
|
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VerticalInputForm {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.TextInput {
|
||||||
|
height: 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.VerticalInputForm input {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// This script is designed to support at a minimum IE 6
|
||||||
|
//
|
||||||
|
// All menu functions are defined outside of initMenu.
|
||||||
|
//
|
||||||
|
// This is so I'm not duplicating functions between-
|
||||||
|
// checks for what browser we have
|
||||||
|
|
||||||
|
function doLogoutAction() {
|
||||||
|
var logoutForm = document.getElementById("Form_Auth_Logout");
|
||||||
|
logoutForm.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the page has fully loaded, connect each button to its code
|
||||||
|
function initMenu() {
|
||||||
|
var logoutButton = document.getElementById("Menu_Auth_Logout");
|
||||||
|
|
||||||
|
if(window.addEventListener) {
|
||||||
|
logoutButton.addEventListener("click", doLogoutAction);
|
||||||
|
} else {
|
||||||
|
logoutButton.attachEvent('onclick', doLogoutAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register load / onload event
|
||||||
|
if(window.addEventListener) {
|
||||||
|
window.addEventListener('load', initMenu);
|
||||||
|
} else if(window.attachEvent) {
|
||||||
|
window.attachEvent('onload', initMenu);
|
||||||
|
} else {
|
||||||
|
alert("Unsupported browser.");
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// really simple IE6 compatible post function
|
||||||
|
|
||||||
|
function PostToEndpoint(url, params) {
|
||||||
|
var form = document.createElement("form");
|
||||||
|
form.method = "POST";
|
||||||
|
form.action = url;
|
||||||
|
|
||||||
|
for (var key in params) {
|
||||||
|
if (params.hasOwnProperty(key)) {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.type = "hidden";
|
||||||
|
input.name = key;
|
||||||
|
input.value = params[key];
|
||||||
|
form.appendChild(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const { csrfSync } = require("csrf-sync");
|
||||||
|
|
||||||
|
const {
|
||||||
|
invalidCsrfTokenError,
|
||||||
|
generateToken,
|
||||||
|
validateRequest,
|
||||||
|
csrfSynchronisedProtection
|
||||||
|
} = csrfSync({
|
||||||
|
getTokenFromRequest: (req) => req.body._csrf,
|
||||||
|
});
|
||||||
|
module.exports = {
|
||||||
|
generateToken,
|
||||||
|
csrfSynchronisedProtection,
|
||||||
|
invalidCsrfTokenError
|
||||||
|
};
|
||||||
+1
-13
@@ -2,7 +2,7 @@ const Sequelize = require('sequelize');
|
|||||||
|
|
||||||
const seqConn = new Sequelize({
|
const seqConn = new Sequelize({
|
||||||
dialect: 'sqlite',
|
dialect: 'sqlite',
|
||||||
storage: process.env.DB_STORAGE,
|
storage: `ndm.db`,
|
||||||
pool: {
|
pool: {
|
||||||
max: 5,
|
max: 5,
|
||||||
min: 0,
|
min: 0,
|
||||||
@@ -12,17 +12,6 @@ const seqConn = new Sequelize({
|
|||||||
logging: console.log,
|
logging: console.log,
|
||||||
});
|
});
|
||||||
|
|
||||||
////// Sessions, Users //////
|
|
||||||
// Sessions
|
|
||||||
const Session = seqConn.define('Session', {
|
|
||||||
sid: {
|
|
||||||
type: Sequelize.TEXT,
|
|
||||||
primaryKey: true
|
|
||||||
},
|
|
||||||
data: Sequelize.TEXT,
|
|
||||||
expires: Sequelize.DATE,
|
|
||||||
});
|
|
||||||
|
|
||||||
// User
|
// User
|
||||||
const User = seqConn.define('User', {
|
const User = seqConn.define('User', {
|
||||||
username: {
|
username: {
|
||||||
@@ -58,7 +47,6 @@ const RegisteredDomain = seqConn.define('RegisteredDomain', {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
db: seqConn,
|
db: seqConn,
|
||||||
models: {
|
models: {
|
||||||
Session,
|
|
||||||
User,
|
User,
|
||||||
RegisteredDomain
|
RegisteredDomain
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Server
|
# Server
|
||||||
SRV_PORT=5001
|
SRV_PORT=5001
|
||||||
|
|
||||||
|
# Cookies
|
||||||
|
SESSKEY="87216653c5e7b5789b87a8b73e9d81b671d3b50d4f3699f8b1e756669ad6533fb1a740cdf5ae32fca7ed63b0dfb88e35c153097060ee43ac9dd4ca685e7bee39ee56284ba21f2fe408777c20d21c1c5c3a8a40f956272d336c6dbebe68c87c561a2225e9131e7f1976cf439903f616175896c21962631c2c1f72d2efb5e12db3cbc91941bff99a1e9d050b6badee79063439b1f5883b49e9285ed3e16d434e6deb2babdc838caa8c51d45db8fd7116c3d3ad5f20f955b115e7d5000d8f0454b151ed42519d3d8fcb38e7510976064d188c184a174d3537c47c7e968de55b563382317873b6dc4013dd33a0700bb9ba143fd19c4a42e76cda7bd2f738280c9643701a291b77fc0d4a05309d0e44272209020539fe3592660476b602e5edda5f496443b8ab82ff1035737f745df3d3be76ba95d83d772ac45989b2c61d8e7d0b"
|
||||||
|
CKYKEY="88ae945c555650606058d58284c1772b3d8b56cee370ddbfcb366c1c4f6be3681e2e218404a4ff0dba21597817e1301fdbfedd711ab3b99c4bc900a8583892f957ea9fc657811ec20fcde7c11a12201e5a8b8c62a4dd56e5a4c06e46b5853c172d010c9fe754f95e61be7fde60701f2de44c641078e3aa51ac74cc68573c7c4b51c1eb5d219d94fe6f6bc0139ea3a731d9097d381a4b00931f71d4615912db0355eb73a44d0ed873f85cc112f945a0ac5d776d9b161cacd9d823c7b770bdc3d5f77d9161e0b45dcd05cf7498eb8d4898a0e6436264f7c11643c19ef0624ac6b08ffe9bf093c19cdfc93d35646aa0b76f603be82e6939"
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_STORAGE="data_devel.db"
|
DB_STORAGE="datadevel.db"
|
||||||
@@ -47,6 +47,9 @@ const hbsHelpers = {
|
|||||||
},
|
},
|
||||||
getMetaInfString: () => {
|
getMetaInfString: () => {
|
||||||
return `${metaInf.name} | ${metaInf.stage}.${metaInf.version}.${metaInf.branch}-${process.env.NODE_ENV}`;
|
return `${metaInf.name} | ${metaInf.stage}.${metaInf.version}.${metaInf.branch}-${process.env.NODE_ENV}`;
|
||||||
|
},
|
||||||
|
getScript: (src, nonce) => {
|
||||||
|
return new String(`<script src="${src}" type="text/javascript" nonce="${nonce}"></script>`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ const app = express();
|
|||||||
const exphbs = require ('express-handlebars');
|
const exphbs = require ('express-handlebars');
|
||||||
const { SetupEnvironment } = require('./environ');
|
const { SetupEnvironment } = require('./environ');
|
||||||
const SetupRouter = require('./router');
|
const SetupRouter = require('./router');
|
||||||
|
const bodyParser = require("body-parser");
|
||||||
|
const sessionMw = require('./session');
|
||||||
|
const csrf = require("./csrf");
|
||||||
|
|
||||||
|
// Setup the environment
|
||||||
|
SetupEnvironment();
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
const database = require('./database');
|
const database = require('./database');
|
||||||
@@ -22,9 +28,6 @@ const { HBSHelpers } = require('./helpers');
|
|||||||
// Security
|
// Security
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
|
|
||||||
// First things first, setup the environment
|
|
||||||
SetupEnvironment();
|
|
||||||
|
|
||||||
// Get what we need for starting the server
|
// Get what we need for starting the server
|
||||||
const serverPort = process.env.SRV_PORT;
|
const serverPort = process.env.SRV_PORT;
|
||||||
|
|
||||||
@@ -32,21 +35,25 @@ const serverPort = process.env.SRV_PORT;
|
|||||||
const db = database.db;
|
const db = database.db;
|
||||||
const sessionStore = new SequelizeStore({
|
const sessionStore = new SequelizeStore({
|
||||||
db: db,
|
db: db,
|
||||||
table: 'Session'
|
tableName: 'Session'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Body parsing
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
// Helmet setup
|
// Helmet setup
|
||||||
app.use(
|
app.use(
|
||||||
helmet.contentSecurityPolicy({
|
helmet.contentSecurityPolicy({
|
||||||
directives: {
|
directives: (req, res) => ({
|
||||||
defaultSrc: ["'self'"],
|
defaultSrc: ["'self'"],
|
||||||
scriptSrc: ["'self'"],
|
scriptSrc: ["'strict-dynamic'", `'nonce-${res.locals.nonce}'`],
|
||||||
objectSrc: ["'none'"],
|
objectSrc: ["'none'"],
|
||||||
styleSrc: ["'self'", "'unsafe-inline'"],
|
styleSrc: ["'self'", "'unsafe-inline'"],
|
||||||
imgSrc: ["'self'", 'data:', '*'],
|
imgSrc: ["'self'", 'data:', '*'],
|
||||||
mediaSrc: ["'self'", 'data:', '*'],
|
mediaSrc: ["'self'", 'data:', '*'],
|
||||||
connectSrc: ["'self'", 'data:', '*']
|
connectSrc: ["'self'", 'data:', '*']
|
||||||
}
|
}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -76,14 +83,21 @@ app.use(session({
|
|||||||
store: sessionStore,
|
store: sessionStore,
|
||||||
cookie: {
|
cookie: {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === 'prod',
|
secure: false,
|
||||||
sameSite: 'strict'
|
sameSite: 'lax',
|
||||||
|
path: '/'
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Setup Assets
|
// Setup Assets
|
||||||
app.use(express.static('assets'));
|
app.use(express.static('assets'));
|
||||||
|
|
||||||
|
// Session middlware
|
||||||
|
app.use(sessionMw.PersistSession);
|
||||||
|
|
||||||
|
// CSRF protection
|
||||||
|
app.use(csrf.csrfSynchronisedProtection);
|
||||||
|
|
||||||
// Setup Router
|
// Setup Router
|
||||||
SetupRouter(app);
|
SetupRouter(app);
|
||||||
|
|
||||||
|
|||||||
Generated
+208
@@ -9,18 +9,75 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argon2": "^0.44.0",
|
||||||
"connect-session-sequelize": "^8.0.6",
|
"connect-session-sequelize": "^8.0.6",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
|
"csrf-sync": "^4.2.1",
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-handlebars": "^9.0.1",
|
"express-handlebars": "^9.0.1",
|
||||||
"express-session": "^1.19.0",
|
"express-session": "^1.19.0",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
|
"joi": "^18.2.1",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"sequelize": "^6.37.8",
|
"sequelize": "^6.37.8",
|
||||||
"sqlite3": "^6.0.1"
|
"sqlite3": "^6.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@epic-web/invariant": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/address": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@hapi/hoek": "^11.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/formula": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/hoek": {
|
||||||
|
"version": "11.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
|
||||||
|
"integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/pinpoint": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/tlds": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-xdi7A/4NZokvV0ewovme3aUO5kQhW9pQ2YD1hRqZGhhSi5rBv4usHYidVocXSi9eihYsznZxLtAiEYYUL6VBGw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@hapi/topo": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@hapi/hoek": "^11.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/fs-minipass": {
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
@@ -33,6 +90,21 @@
|
|||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@phc/format": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@standard-schema/spec": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/debug": {
|
"node_modules/@types/debug": {
|
||||||
"version": "4.1.13",
|
"version": "4.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
|
||||||
@@ -111,6 +183,22 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/argon2": {
|
||||||
|
"version": "0.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz",
|
||||||
|
"integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@phc/format": "^1.0.0",
|
||||||
|
"cross-env": "^10.0.0",
|
||||||
|
"node-addon-api": "^8.5.0",
|
||||||
|
"node-gyp-build": "^4.8.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.17.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||||
@@ -389,6 +477,67 @@
|
|||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-env": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@epic-web/invariant": "^1.0.0",
|
||||||
|
"cross-spawn": "^7.0.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"cross-env": "dist/bin/cross-env.js",
|
||||||
|
"cross-env-shell": "dist/bin/cross-env-shell.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn/node_modules/isexe": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn/node_modules/which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/node-which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csrf-sync": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/csrf-sync/-/csrf-sync-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-+q9tlUSCi/kbwr1NYwn5+MeuNhwxz3wSv1yl42BgIWfIuErZ3HajRwzvZTkfiyIqt1PZT8lQSlffhSYjCneN7g==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"http-errors": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -1084,6 +1233,24 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/joi": {
|
||||||
|
"version": "18.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.1.tgz",
|
||||||
|
"integrity": "sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@hapi/address": "^5.1.1",
|
||||||
|
"@hapi/formula": "^3.0.2",
|
||||||
|
"@hapi/hoek": "^11.0.7",
|
||||||
|
"@hapi/pinpoint": "^2.0.1",
|
||||||
|
"@hapi/tlds": "^1.1.1",
|
||||||
|
"@hapi/topo": "^6.0.2",
|
||||||
|
"@standard-schema/spec": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.18.1",
|
"version": "4.18.1",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||||
@@ -1311,6 +1478,17 @@
|
|||||||
"node": "^20.17.0 || >=22.9.0"
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "4.8.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
|
||||||
|
"integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.1.14",
|
"version": "3.1.14",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
|
||||||
@@ -1415,6 +1593,15 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-scurry": {
|
"node_modules/path-scurry": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||||
@@ -1809,6 +1996,27 @@
|
|||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
|
|||||||
@@ -17,13 +17,16 @@
|
|||||||
"wprod": "set NODE_ENV=prod&& nodemon index.js"
|
"wprod": "set NODE_ENV=prod&& nodemon index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"argon2": "^0.44.0",
|
||||||
"connect-session-sequelize": "^8.0.6",
|
"connect-session-sequelize": "^8.0.6",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
|
"csrf-sync": "^4.2.1",
|
||||||
"dotenv": "^17.4.2",
|
"dotenv": "^17.4.2",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"express-handlebars": "^9.0.1",
|
"express-handlebars": "^9.0.1",
|
||||||
"express-session": "^1.19.0",
|
"express-session": "^1.19.0",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
|
"joi": "^18.2.1",
|
||||||
"nodemon": "^3.1.14",
|
"nodemon": "^3.1.14",
|
||||||
"sequelize": "^6.37.8",
|
"sequelize": "^6.37.8",
|
||||||
"sqlite3": "^6.0.1"
|
"sqlite3": "^6.0.1"
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
const argon2 = require('argon2');
|
||||||
|
|
||||||
|
async function HashPassword(password) {
|
||||||
|
try {
|
||||||
|
return await argon2.hash(password);
|
||||||
|
} catch(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function TestPassword(password, hash) {
|
||||||
|
try {
|
||||||
|
return await argon2.verify(hash, password);
|
||||||
|
} catch(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
HashPassword,
|
||||||
|
TestPassword
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
# Server
|
# Server
|
||||||
SRV_PORT=5000
|
SRV_PORT=5000
|
||||||
|
|
||||||
|
# Cookies
|
||||||
|
SESSKEY="87216653c5e7b5789b87a8b73e9d81b671d3b50d4f3699f8b1e756669ad6533fb1a740cdf5ae32fca7ed63b0dfb88e35c153097060ee43ac9dd4ca685e7bee39ee56284ba21f2fe408777c20d21c1c5c3a8a40f956272d336c6dbebe68c87c561a2225e9131e7f1976cf439903f616175896c21962631c2c1f72d2efb5e12db3cbc91941bff99a1e9d050b6badee79063439b1f5883b49e9285ed3e16d434e6deb2babdc838caa8c51d45db8fd7116c3d3ad5f20f955b115e7d5000d8f0454b151ed42519d3d8fcb38e7510976064d188c184a174d3537c47c7e968de55b563382317873b6dc4013dd33a0700bb9ba143fd19c4a42e76cda7bd2f738280c9643701a291b77fc0d4a05309d0e44272209020539fe3592660476b602e5edda5f496443b8ab82ff1035737f745df3d3be76ba95d83d772ac45989b2c61d8e7d0b"
|
||||||
|
CKYKEY="88ae945c555650606058d58284c1772b3d8b56cee370ddbfcb366c1c4f6be3681e2e218404a4ff0dba21597817e1301fdbfedd711ab3b99c4bc900a8583892f957ea9fc657811ec20fcde7c11a12201e5a8b8c62a4dd56e5a4c06e46b5853c172d010c9fe754f95e61be7fde60701f2de44c641078e3aa51ac74cc68573c7c4b51c1eb5d219d94fe6f6bc0139ea3a731d9097d381a4b00931f71d4615912db0355eb73a44d0ed873f85cc112f945a0ac5d776d9b161cacd9d823c7b770bdc3d5f77d9161e0b45dcd05cf7498eb8d4898a0e6436264f7c11643c19ef0624ac6b08ffe9bf093c19cdfc93d35646aa0b76f603be82e6939"
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
DB_STORAGE="data_prod.db"
|
DB_STORAGE="dataprod.db"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', async (req, res) => {
|
||||||
|
console.log(`nonce: ${res.locals.globalScriptNonce}`);
|
||||||
|
res.render('index', {title: 'Domain Manager'});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const loginValidator = require('../validators/login');
|
||||||
|
const authMw = require('../session');
|
||||||
|
const pwMw = require('../password.js');
|
||||||
|
const database = require('../database.js');
|
||||||
|
const dbConnection = database.db;
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const pageTitle = 'Domain Manager | Login';
|
||||||
|
|
||||||
|
router.get('/login', authMw.AllowIfNotAuthenticated, async (req, res) => {
|
||||||
|
res.render('login', { title: pageTitle, csrfToken: req.csrfToken() });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', authMw.AllowIfNotAuthenticated, async (req, res, next) => {
|
||||||
|
const reqBody = req.body;
|
||||||
|
const validationResult = loginValidator.test(reqBody);
|
||||||
|
const validationError = validationResult.error;
|
||||||
|
let errors = [];
|
||||||
|
|
||||||
|
if(validationError !== undefined)
|
||||||
|
errors = validationError.details;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(errors.length === 0) {
|
||||||
|
const result = await dbConnection.transaction(async(t) => {
|
||||||
|
const user = database.models.User.findOne({
|
||||||
|
where: {
|
||||||
|
username: reqBody.login_username
|
||||||
|
},
|
||||||
|
transaction: t
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(result) {
|
||||||
|
const doesPasswordMatch = await pwMw.TestPassword(reqBody.login_password, result.password);
|
||||||
|
if(doesPasswordMatch === true) {
|
||||||
|
await authMw.CreateSession(req, result);
|
||||||
|
return res.redirect('/');
|
||||||
|
} else {
|
||||||
|
errors.push({message: 'Invalid username or password.'});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errors.push({message: 'Invalid username or password.'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
error.status = 500;
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.render('login', {title: pageTitle, errors: errors, csrfToken: req.csrfToken(true) });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/logout', authMw.AllowIfAuthenticated, async (req, res, next) => {
|
||||||
|
// Just destroy the session
|
||||||
|
req.session.destroy();
|
||||||
|
return res.redirect('/');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const registerValidator = require('../validators/register');
|
||||||
|
const authMw = require('../session');
|
||||||
|
const pwMw = require('../password.js');
|
||||||
|
const database = require('../database.js');
|
||||||
|
const dbConnection = database.db;
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const pageTitle = 'Domain Manager | Register';
|
||||||
|
|
||||||
|
router.get('/register', authMw.AllowIfNotAuthenticated, async (req, res) => {
|
||||||
|
res.render('register', { title: pageTitle, csrfToken: req.csrfToken() });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/register', authMw.AllowIfNotAuthenticated, async (req, res, next) => {
|
||||||
|
const reqBody = req.body;
|
||||||
|
const validationResult = registerValidator.test(reqBody);
|
||||||
|
const validationError = validationResult.error;
|
||||||
|
let errors = [];
|
||||||
|
|
||||||
|
if(validationError !== undefined)
|
||||||
|
errors = validationError.details;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(errors.length === 0) {
|
||||||
|
const hashedPassword = await pwMw.HashPassword(reqBody.register_password);
|
||||||
|
const result = await dbConnection.transaction(async(t) => {
|
||||||
|
const user = await database.models.User.create({
|
||||||
|
username: reqBody.register_username,
|
||||||
|
password: hashedPassword,
|
||||||
|
}, {transaction: t});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(result !== undefined) {
|
||||||
|
await authMw.CreateSession(req, result);
|
||||||
|
return res.redirect('/');
|
||||||
|
} else {
|
||||||
|
errors.push({message: 'Failed to create user.'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(error) {
|
||||||
|
if(error instanceof Sequelize.UniqueConstraintError) {
|
||||||
|
errors.push({message: 'Username is in use.'});
|
||||||
|
} else {
|
||||||
|
error.status = 500;
|
||||||
|
return next(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're here we failed, I specify true for csrfToken to force reset it
|
||||||
|
return res.render('register', {title: pageTitle, errors: errors, csrfToken: req.csrfToken(true) });
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
const { generateToken } = require('./csrf');
|
||||||
|
const database = require('./database');
|
||||||
|
const crypto = require("crypto");
|
||||||
|
|
||||||
|
async function PersistSession(req, res, next) {
|
||||||
|
req.session.visited = true;
|
||||||
|
res.locals.nonce = crypto.randomBytes(16).toString('base64');
|
||||||
|
|
||||||
|
const isLoggedIn = req.session.isLoggedIn;
|
||||||
|
|
||||||
|
if(isLoggedIn) {
|
||||||
|
const userId = req.session.userId;
|
||||||
|
const username = req.session.username;
|
||||||
|
const power = req.session.power;
|
||||||
|
|
||||||
|
req.session.ipAddress = req.ip;
|
||||||
|
|
||||||
|
res.locals.isLoggedIn = isLoggedIn;
|
||||||
|
res.locals.userId = userId;
|
||||||
|
res.locals.username = username;
|
||||||
|
res.locals.power = power;
|
||||||
|
res.locals.csrfToken = generateToken(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function CreateSession(req, user) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
req.session.isLoggedIn = true;
|
||||||
|
req.session.userId = user.id;
|
||||||
|
req.session.username = user.username;
|
||||||
|
req.session.power = user.power;
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch(error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function AllowIfNotAuthenticated(req, res, next) {
|
||||||
|
const isLoggedIn = req.session.isLoggedIn;
|
||||||
|
if(isLoggedIn)
|
||||||
|
return res.redirect('/');
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
function AllowIfAuthenticated(req, res, next) {
|
||||||
|
const isLoggedIn = req.session.isLoggedIn;
|
||||||
|
if(!isLoggedIn)
|
||||||
|
return res.redirect('/');
|
||||||
|
else
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
PersistSession,
|
||||||
|
CreateSession,
|
||||||
|
AllowIfNotAuthenticated,
|
||||||
|
AllowIfAuthenticated
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
const loginSchema = Joi.object().keys({
|
||||||
|
login_username: Joi.string().alphanum().min(3).max(24).required(),
|
||||||
|
login_password: Joi.string().min(8).max(256).required(),
|
||||||
|
}).unknown(true);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
test: (body) => {
|
||||||
|
return loginSchema.validate(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const Joi = require('joi');
|
||||||
|
const userCreateSchema = Joi.object().keys({
|
||||||
|
register_username: Joi.string().alphanum().min(3).max(24).required(),
|
||||||
|
register_password: Joi.string().min(8).max(256).required(),
|
||||||
|
register_confirm_password: Joi.any().valid(Joi.ref('register_password')).required().messages({'any.only': 'Passwords must match.'})
|
||||||
|
// token later
|
||||||
|
}).unknown(true);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
test: (body) => {
|
||||||
|
return userCreateSchema.validate(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<p>This is where you can register domains and manage DNS records.</p>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<title>{{title}}</title>
|
||||||
|
|
||||||
|
{{{getScript "/scripts/menu.js" nonce}}}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{#if isLoggedIn}}
|
||||||
|
<form id="Form_Auth_Logout" action="/logout" method="post">
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
</form>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div id="PageContent">
|
||||||
|
<div id="Logo">
|
||||||
|
<p>Domain Manager</p>
|
||||||
|
</div>
|
||||||
|
<div id="Header">
|
||||||
|
<div id="Header-Left">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
{{#if isLoggedIn}}
|
||||||
|
<span>|</span>
|
||||||
|
<a href="/dns">DNS</a>
|
||||||
|
<span>|</span>
|
||||||
|
<a href="/domains">Domains</a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="Header-Right">
|
||||||
|
{{#unless isLoggedIn}}
|
||||||
|
<a href="/login">Login</a>
|
||||||
|
<span>|</span>
|
||||||
|
<a href="/register">Register</a>
|
||||||
|
{{else}}
|
||||||
|
<span>{{username}}</span>
|
||||||
|
<span>|</span>
|
||||||
|
<a id="Menu_Auth_Logout" href="javascript://void();">Logout</a>
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
{{{body}}}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<br>
|
||||||
|
<div>
|
||||||
|
{{#if errors}}
|
||||||
|
<ul>
|
||||||
|
{{#each errors}}
|
||||||
|
<li>{{this.message}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="CenteredFocusHeader">
|
||||||
|
<h4>Login</h4>
|
||||||
|
</div>
|
||||||
|
<div class="CenteredFocusContent">
|
||||||
|
<form class="VerticalInputForm" action="/login" method="post">
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
<label for="login_username">Username</label>
|
||||||
|
<input class="TextInput" name="login_username" type="text">
|
||||||
|
<label for="login_password">Password</label>
|
||||||
|
<input class="TextInput" name="login_password" type="password">
|
||||||
|
<input type="submit" value="Login">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<br>
|
||||||
|
<div>
|
||||||
|
{{#if errors}}
|
||||||
|
<ul>
|
||||||
|
{{#each errors}}
|
||||||
|
<li>{{this.message}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="CenteredFocusHeader">
|
||||||
|
<h4>Register</h4>
|
||||||
|
</div>
|
||||||
|
<div class="CenteredFocusContent">
|
||||||
|
<form class="VerticalInputForm" action="/register" method="post">
|
||||||
|
<input type="hidden" name="_csrf" value="{{csrfToken}}">
|
||||||
|
<label for="register_username">Username</label>
|
||||||
|
<input class="TextInput" name="register_username" type="text">
|
||||||
|
<label for="register_password">Password</label>
|
||||||
|
<input class="TextInput" name="register_password" type="password">
|
||||||
|
<label for="register_confirm_password">Confirm Password</label>
|
||||||
|
<input class="TextInput" name="register_confirm_password" type="password">
|
||||||
|
<input type="submit" value="Register">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user