| 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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 |
1×
1×
1×
1×
1×
14×
1×
3×
3×
3×
3×
3×
3×
3×
3×
3×
3×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
6×
6×
6×
6×
6×
8×
8×
4×
4×
6×
2×
2×
2×
4×
4×
4×
4×
4×
4×
4×
1×
1×
2×
2×
2×
2×
2×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
1×
| 'use strict'
const request = require('request')
// sub modules
const grant = require('./grant')
const jstoken = require('./token')
// when false, log output is suppressed
exports.showLog = false
// log to console
// @s string to log
function log(s) {
Iif (exports.showLog) {
console.log('grant (routes)> ', s)
}
}
// route for grant
exports.grant = function(req, res) {
const requester = req.user
// where is the data? depends on the Method
const data = req.method === "GET"?req.query:req.body
const grantee = data.grantee
const resource = data.resource
const readOnly = JSON.parse(data.readOnly)
grant.grantPermission(
requester,
grantee,
resource,
readOnly,
(err, success, message)=>{
let msg = message
Iif (err) {
success = false
msg = err
}
const r ={
operation: 'grant',
requester: requester,
grantee: grantee,
resource: resource,
readOnly: readOnly,
success: success,
msg: msg
}
res.jsonp(r)
}
)
}
// route for revoke
exports.revoke = function(req, res) {
const data = req.method === "GET"?req.query:req.body
const requester = req.user
const grantee = data.grantee
const resource = data.resource
const readOnly = JSON.parse(data.readOnly)
Iif (!requester) {
res.jsonp({success:false, msg: 'user is not authenticated' })
return
}
grant.revokePermission(requester,
grantee,
resource,
readOnly,
(err, success, message)=>{
let msg = message
Iif (err) {
success = false
msg = err
}
const r ={
operation: 'revoke',
requester: requester,
grantee: grantee,
resource: resource,
readOnly: readOnly,
success: success,
msg: msg
}
res.jsonp(r)
}
)
}
// this is middleware:
// - It decodes the token (sets req.decoded)
// - It sets req.user
// if authentication is succesful, it calls the next middleware
exports.authenticate = function(req, res, next) {
// debug authentication issues:
// console.log('authenticate headers:', req.headers)
// get token
let token = req.headers.authorization
Iif (!token) {
if (req.method == 'GET')
token = req.query.token
}
Iif (!token) {
res.status(401).jsonp('{"error":"No identity token provided"}')
// res.jsonp({success: false, error: "missing token"})
return
}
// decrypt and verify token
jstoken.verifyToken(token, (err, decoded) => {
Iif(err) {
if (err.message === "PEM_read_bio_PUBKEY failed"){
return res.status(500).jsonp({success:false,
error: 'public auth key is missing'})
}
res.status(401).jsonp({success:false, error: "invalid token: " + err})
return
}
Iif(!decoded.identities || decoded.identities.length === 0) {
res.status(401).jsonp({"success":false, "error":"token must contain identities"})
return
}
// success.
req.user = decoded.identities[0]
req.identities = decoded.identities
req.decoded = decoded
// debug: user has been authenticated
log(req.user,'authenticated')
next()
})
}
// This middelware sets req.userResources to contain all
// the resources available to a user. That user must be
// specified in req.user
exports.userResources = function(req, res, next) {
grant.readAllResourcesForUser(req.identities, (err, items) => {
Iif(err) {
return res.status(500).jsonp({
success: false,
"error": err
})
}
req.userResources = items
next()
})
}
// route that returns all shared resources for a user
// assumes that:
// req.user is set (authenticate middleware)
// req.userResources contains the resources for the user
exports.allResources = function(req, res) {
const user = req.user
const resources = req.userResources
const r = {
success: false,
operation: 'get resources for user',
requester: user,
}
Iif(!user) {
r.error = "Authentication missing"
return res.status(500).jsonp(r)
}
Iif(!resources) {
r.error = "Internal error: resources not specified"
return res.status(500).jsonp(r)
}
r.success = true
r.result = resources
res.jsonp(r)
}
// route to get a single resource with data and permissions
// assumes that req.user, req.resourceName and req.resourceData exist
exports.resource = function(req, res) {
const data = req.resourceData
const resourceName = req.resourceName
const user = req.user
const r = {
success: false,
operation: 'get resource',
requester: user,
resource: resourceName,
id: resourceName
}
Iif(!user) {
r.error = "Authentication missing"
return res.status(500).jsonp(r)
}
Iif(!resourceName) {
r.error = "resource not specified"
return res.status(500).jsonp(r)
}
Iif(!req.resourceData) {
r.error = "resource data not found in request"
return res.status(500).jsonp(r)
}
r.success = true
r.result = data
res.jsonp(r)
}
// This function returns a middleware function that checks wether a
// user has access to a resource.
// - resourceName is the name of the resource
// - readOnly specifies the access
exports.ownsResource = function(resource, readOnly) {
return function(req, res, next) {
log('ownsResource', resource, 'readOnly', readOnly)
let resourceName = resource
// check if the resource name is a route param
// see express route params
Iif (resourceName.startsWith(':')) {
const param = resourceName.substr(1)
// we are counting on a previous param middleware
// to have put the value in the req for us
resourceName = req[param]
}
// check all identities and proceed if any one of them is authorized
for (let i = 0; i < req.identities.length; ++i) {
const authorized = grant.isAuthorizedSync(
req.identities[i], resourceName, readOnly)
if (authorized) {
req.authorizedIdentity = req.identities[i]
break
}
}
if(!req.authorizedIdentity){
const msg = 'insufficient permission for user "' + req.user + '"'
+ ' to access resource "' + resourceName + '"'
log(msg)
return res.status(401).jsonp({
"success": false,
"error": msg
})
}
else {
// read the resource, keep a local copy in the req
grant.readResource(req.authorizedIdentity, resourceName, (err, data) => {
Iif(err) {
return res.status(500).jsonp({
"success": false,
"error": err
})
}
log('Authorized resource: ' + resourceName )
req.resourceData = data
req.resourceName = resourceName
req.resourcePermissions = data.permissions[0].permissions
next()
})
}
}
}
exports.setPermissionsRoutes = function(app) {
// grant user permission to a resource
// (add user to a group)
app.post('/permissions',
exports.authenticate,
exports.grant)
// revoke user permission
// (delete user from a group)
app.delete('/permissions',
exports.authenticate,
exports.revoke)
// get all user permissions for all resources
app.get('/permissions',
exports.authenticate,
exports.userResources,
exports.allResources
)
// get user permissions for a resource
app.get('/permissions/:resourceId',
exports.authenticate,
exports.ownsResource(':resourceId', true),
exports.resource
)
/// param for resource name
app.param('resourceId', function(req, res, next, id) {
req.resourceId = id
next()
})
}
// Middleware that serves an SVG badge that contains the number of
// open Pull Requests on a bitbucket repository
// @repository the bitbucket user and repo (ex: "osrf/cloudsim-portal")
exports.bitbucketBadgeOpenPrs = function (repository) {
const url = 'https://bitbucket.org/!api/2.0/repositories/'
+ repository + '/pullrequests'
return function (req, res) {
request(url, function (error, response, body) {
Iif (error) {
console.error(error)
return
}
if (response.statusCode != 200) {
console.error('error getting PRs, code:', response.statusCode)
res.status(response.statusCode).end(error)
return
}
const bitbucketData = JSON.parse(body)
const pullRequests = bitbucketData.size
let color = '#4c1'
Eif (pullRequests > 0)
color = '#dfb317'
const s = `<svg xmlns="http://www.w3.org/2000/svg" width="128" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="128" height="20" rx="3" fill="#fff"/></mask>
<g mask="url(#a)"><path fill="#555" d="M0 0h81v20H0z"/><path fill="${color}" d="M81 0h47v20H81z"/><path fill="url(#b)" d="M0 0h128v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="40.5" y="15" fill="#010101" fill-opacity=".3">pull requests</text>
<text x="40.5" y="14">pull requests</text>
<text x="103.5" y="15" fill="#010101" fill-opacity=".3">${pullRequests} open</text>
<text x="103.5" y="14">${pullRequests} open</text>
</g></svg>`
log('', pullRequests, 'open PRs for', repository)
// serve it as an svg document
res.setHeader('content-type', 'image/svg+xml;charset=utf-8')
res.end(s)
})
}
}
|