This repository was archived by the owner on Feb 25, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 84
/
Copy pathLDAP.js
184 lines (156 loc) · 5.38 KB
/
LDAP.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
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
/**
* Module dependencies
*/
var async = require('async')
var ADStrategy = require('passport-adauth').Strategy
var LdapStrategy = require('passport-ldapauth').Strategy
var User = require('../models/User')
var Role = require('../models/Role')
/**
* Regexes for utility functions
*/
var domainDnRegex = /,?\s*((?:dc=[^,=\s][^,=]+[^,=\s],?\s*){2,})$/i
var dnPartRegex = /([a-z]+)=([^,\\ ][^,\\]*(?:\\.[^,\\]*)*)/gi
var badExtra = /[^,\s]/g
/**
* Extracts a lowercased domain from a distinguished name for comparison
* purposes.
*
* e.g. "CN=User,OU=MyBusiness,DC=example,DC=com" will return "example.com."
*/
function dnToDomain (dn) {
if (!dn || typeof dn !== 'string') { return null }
var matches = domainDnRegex.exec(dn)
if (matches) {
return matches[1].replace(dnPartRegex, '$1.').toLowerCase()
} else {
return null
}
}
/**
* Normalizes distinguished names, removing any extra whitespace, lowercasing
* the DN, and running some basic validation checks.
*
* If the DN is found to be malformed, will return null.
*/
function normalizeDN (dn) {
if (!dn || typeof dn !== 'string') { return null }
var normalizedDN = ''
// Take advantage of replace() to grab the matched segments of the DN. If what
// is left over contains unexpected characters, we assume the original DN was
// malformed.
var extra = dn.replace(dnPartRegex, function (match) {
normalizedDN += match.toLowerCase() + ','
return ''
})
if (!normalizedDN) { return null }
if (badExtra.test(extra)) { return null }
return normalizedDN.substr(0, normalizedDN.length - 1)
}
/**
* Verifier
*/
function verifier (provider, config) {
return function (req, user, done) {
user.id = (config.serverType || provider.serverType) === 'AD'
? user.objectGUID : user[provider.mapping.id]
User.connect(req, null, user, function (err, connectUser, info) {
if (err) { return done(err) }
if (connectUser && user._groups) {
// Put the distinguished names of the directory server groups the user is
// in into an array.
var rolesToAdd = user._groups.map(function (group) {
return group.dn
})
var rolesToRemove = []
// Create an object which maps normalized DNs to their original format
var ldapGroupDNs = {}
rolesToAdd.forEach(function (dn) {
ldapGroupDNs[normalizeDN(dn)] = dn
})
Role.listByUsers(connectUser, function (err, roles) {
if (err) { return done(err) }
var userDomain = dnToDomain(user.dn)
roles.forEach(function (role) {
if (role) {
var roleDN = normalizeDN(role.name)
// Only modify existing user roles if they are:
// * A valid distinguished name
// * In the same domain as the directory server
if (roleDN && dnToDomain(role.name) === userDomain) {
if (ldapGroupDNs[roleDN]) {
rolesToAdd.splice(rolesToAdd.indexOf(ldapGroupDNs[roleDN]), 1)
} else {
rolesToRemove.push(role.name)
}
}
}
})
// Does not create roles if they have not been defined yet in Connect.
// This is intentional. It allows Connect's roles to remain free of
// clutter, holding only the directory server groups that are needed as
// roles in Connect.
//
// Likewise, if a group is deleted or renamed in the directory server,
// it is up to the administrator to remove or update the related role in
// Connect.
async.parallel([
function (next) {
async.each(rolesToAdd, function (roleName, callback) {
Role.get(roleName, function (err, role) {
if (err) { return callback(err) }
if (!role) { return callback() }
User.addRoles(connectUser, roleName, function (err, result) {
if (err) { return callback(err) }
rolesToAdd.splice(rolesToAdd.indexOf(roleName), 1)
callback()
})
})
}, next)
},
function (next) {
async.each(rolesToRemove, function (roleName, callback) {
User.removeRoles(connectUser, roleName, function (err, result) {
if (err) { return callback(err) }
rolesToRemove.splice(rolesToRemove.indexOf(roleName), 1)
callback()
})
}, next)
}
], function (err) {
done(err, connectUser, info)
})
})
} else if (connectUser) {
done(err, connectUser, info)
} else {
done(null, null, info)
}
})
}
}
LdapStrategy.verifier = verifier
/**
* Initialize
*/
function initialize (provider, configuration) {
var serverType = configuration.serverType || provider.serverType || 'LDAP'
var strategy
if (serverType === 'AD') {
strategy = new ADStrategy(
{ server: configuration, passReqToCallback: true },
verifier(provider, configuration)
)
} else {
strategy = new LdapStrategy(
{ server: configuration, passReqToCallback: true },
verifier(provider, configuration)
)
}
return strategy
}
LdapStrategy.initialize = initialize
/**
* Exports
*/
module.exports = LdapStrategy