change username and password
parent
bad5c1ef24
commit
02d7c6a1d4
|
@ -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" });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_-]+$/;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
Reference in New Issue