feat(lstv2 move): moved lstv2 into this app to keep them combined and easier to maintain
This commit is contained in:
70
lstV2/server/services/auth/controllers/login.ts
Normal file
70
lstV2/server/services/auth/controllers/login.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import {db} from "../../../../database/dbclient.js";
|
||||
import {users} from "../../../../database/schema/users.js";
|
||||
import {eq, sql} from "drizzle-orm";
|
||||
import {checkPassword} from "../utils/checkPassword.js";
|
||||
import {roleCheck} from "./userRoles/getUserAccess.js";
|
||||
import {createLog} from "../../logger/logger.js";
|
||||
import {differenceInDays} from "date-fns";
|
||||
|
||||
/**
|
||||
* Authenticate a user and return a JWT.
|
||||
*/
|
||||
const {sign} = jwt;
|
||||
|
||||
export async function login(
|
||||
username: string,
|
||||
password: string
|
||||
): Promise<{token: string; user: {user_id: string; username: string}}> {
|
||||
const user = await db.select().from(users).where(eq(users.username, username));
|
||||
|
||||
//console.log(user);
|
||||
if (user.length === 0) {
|
||||
throw new Error("Invalid or Missing user");
|
||||
}
|
||||
// check the password
|
||||
const checkedPass = await checkPassword(password, user[0]?.password);
|
||||
//console.log(checkedPass);
|
||||
if (!checkedPass) {
|
||||
throw new Error("Invalid Password");
|
||||
}
|
||||
|
||||
// Create a JWT
|
||||
const secret: string = process.env.JWT_SECRET!;
|
||||
const expiresIn = Number(process.env.JWT_EXPIRES!) || 60;
|
||||
|
||||
// get the user roles
|
||||
const roles = await roleCheck(user[0].user_id);
|
||||
const userData = {
|
||||
user_id: user[0].user_id,
|
||||
username: user[0].username,
|
||||
email: user[0].email,
|
||||
//roles: roles || null,
|
||||
role: user[0].role || null, // this should be removed onces full migration to v2 is completed
|
||||
prod: btoa(`${username.toLowerCase()}:${password}`),
|
||||
};
|
||||
|
||||
// update the user last login
|
||||
try {
|
||||
const lastLog = await db
|
||||
.update(users)
|
||||
.set({lastLogin: sql`NOW()`})
|
||||
.where(eq(users.user_id, user[0].user_id))
|
||||
.returning({lastLogin: users.lastLogin});
|
||||
createLog(
|
||||
"info",
|
||||
"lst",
|
||||
"auth",
|
||||
`Its been ${differenceInDays(lastLog[0]?.lastLogin ?? "", new Date(Date.now()))} days since ${
|
||||
user[0].username
|
||||
} has logged in`
|
||||
);
|
||||
//]);
|
||||
} catch (error) {
|
||||
createLog("error", "lst", "auth", "There was an error updating the user last login");
|
||||
}
|
||||
|
||||
const token = sign({user: userData}, secret, {expiresIn: expiresIn * 60});
|
||||
|
||||
return {token, user: userData};
|
||||
}
|
||||
8
lstV2/server/services/auth/controllers/logout.ts
Normal file
8
lstV2/server/services/auth/controllers/logout.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Logout (clear the token).
|
||||
* This is a placeholder function since JWTs are stateless.
|
||||
* In a real app, you might want to implement token blacklisting.
|
||||
*/
|
||||
export function logout(): {message: string} {
|
||||
return {message: "Logout successful"};
|
||||
}
|
||||
62
lstV2/server/services/auth/controllers/register.ts
Normal file
62
lstV2/server/services/auth/controllers/register.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../database/dbclient.js";
|
||||
import { users } from "../../../../database/schema/users.js";
|
||||
import { createPassword } from "../utils/createPassword.js";
|
||||
import { setSysAdmin } from "./userRoles/setSysAdmin.js";
|
||||
import { createLog } from "../../logger/logger.js";
|
||||
|
||||
export const registerUser = async (
|
||||
username: string,
|
||||
password: string,
|
||||
email: string
|
||||
) => {
|
||||
const usercount = await db.select().from(users);
|
||||
|
||||
// make sure the user dose not already exist in the system
|
||||
const userCheck = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
|
||||
if (userCheck.length === 1) {
|
||||
return {
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
};
|
||||
}
|
||||
|
||||
// make sure we only send over a username that is all lowercase
|
||||
username = username.toLowerCase();
|
||||
|
||||
// get the good kinda password
|
||||
password = await createPassword(password);
|
||||
|
||||
try {
|
||||
const user = await db
|
||||
.insert(users)
|
||||
.values({ username, email, password })
|
||||
.returning({ user: users.username, email: users.email });
|
||||
|
||||
if (usercount.length === 0) {
|
||||
createLog(
|
||||
"info",
|
||||
"auth",
|
||||
"auth",
|
||||
`${username} is the first user and will be set to system admin.`
|
||||
);
|
||||
const updateUser = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
setSysAdmin(updateUser, "systemAdmin");
|
||||
}
|
||||
|
||||
return { success: true, message: "User Registered", user };
|
||||
} catch (error) {
|
||||
createLog("error", "auth", "auth", `${error}`);
|
||||
return {
|
||||
success: false,
|
||||
message: `${username} already exists please login or reset password, if you feel this is an error please contact your admin.`,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const getAllUsersRoles = async () => {
|
||||
/**
|
||||
* returns all users that are in lst
|
||||
*/
|
||||
createLog("info", "apiAuthedRoute", "auth", "Get all users");
|
||||
const { data, error } = await tryCatch(db.select().from(userRoles));
|
||||
|
||||
if (error) {
|
||||
return returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
return returnRes(true, "auth", "auth", "All users.", "info", data);
|
||||
};
|
||||
45
lstV2/server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
45
lstV2/server/services/auth/controllers/userAdmin/getUsers.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { returnRes } from "../../../../globalUtils/routeDefs/returnRes.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
|
||||
export const getAllUsers = async () => {
|
||||
/**
|
||||
* returns all users that are in lst
|
||||
*/
|
||||
createLog("info", "apiAuthedRoute", "auth", "Get all users");
|
||||
|
||||
// get all users
|
||||
const { data, error } = await tryCatch(db.select().from(users));
|
||||
|
||||
// add there modules they are in
|
||||
const { data: m, error: em } = await tryCatch(db.select().from(userRoles));
|
||||
|
||||
const user: any = data;
|
||||
const userData = user.map((i: any) => {
|
||||
// module in
|
||||
const module = m?.filter((x: any) => x.user_id === i.user_id);
|
||||
|
||||
if (module) {
|
||||
return { ...i, moduleRoles: module };
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
if (error) {
|
||||
returnRes(
|
||||
false,
|
||||
"auth",
|
||||
"auth",
|
||||
"There was an error getting users",
|
||||
"error",
|
||||
new Error("No user exists.")
|
||||
);
|
||||
}
|
||||
|
||||
//returnRes(true, "All users.", data);
|
||||
return { success: true, message: "All users", data: userData };
|
||||
};
|
||||
@@ -0,0 +1,102 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { tryCatch } from "../../../../globalUtils/tryCatch.js";
|
||||
import type { User } from "../../../../types/users.js";
|
||||
import { createPassword } from "../../utils/createPassword.js";
|
||||
import { createLog } from "../../../logger/logger.js";
|
||||
import { sendEmail } from "../../../notifications/controller/sendMail.js";
|
||||
import { settings } from "../../../../../database/schema/settings.js";
|
||||
import { getSettings } from "../../../server/controller/settings/getSettings.js";
|
||||
|
||||
export const updateUserADM = async (userData: User) => {
|
||||
/**
|
||||
* The user model will need to be passed over so we can update per the request on the user.
|
||||
* password, username, email.
|
||||
*/
|
||||
|
||||
createLog(
|
||||
"info",
|
||||
"apiAuthedRoute",
|
||||
"auth",
|
||||
`${userData.user_id} is being updated.`
|
||||
);
|
||||
// get the orignal user info
|
||||
const { data: user, error: userError } = await tryCatch(
|
||||
db.select().from(users).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (userError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
userError,
|
||||
};
|
||||
}
|
||||
if (user?.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
message:
|
||||
"The user you are looking for has either been deleted or dose not exist.",
|
||||
};
|
||||
}
|
||||
|
||||
//const { data: s, error: se } = await tryCatch(db.select().from(settings));
|
||||
const { data: s, error: se } = await tryCatch(getSettings());
|
||||
if (se) {
|
||||
return {
|
||||
success: false,
|
||||
message: `There was an error getting setting data to post to the server.`,
|
||||
data: se,
|
||||
};
|
||||
}
|
||||
|
||||
const set: any = s;
|
||||
const server = set.filter((n: any) => n.name === "server");
|
||||
const port = set.filter((n: any) => n.name === "serverPort");
|
||||
|
||||
const upd_user = user as User;
|
||||
const password: string = userData.password
|
||||
? await createPassword(userData.password!)
|
||||
: upd_user.password!;
|
||||
const data = {
|
||||
username: userData.username ? userData.username : upd_user?.username,
|
||||
password: password,
|
||||
email: userData.email ? userData.email : upd_user.email,
|
||||
role: userData.role ? userData.role : upd_user.role,
|
||||
};
|
||||
|
||||
// term ? ilike(posts.title, term) : undefined
|
||||
const { data: updData, error: updError } = await tryCatch(
|
||||
db.update(users).set(data).where(eq(users.user_id, userData.user_id!))
|
||||
);
|
||||
|
||||
if (updError) {
|
||||
return {
|
||||
success: false,
|
||||
message: "There was an error getting the user",
|
||||
updError,
|
||||
};
|
||||
}
|
||||
|
||||
if (userData?.password!.length > 0) {
|
||||
// send this user an email so they have the randomized password.
|
||||
await sendEmail({
|
||||
email: user[0]?.email,
|
||||
subject: "LST - Password reset.",
|
||||
template: "passwordReset",
|
||||
context: {
|
||||
password: userData.password!,
|
||||
username: user[0].username!,
|
||||
server: server[0].value,
|
||||
port: port[0].value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `${userData.username} has been updated.`,
|
||||
updData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
pass over a users uuid and return all modules they have permission too.
|
||||
in the login route we attach it to user under roles.
|
||||
*/
|
||||
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
|
||||
export const roleCheck = async (user_id: string | undefined) => {
|
||||
if (!user_id) {
|
||||
throw Error("Missing user_id");
|
||||
}
|
||||
let returnRoles: any = [];
|
||||
// get the user roles by the user_id
|
||||
returnRoles = await db
|
||||
.select({
|
||||
user_id: userRoles.user_id,
|
||||
role_id: userRoles.role_id,
|
||||
module_id: userRoles.module_id,
|
||||
role: userRoles.role,
|
||||
})
|
||||
.from(userRoles)
|
||||
.where(eq(userRoles.user_id, user_id));
|
||||
|
||||
if (returnRoles[0]?.role.includes("systemAdmin")) {
|
||||
const roles = await db
|
||||
.select({
|
||||
user_id: userRoles.user_id,
|
||||
role_id: userRoles.role_id,
|
||||
module_id: userRoles.module_id,
|
||||
role: userRoles.role,
|
||||
})
|
||||
.from(userRoles)
|
||||
.where(eq(userRoles.user_id, user_id));
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
return returnRoles;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import {users} from "../../../../../database/schema/users.js";
|
||||
import {eq} from "drizzle-orm";
|
||||
import {db} from "../../../../../database/dbclient.js";
|
||||
import {userRoles} from "../../../../../database/schema/userRoles.js";
|
||||
import {modules} from "../../../../../database/schema/modules.js";
|
||||
import {roles} from "../../../../../database/schema/roles.js";
|
||||
import {createLog} from "../../../logger/logger.js";
|
||||
|
||||
export const setSysAdmin = async (user: any, roleName: any): Promise<void> => {
|
||||
// remove all userRoles to prevent errors
|
||||
try {
|
||||
const remove = await db.delete(userRoles).where(eq(userRoles.user_id, user[0].user_id));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
// now we want to add the user to the system admin.
|
||||
const module = await db.select().from(modules);
|
||||
const role = await db.select().from(roles).where(eq(roles.name, roleName));
|
||||
|
||||
for (let i = 0; i < module.length; i++) {
|
||||
try {
|
||||
const userRole = await db.insert(userRoles).values({
|
||||
user_id: user[0].user_id,
|
||||
role_id: role[0].role_id,
|
||||
module_id: module[i].module_id,
|
||||
role: roleName,
|
||||
});
|
||||
createLog(
|
||||
"info",
|
||||
user[0].username,
|
||||
"auth",
|
||||
`${user[0].username} has been granted access to ${module[i].name} with the role ${roleName}`
|
||||
);
|
||||
} catch (error) {
|
||||
createLog("info", "lst", "auth", `Error settings user access: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
111
lstV2/server/services/auth/controllers/userRoles/setUserRoles.ts
Normal file
111
lstV2/server/services/auth/controllers/userRoles/setUserRoles.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
pass over a users uuid and return all modules they have permission too.
|
||||
in the login route we attach it to user under roles.
|
||||
*/
|
||||
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "../../../../../database/dbclient.js";
|
||||
import { userRoles } from "../../../../../database/schema/userRoles.js";
|
||||
import { users } from "../../../../../database/schema/users.js";
|
||||
import { modules } from "../../../../../database/schema/modules.js";
|
||||
import { roles } from "../../../../../database/schema/roles.js";
|
||||
import { setSysAdmin } from "./setSysAdmin.js";
|
||||
|
||||
export const setUserAccess = async (
|
||||
username: string,
|
||||
moduleName: string,
|
||||
roleName: string,
|
||||
override?: string
|
||||
) => {
|
||||
// get the user roles by the user_id
|
||||
const user = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username));
|
||||
const module = await db
|
||||
.select()
|
||||
.from(modules)
|
||||
.where(eq(modules.name, moduleName));
|
||||
|
||||
if (
|
||||
process.env.SECRETOVERRIDECODE != override &&
|
||||
roleName === "systemAdmin"
|
||||
) {
|
||||
return {
|
||||
success: false,
|
||||
message: "The override code provided is invalid.",
|
||||
};
|
||||
}
|
||||
|
||||
const role = await db.select().from(roles).where(eq(roles.name, roleName));
|
||||
|
||||
/**
|
||||
* For system admin we want to do a little more
|
||||
*/
|
||||
|
||||
if (roleName === "systemAdmin") {
|
||||
await setSysAdmin(user, roleName);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
|
||||
};
|
||||
}
|
||||
//console.log(user, module, role);
|
||||
|
||||
// set the user
|
||||
try {
|
||||
const userRole = await db
|
||||
.insert(userRoles)
|
||||
.values({
|
||||
user_id: user[0].user_id,
|
||||
role_id: role[0].role_id,
|
||||
module_id: module[0].module_id,
|
||||
role: roleName,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: userRoles.user_id,
|
||||
set: { role_id: role[0].role_id, role: roleName },
|
||||
});
|
||||
//.returning({user: users.username, email: users.email});
|
||||
|
||||
// return c.json({message: "User Registered", user}, 200);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} has been granted access to ${moduleName} with the role ${roleName}`,
|
||||
};
|
||||
} catch (error) {
|
||||
await changeRole(
|
||||
roleName,
|
||||
user[0].user_id,
|
||||
module[0].module_id,
|
||||
role[0].role_id
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
message: `${username} access on ${moduleName} has been changed to ${roleName}`,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const changeRole = async (
|
||||
role: any,
|
||||
userID: any,
|
||||
moduleID: any,
|
||||
roleID: any
|
||||
) => {
|
||||
await db
|
||||
.delete(userRoles)
|
||||
.where(
|
||||
and(
|
||||
eq(userRoles.user_id, userID),
|
||||
eq(userRoles.module_id, moduleID)
|
||||
)
|
||||
);
|
||||
|
||||
const userRole = await db.insert(userRoles).values({
|
||||
user_id: userID,
|
||||
role_id: roleID,
|
||||
module_id: moduleID,
|
||||
role: role,
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import {eq, sql} from "drizzle-orm";
|
||||
import {db} from "../../../../../database/dbclient.js";
|
||||
import {users} from "../../../../../database/schema/users.js";
|
||||
import {createLog} from "../../../logger/logger.js";
|
||||
import {createPassword} from "../../utils/createPassword.js";
|
||||
|
||||
const blacklistedTokens = new Set();
|
||||
|
||||
function blacklistToken(token: string) {
|
||||
blacklistedTokens.add(token);
|
||||
setTimeout(() => blacklistedTokens.delete(token), 3600 * 1000); // Remove after 1 hour
|
||||
}
|
||||
|
||||
function isTokenBlacklisted(token: string) {
|
||||
return blacklistedTokens.has(token);
|
||||
}
|
||||
|
||||
export const updateProfile = async (user: any, data: any, token: string) => {
|
||||
if (isTokenBlacklisted(token)) {
|
||||
createLog("warn", user.username, "auth", `${user.username} is trying to use a black listed token`);
|
||||
throw Error("This token was already used");
|
||||
}
|
||||
|
||||
//re salt and encrypt the password
|
||||
try {
|
||||
const saltPass = await createPassword(data.password);
|
||||
// update the password
|
||||
const profileUpdate = await db
|
||||
.update(users)
|
||||
.set({password: saltPass, upd_user: user.username, upd_date: sql`NOW()`})
|
||||
.where(eq(users.user_id, user.user_id));
|
||||
|
||||
blacklistToken(token);
|
||||
} catch (error) {
|
||||
createLog(
|
||||
"error",
|
||||
user.username,
|
||||
"auth",
|
||||
`Error: ${JSON.stringify(error)}, "There was an error updating the users profile"`
|
||||
);
|
||||
}
|
||||
};
|
||||
17
lstV2/server/services/auth/controllers/verifyToken.ts
Normal file
17
lstV2/server/services/auth/controllers/verifyToken.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import {sign, verify} from "jsonwebtoken";
|
||||
|
||||
/**
|
||||
* Verify a JWT and return the decoded payload.
|
||||
*/
|
||||
|
||||
const secret: string = process.env.JWT_SECRET! || "bnghsjhsd";
|
||||
const expiresIn: string = process.env.JWT_EXPIRES! || "1h";
|
||||
|
||||
export function verifyToken(token: string): {userId: number} {
|
||||
try {
|
||||
const payload = verify(token, secret) as {userId: number};
|
||||
return payload;
|
||||
} catch (err) {
|
||||
throw new Error("Invalid token");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user