-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindex.js
150 lines (130 loc) · 3.53 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import express from "express";
import jwt from "express-jwt";
import { GRPC as Cerbos } from "@cerbos/grpc";
import db from "./db.js";
const cerbos = new Cerbos("localhost:3593", { tls: false });
const app = express();
const checkJwt = jwt({ secret: "yoursecret", algorithms: ["HS256"] });
// Extract data from the JWT (check DB etc) and create the principal object to be sent to Cerbos
const jwtToPrincipal = ({ sub, iat, roles = [], ...rest }) => {
return {
id: sub,
roles,
attributes: rest,
};
};
// READ
app.get("/contacts/:id", checkJwt, async (req, res) => {
// load the contact
const contact = db.findOne(req.params.id);
if (!contact) {
return res.status(404).json({ error: "Contact not found" });
}
// check user is authorized
const decision = await cerbos.checkResource({
principal: jwtToPrincipal(req.user),
resource: {
kind: "contact",
id: contact.id,
attributes: contact,
},
actions: ["read"],
});
// authorized for read action
if (decision.isAllowed("read")) {
return res.json(contact);
} else {
return res.status(403).json({ error: "Unauthorized" });
}
});
// CREATE
app.post("/contacts/new", checkJwt, async (req, res) => {
// check user is authorized
const decision = await cerbos.checkResource({
principal: jwtToPrincipal(req.user),
resource: {
kind: "contact",
id: "new",
},
actions: ["create"],
});
// authorized for create action
if (decision.isAllowed("create")) {
return res.json({ result: "Created contact" });
} else {
return res.status(403).json({ error: "Unauthorized" });
}
});
// UPDATE
app.patch("/contacts/:id", checkJwt, async (req, res) => {
const contact = db.findOne(req.params.id);
if (!contact) {
return res.status(404).json({ error: "Contact not found" });
}
const decision = await cerbos.checkResource({
principal: jwtToPrincipal(req.user),
resource: {
kind: "contact",
id: contact.id,
attributes: contact,
},
actions: ["update"],
});
if (allowed.isAllowed("update")) {
return res.json({
result: `Updated contact ${req.params.id}`,
});
} else {
return res.status(403).json({ error: "Unauthorized" });
}
});
// DELETE
app.delete("/contacts/:id", checkJwt, async (req, res) => {
const contact = db.findOne(req.params.id);
if (!contact) {
return res.status(404).json({ error: "Contact not found" });
}
const decision = await cerbos.checkResource({
principal: jwtToPrincipal(req.user),
resource: {
kind: "contact",
id: contact.id,
attributes: contact,
},
actions: ["delete"],
});
if (decision.isAllowed("delete")) {
return res.json({
result: `Contact ${req.params.id} deleted`,
});
} else {
return res.status(403).json({ error: "Unauthorized" });
}
});
// LIST
app.get("/contacts", checkJwt, async (req, res) => {
// load the contacts
const contacts = db.find(req.params.id);
// check user is authorized
const decision = await cerbos.checkResources({
principal: jwtToPrincipal(req.user),
resources: contacts.map((contact) => ({
resource: {
kind: "contact",
id: contact.id,
attributes: contact,
},
actions: ["list"],
})),
});
// filter only those authorised
const result = contacts.filter((c) =>
decision.isAllowed({
resource: { kind: "contact", id: c.id },
action: "list",
})
);
// return the contact
return res.json(result);
});
app.listen(3000, () => console.log("Listening on port 3000"));