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' },
|
91 | defaultLanguage: 'en',
|
92 | HTMLExtraHead: '',
|
93 | checkMails: false,
|
94 | SMTPPort: undefined,
|
95 | SMTPHost: undefined,
|
96 | SMTPUser: undefined,
|
97 | SMTPPass: undefined,
|
98 | SMTPEmailFrom: undefined,
|
99 | SMTPSSL: false,
|
100 | SMTPTLS: true,
|
101 | tokenDuration: 60,
|
102 | useFirstLastNameInPads: false,
|
103 | insensitiveMailMatch: false,
|
104 | authMethod: 'internal',
|
105 | availableAuthMethods: [ 'internal', 'ldap', 'cas' ],
|
106 | authLdapSettings: {
|
107 | url: 'ldaps://ldap.example.org',
|
108 | bindDN: 'uid=ldap,ou=users,dc=example,dc=org',
|
109 | bindCredentials: 'S3cr3t',
|
110 | searchBase: 'ou=users,dc=example,dc=org',
|
111 | searchFilter: '(uid={{username}})',
|
112 | tlsOptions: {
|
113 | rejectUnauthorized: true
|
114 | },
|
115 | properties: {
|
116 | login: 'uid',
|
117 | email: 'mail',
|
118 | firstname: 'givenName',
|
119 | lastname: 'sn'
|
120 | },
|
121 | defaultLang: 'en'
|
122 | },
|
123 | authCasSettings: {
|
124 | serverUrl: 'https://cas.example.org/cas',
|
125 | protocolVersion: 3.0,
|
126 | properties: {
|
127 | login: 'login',
|
128 | email: 'email',
|
129 | firstname: 'firstname',
|
130 | lastname: 'lastname'
|
131 | },
|
132 | defaultLang: 'en'
|
133 | },
|
134 | allPadsPublicsAuthentifiedOnly: false,
|
135 | },
|
136 |
|
137 | |
138 |
|
139 |
|
140 |
|
141 |
|
142 | cache: {},
|
143 |
|
144 | |
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | init: function (callback) {
|
153 | callback = callback || function () {};
|
154 | if (!ld.isFunction(callback)) {
|
155 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
156 | }
|
157 |
|
158 | var confDefaults = ld.transform(configuration.DEFAULTS,
|
159 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
160 |
|
161 | var initFromDatabase = function () {
|
162 | storage.fn.getKeys(ld.keys(confDefaults), function (err, res) {
|
163 | if (err) { return callback(err); }
|
164 |
|
165 | var pushLdapSettingsToDB = false;
|
166 | configuration.cache = ld.transform(res, function (memo, val, key) {
|
167 | key = key.replace(DBPREFIX, '');
|
168 |
|
169 | |
170 |
|
171 | if (key === 'authLdapSettings' && ld.isEqual(val, configuration.DEFAULTS.authLdapSettings) &&
|
172 | settings.ep_mypads && settings.ep_mypads.ldap) {
|
173 | |
174 |
|
175 | val = settings.ep_mypads.ldap;
|
176 | pushLdapSettingsToDB = true;
|
177 | }
|
178 |
|
179 | if (ld.isUndefined(val)) {
|
180 | memo[key] = configuration.DEFAULTS[key];
|
181 | } else {
|
182 | memo[key] = val;
|
183 | }
|
184 | });
|
185 |
|
186 | if (!pushLdapSettingsToDB) {
|
187 | return callback(null);
|
188 | }
|
189 |
|
190 | |
191 |
|
192 | configuration.cache.authMethod = 'ldap';
|
193 |
|
194 | var kv = {
|
195 | authLdapSettings: configuration.cache.authLdapSettings,
|
196 | authMethod: 'ldap'
|
197 | };
|
198 | var dbKv = ld.transform(kv,
|
199 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
200 |
|
201 | storage.fn.setKeys(dbKv, callback);
|
202 | });
|
203 | };
|
204 |
|
205 |
|
206 |
|
207 | var force = {
|
208 | availableAuthMethods: configuration.DEFAULTS.availableAuthMethods,
|
209 | languages: configuration.DEFAULTS.languages
|
210 | };
|
211 | var dbForce = ld.transform(force,
|
212 | function (memo, val, key) { memo[DBPREFIX + key] = val; });
|
213 |
|
214 | storage.fn.setKeys(dbForce, function(err) {
|
215 | if (err) { return callback(err); }
|
216 |
|
217 | storage.fn.setKeysIfNotExists(confDefaults, function (err) {
|
218 | if (err) { return callback(err); }
|
219 | initFromDatabase();
|
220 | });
|
221 | });
|
222 | },
|
223 |
|
224 | |
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 | get: function (key) {
|
231 | if (!ld.isString(key)) {
|
232 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
233 | }
|
234 | return configuration.cache[key];
|
235 | },
|
236 |
|
237 | |
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | set: function (key, value, callback) {
|
250 | if (!ld.isString(key)) {
|
251 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
252 | }
|
253 | if (ld.isUndefined(value)) {
|
254 | throw new TypeError('BACKEND.ERROR.TYPE.VALUE_REQUIRED');
|
255 | }
|
256 | if (!ld.isFunction(callback)) {
|
257 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
258 | }
|
259 | var dbSet = function(key, value) {
|
260 | db.set(DBPREFIX + key, value, function (err) {
|
261 | if (err) { return callback(err); }
|
262 | configuration.cache[key] = value;
|
263 | callback();
|
264 | });
|
265 | };
|
266 | if (key === 'authLdapSettings' || key === 'authCasSettings') {
|
267 | delete value.attrs;
|
268 | }
|
269 |
|
270 | if (key === 'authLdapSettings') {
|
271 |
|
272 | var ldapErr = new Error('BACKEND.ERROR.CONFIGURATION.UNABLE_TO_BIND_TO_LDAP');
|
273 | var ldapSettings = ld.cloneDeep(value);
|
274 |
|
275 | |
276 |
|
277 | delete ldapSettings.bindDN;
|
278 | delete ldapSettings.bindCredentials;
|
279 |
|
280 | var client = ldap.createClient(ldapSettings);
|
281 | client.on('error', function (err) {
|
282 | console.error('LDAP settings change: error. See below for details.');
|
283 | console.error(err);
|
284 | return callback(ldapErr);
|
285 | });
|
286 | client.bind(value.bindDN, value.bindCredentials, function(err) {
|
287 | if (err) {
|
288 | console.error(err);
|
289 | return callback(ldapErr);
|
290 | }
|
291 | client.unbind(function(err) {
|
292 | client.destroy();
|
293 | if (err) {
|
294 | console.error(err);
|
295 | } else {
|
296 |
|
297 | dbSet(key, value);
|
298 | }
|
299 | });
|
300 | });
|
301 | } else {
|
302 | dbSet(key, value);
|
303 | }
|
304 | },
|
305 |
|
306 | |
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 | del: function (key, callback) {
|
317 | if (!ld.isString(key)) {
|
318 | throw new TypeError('BACKEND.ERROR.TYPE.KEY_STR');
|
319 | }
|
320 | if (!ld.isFunction(callback)) {
|
321 | throw new TypeError('BACKEND.ERROR.TYPE.CALLBACK_FN');
|
322 | }
|
323 | db.remove(DBPREFIX + key, function (err) {
|
324 | if (err) { return callback(err); }
|
325 | delete configuration.cache[key];
|
326 | callback();
|
327 | });
|
328 | },
|
329 |
|
330 | |
331 |
|
332 |
|
333 |
|
334 |
|
335 | all: function () { return configuration.cache; },
|
336 |
|
337 | |
338 |
|
339 |
|
340 |
|
341 |
|
342 | public: function () {
|
343 | var all = configuration.all();
|
344 | return ld.pick(all, 'title', 'passwordMin', 'passwordMax', 'languages',
|
345 | 'HTMLExtraHead', 'openRegistration', 'hideHelpBlocks', 'useFirstLastNameInPads',
|
346 | 'authMethod', 'authCasSettings', 'allPadsPublicsAuthentifiedOnly'
|
347 | );
|
348 | },
|
349 |
|
350 | |
351 |
|
352 |
|
353 |
|
354 |
|
355 | isNotInternalAuth: function() {
|
356 | return (configuration.get('authMethod') !== 'internal');
|
357 | }
|
358 | };
|
359 |
|
360 | return configuration;
|
361 |
|
362 | }).call(this);
|
363 |
|
364 |
|