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 | var settings;
|
31 | try {
|
32 | settings = require('ep_etherpad-lite/node/utils/Settings');
|
33 | }
|
34 | catch (e) {
|
35 | if (process.env.TEST_LDAP) {
|
36 | settings = {
|
37 | 'ep_mypads': {
|
38 | 'ldap': {
|
39 | 'url': 'ldap://rroemhild-test-openldap',
|
40 | 'bindDN': 'cn=admin,dc=planetexpress,dc=com',
|
41 | 'bindCredentials': 'GoodNewsEveryone',
|
42 | 'searchBase': 'ou=people,dc=planetexpress,dc=com',
|
43 | 'searchFilter': '(uid={{username}})',
|
44 | 'properties': {
|
45 | 'login': 'uid',
|
46 | 'email': 'mail',
|
47 | 'firstname': 'givenName',
|
48 | 'lastname': 'sn'
|
49 | },
|
50 | 'defaultLang': 'fr'
|
51 | }
|
52 | }
|
53 | };
|
54 | } else {
|
55 | settings = {};
|
56 | }
|
57 | }
|
58 | module.exports = (function() {
|
59 | 'use strict';
|
60 |
|
61 |
|
62 | var ld = require('lodash');
|
63 | var ldap = require('ldapjs');
|
64 | var storage = require('./storage.js');
|
65 | var db = storage.db;
|
66 |
|
67 | var DBPREFIX = storage.DBPREFIX.CONF;
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 | var configuration = {
|
75 |
|
76 | |
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | DEFAULTS: {
|
83 | title: 'MyPads',
|
84 | rootUrl: '',
|
85 | allowEtherPads: true,
|
86 | openRegistration: true,
|
87 | hideHelpBlocks: false,
|
88 | passwordMin: 8,
|
89 | passwordMax: 30,
|
90 | languages: { en: 'English', fr: 'Français', de: 'Deutsch', es: 'Español', it: 'Italiano'},
|
91 | loginMsg: { en: '', fr: '', de: '', es: '', it: '' },
|
92 | defaultLanguage: 'en',
|
93 | HTMLExtraHead: '',
|
94 | checkMails: false,
|
95 | SMTPPort: undefined,
|
96 | SMTPHost: undefined,
|
97 | SMTPUser: undefined,
|
98 | SMTPPass: undefined,
|
99 | SMTPEmailFrom: undefined,
|
100 | SMTPSSL: false,
|
101 | SMTPTLS: true,
|
102 | tokenDuration: 60,
|
103 | useFirstLastNameInPads: false,
|
104 | insensitiveMailMatch: false,
|
105 | authMethod: 'internal',
|
106 | availableAuthMethods: [ 'internal', 'ldap', 'cas' ],
|
107 | authLdapSettings: {
|
108 | url: 'ldaps://ldap.example.org',
|
109 | bindDN: 'uid=ldap,ou=users,dc=example,dc=org',
|
110 | bindCredentials: 'S3cr3t',
|
111 | searchBase: 'ou=users,dc=example,dc=org',
|
112 | searchFilter: '(uid={{username}})',
|
113 | tlsOptions: {
|
114 | rejectUnauthorized: true
|
115 | },
|
116 | properties: {
|
117 | login: 'uid',
|
118 | email: 'mail',
|
119 | firstname: 'givenName',
|
120 | lastname: 'sn'
|
121 | },
|
122 | defaultLang: 'en'
|
123 | },
|
124 | authCasSettings: {
|
125 | serverUrl: 'https://cas.example.org/cas',
|
126 | protocolVersion: 3.0,
|
127 | properties: {
|
128 | login: 'login',
|
129 | email: 'email',
|
130 | firstname: 'firstname',
|
131 | lastname: 'lastname'
|
132 | },
|
133 | defaultLang: 'en'
|
134 | },
|
135 | allPadsPublicsAuthentifiedOnly: false,
|
136 | deleteJobQueue: false
|
137 | },
|
138 |
|
139 | |
140 |
|
141 |
|
142 |
|
143 |
|
144 | cache: {},
|
145 |
|
146 | |
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 | init: function (callback) {
|
155 | callback = callback || function () {};
|
156 | if (!ld.isFunction(callback)) {
|
157 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
158 | }
|
159 |
|
160 | var confDefaults = ld.transform(configuration.DEFAULTS,
|
161 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
162 |
|
163 | var initFromDatabase = function () {
|
164 | storage.fn.getKeys(ld.keys(confDefaults), function (err, res) {
|
165 | if (err) { return callback(err); }
|
166 |
|
167 | var pushLdapSettingsToDB = false;
|
168 | configuration.cache = ld.transform(res, function (memo, val, key) {
|
169 | key = key.replace(DBPREFIX, '');
|
170 |
|
171 | |
172 |
|
173 | if (key === 'authLdapSettings' && ld.isEqual(val, configuration.DEFAULTS.authLdapSettings) &&
|
174 | settings.ep_mypads && settings.ep_mypads.ldap) {
|
175 | |
176 |
|
177 | val = settings.ep_mypads.ldap;
|
178 | pushLdapSettingsToDB = true;
|
179 | }
|
180 |
|
181 | if (ld.isUndefined(val)) {
|
182 | memo[key] = configuration.DEFAULTS[key];
|
183 | } else {
|
184 | memo[key] = val;
|
185 | }
|
186 | });
|
187 |
|
188 | if (!pushLdapSettingsToDB) {
|
189 | return callback(null);
|
190 | }
|
191 |
|
192 | |
193 |
|
194 | configuration.cache.authMethod = 'ldap';
|
195 |
|
196 | var kv = {
|
197 | authLdapSettings: configuration.cache.authLdapSettings,
|
198 | authMethod: 'ldap'
|
199 | };
|
200 | var dbKv = ld.transform(kv,
|
201 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
202 |
|
203 | storage.fn.setKeys(dbKv, callback);
|
204 | });
|
205 | };
|
206 |
|
207 |
|
208 |
|
209 | var force = {
|
210 | availableAuthMethods: configuration.DEFAULTS.availableAuthMethods,
|
211 | languages: configuration.DEFAULTS.languages
|
212 | };
|
213 | var dbForce = ld.transform(force,
|
214 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
215 |
|
216 | storage.fn.setKeys(dbForce, function(err) {
|
217 | if (err) { return callback(err); }
|
218 |
|
219 | storage.fn.setKeysIfNotExists(confDefaults, function (err) {
|
220 | if (err) { return callback(err); }
|
221 | initFromDatabase();
|
222 | });
|
223 | });
|
224 | },
|
225 |
|
226 | |
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 | get: function (key) {
|
233 | if (!ld.isString(key)) {
|
234 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
235 | }
|
236 | return configuration.cache[key];
|
237 | },
|
238 |
|
239 | |
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 | set: function (key, value, callback) {
|
252 | if (!ld.isString(key)) {
|
253 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
254 | }
|
255 | if (ld.isUndefined(value)) {
|
256 | throw new TypeError('BACKEND.ERROR.TYPE.VALUE_REQUIRED');
|
257 | }
|
258 | if (!ld.isFunction(callback)) {
|
259 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
260 | }
|
261 | var dbSet = function(key, value) {
|
262 | db.set(DBPREFIX + key, value, function (err) {
|
263 | if (err) { return callback(err); }
|
264 | configuration.cache[key] = value;
|
265 | callback();
|
266 | });
|
267 | };
|
268 | if (key === 'authLdapSettings' || key === 'authCasSettings') {
|
269 | delete value.attrs;
|
270 | }
|
271 |
|
272 | if (key === 'authLdapSettings') {
|
273 |
|
274 | var ldapErr = new Error('BACKEND.ERROR.CONFIGURATION.UNABLE_TO_BIND_TO_LDAP');
|
275 | var ldapSettings = ld.cloneDeep(value);
|
276 |
|
277 | |
278 |
|
279 | delete ldapSettings.bindDN;
|
280 | delete ldapSettings.bindCredentials;
|
281 |
|
282 | var client = ldap.createClient(ldapSettings);
|
283 | client.on('error', function (err) {
|
284 | console.error('LDAP settings change: error. See below for details.');
|
285 | console.error(err);
|
286 | return callback(ldapErr);
|
287 | });
|
288 | client.bind(value.bindDN, value.bindCredentials, function(err) {
|
289 | if (err) {
|
290 | console.error(err);
|
291 | return callback(ldapErr);
|
292 | }
|
293 | client.unbind(function(err) {
|
294 | client.destroy();
|
295 | if (err) {
|
296 | console.error(err);
|
297 | } else {
|
298 |
|
299 | dbSet(key, value);
|
300 | }
|
301 | });
|
302 | });
|
303 | } else {
|
304 | dbSet(key, value);
|
305 | }
|
306 | },
|
307 |
|
308 | |
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 | del: function (key, callback) {
|
319 | if (!ld.isString(key)) {
|
320 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
321 | }
|
322 | if (!ld.isFunction(callback)) {
|
323 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
324 | }
|
325 | db.remove(DBPREFIX + key, function (err) {
|
326 | if (err) { return callback(err); }
|
327 | delete configuration.cache[key];
|
328 | callback();
|
329 | });
|
330 | },
|
331 |
|
332 | |
333 |
|
334 |
|
335 |
|
336 |
|
337 | all: function () { return configuration.cache; },
|
338 |
|
339 | |
340 |
|
341 |
|
342 |
|
343 |
|
344 | public: function () {
|
345 | var all = configuration.all();
|
346 | return ld.pick(all, 'title', 'passwordMin', 'passwordMax', 'languages',
|
347 | 'HTMLExtraHead', 'openRegistration', 'hideHelpBlocks', 'useFirstLastNameInPads',
|
348 | 'authMethod', 'authCasSettings', 'allPadsPublicsAuthentifiedOnly', 'defaultLanguage',
|
349 | 'loginMsg'
|
350 | );
|
351 | },
|
352 |
|
353 | |
354 |
|
355 |
|
356 |
|
357 |
|
358 | isNotInternalAuth: function() {
|
359 | return (configuration.get('authMethod') !== 'internal');
|
360 | }
|
361 | };
|
362 |
|
363 | return configuration;
|
364 |
|
365 | }).call(this);
|
366 |
|
367 |
|