change username and password

main
Netcup Gituser 2023-12-05 23:13:29 +01:00
parent bad5c1ef24
commit 02d7c6a1d4
6 changed files with 193 additions and 25 deletions

View File

@ -1,16 +1,22 @@
import { Request, Response } from "express";
import bcrypt from "bcrypt";
import { User } from "../models/user";
import { saveSession } from "../utils/utils";
import {
saveSession,
hashPassword,
decodeBase64,
matchPassword,
} from "../utils/utils";
import { MONGODB_IGNORED_FIELDS } from "../utils/constants";
import { isUsernameValid, isPasswordValid } from "../validation/validation";
import { Session } from "../models/session";
export async function SignUp(req: Request, res: Response) {
if (!req.body.accountName || !req.body.username || !req.body.password) {
const { accountName, username, password } = req.body;
if (!accountName || !username || !password) {
return res.status(400).json({ status: "err" });
}
const { accountName, username, password } = req.body;
const existingUser = await User.findOne({ accountName })
.select("accountName -_id")
.lean();
@ -19,18 +25,12 @@ export async function SignUp(req: Request, res: Response) {
return res.status(400).json({ status: 1 });
}
const isBase64Password =
/^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/.test(
password
);
if (!isBase64Password) {
if (!isPasswordValid(password)) {
return res.status(400).json({ status: "err" });
}
const decodedPassword = Buffer.from(password, "base64").toString("utf-8");
const hashedPassword = await bcrypt.hash(decodedPassword, 10);
const decodedPassword = decodeBase64(password);
const hashedPassword = await hashPassword(decodedPassword);
const user = new User({
accountName: accountName,
@ -70,12 +70,8 @@ export async function Login(req: Request, res: Response) {
return res.status(401).json({ status: "err" });
}
const decodedPassword = Buffer.from(password, "base64").toString("utf-8");
const isPasswordValid = await bcrypt.compare(
decodedPassword,
user.password
);
const decodedPassword = decodeBase64(password);
const isPasswordValid = await matchPassword(decodedPassword, user.password);
if (!isPasswordValid) {
return res.status(401).json({ status: "err" });
@ -100,8 +96,6 @@ export async function GetUserProfile(req: Request, res: Response) {
return res.status(404).json({ status: "err" });
}
console.log("user:", user);
res.json({
accountName: user.accountName,
username: user.username,
@ -111,3 +105,75 @@ export async function GetUserProfile(req: Request, res: Response) {
res.status(500).json({ status: "err" });
}
}
export async function ChangeUsername(req: Request, res: Response) {
const { accountName, newUsername } = req.body;
if (!accountName || !newUsername) {
return res.status(400).json({ status: "err" });
}
if (!isUsernameValid(newUsername)) {
return res.status(400).json({ status: "err" });
}
try {
await User.updateOne(
{ accountName: accountName },
{ $set: { username: newUsername } }
);
res.status(200).json({ status: "ok" });
} catch (error) {
console.error(error);
res.status(500).json({ status: "err" });
}
}
export async function ChangePassword(req: Request, res: Response) {
const { accountName, currentPassword, newPassword } = req.body;
if (!accountName || !currentPassword || !newPassword) {
return res.status(400).json({ status: "err" });
}
try {
const decodedCurrentPassword = decodeBase64(currentPassword);
const existingUser = await User.findOne({ accountName })
.select("password -_id")
.lean();
if (!existingUser) {
return res.status(400).json({ status: 1 });
}
const decodedNewPassword = decodeBase64(newPassword);
if (!isPasswordValid(decodedCurrentPassword)) {
return res.status(400).json({ status: "err" });
}
const isPasswordMatching = await matchPassword(
decodedCurrentPassword,
existingUser.password
);
if (!isPasswordMatching || !isPasswordValid(decodedNewPassword)) {
return res.status(401).json({ status: "err" });
}
await User.updateOne(
{ accountName: accountName },
{ $set: { password: await hashPassword(decodedNewPassword) } }
);
res.status(200).json({ status: "ok" });
// delete all sessions
await Session.deleteMany({ accountName: accountName });
} catch (error) {
console.error(error);
res.status(500).json({ status: "err" });
}
}

View File

@ -1,5 +1,15 @@
import mongoose, { InferSchemaType, Schema } from "mongoose";
export interface IUser extends Document {
accountName: string;
username: string;
password: string;
followers: number;
following: number;
visited: number;
isAdmin: boolean;
}
export const userSchema = new Schema({
accountName: String,
username: String,
@ -24,4 +34,4 @@ export const userSchema = new Schema({
export type User = InferSchemaType<typeof userSchema>;
export const User = mongoose.model<User>("User", userSchema);
export const User = mongoose.model<IUser>("User", userSchema);

View File

@ -1,6 +1,7 @@
import express from "express";
import express, { Router } from "express";
const router = express.Router();
import * as userController from "../controllers/userController";
import { sessionProtection } from "../middleware/authMiddleware";
/**
* @swagger
@ -126,6 +127,15 @@ router.post("/signup", userController.SignUp);
*/
router.post("/login", userController.Login);
router.get("/profile/:accountName", userController.GetUserProfile);
router.get(
"/profile/:accountName",
sessionProtection,
userController.GetUserProfile
);
router.post("/username", sessionProtection, userController.ChangeUsername);
router.post("/password", sessionProtection, userController.ChangePassword);
//router.post("/logout", sessionProtection, userController.Logout);
export default router;

View File

@ -9,3 +9,16 @@ export const MONGODB_IGNORED_FIELDS: string = "-password -_id -__v";
// Header name for the session ID
export const HEADER_X_AUTHORIZATION: string = "x-authorization";
export const USERNAME_MIN_LENGTH: number = 3;
export const USERNAME_MAX_LENGTH: number = 20;
export const USERNAME_REGEX: RegExp = /^[a-zA-Z0-9_]+$/; // Alphanumeric and underscore
export const PASSWORD_MIN_LENGTH: number = 8;
export const PASSWORD_MAX_LENGTH: number = 64;
export const PASSWORD_REGEX: RegExp =
/^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/;
export const ACCOUNT_NAME_MIN_LENGTH: number = 3;
export const ACCOUNT_NAME_MAX_LENGTH: number = 20;
// alphanumerics, numbers, underscores, and dashes
export const ACCOUNT_NAME_REGEX: RegExp = /^[a-zA-Z0-9_-]+$/;

View File

@ -1,6 +1,7 @@
import crypto from "crypto";
import { Session } from "../models/session";
import { Response } from "express";
import bcrypt from "bcrypt";
export async function saveSession(res: Response, accountName: string) {
try {
@ -23,3 +24,15 @@ export async function saveSession(res: Response, accountName: string) {
res.status(500).json({ status: "err" });
}
}
export async function matchPassword(decodedPassword: string, password: string) {
return await bcrypt.compare(decodedPassword, password);
}
export async function hashPassword(password: string) {
return await bcrypt.hash(password, 10);
}
export function decodeBase64(value: string) {
return Buffer.from(value, "base64").toString("utf-8");
}

View File

@ -0,0 +1,56 @@
import {
USERNAME_MAX_LENGTH,
USERNAME_MIN_LENGTH,
USERNAME_REGEX,
PASSWORD_MAX_LENGTH,
PASSWORD_MIN_LENGTH,
PASSWORD_REGEX,
} from "../utils/constants";
export function isUsernameValid(username: string) {
if (!username) {
return false;
}
if (!USERNAME_REGEX.test(username)) {
return false;
}
if (username.length > USERNAME_MAX_LENGTH) {
return false;
}
if (username.length < USERNAME_MIN_LENGTH) {
return false;
}
return true;
}
export function isPasswordValid(password: string) {
if (!password) {
return false;
}
if (!PASSWORD_REGEX.test(password)) {
return false;
}
if (password.length > PASSWORD_MAX_LENGTH) {
return false;
}
if (password.length < PASSWORD_MIN_LENGTH) {
return false;
}
return true;
}
export function isAccountNameValid(accountName: string) {
if (!accountName) {
return false;
}
return true;
}