1 | 'use strict'
|
2 |
|
3 | const fileAuthorizers = require('./fileAuthorizers')
|
4 |
|
5 | exports.getAuthorizers = getAuthorizers
|
6 | exports.createAuthCheck = createAuthCheck
|
7 |
|
8 | function 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 |
|
14 | function getDefinitionIds(security) {
|
15 | return Object.keys(security || {})
|
16 | .filter(id => !id.startsWith('x-'))
|
17 | }
|
18 |
|
19 | function 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 |
|
28 | function 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 |
|
37 | function 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.verifyScopes = function verifyScopes(scopes) {
|
58 | return verifyRequiredScopes(requiredScopes, scopes, ext)
|
59 | }
|
60 | try {
|
61 | const result = authorizer(req, res, err => err ? reject(err) : resolve())
|
62 | if (result && result.catch) result.catch(e => reject(e))
|
63 | } catch(e) {
|
64 | reject(e)
|
65 | }
|
66 | } else {
|
67 |
|
68 |
|
69 |
|
70 | const e = new Error('Unauthorized')
|
71 | e.statusCode = e.status = 401
|
72 | reject(e)
|
73 | }
|
74 | })
|
75 | }))
|
76 | .then(() => next())
|
77 | .catch(e => next(e))
|
78 | }
|
79 | }
|
80 |
|
81 | function verifyRequiredScopes(requiredScopes, scopes, ext) {
|
82 | if (!requiredScopes || !requiredScopes.length) return undefined
|
83 | if (!scopes) scopes = []
|
84 |
|
85 | let hasRequiredScopes = false
|
86 | if (ext && ext.OR_scopes) {
|
87 |
|
88 |
|
89 | hasRequiredScopes = requiredScopes.some(scope => scopes.indexOf(scope) !== -1)
|
90 | } else {
|
91 | hasRequiredScopes = requiredScopes.every(scope => scopes.indexOf(scope) !== -1)
|
92 | }
|
93 | if (!hasRequiredScopes) {
|
94 | const e = new Error('Forbidden')
|
95 | e.statusCode = e.status = 403
|
96 | e.requiredScopes = requiredScopes
|
97 | return e
|
98 | }
|
99 | return undefined
|
100 | }
|