Skip to content

Commit

Permalink
Merge pull request #202 from Peppermint-Lab/next
Browse files Browse the repository at this point in the history
0.3.6
  • Loading branch information
potts99 authored Dec 4, 2023
2 parents 952cee0 + b6c349e commit 1520597
Show file tree
Hide file tree
Showing 65 changed files with 4,588 additions and 974 deletions.
5 changes: 5 additions & 0 deletions apps/api/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ services:
volumes:
- postgres_data:/var/lib/postgresql/data

mailhog:
image: jcalonso/mailhog
ports:
- 1025:1025 # smtp server
- 8025:8025 # web ui

volumes:
postgres_data:
2 changes: 2 additions & 0 deletions apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@types/bcrypt": "^5.0.0",
"@types/jsonwebtoken": "^8.5.8",
"@types/node": "^17.0.23",
"@types/nodemailer": "^6.4.14",
"@types/passport-local": "^1.0.35",
"prisma": "5.2.0",
"ts-node": "^10.7.0",
Expand All @@ -38,6 +39,7 @@
"imap": "^0.8.19",
"jsonwebtoken": "9.0.2",
"mailparser": "^3.6.5",
"nodemailer": "^6.9.7",
"posthog-node": "^3.1.3",
"prisma": "^5.6.0"
},
Expand Down
307 changes: 304 additions & 3 deletions apps/api/src/controllers/auth.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import axios from "axios";
import bcrypt from "bcrypt";
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import jwt from "jsonwebtoken";
import { track } from "../lib/hog";
import { checkToken } from "../lib/jwt";
import { forgotPassword } from "../lib/nodemailer/auth/forgot-password";
import { prisma } from "../prisma";

export function authRoutes(fastify: FastifyInstance) {
Expand Down Expand Up @@ -42,7 +45,7 @@ export function authRoutes(fastify: FastifyInstance) {
});
}

