Skip to content

Commit

Permalink
feat: more role based access work
Browse files Browse the repository at this point in the history
  • Loading branch information
potts99 committed Nov 13, 2024
1 parent fd61fb5 commit a567edb
Show file tree
Hide file tree
Showing 11 changed files with 767 additions and 55 deletions.
224 changes: 224 additions & 0 deletions apps/api/src/controllers/roles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { track } from "../lib/hog";
import { requirePermission } from "../lib/roles";
import { checkSession } from "../lib/session";
import { prisma } from "../prisma";

export function roleRoutes(fastify: FastifyInstance) {
// Create a new role
fastify.post(
"/api/v1/role/create",
{
preHandler: requirePermission(['role::create']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const user = await checkSession(request);
const { name, description, permissions, isDefault }: any = request.body;

const existingRole = await prisma.role.findUnique({
where: { name },
});

if (existingRole) {
return reply.status(400).send({
message: "Role already exists",
success: false
});
}

await prisma.role.create({
data: {
name,
description,
permissions,
isDefault: isDefault || false,
},
});

const client = track();
client.capture({
event: "role_created",
distinctId: "uuid",
});
client.shutdownAsync();

reply.status(200).send({ message: "Role created!", success: true });
}
);

// Get all roles
fastify.get(
"/api/v1/roles/all",
{
preHandler: requirePermission(['role::read']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const roles = await prisma.role.findMany({
include: {
users: true,
},
});

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

// Get role by ID
fastify.get(
"/api/v1/role/:id",
{
preHandler: requirePermission(['role::read']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;

const role = await prisma.role.findUnique({
where: { id },
include: {
users: true,
},
});

if (!role) {
return reply.status(404).send({
message: "Role not found",
success: false
});
}

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

// Update role
fastify.put(
"/api/v1/role/:id/update",
{
preHandler: requirePermission(['role::update']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;
const { name, description, permissions, isDefault }: any = request.body;

try {
const updatedRole = await prisma.role.update({
where: { id },
data: {
name,
description,
permissions,
isDefault,
updatedAt: new Date(),
},
});

reply.status(200).send({ role: updatedRole, success: true });
} catch (error: any) {
if (error.code === 'P2025') {
return reply.status(404).send({
message: "Role not found",
success: false
});
}
throw error;
}
}
);

// Delete role
fastify.delete(
"/api/v1/role/:id/delete",
{
preHandler: requirePermission(['role::delete']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;

try {
await prisma.role.delete({
where: { id },
});

reply.status(200).send({ success: true });
} catch (error: any) {
if (error.code === 'P2025') {
return reply.status(404).send({
message: "Role not found",
success: false
});
}
throw error;
}
}
);

// Assign role to user
fastify.post(
"/api/v1/role/assign",
{
// preHandler: requirePermission(['role::assign']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { userId, roleId }: any = request.body;

try {
const updatedUser = await prisma.user.update({
where: { id: userId },
data: {
roles: {
connect: { id: roleId },
},
},
include: {
roles: true,
},
});

reply.status(200).send({ user: updatedUser, success: true });
} catch (error: any) {
if (error.code === 'P2025') {
return reply.status(404).send({
message: "User or Role not found",
success: false
});
}
throw error;
}
}
);

// Remove role from user
fastify.post(
"/api/v1/role/remove",
{
// preHandler: requirePermission(['role::remove']),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { userId, roleId }: any = request.body;

try {
const updatedUser = await prisma.user.update({
where: { id: userId },
data: {
roles: {
disconnect: { id: roleId },
},
},
include: {
roles: true,
},
});

reply.status(200).send({ user: updatedUser, success: true });
} catch (error: any) {
if (error.code === 'P2025') {
return reply.status(404).send({
message: "User or Role not found",
success: false
});
}
throw error;
}
}
);
}
6 changes: 3 additions & 3 deletions apps/api/src/controllers/webhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function webhookRoutes(fastify: FastifyInstance) {
fastify.post(
"/api/v1/webhook/create",
{
preHandler: requirePermission(['webhook:create']),
preHandler: requirePermission(["webhook::create"]),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const user = await checkSession(request);
Expand Down Expand Up @@ -42,7 +42,7 @@ export function webhookRoutes(fastify: FastifyInstance) {
fastify.get(
"/api/v1/webhooks/all",
{
preHandler: requirePermission(['webhook:read']),
preHandler: requirePermission(["webhook::read"]),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const webhooks = await prisma.webhooks.findMany({});
Expand All @@ -55,7 +55,7 @@ export function webhookRoutes(fastify: FastifyInstance) {
fastify.delete(
"/api/v1/admin/webhook/:id/delete",
{
preHandler: requirePermission(['webhook:delete']),
preHandler: requirePermission(["webhook::delete"]),
},
async (request: FastifyRequest, reply: FastifyReply) => {
const { id }: any = request.params;
Expand Down
9 changes: 8 additions & 1 deletion apps/api/src/lib/roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ export function hasPermission(
// Add permissions from default role if it exists
const defaultRole = user.roles.find((role) => role.isDefault);
if (defaultRole) {
defaultRole.permissions.forEach((perm) => userPermissions.add(perm as Permission));
defaultRole.permissions.forEach((perm) =>
userPermissions.add(perm as Permission)
);
}

// Add permissions from additional roles
Expand Down Expand Up @@ -80,6 +82,11 @@ export function requirePermission(
})
: null;

// Admins have all permissions
if (user?.isAdmin) {
next();
}

if (!userWithRoles) {
throw new Error("User not authenticated");
}
Expand Down
Loading

0 comments on commit a567edb

Please sign in to comment.