• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

suculent / thinx-device-api / #252646970

27 Oct 2017 03:17PM UTC coverage: 12.466% (+1.3%) from 11.197%
#252646970

push

suculent
added support for displaying/exporting extended SigFox attributes

37 of 1808 branches covered (2.05%)

Branch coverage included in aggregate %.

735 of 4385 relevant lines covered (16.76%)

0.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.14
/lib/thinx/transfer.js
1
/** This THiNX-RTM API module is responsible for device transfer management. */
2

3
var Transfer = (function() {
1✔
4

5
        var app_config = require("../../conf/config.json");
1✔
6
        if (typeof(process.env.CIRCLE_USERNAME) !== "undefined") {
1!
7
                console.log("ยป Configuring for Circle CI...");
×
8
                app_config = require("../../conf/config-test.json");
×
9
        }
10
        var db = app_config.database_uri;
1✔
11

12
        var prefix = "";
1✔
13
        try {
1✔
14
                var pfx_path = app_config.project_root + '/conf/.thx_prefix';
1✔
15
                if (fs.existsSync(pfx_path)) {
1!
16
                        prefix = fs.readFileSync(pfx_path) + "_";
×
17
                }
18
        } catch (e) {
19
                //console.log(e);
20
        }
21

22
        var devicelib = require("nano")(db).use(prefix + "managed_devices");
1✔
23
        var userlib = require("nano")(db).use(prefix + "managed_users");
1✔
24
        var sha256 = require("sha256");
1✔
25
        var redis = require("redis");
1✔
26
        var client = redis.createClient();
1✔
27

28
        var fs = require("fs");
1✔
29
        var exec = require("child_process");
1✔
30
        var mkdirp = require("mkdirp");
1✔
31
        var Emailer = require("email").Email;
1✔
32

33
        var alog = require("./audit");
1✔
34
        var device = require("./device");
1✔
35
        var deploy = require("./deployment");
1✔
36

37
        var uuidV1 = require("uuid/v1");
1✔
38

39
        var Rollbar = require("rollbar");
1✔
40

41
        var rollbar = new Rollbar({
1✔
42
                accessToken: "5505bac5dc6c4542ba3bd947a150cb55",
43
                handleUncaughtExceptions: true,
44
                handleUnhandledRejections: true
45
        });
46

47
        // public
48
        var _public = {
1✔
49
                request: function(owner, body, callback) {
50

51
                        // body should look like { "to":"some@email.com", "udids" : [ "some-udid", "another-udid" ] }
52

53
                        // THX-396
54

55
                        // when true, sources will be COPIED to new owner as well
56
                        if (typeof(body.mig_sources) === "undefined") {
×
57
                                body.mig_sources = false;
×
58
                        }
59

60
                        // when true, API Keys will be TRANSFERRED:
61
                        // - MQTT combination udid/apikey does not change
62
                        // - API Keys are deleted from sender and added to recipient
63
                        if (typeof(body.mig_apikeys) === "undefined") {
×
64
                                body.mig_apikeys = false;
×
65
                        }
66

67
                        // Generic Check
68
                        if (typeof(body.to) === "undefined") {
×
69
                                callback(false, "missing_recipient");
×
70
                                return;
×
71
                        }
72

73
                        if (typeof(body.udids) === "undefined") {
×
74
                                callback(false, "missing_subject");
×
75
                                return;
×
76
                        }
77

78
                        var recipient_id = sha256(body.to);
×
79

80
                        function exit_on_transfer(udid, result_callback) {
81
                                client.get("dtr:" + udid, function(err, reply) {
×
82
                                        if (!err) {
×
83
                                                result_callback(false);
×
84
                                        }
85
                                });
86
                        }
87

88
                        var result = true;
×
89

90
                        function result_callback(status) {
91
                                result = status;
×
92
                        }
93

94
                        // check whether this device is not transferred already
95
                        for (var udid in body.udids) {
×
96
                                exit_on_transfer(udid, result_callback);
×
97
                        }
98

99
                        // When the loop above completes (we should give it a time...)
100
                        // `result` should stay true otherwise there's already
101
                        // transfer in progress.
102

103
                        if (result === false) {
×
104
                                callback(false, "transfer_already_in_progress");
×
105
                                return;
×
106
                        }
107

108
                        // gives 5 secs to process exit_on_transfer callbacks
109

110
                        setTimeout(function() {
×
111

112
                                userlib.get(recipient_id, function(err, recipient) {
×
113

114
                                        if (err) {
×
115
                                                callback(false, "recipient_unknown");
×
116
                                                console.log("Transfer target " + body.to + " id " + recipient_id +
×
117
                                                        "not found?");
118
                                                return;
×
119
                                        }
120

121
                                        // 2. add recipient to body as "from"
122
                                        body.from = recipient.email;
×
123

124
                                        // 2. add recipient to body as "from"
125

126
                                        // 3. store as "dt:uuid()" to redis
127
                                        var transfer_uuid = uuidV1(); // used for email
×
128
                                        var transfer_id = "dt:" + transfer_uuid;
×
129

130
                                        client.set(transfer_id, JSON.stringify(body), function(err, result) {
×
131

132
                                                if (err) {
×
133
                                                        console.log("Transfer redis save error: " + err);
×
134
                                                        callback(false, "transfer_ticket_save_error");
×
135
                                                        return;
×
136
                                                }
137

138
                                                // 4. respond with success/failure to the request
139
                                                callback(true, "transfer_requested");
×
140

141
                                                function store_pending_transfer(udid) {
142
                                                        client.set("dtr:" + udid, transfer_id);
×
143
                                                        client.expire("dtr:" + udid, transfer_id);
×
144
                                                }
145

146
                                                for (var udid in body.udids) {
×
147
                                                        store_pending_transfer(udid);
×
148
                                                }
149

150
                                                var htmlDeviceList = "<p><ul>";
×
151
                                                for (var dindex in body.udids) {
×
152
                                                        htmlDeviceList += "<li>" + body.udids[dindex] + "</li>";
×
153
                                                }
154
                                                htmlDeviceList += "</ul></p>";
×
155

156
                                                var plural = "";
×
157
                                                if (body.udids.length > 1) {
×
158
                                                        plural = "s";
×
159
                                                }
160

161
                                                // 5. send an e-mail to the recipient with request outline and links to accept
162
                                                var recipientTransferEmail = new Emailer({
×
163
                                                        bodyType: "html",
164
                                                        from: "THiNX <api@thinx.cloud>",
165
                                                        to: body.to,
166
                                                        subject: "Device transfer requested",
167
                                                        body: "<!DOCTYPE html><p>Hello " + body.to + ".</p>" +
168
                                                                "<p> User with e-mail " + body.from +
169
                                                                " is transferring following device" + plural + " to you:</p>" +
170
                                                                htmlDeviceList +
171
                                                                "<p>You may " +
172
                                                                "<a href='https://rtm.thinx.cloud:7443/api/transfer/accept?transfer_id=" +
173
                                                                transfer_uuid + "'>Accept</a> or" +
174
                                                                "<a href='https://rtm.thinx.cloud:7443/api/transfer/decline?transfer_id=" +
175
                                                                transfer_uuid + "'>Decline</a> this offer.</p>" +
176
                                                                "</html>"
177
                                                });
178

179
                                                console.log("Sending transfer e-mail to recipient: " + JSON.stringify(
×
180
                                                        recipientTransferEmail));
181

182
                                                recipientTransferEmail.send(function(err) {
×
183
                                                        if (err) {
×
184
                                                                console.log(err);
×
185
                                                                callback(false, err);
×
186
                                                        } else {
187
                                                                console.log("Recipient transfer e-mail sent.");
×
188
                                                                if (body.to == "cimrman@thinx.cloud") {
×
189
                                                                        callback(true, transfer_id);
×
190
                                                                } else {
191
                                                                        callback(true, "email_sent");
×
192
                                                                }
193
                                                        }
194
                                                });
195

196
                                                var senderTransferEmail = new Emailer({
×
197
                                                        bodyType: "html",
198
                                                        from: "THiNX <api@thinx.cloud>",
199
                                                        to: body.from,
200
                                                        subject: "Device transfer requested",
201
                                                        body: "<!DOCTYPE html><p>Hello " + body.to + ".</p>" +
202
                                                                "<p> You have requested to transfer following devices to " +
203
                                                                body.to +
204
                                                                ":</p>" +
205
                                                                htmlDeviceList +
206
                                                                "<p>You will be notified when your offer will be accepted or declined.</p>" +
207
                                                                "</html>"
208
                                                });
209

210
                                                console.log("Sending transfer e-mail to sender: " + JSON.stringify(
×
211
                                                        senderTransferEmail));
212

213
                                                senderTransferEmail.send(function(err) {
×
214
                                                        if (err) {
×
215
                                                                console.log(err);
×
216
                                                                callback(false, err);
×
217
                                                        } else {
218
                                                                console.log("Sender transfer e-mail sent.");
×
219
                                                                if (body.to == "cimrman@thinx.cloud") {
×
220
                                                                        callback(true, transfer_id);
×
221
                                                                } else {
222
                                                                        callback(true, "email_sent");
×
223
                                                                }
224
                                                        }
225
                                                });
226
                                        });
227
                                });
228
                        }, 5000);
229
                },
230

231
                accept: function(body, callback) {
232

233
                        // minimum body should look like { "transfer_id":"uuid" }
234
                        // optional body should look like { "transfer_id":"uuid", "udids" : [ ... ] }
235

236
                        if (typeof(body.transfer_id) === "undefined") {
×
237
                                callback(false, "missing_transfer_id");
×
238
                                return;
×
239
                        }
240

241
                        console.log("[transfer][accept] body: " + JSON.stringify(body));
×
242

243
                        /*
244
                        [Mon Jul 03 2017 21:34:48] [LOG]   [transfer][accept] body: {"transfer_id":"16ab44c0-602c-11e7-a6a1-79c54ef0916f","udids":[]}
245
[Mon Jul 03 2017 21:34:48] [LOG]   fethching dtid: dt:16ab44c0-602c-11e7-a6a1-79c54ef0916f
246

247
==> /root/.pm2/logs/index-error-0.log <==
248
TypeError: First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.
249
    at fromObject (buffer.js:280:9)
250
    at Function.Buffer.from (buffer.js:106:10)
251
    at new Buffer (buffer.js:85:17)
252
    at module.exports (/root/thinx-device-api/node_modules/sha256/lib/nodecrypto.js:14:12)
253
    at Command.callback (/root/thinx-device-api/lib/thinx/transfer.js:210:21)
254
    at normal_reply (/root/thinx-device-api/node_modules/redis/index.js:721:21)
255
    at RedisClient.return_reply (/root/thinx-device-api/node_modules/redis/index.js:819:9)
256
    at JavascriptRedisParser.returnReply (/root/thinx-device-api/node_modules/redis/index.js:192:18)
257
    at JavascriptRedisParser.execute (/root/thinx-device-api/node_modules/redis-parser/lib/parser.js:574:12)
258
    at Socket.<anonymous> (/root/thinx-device-api/node_modules/redis/index.js:274:27)
259

260
==> /root/.pm2/logs/index-out-0.log <==
261
[Mon Jul 03 2017 21:34:48] [LOG]   json_keys: "[object Object]"
262
[Mon Jul 03 2017 21:34:48] [LOG]   json_keys: "[object Object]"
263
[Mon Jul 03 2017 21:34:48] [LOG]   recipient_email: undefined
264

265

266
*/
267

268
                        var transfer_id = body.transfer_id;
×
269
                        var udids = null;
×
270

271
                        // Possibly partial transfer but we don't know until count
272
                        if (typeof(body.udids) !== "undefined") {
×
273
                                udids = body.udids;
×
274
                        }
275

276
                        var dtid = "dt:" + transfer_id;
×
277

278
                        console.log("fethching dtid: " + dtid);
×
279

280
                        client.get(dtid, function(err, json_keys) {
×
281

282
                                if (err) {
×
283
                                        console.log("[transfer] transfer_id not found.");
×
284
                                        callback(false, "transfer_id_invalid");
×
285
                                        return;
×
286
                                }
287

288
                                // TODO: Audit log
289
                                if (json_keys === null) {
×
290
                                        console.log("No udids remaining, expiring record...");
×
291
                                        client.expire(dtid, 0);
×
292
                                        callback(true, "transfer_completed");
×
293
                                        return;
×
294
                                }
295

296
                                console.log("json_keys: " + JSON.stringify(json_keys));
×
297

298
                                if ((typeof(body.list) !== "undefined") && body.list === true) {
×
299
                                        callback(true, json_keys);
×
300
                                        return;
×
301
                                }
302

303
                                if (udids.length === 0) {
×
304
                                        // perform on all devices if udids not given
305
                                        udids = json_keys.udids;
×
306
                                }
307

308
                                var expiry = 3600; // 1 hour to let user accept/decline different devices
×
309

310
                                var recipient_email = json_keys.to;
×
311

312
                                if (typeof(recipient_email) === "undefined") {
×
313
                                        callback(false, "recipient_to_must_be_set");
×
314
                                        return;
×
315
                                }
316

317
                                console.log("recipient_email: " + recipient_email);
×
318
                                var recipient = sha256(recipient_email);
×
319
                                var original_owner_email = json_keys.from;
×
320

321
                                if (typeof(recipient_email) === "undefined") {
×
322
                                        callback(false, "originator_from_must_be_set");
×
323
                                        return;
×
324
                                }
325

326
                                var original_owner = sha256(original_owner_email);
×
327

328
                                // Check if there are some devices left
329
                                if (json_keys.udids.length === 0) {
×
330
                                        client.expire(dtid, 0);
×
331
                                        for (var udid in udids) {
×
332
                                                client.expire("dtr:" + udid, 0);
×
333
                                        }
334
                                        callback(true, "transfer_completed");
×
335
                                        return;
×
336
                                }
337

338
                                console.log("Accept transfer " + transfer_id);
×
339

340
                                alog.log(original_owner, "Accepting device transfer: " +
×
341
                                        transfer_id +
342
                                        " for devices: " + JSON.stringify(udids));
343
                                alog.log(recipient, "Accepting device transfer: " + transfer_id +
×
344
                                        " for devices: " + JSON.stringify(udids));
345

346
                                console.log("[OID:" + recipient + "] [TRANSFER_ACCEPT] " + JSON.stringify(
×
347
                                        udids));
348

349
                                var responseCallback = function(status, message) {
×
350
                                        console.log("Transfer result - status:" + status +
×
351
                                                " message(stringified): " +
352
                                                JSON.stringify(message));
353
                                };
354

355
                                function rename(from, to) {
356
                                        fs.rename(original_path, destination_path, function(err) {
×
357
                                                if (err) throw err;
×
358
                                                console.log("Device builds artefacts transferred.");
×
359
                                        });
360
                                }
361

362
                                for (var dindex in udids) {
×
363
                                        var xudid = udids[dindex];
×
364
                                        device.edit(original_owner, {
×
365
                                                udid: xudid,
366
                                                owner: recipient
367
                                        }, responseCallback);
368
                                        delete json_keys.udids[xudid];
×
369

370
                                        // Move all data:
371
                                        var original_path = deploy.pathForDevice(original_owner, xudid);
×
372
                                        var destination_path = deploy.pathForDevice(recipient, xudid);
×
373
                                        rename(original_path, destination_path);
×
374

375
                                        // Move all repositories:
376
                                        if (body.mig_sources === true) {
×
377
                                                console.log(
×
378
                                                        "TODO #THX-396: Migrate sources for those devices from original owner to recipient!"
379
                                                );
380
                                                // TODO: * copy respective source and attach
381
                                        }
382

383
                                        // Move all repositories:
384
                                        if (body.api_keys === true) {
×
385
                                                console.log(
×
386
                                                        "TODO #THX-396: Migrate API Keys from original owner to recipient in Redis!"
387
                                                );
388
                                        }
389

390
                                }
391

392
                                // Store remaining (not accepted) keys
393
                                client.set(dtid, JSON.stringify(json_keys), function(err, response) {
×
394
                                        if (err) {
×
395
                                                callback(false, err);
×
396
                                        } else {
397
                                                if (json_keys.udids.length > 1) {
×
398
                                                        callback(true, "transfer_partially_completed");
×
399
                                                        client.expire(dtid, expiry);
×
400
                                                } else {
401
                                                        callback(true, "transfer_completed");
×
402
                                                        client.expire(dtid, 0);
×
403
                                                }
404
                                        }
405
                                });
406
                        });
407
                },
408

409
                decline: function(body, callback) {
410

411
                        // minimum body should look like { "transfer_id":"uuid" }
412
                        // optional body should look like { "transfer_id":"uuid", "udids" : [ ... ] }
413

414
                        if (typeof(body.transfer_id) === "undefined") {
×
415
                                callback(false, "missing_transfer_id");
×
416
                                return;
×
417
                        }
418

419
                        console.log("[transfer][decline] body: " + JSON.stringify(body));
×
420

421
                        var transfer_id = body.transfer_id;
×
422
                        var udids = null;
×
423

424
                        // Possibly partial transfer but we don't know until count
425
                        if (typeof(body.udids) !== "undefined") {
×
426
                                udids = body.udids;
×
427
                        }
428

429
                        var dtid = "dt:" + transfer_id;
×
430
                        client.get(dtid, function(err, json_keys) {
×
431

432
                                if (udids.length === 0) {
×
433
                                        // perform on all devices if udids not given
434
                                        udids = json_keys.udids;
×
435
                                }
436

437
                                var expiry = 3600; // 1 hour to let user accept/decline different devices
×
438

439
                                if (err) {
×
440
                                        console.log("[transfer] transfer_id not found.");
×
441
                                        callback(false, "transfer_id_invalid");
×
442
                                        return;
×
443
                                }
444

445
                                var recipient_email = json_keys.to;
×
446
                                var recipient = sha256(recipient_email);
×
447
                                var original_owner_email = json_keys.from;
×
448
                                var original_owner = sha256(original_owner_email);
×
449

450
                                // Check if there are some devices left
451
                                if (json_keys.udids.length === 0) {
×
452
                                        callback(true, "transfer_completed");
×
453
                                        client.expire(dtid, 0);
×
454
                                        return;
×
455
                                }
456

457
                                console.log("Decline transfer " + transfer_id);
×
458

459
                                alog.log(original_owner, "Declining device transfer: " +
×
460
                                        transfer_id +
461
                                        " for devices: " + JSON.stringify(udids));
462
                                alog.log(recipient, "Declining device transfer: " + transfer_id +
×
463
                                        " for devices: " + JSON.stringify(udids));
464

465
                                console.log("[OID:" + recipient + "] [TRANSFER_DECLINE] " + JSON
×
466
                                        .stringify(
467
                                                udids));
468

469
                                var responseCallback = function(status, message) {
×
470
                                        console.log("Transfer result - status:" + status +
×
471
                                                " message(stringified): " +
472
                                                JSON.stringify(message));
473
                                };
474

475
                                for (var dindex in udids) {
×
476
                                        var udid = udids[dindex];
×
477
                                        delete json_keys.udids[udid];
×
478
                                }
479

480
                                // Store remaining (not declined) keys
481
                                client.set(dtid, JSON.stringify(json_keys), function(err, response) {
×
482
                                        if (err) {
×
483
                                                callback(false, err);
×
484
                                        } else {
485
                                                if (json_keys.udids.length > 1) {
×
486
                                                        callback(true, "transfer_partially_completed");
×
487
                                                        client.expire(dtid, expiry);
×
488
                                                } else {
489
                                                        callback(true, "transfer_completed");
×
490
                                                        client.expire(dtid, 0);
×
491
                                                }
492

493
                                        }
494
                                });
495
                        });
496
                }
497

498
        };
499

500
        return _public;
1✔
501

502
})();
503

504
exports.request = Transfer.request;
1✔
505
exports.accept = Transfer.accept;
1✔
506
exports.decline = Transfer.decline;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc