-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
250 lines (210 loc) · 7.67 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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
const {
SME_EMAIL,
SME_PASSWORD,
OIDC_NAME,
OIDC_USER,
POD_URL,
POD_URL_TEST,
POD_WORKFLOW_URL,
POD_FINANCE_URL,
POD_ASSET_URL,
POD_BANK_URL,
POD_ACTIVITIES_URL,
ACTIVITY_INSTANCES_URL
} = require('./constants');
const fs = require('fs');
const path = require('path');
const { calculateJWKThumbprint } = require('./utils');
const express = require('express');
const { Session } = require('@inrupt/solid-client-authn-node');
const N3 = require('n3');
const { DataFactory } = N3;
const { namedNode} = DataFactory;
const { n3reasoner } = require('eyereasoner');
const jwt = require('jsonwebtoken');
const app = express();
const port = 3000;
const HttpMethod = {
GET: 0,
POST: 1,
PUT: 2,
DELETE: 3
}
// Middleware for authenticating the SME
const authenticateSME = async (req, res, next) => {
try {
const oidcIssuer = OIDC_USER;
const response = await fetch(oidcIssuer + 'idp/credentials/', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
email: SME_EMAIL,
password: SME_PASSWORD,
name: OIDC_NAME
})
});
const { id, secret } = await response.json();
// Authenticate using solid-client-authn-node
const smeSession = new Session()
await smeSession.login({
oidcIssuer: oidcIssuer,
clientId: id,
clientSecret: secret,
});
req.smeSession = smeSession;
req.authString = `${encodeURIComponent(id)}:${encodeURIComponent(secret)}`;
next();
} catch (error) {
console.error(`Error authenticating user: ${error.message}`);
res.status(401).send('Authentication failed');
}
};
const forwardRequestToPodAsSME = async (req, res, next, url) => {
try {
const { default: fetch } = await import('node-fetch');
const podUrl = POD_URL;
const method = req.method;
const podTestUrl = POD_URL_TEST
const requestOptions = {
method: method,
headers: {'content-type':req.headers['content-type']},
};
if (method === 'PUT' || method === 'POST') {
requestOptions.body = req.body
}
const pod_response = await req.smeSession.fetch(`${podUrl}${url}`, requestOptions);
if (method === 'GET') {
req.podResponse = await pod_response.text();
}
else {
req.podResponse = await pod_response;
}
next();
} catch (error) {
console.error(`Error forwarding PUT request to Solid Pod as SME: ${error.message}`);
console.error('Response from Solid Pod:', error.response);
res.status(500).send('Error forwarding PUT request to Solid Pod as SME');
}
};
async function hasAccess(req, res, webId, uri, method) {
// TODO for Apoorva
console.log("URI to check access", uri)
const solidPodActivity = await fetchSolidPodInfo(req, res, webId, POD_ACTIVITIES_URL);
// console.log(solidPodActivity)
const solidPodWorkflow = await fetchSolidPodInfo(req, res, webId, POD_WORKFLOW_URL);
// console.log(typeof (solidPodWorkflow))
const solidPodFinance = await fetchSolidPodInfo(req, res, webId, POD_FINANCE_URL);
// console.log(solidPodFinance)
const solidPodAsset = await fetchSolidPodInfo(req, res, webId, POD_ASSET_URL);
// console.log(solidPodAsset)
const activityInstances = await fetchSolidPodInfo(req, res, webId, ACTIVITY_INSTANCES_URL);
// console.log(activityInstances)
const activitiesInstancesRulesPath = path.join(__dirname, '/rules/n3_rules.n3');
const activitiesInstancesRules = fs.readFileSync(activitiesInstancesRulesPath, 'utf-8');
const activities_datastring = `${activityInstances}\n${activitiesInstancesRules}`;
// The result of the query (as a string)
const resultString = await n3reasoner(activities_datastring);
console.log(resultString)
return await checkSolidPodAccess(req, res, webId, uri, method);
//
// 1. then write the rules for sequencial activity
//
}
async function fetchSolidPodInfo(req, res, webId, podEndpoint) {
const sme_response = await req.smeSession.fetch(`${podEndpoint}`);
const rdfData = await sme_response.text();
// Parse RDF data
return rdfData
}
async function checkSolidPodAccess(req, res, webId, uri, method) {
const solidPodPolicies = await fetchSolidPodPolicies(req, res, webId);
console.log("solidPodPolicies ", solidPodPolicies)
// Check if the role CEO has policies for the specified method
const roleCEOPolicies = solidPodPolicies[webId];
// Check if the method exists and if the specified URI is allowed
if (roleCEOPolicies && (roleCEOPolicies[method].includes(uri))) {
return true; // Access granted
}
return false; // Access denied
}
async function fetchSolidPodPolicies(req, res, webId) {
const podEndpoint = POD_BANK_URL;
const sme_response = await req.smeSession.fetch(`${podEndpoint}`);
const rdfData = await sme_response.text();
// Parse RDF data
const solidPodPolicies = parseRdfDataForWebID(rdfData, webId);
return solidPodPolicies
}
async function parseRdfDataForWebID(rdfData, webId) {
const store = new N3.Store()
const parser = new N3.Parser();
try {
const parsedRdf = parser.parse(rdfData);
store.addQuads(parsedRdf)
} catch (err) {
console.error('Error parsing RDF data:', err);
return;
}
const accessPolicies = {};
for (const quad of store.getSubjects(namedNode('http://www.w3.org/ns/org#heldBy'),
namedNode(webId))){
const post = quad.id;
const role = store.getObjects(namedNode(post), namedNode('http://www.w3.org/ns/org#role'))[0].id
const accessQuads = store.getObjects(namedNode(role), namedNode('https://solid.ti.rw.fau.de/public/ns/frog#access'))
for (const accessQuad of accessQuads) {
const method = store.getObjects(accessQuad, namedNode('https://solid.ti.rw.fau.de/public/ns/frog#httpMethod'))[0].id
const uri = store.getObjects(accessQuad, namedNode('https://solid.ti.rw.fau.de/public/ns/frog#uri'))[0].id
const method_literal = method.slice(1, -1)
if (!accessPolicies[webId]) {
accessPolicies[webId] = {};
}
if (!accessPolicies[webId][method_literal]) {
accessPolicies[webId][method_literal] = [];
}
accessPolicies[webId][method_literal].push(uri);
}
}
return accessPolicies;
}
// Middleware for forwarding the PUT request to the Solid Pod authenticated as SME
// Set up middleware
app.use(express.text());
app.use(authenticateSME);
app.all('*', async (req, res, next) => {
const path = req.originalUrl;
console.log(req.method, "route from Postman:", path);
const webId = 'https://tom.solid.aifb.kit.edu/profile/card#me';
const uri = 'https://bank.solid.aifb.kit.edu/offer/1'
// const webId = 'https://apoorva.solid.aifb.kit.edu/profile/card#me';
const accessGranted = await hasAccess(req, res, webId, uri, req.method);
console.log('Access Granted:', accessGranted);
if (accessGranted) {
const accessToken = req.headers['authorization'].replace('DPoP ', '');
const dpopProofFromRequest = req.headers['dpop'];
const decodedDPoPProof = jwt.decode(dpopProofFromRequest, {complete: true});
const decodedAccessToken = jwt.decode(accessToken, {complete: true});
const thumbprint = calculateJWKThumbprint(decodedDPoPProof.header.jwk);
if (decodedAccessToken.payload.cnf.jkt === thumbprint) {
await forwardRequestToPodAsSME(req, res, next, path);
}
else {
res.status(401).json({ message: 'Invalid client for this access token' });
}
}
else {
res.status(403).json({ message: 'Access forbidden' });
}
}, (req, res) => {
const { podResponse } = req;
res.send(podResponse);
});
// Start the server
function main() {
const port = 3000; // Specify the port you want to listen on
app.listen(port, () => {
console.log(`Delegation proxy listening at http://localhost:${port}`);
});
}
module.exports = main;
// Call the main function
main();