/
routeSecurity.js
101 lines (88 loc) · 3.36 KB
/
routeSecurity.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
'use strict'
const fileAuthorizers = require('./fileAuthorizers')
exports.getAuthorizers = getAuthorizers
exports.createAuthCheck = createAuthCheck
function getAuthorizers(securityDefinitions, options) {
return getDefinitionIds(securityDefinitions)
.map(id => ({ id, auth: getAuthorizer(id, securityDefinitions[id], options) }))
.reduce((authorizers, info) => authorizers.set(info.id, info.auth), new Map())
}
function getDefinitionIds(security) {
return Object.keys(security || {})
.filter(id => !id.startsWith('x-'))
}
function getAuthorizer(id, securityScheme, options) {
let authorizer
if (typeof options.authorizers.create === 'function') authorizer = options.authorizers.create(id, securityScheme)
if (authorizer) fileAuthorizers.disableAuthorizer(id, options)
else authorizer = requireAuthorizer(id, securityScheme, options)
if (authorizer && authorizer.default) authorizer = authorizer.default
return authorizer
}
function requireAuthorizer(id, securityScheme, options) {
const fileInfo = fileAuthorizers.enableAuthorizer(id, securityScheme, options)
try { return require(fileInfo.path) }
catch(e) {
if (e.code === 'MODULE_NOT_FOUND') return null
else throw e
}
}
function createAuthCheck(operation, authorizers) {
let security = operation.security || []
if (security && !Array.isArray(security)) security = [ security ]
if (!security.length) return null
const securityExt = operation['x-security']
return function authorize(req, res, next) {
return Promise.all(security.map(scheme => {
const id = Object.keys(scheme)[0]
if (!id || id.startsWith('x-')) return null
return new Promise((resolve, reject) => {
const authorizer = authorizers.get(id)
const ext = (securityExt && securityExt[id])
? securityExt[id]
: {}
if (typeof authorizer === 'function') {
const requiredScopes = scheme[id]
req.requiredScopes = requiredScopes
req.verifyScopes = function verifyScopes(scopes) {
return verifyRequiredScopes(requiredScopes, scopes, ext)
}
try {
const result = authorizer(req, res, err => err ? reject(err) : resolve())
if (result && result.catch) result.catch(e => reject(e))
} catch(e) {
reject(e)
}
} else {
// Unable to determine at this point if authorized but don't
// have correct scope(s), or if authorization is required.
// Assume the latter.
const e = new Error('Unauthorized')
e.statusCode = e.status = 401
reject(e)
}
})
}))
.then(() => next())
.catch(e => next(e))
}
}
function verifyRequiredScopes(requiredScopes, scopes, ext) {
if (!requiredScopes || !requiredScopes.length) return undefined
if (!scopes) scopes = []
let hasRequiredScopes = false
if (ext && ext.OR_scopes) {
// This extension allows for the logical OR of token scopes, so if one
// or more scopes exist, access is permitted.
hasRequiredScopes = requiredScopes.some(scope => scopes.indexOf(scope) !== -1)
} else {
hasRequiredScopes = requiredScopes.every(scope => scopes.indexOf(scope) !== -1)
}
if (!hasRequiredScopes) {
const e = new Error('Forbidden')
e.statusCode = e.status = 403
e.requiredScopes = requiredScopes
return e
}
return undefined
}