UNPKG

13.3 kBJavaScriptView Raw
1var util = require('util'),
2 https = require('https'),
3 url = require('url'),
4 crypto = require ('crypto'),
5 crack = require("crack"),
6 spawn = require('child_process').spawn;
7 task = require('./base');
8
9var exchangeConfig = project.config.consumerConfig.exchange;
10var wsdlUrl = exchangeConfig.wsdlUrl;
11var servicesUrl = exchangeConfig.servicesUrl;
12
13var exchange = module.exports = function (config) {
14 this.init (config);
15};
16
17util.inherits(exchange, task);
18
19util.extend(exchange.prototype, {
20 run: function () {
21 this.failed('use method [login|profile|check|searchUsers]');
22 },
23
24 login: function () {
25 var self = this,
26 login = self.credentials.login,
27 password = self.credentials.password;
28
29 //var auth = 'Basic ' + new Buffer(login + ":" + password).toString('base64');
30 var options = url.parse(wsdlUrl);
31
32 options.method = 'GET';
33
34 var processNtlmResponse = function(exit_code, err, data){
35 //console.log('data', data, exit_code);
36 switch (exit_code)
37 {
38 case 0:
39 console.log('login completed 200');
40 self.completed({
41 statusCode: 200,
42 err: '',
43 accessAllowed: true,
44 success: true
45 });
46 break;
47 default:
48 console.log('login completed 401');
49 self.completed({
50 statusCode: 401,
51 err: 'User not authorized',
52 accessAllowed: false,
53 success: false
54 });
55 break;
56 }
57 };
58
59 var processBasicResponse = function(response){
60 switch (response.statusCode)
61 {
62 case 200:
63 self.completed({
64 statusCode: 200,
65 err: '',
66 accessAllowed: true,
67 success: true
68 });
69 break;
70 default:
71 self.completed({
72 statusCode: 401,
73 err: 'User not authorized',
74 accessAllowed: false,
75 success: false
76 });
77 break;
78 }
79 response.destroy();
80 };
81
82
83 if (self.exchangeAuthType == 'ntlm') {
84 this.getNtlmRequest(
85 [
86 wsdlUrl,
87 '--ntlm',
88 '-f',
89 '-s',
90 '-S',
91 '--user',
92 login + ':' + password
93 ],
94 processNtlmResponse
95 );
96 } else {
97 options.auth = login + ":" + password;
98 var req = this.getBasicRequest(options, processBasicResponse);
99 req.on('error', function(e) {
100 console.error(e);
101 });
102 req.end();
103 }
104 },
105
106 getBasicRequest: function (options, responseCb) {
107 var req = https.request(options, responseCb);
108 return req;
109 },
110
111 getNtlmRequest: function (options, callback) {
112 //console.log('options', options);
113 var curl = spawn('curl',
114 options
115 /*[
116 'https://pochta.rian.ru/EWS/Exchange.asmx',
117 '-H',
118 'Content-Type: text/xml',
119 '--ntlm',
120 '-f',
121 '-s',
122 '-S',
123 '--user',
124 login + ':' + password,
125 '-d',
126 '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"><soap:Body><ResolveNames xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" ReturnFullContactData="true"><UnresolvedEntry>bakhcheev</UnresolvedEntry></ResolveNames></soap:Body></soap:Envelope>'
127 ]*/
128 ),
129 data = '',
130 err = null;
131
132 curl.stdout.on('data', function(chunk) {
133 data += chunk;
134 });
135
136 curl.stderr.on('data', function(err_msg) {
137 if (err === null) { err = ''; }
138 err += err_msg;
139 });
140
141 curl.on('error', function (err){console.error(err);});
142
143 curl.on('exit', function(exit_code) {
144 callback(exit_code, err, data);
145 });
146 },
147
148 encode: function (str) {
149 return new Buffer(str).toString('base64');
150 },
151
152 decode: function (str) {
153 return new Buffer(str, 'base64').toString('utf8');
154 },
155
156 profile: function() {
157 var self = this,
158 ldapRequest = self.ldapResponse,
159 sessionUID = self.sessionUID,
160 user = ldapRequest.data && ldapRequest.data.length && ldapRequest.data[0],
161 credentials = self.credentials;
162
163 if (user) {
164 if (Object.prototype.toString.call( user.memberof ) === '[object String]') {
165 // we came here from login
166 user.memberof = [ user.memberof ];
167 } else {
168 // we came here from ldap
169 user.memberof = user.memberof.map(function(item) {
170 return item.split(',')[0].split('=')[1];
171 });
172 }
173
174 var result = {
175 email: user.mail || user.email,
176 name: user.cn || user.name,
177 groupIds: user.memberof,
178 sessionUIDs: sessionUID,
179 authType: 'exchange',
180 tokens: {
181 login: credentials.login,
182 password: this.encode(credentials.password)
183 }
184 };
185
186 if (user.thumbnailphoto){
187 var shasum = crypto.createHash('sha1');
188 shasum.update(user.mail);
189 var filePath = '/images/avatars/'+shasum.digest('hex')+'.png';
190 var cacheFileStream = project.root.fileIO('htdocs'+filePath).writeStream({flags: 'w', mode: 0666});
191 cacheFileStream.write(new Buffer(user.thumbnailphoto, 'base64'));
192
193 result.avatar = filePath;
194 }
195 if (user.department){
196 result.department = user.department;
197 }
198 if (user.division){
199 result.division = user.division;
200 }
201
202 self.completed(result);
203 } else {
204 self.failed({
205 statusCode: 404,
206 msg: 'User Not Found!'
207 });
208 }
209
210 },
211
212 check: function() {
213 var self = this,
214 user = self.user;
215
216 if (user && user.authType == 'exchange' && user.tokens && user.tokens.login && user.tokens.password) {
217
218 self.credentials = {
219 login: user.tokens.login,
220 password: this.decode(user.tokens.password)
221 };
222
223 self.login();
224
225 } else {
226
227 self.completed({
228 accessAllowed: true
229 });
230
231 }
232 },
233
234 tmpl: function (str, obj) {
235 return str.replace(
236 /{\$(.+?)}/g,
237 function (_, key) { return obj[key]; }
238 );
239 },
240
241 prepareQuery: function () {
242 var queryTpl = [
243 '<?xml version="1.0" encoding="utf-8"?>',
244 '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">',
245 '<soap:Body>',
246 '<ResolveNames xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" ReturnFullContactData="true">',
247 '<UnresolvedEntry>{$filter}</UnresolvedEntry>',
248 '</ResolveNames>',
249 '</soap:Body>',
250 '</soap:Envelope>'
251 ].join(' ');
252
253
254 },
255
256 encodePassword: function () {
257 this.completed({
258 password: this.encode(this.plainPassword)
259 });
260 },
261
262 searchUsers: function () {
263 var self = this,
264 user = self.user;
265
266 if (user && user.authType == 'exchange' && user.tokens && user.tokens.login && user.tokens.password) {
267
268 var login = user.tokens.login,
269 password = this.decode(user.tokens.password);
270
271 var options = url.parse(servicesUrl);
272
273 var queryTpl = [
274 '<?xml version="1.0" encoding="utf-8"?>',
275 '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">',
276 '<soap:Body>',
277 '<ResolveNames xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" ReturnFullContactData="true">',
278 '<UnresolvedEntry>{$filter}</UnresolvedEntry>',
279 '</ResolveNames>',
280 '</soap:Body>',
281 '</soap:Envelope>'
282 ].join(' ');
283
284 var query = this.tmpl(queryTpl, this.pager);
285
286 options.auth = login + ':' + password;
287 options.port = 443;
288 options.headers = {
289 'Content-Type': 'text/xml'
290 };
291 options.method = 'POST';
292
293 var processNtlmResponse = function(exit_code, err, data){
294 //console.log('data', data, exit_code);
295
296 switch (exit_code)
297 {
298 case 0:
299 console.log('searchUsers completed 200');
300 // process data
301 processData(data);
302 break;
303 default:
304 console.log('searchUsers completed 401');
305 self.completed({
306 data: null,
307 total: 0,
308 success: false,
309 error: '401 - not authorized'
310 });
311 break;
312 }
313 };
314
315 var processData = function (exchangeXmlAnswer) {
316 var error = '';
317 var users = [];
318
319 var crackedResponse = crack(exchangeXmlAnswer);
320 var objResponse = crackedResponse.toJS();
321
322 if (objResponse.Body.Fault) {
323 console.log('!!!!!!!!!!!!!!! RESPONSE FAULT:', objResponse.Body.Fault.faultstring.__content);
324 self.failed({
325 statusCode: 500,
326 msg: 'Response Fault!'
327 });
328 return;
329 }
330
331 var objResolveNamesResponseMessage = objResponse.Body.ResolveNamesResponse.ResponseMessages.ResolveNamesResponseMessage;
332
333 if (objResolveNamesResponseMessage.ResponseClass != 'Success' && objResolveNamesResponseMessage.ResponseClass != 'Warning'){
334 console.log('!!!!!!!!!!!!!!! NOT SUCCESS!');
335 self.failed({
336 statusCode: 500,
337 msg: 'Response Not Success!'
338 });
339 error = 'Response Not Success!';
340 //return;
341 }
342
343 if (!error) {
344 var objResolutionSet = objResolveNamesResponseMessage.ResolutionSet;
345 if (Object.prototype.toString.call( objResolutionSet ) === '[object Array]') {
346 console.log('Found: ' + objResolutionSet.TotalItemsInView);
347 console.log('Query:', self.pager.filter);
348 }
349 if (Object.prototype.toString.call( objResolutionSet.Resolution ) === '[object Array]') {
350 objResolutionSet.Resolution.forEach(function (objResolution){
351 var objMailbox = objResolution.Mailbox;
352 var objContact = objResolution.Contact;
353 users.push({
354 name: objMailbox.Name,
355 authType: 'exchange',
356 avatar: '',
357 email: objMailbox.EmailAddress,
358 _id: objMailbox.EmailAddress,
359 text: objMailbox.Name,
360 memberof: objContact.Department
361 });
362 //console.log('Name: ' + objMailbox.Name);
363 //console.log('Email: ' + objMailbox.EmailAddress);
364 });
365 } else if (Object.prototype.toString.call( objResolutionSet.Resolution ) === '[object Object]') {
366 users.push({
367 name: objResolutionSet.Resolution.Mailbox.Name,
368 link: undefined,
369 authType: 'exchange',
370 avatar: '',
371 email: objResolutionSet.Resolution.Mailbox.EmailAddress,
372 _id: objResolutionSet.Resolution.Mailbox.EmailAddress,
373 text: objResolutionSet.Resolution.Mailbox.Name,
374 memberof: objResolutionSet.Resolution.Contact.Department
375 });
376 //console.log('Name: ' + objResolutionSet.Resolution.Mailbox.Name);
377 //console.log('Email: ' + objResolutionSet.Resolution.Mailbox.EmailAddress);
378 }
379 }
380
381 self.completed({
382 data: users || null,
383 total: users ? users.length : 0,
384 success: !error,
385 error: error
386 });
387 }
388
389 var processBasicResponse = function (response) {
390 var exchangeXmlAnswer = [];
391
392 response.setEncoding('utf-8');
393
394 response.on('data', function (data) {
395 exchangeXmlAnswer.push(data);
396 });
397
398 response.on('end', function () {
399
400
401 if (exchangeXmlAnswer.length) {
402 exchangeXmlAnswer = exchangeXmlAnswer.join('');
403
404 // process data
405 processData(exchangeXmlAnswer);
406 }
407 self.completed({
408 data: users || null,
409 total: users ? users.length : 0,
410 success: !error,
411 error: error
412 });
413 });
414 }
415
416 // RUN REQUEST!!!
417 if (self.exchangeAuthType=='ntlm') {
418 this.getNtlmRequest(
419 [
420 servicesUrl,
421 '-H',
422 'Content-Type: text/xml',
423 '--ntlm',
424 '-f',
425 '-s',
426 '-S',
427 '--user',
428 login + ':' + password,
429 '-d',
430 query
431 ],
432 processNtlmResponse
433 );
434 } else {
435 options.auth = login + ":" + password;
436 var req = this.getBasicRequest(options, processBasicResponse);
437 req.on('error', function(e) {
438 console.error(e);
439 });
440 req.write(query);
441 req.end();
442 }
443
444 } else {
445
446 self.completed({
447
448 });
449
450 }
451 }
452});