continued
implemented HTMX implemented ORM (sequelize)
This commit is contained in:
parent
2a9bd4e81b
commit
d756a192e4
71 changed files with 3822 additions and 694 deletions
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
import {
|
||||
getUsers,
|
||||
getGroups
|
||||
} from "../../lib/mysql.mjs";
|
||||
|
||||
|
||||
export const get = async function (request, response) {
|
||||
// if (!request.isLoginCompleted()) {
|
||||
// response.redirect('/login');
|
||||
// return;
|
||||
// }
|
||||
|
||||
switch (request.params.page) {
|
||||
case 'users':
|
||||
response.render(`ui/admin.njk`, {
|
||||
page: 'users',
|
||||
users: await getUsers()
|
||||
});
|
||||
break;
|
||||
case 'groups':
|
||||
response.render(`ui/admin.njk`, {
|
||||
page: 'groups',
|
||||
groups: await getGroups()
|
||||
});
|
||||
break;
|
||||
default:
|
||||
response.redirect('/admin/users');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export const post = async function (request, response) {
|
||||
console.log(request.body);
|
||||
response.redirect("/login");
|
||||
}
|
||||
17
routes/admin/groups.mjs
Normal file
17
routes/admin/groups.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let currUser = await request.getUser();
|
||||
|
||||
response.render(`views/admin/groups.njk`, {
|
||||
user: {
|
||||
firstName: currUser.givenname,
|
||||
lastName: currUser.sn,
|
||||
mail: currUser.mail,
|
||||
}
|
||||
});
|
||||
}
|
||||
17
routes/admin/users.mjs
Normal file
17
routes/admin/users.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let currUser = await request.getUser();
|
||||
|
||||
response.render(`views/admin/users.njk`, {
|
||||
user: {
|
||||
firstName: currUser.givenname,
|
||||
lastName: currUser.sn,
|
||||
mail: currUser.mail,
|
||||
}
|
||||
});
|
||||
}
|
||||
59
routes/htmx/admin/groups/[groupid].mjs
Normal file
59
routes/htmx/admin/groups/[groupid].mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
import {
|
||||
Group,
|
||||
User
|
||||
} from "../../../../lib/database/connect.mjs";
|
||||
|
||||
import {
|
||||
getConfig
|
||||
} from "../../../../lib/config.mjs";
|
||||
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const dbGroup = await Group.findByPk(request.params.groupid);
|
||||
const config = await getConfig();
|
||||
|
||||
const userList = await User.findAll({
|
||||
attributes: [
|
||||
'id',
|
||||
'uidnumber',
|
||||
'name',
|
||||
'othergroups',
|
||||
'primarygroup'
|
||||
]
|
||||
}).filter((user) => {
|
||||
// check if users primary group is the group being edited
|
||||
if (user.primarygroup == dbGroup.gidnumber) {
|
||||
return true;
|
||||
}
|
||||
// check if users other groups include the group being edited
|
||||
if (user.othergroups.split(',').map(gidnumber => parseInt(gidnumber)).includes(dbGroup.gidnumber)) {
|
||||
return true;
|
||||
}
|
||||
// else exclude user
|
||||
return false;
|
||||
}).map((user) => {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
uidnumber: user.uidnumber,
|
||||
};
|
||||
});
|
||||
|
||||
response.render(`views/htmx/admin/editGroup.njk`, {
|
||||
group: {
|
||||
id: dbGroup.id,
|
||||
name: dbGroup.name,
|
||||
gidnumber: dbGroup.gidnumber,
|
||||
},
|
||||
ldap: {
|
||||
baseDN: config.ldap.baseDN,
|
||||
},
|
||||
userList: userList
|
||||
});
|
||||
}
|
||||
18
routes/htmx/admin/groups/table.mjs
Normal file
18
routes/htmx/admin/groups/table.mjs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
import {
|
||||
Group
|
||||
} from "../../../../lib/database/connect.mjs";
|
||||
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let groupList = await Group.findAll();
|
||||
|
||||
response.render(`views/htmx/admin/groupTable.njk`, {
|
||||
groupList: groupList
|
||||
});
|
||||
}
|
||||
53
routes/htmx/admin/users/[userid].mjs
Normal file
53
routes/htmx/admin/users/[userid].mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
import {
|
||||
Group,
|
||||
User
|
||||
} from "../../../../lib/database/connect.mjs";
|
||||
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const dbUser = await User.findByPk(request.params.userid);
|
||||
|
||||
if (!dbUser) {
|
||||
response.status(404).end('User not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const userGroups = await (async () => {
|
||||
if (!dbUser.othergroups || dbUser.othergroups.length == 0) {
|
||||
return [];
|
||||
} else {
|
||||
return dbUser.othergroups.split(',').map(gid => parseInt(gid));
|
||||
}
|
||||
})();
|
||||
|
||||
const groupList = (await Group.findAll()).map((dbGroup) => {
|
||||
return {
|
||||
id: dbGroup.id,
|
||||
gidnumber: dbGroup.gidnumber,
|
||||
name: dbGroup.name,
|
||||
isPrimaryGroup: dbUser.primarygroup == dbGroup.gidnumber,
|
||||
isOtherGroup: userGroups.includes(dbGroup.gidnumber)
|
||||
};
|
||||
});
|
||||
|
||||
console.log(groupList);
|
||||
|
||||
response.render(`views/htmx/admin/editUser.njk`, {
|
||||
user: {
|
||||
id: dbUser.id,
|
||||
username: dbUser.name,
|
||||
uidnumber: dbUser.uidnumber,
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
disabled: dbUser.disabled,
|
||||
},
|
||||
groupList: groupList
|
||||
});
|
||||
}
|
||||
18
routes/htmx/admin/users/table.mjs
Normal file
18
routes/htmx/admin/users/table.mjs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
import {
|
||||
User
|
||||
} from "../../../../lib/database/connect.mjs";
|
||||
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let userList = await User.findAll();
|
||||
|
||||
response.render(`views/htmx/admin/userTable.njk`, {
|
||||
userList: userList
|
||||
});
|
||||
}
|
||||
75
routes/htmx/authForm.mjs
Normal file
75
routes/htmx/authForm.mjs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
|
||||
import crypto from "crypto";
|
||||
|
||||
import {
|
||||
User
|
||||
} from "../../lib/database/connect.mjs";
|
||||
|
||||
|
||||
function sendAuthForm(response, errors=[]) {
|
||||
response.render(`views/htmx/authForm.njk`, {
|
||||
errors: errors
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const get = async function(request, response) {
|
||||
sendAuthForm(response);
|
||||
// response.set('HX-Redirect', '/profile').status(200).end();
|
||||
}
|
||||
|
||||
export const post = async function(request, response) {
|
||||
if (!request.body.username || !request.body.password) {
|
||||
sendAuthForm(response, [{
|
||||
title: 'Username and password are required',
|
||||
detail: 'Username or Password was not received.'
|
||||
}]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof request.body.username != 'string' || typeof request.body.password != 'string') {
|
||||
sendAuthForm(response, [{
|
||||
title: 'Invalid input types',
|
||||
detail: 'Username and Password must be strings.'
|
||||
}]);
|
||||
return;
|
||||
}
|
||||
|
||||
let username = request.body.username;
|
||||
let password = crypto.createHash('sha256').update(request.body.password).digest('hex')
|
||||
|
||||
// let loginResult = await login(username, password)
|
||||
let loginUser = await User.findOne({
|
||||
where: {
|
||||
name: username,
|
||||
passsha256: password
|
||||
}
|
||||
});
|
||||
|
||||
if (loginUser == null) {
|
||||
sendAuthForm(response, [{
|
||||
title: 'Login failed',
|
||||
detail: 'Invalid Username or Password.'
|
||||
}]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loginUser.disabled == 1) {
|
||||
sendAuthForm(response, [{
|
||||
title: 'User disabled',
|
||||
detail: 'This user account is disabled.'
|
||||
}]);
|
||||
return;
|
||||
}
|
||||
|
||||
request.session.userid = loginUser.id;
|
||||
request.session.save();
|
||||
|
||||
if (loginUser.otpsecret == '' || loginUser.otpsecret == null) {
|
||||
request.setAuthState('authenticated');
|
||||
response.set('HX-Redirect', '/profile').status(200).end();
|
||||
} else {
|
||||
request.setAuthState('totp-verfication');
|
||||
response.redirect('/htmx/totpForm');
|
||||
}
|
||||
}
|
||||
27
routes/htmx/profile/data/edit.mjs
Normal file
27
routes/htmx/profile/data/edit.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let dbUser = await request.getUser();
|
||||
|
||||
response.render(`views/htmx/profile/editData.njk`, {
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
data: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const post = async function (request, response) {
|
||||
console.log(request.body);
|
||||
response.redirect("/login");
|
||||
}
|
||||
29
routes/htmx/profile/data/show.mjs
Normal file
29
routes/htmx/profile/data/show.mjs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let dbUser = await request.getUser();
|
||||
|
||||
response.render(`views/htmx/profile/showData.njk`, {
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
data: {
|
||||
uidnumber: dbUser.uidnumber,
|
||||
username: dbUser.name,
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const post = async function (request, response) {
|
||||
console.log(request.body);
|
||||
response.redirect("/login");
|
||||
}
|
||||
30
routes/htmx/profile/mfa/show.mjs
Normal file
30
routes/htmx/profile/mfa/show.mjs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
generateOTPQRCode
|
||||
} from "../../../../lib/otp.mjs";
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.set('HX-Redirect', '/login').status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let dbUser = await request.getUser();
|
||||
|
||||
response.render(`views/htmx/profile/showMFA.njk`, {
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
data: {
|
||||
otpsecret: dbUser.otpsecret,
|
||||
yubikey: dbUser.yubikey,
|
||||
qrcode: await generateOTPQRCode(dbUser.mail, dbUser.otpsecret)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const post = async function (request, response) {
|
||||
console.log(request.body);
|
||||
response.redirect("/login");
|
||||
}
|
||||
53
routes/htmx/totpForm.mjs
Normal file
53
routes/htmx/totpForm.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
import {
|
||||
validateOTPCode
|
||||
} from "../../lib/otp.mjs";
|
||||
|
||||
|
||||
function sendTOTPForm(response, errors=[]) {
|
||||
response.render(`views/htmx/totpForm.njk`, {
|
||||
errors: errors
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export const get = async function(request, response) {
|
||||
if (request.getAuthState() != 'totp-verfication') {
|
||||
response.redirect('/htmx/authForm');
|
||||
return;
|
||||
}
|
||||
|
||||
sendTOTPForm(response);
|
||||
}
|
||||
|
||||
export const post = async function(request, response) {
|
||||
// redirect if not in TOTP verification state
|
||||
if (request.getAuthState() != 'totp-verfication') {
|
||||
response.redirect('/htmx/authForm');
|
||||
return;
|
||||
}
|
||||
|
||||
// validate input
|
||||
if (!request.body.otpToken || typeof request.body.otpToken != 'string') {
|
||||
sendTOTPForm(response, [{
|
||||
title: 'OTP token is required',
|
||||
detail: 'no OTP token was received.'
|
||||
}]);
|
||||
return;
|
||||
}
|
||||
|
||||
let otpToken = request.body.otpToken;
|
||||
let dbUser = await request.getUser();
|
||||
|
||||
let validationResult = await validateOTPCode(dbUser.mail, dbUser.otpsecret, otpToken);
|
||||
|
||||
if (validationResult != null) {
|
||||
request.setAuthState('authenticated');
|
||||
response.set('HX-Redirect', '/profile').status(200).end();
|
||||
} else {
|
||||
sendTOTPForm(response, [{
|
||||
title: 'OTP validation failed',
|
||||
detail: 'the provided OTP token is invalid. Please try again.'
|
||||
}]);
|
||||
}
|
||||
}
|
||||
4
routes/index.mjs
Normal file
4
routes/index.mjs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
response.redirect(`/profile`);
|
||||
}
|
||||
|
|
@ -1,78 +1,8 @@
|
|||
|
||||
import crypto from "crypto";
|
||||
|
||||
import {
|
||||
login,
|
||||
getUser,
|
||||
getUserMFA
|
||||
} from "../lib/mysql.mjs";
|
||||
|
||||
import {
|
||||
validateOTPCode
|
||||
} from "../lib/otp.mjs";
|
||||
|
||||
export const get = async function(request, response) {
|
||||
if (typeof request.session.userid != 'string') {
|
||||
response.render(`ui/login.njk`, {
|
||||
step: 'login'
|
||||
});
|
||||
return;
|
||||
if (request.getAuthState() == 'authenticated') {
|
||||
response.redirect('/profile');
|
||||
}
|
||||
if (request.session.otpVerified != true) {
|
||||
response.render(`ui/login.njk`, {
|
||||
step: 'otp'
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export const post = async function(request, response) {
|
||||
if (typeof request.body.username == 'string' && typeof request.body.password == 'string') {
|
||||
let username = request.body.username;
|
||||
let password = crypto.createHash('sha256').update(request.body.password).digest('hex')
|
||||
|
||||
let loginResult = await login(username, password)
|
||||
|
||||
if (loginResult == null) {
|
||||
response.render(`ui/login.njk`, {
|
||||
step: 'login',
|
||||
error: 'login failed'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
request.session.userid = loginResult.id;
|
||||
request.session.login = {
|
||||
completed: false,
|
||||
otpVerified: false
|
||||
}
|
||||
request.session.save();
|
||||
|
||||
if (loginResult.otpsecret != '' && loginResult.yubikey != '') {
|
||||
response.render(`ui/login.njk`, {
|
||||
step: 'otp'
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
request.session.login.completed = true;
|
||||
response.redirect('/profile')
|
||||
}
|
||||
} else if (typeof request.body.otpToken == 'string') {
|
||||
let otpToken = request.body.otpToken;
|
||||
let userData = await getUser(request.session.userid);
|
||||
let mfaData = await getUserMFA(request.session.userid);
|
||||
|
||||
let validationResult = await validateOTPCode(userData.mail, mfaData.otpsecret, otpToken);
|
||||
|
||||
if (validationResult != null) {
|
||||
request.session.login.completed = true;
|
||||
response.redirect('/profile');
|
||||
} else {
|
||||
request.session.destroy();
|
||||
response.render(`ui/login.njk`, {
|
||||
step: 'login',
|
||||
error: 'otp failed'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
response.render(`views/login.njk`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
export const get = async function(request, response) {
|
||||
request.session.destroy();
|
||||
response.render(`ui/logout.njk`);
|
||||
response.render(`views/logout.njk`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,49 +1,58 @@
|
|||
|
||||
import {
|
||||
getUser,
|
||||
getUserMFA
|
||||
} from "../../lib/mysql.mjs";
|
||||
|
||||
import {
|
||||
generateOTPQRCode
|
||||
} from "../../lib/otp.mjs";
|
||||
|
||||
|
||||
export const get = async function(request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
let userData = await getUser(request.session.userid);
|
||||
let mfaData = await getUserMFA(request.session.userid);
|
||||
let dbUser = await request.getUser();
|
||||
|
||||
switch (request.params.page) {
|
||||
case 'personal':
|
||||
response.render(`ui/profile.njk`, {
|
||||
response.render(`views/profile.njk`, {
|
||||
page: 'profile/personal',
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
data: {
|
||||
firstName: userData.givenname,
|
||||
lastName: userData.sn,
|
||||
mail: userData.mail,
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'security':
|
||||
response.render(`ui/profile.njk`, {
|
||||
response.render(`views/profile.njk`, {
|
||||
page: 'profile/security',
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
otp: {
|
||||
active: mfaData.otpsecret != '' ? true : false,
|
||||
qrcode: await generateOTPQRCode(userData.mail, mfaData.otpsecret)
|
||||
active: dbUser.otpsecret != '' ? true : false,
|
||||
qrcode: await generateOTPQRCode(dbUser.mail, dbUser.otpsecret)
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'createOTPSecret':
|
||||
response.render(`ui/profile.njk`, {
|
||||
response.render(`views/profile.njk`, {
|
||||
page: 'profile/createOTPSecret',
|
||||
user: {
|
||||
firstName: dbUser.givenname,
|
||||
lastName: dbUser.sn,
|
||||
mail: dbUser.mail,
|
||||
},
|
||||
otp: {
|
||||
active: mfaData.otpsecret != '' ? true : false,
|
||||
qrcode: await generateOTPQRCode(userData.mail, mfaData.otpsecret)
|
||||
qrcode: await generateOTPQRCode(dbUser.mail, dbUser.otpsecret)
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
export const get = async function (request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from "../../../lib/mysql.mjs";
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ export const get = async function (request, response) {
|
|||
|
||||
|
||||
export const post = async function (request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import {
|
|||
} from "../../../lib/mysql.mjs";
|
||||
|
||||
export const get = async function (request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ export const get = async function (request, response) {
|
|||
|
||||
|
||||
export const post = async function (request, response) {
|
||||
if (!request.isLoginCompleted()) {
|
||||
if (request.getAuthState() != 'authenticated') {
|
||||
response.redirect('/login');
|
||||
return;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue