This repository has been archived by the owner on Aug 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 19
/
authentication.js
144 lines (102 loc) · 4.52 KB
/
authentication.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
'use strict'
const Hoek = require('@hapi/hoek')
const Boom = require('@hapi/boom')
async function loadUser (job) {
const { userId } = job
try {
const organizationId = await job.udaru.getUserOrganizationId(userId)
job.currentUser = await job.udaru.users.read({ id: userId, organizationId })
} catch (e) {
throw Boom.unauthorized('Bad credentials')
}
}
function canImpersonate (request, user) {
return user.organizationId === request.server.udaruConfig.get('authorization.superUser.organization.id')
}
async function impersonate (job) {
const { currentUser } = job
job.organizationId = currentUser.organizationId
if (canImpersonate(job.request, currentUser) && job.requestedOrganizationId) job.organizationId = job.requestedOrganizationId
}
async function checkAuthorization (udaru, userId, action, organizationId, resource, done) {
const result = await udaru.authorize.isUserAuthorized({ userId, action, organizationId, resource })
return result.access
}
async function buildResourcesForUser (udaru, builder, buildParams, organizationId) {
const resources = [builder(buildParams)]
try {
const user = await udaru.users.read({ id: buildParams.userId, organizationId: organizationId })
for (const team of user.teams) {
buildParams.teamId = team.id
resources.push(builder(buildParams))
}
return resources
} catch (err) {
if (err.output && err.output.statusCode === 404) return resources
throw err
}
}
function buildResources (options, udaru, authParams, request, organizationId) {
let resource = authParams.resource
if (resource) return [resource]
const resourceType = request.route.path.split('/')[2]
const resourceBuilder = request.server.udaruConfig.get('AuthConfig.resources')[resourceType]
if (!resourceBuilder) throw Boom.badImplementation('Resource builder not found')
const requestParams = authParams.getParams ? authParams.getParams(request) : {}
const buildParams = Object.assign({}, {organizationId}, requestParams)
if (resourceType === 'users' && buildParams.userId) return buildResourcesForUser(udaru, resourceBuilder, buildParams, organizationId)
return [resourceBuilder(buildParams)]
}
async function authorize (job) {
const resources = await buildResources(job.options, job.udaru, job.authParams, job.request, job.organizationId)
const action = job.authParams.action
const userId = job.currentUser.id
const organizationId = job.currentUser.organizationId
const valids = await Promise.all(resources.map(resource => checkAuthorization(job.udaru, userId, action, organizationId, resource)))
if (!valids.includes(true)) throw Boom.forbidden('Invalid credentials', 'udaru')
}
async function validate (options, server, request, userId, callback) {
const authParams = server.udaruAuthorization.getAuthParams(request)
const udaru = request.udaruCore
if (!authParams) throw Boom.forbidden('Invalid credentials', 'udaru')
const job = {udaru, options, userId, request, authParams, requestedOrganizationId: request.headers.org}
await loadUser(job)
await impersonate(job)
await authorize(job)
return {user: job.currentUser, organizationId: job.organizationId}
}
async function authenticate (server, settings, request) {
const req = request.raw.req
const authorization = req.headers.authorization
if (!authorization) throw Boom.unauthorized('Missing authorization', 'udaru')
const userId = String(authorization)
const credentials = await settings.validateFunc(settings, server, request, userId)
request.udaru = credentials
return {credentials: {scope: 'udaru'}}
}
module.exports = {
name: 'Udaru Authentication',
version: '0.0.1',
validate,
register (server, options) {
server.auth.scheme('udaru', function (server, options) {
Hoek.assert(options, 'Missing service auth strategy options')
if (typeof options.validateFunc === 'undefined' || options.validateFunc === null) options.validateFunc = validate
Hoek.assert(typeof options.validateFunc === 'function', 'options.validateFunc must be a valid function')
const settings = Hoek.clone(options)
const scheme = {
async authenticate (request, h) {
return h.authenticated(await authenticate(server, settings, request))
},
payload (request, h) {
if (server.udaruAuthorization.needTeamsValidation(request)) return request.server.udaruAuthorization.validateTeamsInPayload(server, request, h)
return h.continue
},
options: {
payload: true
}
}
return scheme
})
}
}