await prisma.user.create({
const user = await prisma.user.create({
data: {
email,
password: await bcrypt.hash(password, 10),
Expand All @@ -51,13 +54,116 @@ export function authRoutes(fastify: FastifyInstance) {
},
});

const hog = track();

hog.capture({
event: "user_registered",
distinctId: user.id,
});

reply.send({
success: true,
});
}
);

// User login route
// Forgot password & generate code
fastify.post(
"/api/v1/auth/password-reset",
async (request: FastifyRequest, reply: FastifyReply) => {
const { email, link } = request.body as { email: string; link: string };

let user = await prisma.user.findUnique({
where: { email },
});

if (!user) {
reply.code(401).send({
message: "Invalid email",
success: false,
});
}

function generateRandomCode() {
const min = 100000; // Minimum 6-digit number
const max = 999999; // Maximum 6-digit number
return Math.floor(Math.random() * (max - min + 1)) + min;
}

const code = generateRandomCode();

const uuid = await prisma.passwordResetToken.create({
data: {
userId: user!.id,
code: String(code),
},
});

forgotPassword(email, String(code), link, uuid.id);

reply.send({
success: true,
});
}
);

// Check code & uuid us valid
fastify.post(
"/api/v1/auth/password-reset/code",
async (request: FastifyRequest, reply: FastifyReply) => {
const { code, uuid } = request.body as { code: string; uuid: string };

const reset = await prisma.passwordResetToken.findUnique({
where: { code: code, id: uuid },
});

if (!reset) {
reply.code(401).send({
message: "Invalid Code",
success: false,
});
} else {
reply.send({
success: true,
});
}
}
);

// Reset users password via code
fastify.post(
"/api/v1/auth/password-reset/password",
async (request: FastifyRequest, reply: FastifyReply) => {
const { password, code } = request.body as {
password: string;
code: string;
};

const user = await prisma.passwordResetToken.findUnique({
where: { code: code },
});

if (!user) {
reply.code(401).send({
message: "Invalid Code",
success: false,
});
}

await prisma.user.update({
where: { id: user!.userId },
data: {
password: await bcrypt.hash(password, 10),
},
});

reply.send({
success: true,
});
}
);

// User password login route
fastify.post(
"/api/v1/auth/login",
{
Expand Down Expand Up @@ -137,6 +243,133 @@ export function authRoutes(fastify: FastifyInstance) {
}
);

// Checks if a user is SSO or password
fastify.post(
"/api/v1/auth/sso/check",
async (request: FastifyRequest, reply: FastifyReply) => {
let { email } = request.body as {
email: string;
};

let user = await prisma.user.findUnique({
where: { email },
});

if (!user) {
reply.code(401).send({
message: "Invalid email",
success: false,
});
}

const authtype = await prisma.config.findMany({
where: {
sso_active: true,
},
});

const provider = await prisma.provider.findMany();
const oauth = provider[0];

console.log(authtype);

if (authtype.length === 0) {
reply.code(200).send({
success: true,
message: "SSO not enabled",
oauth: false,
});
}

const url = "https://github.com/login/oauth/authorize";

reply.send({
oauth: true,
success: true,
ouath_url: `${url}?client_id=${oauth.clientId}&redirect_uri=${oauth.redirectUri}&state=${email}&login=${email}&scope=user`,
});
}
);

// SSO api callback route
fastify.get(
"/api/v1/auth/sso/login/callback",
async (request: FastifyRequest, reply: FastifyReply) => {
const { code, state } = request.query as { code: string; state: string };

const provider = await prisma.provider.findFirst({});

const data = await axios.post(
`https://github.com/login/oauth/access_token`,
{
client_id: provider?.clientId,
client_secret: provider?.clientSecret,
code: code,
redirect_uri: provider?.redirectUri,
},
{
headers: {
Accept: "application/json",
},
}
);

const access_token = data.data;

if (access_token) {
const gh = await axios.get(`https://api.github.com/user/emails`, {
headers: {
Accept: "application/vnd.github+json",
Authorization: `token ${access_token.access_token}`,
},
});

const emails = gh.data;

const filter = emails.filter((e: any) => e.primary === true);

let user = await prisma.user.findUnique({
where: { email: filter[0].email },
});

if (!user) {
reply.send({
success: false,
message: "Invalid email",
});
}

var b64string = process.env.SECRET;
var buf = new Buffer(b64string!, "base64"); // Ta-da

let token = jwt.sign(
{
data: { id: user!.id },
},
buf,
{ expiresIn: "8h" }
);

await prisma.session.create({
data: {
userId: user!.id,
sessionToken: token,
expires: new Date(Date.now() + 8 * 60 * 60 * 1000),
},
});

reply.send({
token,
success: true,
});
} else {
reply.status(403).send({
success: false,
});
}
}
);

// Delete a user
fastify.delete(
"/api/v1/auth/user/:id",
Expand Down Expand Up @@ -177,10 +410,12 @@ export function authRoutes(fastify: FastifyInstance) {

if (!user) {
reply.code(401).send({
message: "Invalid email or password",
message: "Invalid user",
});
}

const config = await prisma.config.findFirst();

const data = {
id: user!.id,
email: user!.email,
Expand All @@ -191,6 +426,7 @@ export function authRoutes(fastify: FastifyInstance) {
ticket_status_changed: user!.notify_ticket_status_changed,
ticket_comments: user!.notify_ticket_comments,
ticket_assigned: user!.notify_ticket_assigned,
sso_status: config!.sso_active,
};

reply.send({
Expand Down Expand Up @@ -282,6 +518,50 @@ export function authRoutes(fastify: FastifyInstance) {
}
);

// Update a users Email notification settings
fastify.put(
"/api/v1/auth/profile/notifcations/emails",
async (request: FastifyRequest, reply: FastifyReply) => {
const bearer = request.headers.authorization!.split(" ")[1];

//checks if token is valid and returns valid token
const token = checkToken(bearer);

if (token) {
let session = await prisma.session.findUnique({
where: {
sessionToken: bearer,
},
});

const {
notify_ticket_created,
notify_ticket_assigned,
notify_ticket_comments,
notify_ticket_status_changed,
} = request.body as any;

let user = await prisma.user.update({
where: { id: session?.userId },
data: {
notify_ticket_created: notify_ticket_created,
notify_ticket_assigned: notify_ticket_assigned,
notify_ticket_comments: notify_ticket_comments,
notify_ticket_status_changed: notify_ticket_status_changed,
},
});

reply.send({
user,
});
} else {
reply.send({
sucess: false,
});
}
}
);

// Logout a user (deletes session)
fastify.get(
"/api/v1/auth/user/:id/logout",
Expand All @@ -299,4 +579,25 @@ export function authRoutes(fastify: FastifyInstance) {
}
}
);

// Update a users role
fastify.put(
"/api/v1/auth/user/role",
async (request: FastifyRequest, reply: FastifyReply) => {
const bearer = request.headers.authorization!.split(" ")[1];
const token = checkToken(bearer);
if (token) {
const { id, role } = request.body as { id: string; role: boolean };

await prisma.user.update({
where: { id },
data: {
isAdmin: role,
},
});

reply.send({ success: true });
}
}
);
}
Loading

0 comments on commit 1520597

Please sign in to comment.