UNPKG

3.44 kBJavaScriptView Raw
1'use strict'
2
3const fileAuthorizers = require('./fileAuthorizers')
4
5exports.getAuthorizers = getAuthorizers
6exports.createAuthCheck = createAuthCheck
7
8function getAuthorizers(securityDefinitions, options) {
9 return getDefinitionIds(securityDefinitions)
10 .map(id => ({ id, auth: getAuthorizer(id, securityDefinitions[id], options) }))
11 .reduce((authorizers, info) => authorizers.set(info.id, info.auth), new Map())
12}
13
14function getDefinitionIds(security) {
15 return Object.keys(security || {})
16 .filter(id => !id.startsWith('x-'))
17}
18
19function getAuthorizer(id, securityScheme, options) {
20 let authorizer
21 if (typeof options.authorizers.create === 'function') authorizer = options.authorizers.create(id, securityScheme)
22 if (authorizer) fileAuthorizers.disableAuthorizer(id, options)
23 else authorizer = requireAuthorizer(id, securityScheme, options)
24 if (authorizer && authorizer.default) authorizer = authorizer.default
25 return authorizer
26}
27
28function requireAuthorizer(id, securityScheme, options) {
29 const fileInfo = fileAuthorizers.enableAuthorizer(id, securityScheme, options)
30 try { return require(fileInfo.path) }
31 catch(e) {
32 if (e.code === 'MODULE_NOT_FOUND') return null
33 else throw e
34 }
35}
36
37function createAuthCheck(operation, authorizers) {
38 let security = operation.security || []
39 if (security && !Array.isArray(security)) security = [ security ]
40 if (!security.length) return null
41
42 const securityExt = operation['x-security']
43
44 return function authorize(req, res, next) {
45 return Promise.all(security.map(scheme => {
46 const id = Object.keys(scheme)[0]
47 if (!id || id.startsWith('x-')) return null
48
49 return new Promise((resolve, reject) => {
50 const authorizer = authorizers.get(id)
51 const ext = (securityExt && securityExt[id])
52 ? securityExt[id]
53 : {}
54
55 if (typeof authorizer === 'function') {
56 const requiredScopes = scheme[id]
57 req.requiredScopes = requiredScopes
58 req.verifyScopes = function verifyScopes(scopes) {
59 return verifyRequiredScopes(requiredScopes, scopes, ext)
60 }
61 try {
62 const result = authorizer(req, res, err => err ? reject(err) : resolve())
63 if (result && result.catch) result.catch(e => reject(e))
64 } catch(e) {
65 reject(e)
66 }
67 } else {
68 // Unable to determine at this point if authorized but don't
69 // have correct scope(s), or if authorization is required.
70 // Assume the latter.
71 const e = new Error('Unauthorized')
72 e.statusCode = e.status = 401
73 reject(e)
74 }
75 })
76 }))
77 .then(() => next())
78 .catch(e => next(e))
79 }
80}
81
82function verifyRequiredScopes(requiredScopes, scopes, ext) {
83 if (!requiredScopes || !requiredScopes.length) return undefined
84 if (!scopes) scopes = []
85
86 let hasRequiredScopes = false
87 if (ext && ext.OR_scopes) {
88 // This extension allows for the logical OR of token scopes, so if one
89 // or more scopes exist, access is permitted.
90 hasRequiredScopes = requiredScopes.some(scope => scopes.indexOf(scope) !== -1)
91 } else {
92 hasRequiredScopes = requiredScopes.every(scope => scopes.indexOf(scope) !== -1)
93 }
94 if (!hasRequiredScopes) {
95 const e = new Error('Forbidden')
96 e.statusCode = e.status = 403
97 e.requiredScopes = requiredScopes
98 return e
99 }
100 return undefined
101}