-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathindex.js
109 lines (89 loc) · 3.25 KB
/
index.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
const Promise = require('bluebird');
const rfc2253 = require('rfc2253');
const LdapAuth = require('ldapauth-fork');
const Cache = require('ldapauth-fork/lib/cache');
const md5 = require('md5');
// environment variable name to set ldap admin password
// Note: it will override the one in config file.
const LDAP_ADMIN_PASS_ENV = 'LDAP_ADMIN_PASS';
Promise.promisifyAll(LdapAuth.prototype);
function authenticatedUserGroups(user, groupNameAttribute) {
return [
user.cn,
// _groups or memberOf could be single els or arrays.
...user._groups ? [].concat(user._groups).map((group) => group[groupNameAttribute]) : [],
...user.memberOf ? [].concat(user.memberOf).map((groupDn) => rfc2253.parse(groupDn).get('CN')) : [],
];
}
function Auth(config, stuff) {
const self = Object.create(Auth.prototype);
self._users = {};
// config for this module
self._config = config;
// verdaccio logger
self._logger = stuff.logger;
// pass verdaccio logger to ldapauth
self._config.client_options.log = stuff.logger;
// always set ldapauth cache false
self._config.client_options.cache = false;
// TODO: Set more defaults
self._config.groupNameAttribute = self._config.groupNameAttribute || 'cn';
if (config.cache) {
const size = typeof config.cache.size === 'number' ? config.cache.size : 100;
const expire = typeof config.cache.expire === 'number' ? config.cache.expire : 300;
self._userCache = new Cache(size, expire, stuff.logger, 'user');
}
if (LDAP_ADMIN_PASS_ENV in process.env) {
self._config.client_options.adminPassword = process.env[LDAP_ADMIN_PASS_ENV];
}
return self;
}
module.exports = Auth;
//
// Attempt to authenticate user against LDAP backend
//
Auth.prototype.authenticate = function (username, password, callback) {
if (this._config.cache) {
const cached = this._userCache.get(username );
if (cached && cached.password && md5(password) === cached.password) {
if (cached.error) {
return callback(null, false);
}
const userGroups = authenticatedUserGroups(cached.user, this._config.groupNameAttribute);
userGroups.cacheHit = true;
return callback(null, userGroups);
}
}
// ldap client
const ldapClient = new LdapAuth(this._config.client_options);
let currentUser;
let currentError;
ldapClient.authenticateAsync(username, password)
.then((user) => {
if (!user) {
return [];
}
currentUser = user;
return authenticatedUserGroups(user, this._config.groupNameAttribute);
})
.catch((err) => {
currentError = err;
// 'No such user' is reported via error
this._logger.warn({ username, err }, `verdaccio-ldap error ${err}`);
return false; // indicates failure
})
.finally(() => {
if (this._config.cache) {
this._userCache.set(username, { password: md5(password), user: currentUser, error: currentError });
}
// This will do nothing with Node 10 (https://github.com/joyent/node-ldapjs/issues/483)
ldapClient.closeAsync()
.catch((err) => {
this._logger.warn({ err }, `verdaccio-ldap error on close ${err}`);
});
})
.asCallback(callback);
ldapClient.on('error', (err) => {
this._logger.warn({ err }, `verdaccio-ldap error ${err}`);
});
};