Completed
Push — master ( e4d488...d7a2df )
by Sander
30s
created

js/background/service/background.js (1 issue)

Labels
Severity
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    API.runtime.onInstalled.addListener(function () {
21
        var prot = (typeof browser !== 'undefined') ? 'moz-extension' : 'chrome-extension';
0 ignored issues
show
The variable browser seems to be never declared. If this is a global, consider adding a /** global: browser */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
22
        var url = prot+'://' + API.runtime.id + '/html/browser_action/browser_action.html';
23
        API.tabs.create({url: url});
24
    });
25
26
    var master_password = null;
27
28
    function getMasterPasswordSet() {
29
        return (master_password !== null);
30
    }
31
32
    _self.getMasterPasswordSet = getMasterPasswordSet;
33
34
    function setMasterPassword(opts) {
35
        master_password = opts.password;
36
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
37
            // Save the password in plain text on user request.
38
            // No secure local storage is available :/
39
            storage.set('master_password', opts.password);
40
        } else {
41
            storage.set('master_password', null);
42
        }
43
44
        if (opts.password) {
45
            getSettings();
46
        } else {
47
            displayLogoutIcons();
48
        }
49
50
    }
51
52
    _self.setMasterPassword = setMasterPassword;
53
54
55
    var testMasterPasswordAgainst;
56
57
    function isMasterPasswordValid(password) {
58
        try {
59
            PAPI.decryptString(testMasterPasswordAgainst, password);
60
            return true;
61
        } catch (e) {
62
            return false;
63
        }
64
    }
65
66
    _self.isMasterPasswordValid = isMasterPasswordValid;
67
68
69
    var local_credentials = [];
70
    var local_vault = [];
71
    var encryptedFieldSettings = ['accounts'];
72
    _self.settings = {};
73
    _self.ticker = null;
74
    _self.running = false;
75
    function getSettings() {
76
77
        storage.get('settings').then(function (_settings) {
78
            if ((!_settings || Object.keys(_settings).length === 0 || !_settings.hasOwnProperty('accounts')) && !master_password) {
79
                return;
80
            }
81
            if (!master_password && _settings.hasOwnProperty('accounts') && _settings.accounts.length > 0) {
82
                _self.settings.isInstalled = 1;
83
                testMasterPasswordAgainst = _settings.accounts;
84
                return;
85
            }
86
87
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
88
                var field = encryptedFieldSettings[i];
89
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
90
            }
91
92
            _self.settings = _settings;
93
94
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
95
                _self.settings.ignored_sites = [];
96
            }
97
98
            if (!_self.settings.hasOwnProperty('no_results_found_tab')) {
99
                _self.settings.no_results_found_tab = 'list';
100
            }
101
102
            if (!_self.settings.hasOwnProperty('enablePasswordPicker')) {
103
                _self.settings.enablePasswordPicker = !_self.settings.disablePasswordPicker;
104
            }
105
106
            if (!_self.settings.hasOwnProperty('enableAutoFill')) {
107
                _self.settings.enableAutoFill = !_self.settings.disableAutoFill;
108
            }
109
110
            if (!_self.settings.hasOwnProperty('enableUpdateUrl')) {
111
                _self.settings.enableUpdateUrl = true;
112
            }
113
            if (!_self.settings.hasOwnProperty('passwordPickerGotoList')) {
114
                _self.settings.passwordPickerGotoList = false;
115
            }
116
117
            getCredentials();
118
119
            if (_self.running) {
120
                clearInterval(_self.ticker);
121
            }
122
            _self.running = true;
123
            _self.ticker = setInterval(function () {
124
125
            }, _self.settings.refreshTime * 1000);
126
127
        });
128
    }
129
130
    _self.getSettings = getSettings;
131
132
    function getRuntimeSettings() {
133
        return _self.settings;
134
    }
135
136
    _self.getRuntimeSettings = getRuntimeSettings;
137
138
    function getSetting(name) {
139
        return _self.settings[name];
140
    }
141
142
    _self.getSetting = getSetting;
143
144
    function saveSettings(settings, cb) {
145
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
146
            var field = encryptedFieldSettings[i];
147
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
148
        }
149
150
        if (!settings.hasOwnProperty('ignored_sites')) {
151
            settings.ignored_sites = [];
152
        }
153
154
        if (!_self.settings.hasOwnProperty('password_picker_first_tab')) {
155
            _self.settings.disable_browser_autofill = 'list';
156
        }
157
158
        //window.settings contains the run-time settings
159
        _self.settings = settings;
160
161
162
        storage.set('settings', settings).then(function () {
163
            getSettings();
164
        });
165
166
    }
167
168
    _self.saveSettings = saveSettings;
169
170
    function resetSettings() {
171
        storage.set('settings', {});
172
        _self.settings = {};
173
    }
174
175
    _self.resetSettings = resetSettings;
176
177
178
    function getCredentials() {
179
        if (!master_password) {
180
            return;
181
        }
182
        //console.log('Loading vault with the following settings: ', settings);
183
        var tmpList = [];
184
185
        for (var i = 0; i < _self.settings.accounts.length; i++) {
186
            var account = _self.settings.accounts[i];
187
            /* jshint ignore:start */
188
            (function (inner_account) {
189
                PAPI.getVault(inner_account, function (vault) {
190
                    if (vault.hasOwnProperty('error')) {
191
                        return;
192
                    }
193
                    var _credentials = vault.credentials;
194
                    for (var i = 0; i < _credentials.length; i++) {
195
                        var key = inner_account.vault_password;
196
                        var credential = _credentials[i];
197
                        if (credential.hidden === 1) {
198
                            continue;
199
                        }
200
                        var usedKey = key;
201
                        //Shared credentials are not implemented yet
202
                        if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
203
                            usedKey = PAPI.decryptString(credential.shared_key, key);
204
205
                        }
206
                        credential = PAPI.decryptCredential(credential, usedKey);
207
                        credential.account = inner_account;
208
                        if (credential.delete_time === 0) {
209
                            tmpList.push(credential);
210
                        }
211
212
                    }
213
                    delete vault.credentials;
214
                    local_vault = vault;
215
                    local_credentials = tmpList;
216
217
                    getSharedCredentials(inner_account);
218
219
220
                });
221
            }(account));
222
            /* jshint ignore:end */
223
        }
224
    }
225
226
    _self.getCredentials = getCredentials;
227
228
    function getSharedCredentials(account) {
229
        PAPI.getCredendialsSharedWithUs(account, account.vault.guid, function (credentials) {
230
            for (var i = 0; i < credentials.length; i++) {
231
                var _shared_credential = credentials[i];
232
                var _shared_credential_data;
233
                var sharedKey = PAPI.decryptString(_shared_credential.shared_key, account.vault_password);
234
                try {
235
                    _shared_credential_data = PAPI.decryptSharedCredential(_shared_credential.credential_data, sharedKey);
236
                } catch (e) {
237
238
                }
239
                if (_shared_credential_data) {
240
                    delete _shared_credential.credential_data;
241
                    _shared_credential_data.acl = _shared_credential;
242
                    _shared_credential_data.acl.permissions = new SharingACL(_shared_credential_data.acl.permissions);
243
                    _shared_credential_data.tags_raw = _shared_credential_data.tags;
244
                    _shared_credential_data.account = account;
245
                    local_credentials.push(_shared_credential_data);
246
                }
247
            }
248
            updateTabsIcon();
249
        });
250
    }
251
252
    function getCredentialsByUrl(_url, sender) {
253
        if (!master_password) {
254
            return [];
255
        }
256
        if (!_url || _url === '') {
257
            return [];
258
        }
259
        if (Array.isArray(_url)) {
260
            _url = _url.pop();
261
        }
262
263
        var p = document.createElement('a');
264
        p.href = _url;
265
        if (p.pathname) {
266
            //_url = _url.substring(0, _url.lastIndexOf("/"));
267
        }
268
269
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
270
        var found_list = [];
271
        for (var i = 0; i < local_credentials.length; i++) {
272
            var credential_url = local_credentials[i].url;
273
            if (!/^(ht)tps?:\/\//i.test(credential_url) && credential_url !== '' && _url) {
274
                try {
275
                    var protocol = _url.split('://').shift();
276
                    credential_url = protocol + "://" + credential_url;
277
                } catch (e) {
278
                    //ignore
279
                }
280
            }
281
            credential_url = processURL(credential_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
282
            if (credential_url) {
283
                if (credential_url.split("\n").indexOf(url) !== -1) {
284
                    found_list.push(local_credentials[i]);
285
                }
286
            }
287
288
        }
289
        return found_list;
290
    }
291
292
    _self.getCredentialsByUrl = getCredentialsByUrl;
293
294
295
    function saveCredential(credential) {
296
        //@TODO save shared password
297
        if (!credential.credential_id) {
298
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
299
                local_credentials.push(createdCredential);
300
            });
301
        } else {
302
            var credential_index;
303
            for (var i = 0; i < local_credentials.length; i++) {
304
                if (local_credentials[i].guid === credential.guid) {
305
                    credential_index = i;
306
                    break;
307
                }
308
            }
309
310
            if (credential.hasOwnProperty('acl')) {
311
                var permissons = new SharingACL(credential.acl.permissions.permission);
312
                if (!permissons.hasPermission(0x02)) {
313
                    return;
314
                }
315
            }
316
317
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
318
                if (credential_index) {
319
                    local_credentials[credential_index] = updatedCredential;
320
                }
321
            });
322
        }
323
    }
324
325
    _self.saveCredential = saveCredential;
326
327
    function getCredentialByGuid(guid) {
328
        for (var i = 0; i < local_credentials.length; i++) {
329
            var credential = local_credentials[i];
330
            if (credential.guid === guid) {
331
                return credential;
332
            }
333
        }
334
    }
335
336
    _self.getCredentialByGuid = getCredentialByGuid;
337
338
    function getCredentialForHTTPAuth(req) {
339
        return getCredentialsByUrl(req.url)[0];
340
    }
341
342
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
343
344
    var mined_data = [];
345
346
    function minedForm(data, sender) {
347
        var url = sender.url;
348
        var existingLogins = getCredentialsByUrl(sender.url);
349
        var title = API.i18n.getMessage('detected_new_login') + ':';
350
        var minedMatchingID = null;
351
        for (var j = 0; j < existingLogins.length; j++) {
352
            var login = existingLogins[j];
353
            if (login.username === data.username) {
354
                if (login.password !== data.password) {
355
                    minedMatchingID = login.guid;
356
                    title = API.i18n.getMessage('detected_changed_login') + ':';
357
                }
358
                else {
359
                    //console.log('No changes detected');
360
                    delete mined_data[sender.tab.id];
361
                    return;
362
                }
363
            }
364
        }
365
        mined_data[sender.tab.id] = {
366
            title: title,
367
            url: url,
368
            username: data.username,
369
            password: data.password,
370
            label: sender.title,
371
            guid: minedMatchingID
372
        };
373
374
        //console.log('Done mining, ', mined_data, sender.tab.id);
375
    }
376
377
    _self.minedForm = minedForm;
378
379
    function getMinedData(args, sender) {
380
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
381
        var senderUrl = sender.tab.url;
382
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
383
        if (!_self.settings) {
384
            return null;
385
        }
386
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
387
            return mined_data[sender.tab.id];
388
        }
389
        var matches = _self.settings.ignored_sites.filter(function (item) {
390
            return typeof item === 'string' && site.indexOf(item) > -1;
391
        });
392
393
        if (matches.length !== 0) {
394
            return null;
395
        }
396
        return mined_data[sender.tab.id];
397
    }
398
399
    _self.getMinedData = getMinedData;
400
401
    function clearMined(args, sender) {
402
        delete mined_data[sender.tab.id];
403
    }
404
405
    _self.clearMined = clearMined;
406
407
    function saveMinedCallback(args) {
408
        createIconForTab(args.sender.tab);
409
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
410
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
411
            });
412
        });
413
    }
414
415
    function ignoreSite(_url) {
416
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
417
            _self.settings.ignored_sites = [];
418
        }
419
        var site = processURL(_url, false, false, true, false);
420
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
421
            _self.settings.ignored_sites.push(site);
422
            saveSettings(_self.settings);
423
        }
424
        clearMined();
425
    }
426
427
    _self.ignoreSite = ignoreSite;
428
429
    function ignoreURL(url) {
430
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
431
            _self.settings.ignored_sites = [];
432
        }
433
        if (_self.settings.ignored_sites.indexOf(url) === -1) {
434
            _self.settings.ignored_sites.push(url);
435
            saveSettings(_self.settings);
436
        }
437
    }
438
439
    _self.ignoreURL = ignoreURL;
440
441
    function passToParent(args, sender) {
442
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
443
        });
444
    }
445
446
    _self.passToParent = passToParent;
447
448
    function getActiveTab(opt) {
449
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
450
            var tab = tabs[0];
451
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
452
            });
453
        });
454
    }
455
456
    _self.getActiveTab = getActiveTab;
457
458
    function updateCredentialUrlDoorhanger(login) {
459
        if(!_self.settings.enableUpdateUrl){
460
            return;
461
        }
462
463
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
464
            var tab = tabs[0];
465
            var data = login;
466
            data.url = tab.url;
467
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
468
            API.tabs.sendMessage(tab.id, {
469
                method: 'showUrlUpdateDoorhanger',
470
                args: {data: data}
471
            });
472
        });
473
    }
474
475
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
476
477
    function updateCredentialUrl(data, sender) {
478
        mined_data[sender.tab.id] = data;
479
        saveMined({}, sender);
480
481
    }
482
483
    _self.updateCredentialUrl = updateCredentialUrl;
484
485
    function saveMined(args, sender) {
486
        var data = mined_data[sender.tab.id];
487
        var credential = {},
488
            credential_index;
489
490
        if (data.guid === null) {
491
            credential = PAPI.newCredential();
492
        } else {
493
            for (var i = 0; i < local_credentials.length; i++) {
494
                if (local_credentials[i].guid === data.guid) {
495
                    credential = local_credentials[i];
496
                    credential_index = i;
497
                    break;
498
                }
499
            }
500
        }
501
        if (!credential.hasOwnProperty('account')) {
502
            credential.account = args.account;
503
        }
504
        credential.username = data.username;
505
        credential.password = data.password;
506
        credential.url = sender.tab.url;
507
        if (credential.guid !== null) {
508
            PAPI.updateCredential(credential.account, credential, credential.account.vault_password, function (updatedCredential) {
509
                updatedCredential.account = credential.account;
510
                if (credential_index) {
511
                    local_credentials[credential_index] = updatedCredential;
512
                }
513
                saveMinedCallback({credential: credential, updated: true, sender: sender});
514
                delete mined_data[sender.tab.id];
515
            });
516
        } else {
517
            credential.label = sender.tab.title;
518
            credential.vault_id = credential.account.vault.vault_id;
519
            PAPI.createCredential(credential.account, credential, credential.account.vault_password, function (createdCredential) {
520
                createdCredential.account = args.account;
521
                saveMinedCallback({credential: credential, updated: false, sender: sender});
522
                local_credentials.push(createdCredential);
523
                delete mined_data[sender.tab.id];
524
            });
525
        }
526
    }
527
528
    _self.saveMined = saveMined;
529
530
    function searchCredential(searchText) {
531
        searchText = searchText.toLowerCase();
532
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
533
        var results = [];
534
        for (var i = 0; i < local_credentials.length; i++) {
535
            var credential = local_credentials[i];
536
            for (var f = 0; f < searchFields.length; f++) {
537
                var field = searchFields[f];
538
                if (!credential[field]) {
539
                    continue;
540
                }
541
542
                var field_value = credential[field].toLowerCase();
543
                if (field_value.indexOf(searchText) !== -1) {
544
                    results.push(credential);
545
                    break;
546
                }
547
            }
548
        }
549
        return results;
550
    }
551
552
    _self.searchCredential = searchCredential;
553
554
555
    function injectCreateCredential(args, sender) {
556
        var account = getRuntimeSettings().accounts[parseInt(args.vaultIndex)];
557
        var credential = PAPI.newCredential();
558
        credential.label = args.label;
559
        credential.username = args.username;
560
        credential.password = args.password;
561
        credential.url = sender.tab.url;
562
        credential.vault_id = account.vault.vault_id;
563
        PAPI.createCredential(account, credential, account.vault_password, function (createdCredential) {
564
            credential.account = account;
565
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
566
            local_credentials.push(createdCredential);
567
568
        });
569
    }
570
571
    self.injectCreateCredential = injectCreateCredential;
572
573
    function isVaultKeySet() {
574
        return (_self.settings.vault_password !== null);
575
    }
576
577
    _self.isVaultKeySet = isVaultKeySet;
578
579
    function isAutoFillEnabled() {
580
        if (!_self.settings.hasOwnProperty('enableAutoFill')) {
581
            return true;
582
        }
583
        return _self.settings.enableAutoFill;
584
    }
585
586
    _self.isAutoFillEnabled = isAutoFillEnabled;
587
588
    function isAutoSubmitEnabled() {
589
        if (!_self.settings.hasOwnProperty('enableAutoSubmit')) {
590
            return false;
591
        }
592
        return _self.settings.enableAutoSubmit;
593
    }
594
595
    _self.isAutoSubmitEnabled = isAutoSubmitEnabled;
596
597
    var doorhangerData = null;
598
599
    function setDoorhangerData(data) {
600
        doorhangerData = data;
601
    }
602
603
    _self.setDoorhangerData = setDoorhangerData;
604
605
    function getDoorhangerData() {
606
        return doorhangerData;
607
    }
608
609
    _self.getDoorhangerData = getDoorhangerData;
610
611
    function closeSetupTab() {
612
        API.tabs.query({url: 'chrome-extension://' + API.runtime.id + '/html/browser_action/browser_action.html'}).then(function (tabs) {
613
            if (tabs && tabs[0]) {
614
                API.tabs.remove(tabs[0].id);
615
            }
616
        });
617
    }
618
619
    _self.closeSetupTab = closeSetupTab;
620
621
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
622
623
        if (!msg || !msg.hasOwnProperty('method')) {
624
            return;
625
        }
626
        var result = false;
627
        if (_self[msg.method]) {
628
            result = _self[msg.method](msg.args, sender);
629
        } else {
630
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
631
        }
632
633
        sendResponse(result);
634
    });
635
636
    var defaultColor = '#0082c9';
637
638
    function createIconForTab(tab) {
639
        if (!master_password) {
640
            return;
641
        }
642
        var tabUrl = tab.url;
643
        var logins = getCredentialsByUrl(tabUrl);
644
        if (tab.active) {
645
            window.contextMenu.setContextItems(logins);
646
        }
647
        var credentialAmount = logins.length;
648
        API.browserAction.setBadgeText({
649
            text: credentialAmount.toString(),
650
            tabId: tab.id
651
        });
652
        API.browserAction.setBadgeBackgroundColor({
653
            color: defaultColor,
654
            tabId: tab.id
655
        });
656
657
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
658
        API.browserAction.setTitle({
659
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
660
            tabId: tab.id
661
        });
662
    }
663
664
    function displayLogoutIcons() {
665
        if (_self.settings) {
666
            API.tabs.query({}).then(function (tabs) {
667
                for (var t = 0; t < tabs.length; t++) {
668
                    var tab = tabs[t];
669
                    API.browserAction.setBadgeText({
670
                        text: '🔑',
671
                        tabId: tab.id
672
                    });
673
                    API.browserAction.setBadgeBackgroundColor({
674
                        color: '#ff0000',
675
                        tabId: tab.id
676
                    });
677
                    API.browserAction.setTitle({
678
                        title: API.i18n.getMessage('browser_action_title_locked'),
679
                        tabId: tab.id
680
                    });
681
                }
682
            });
683
        }
684
    }
685
686
    function updateTabsIcon() {
687
        API.tabs.query({}).then(function (tabs) {
688
            for (var t = 0; t < tabs.length; t++) {
689
                var tab = tabs[t];
690
                createIconForTab(tab);
691
            }
692
        });
693
    }
694
695
696
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
697
        if (master_password) {
698
            createIconForTab(tab);
699
        } else {
700
            displayLogoutIcons();
701
        }
702
    });
703
704
    API.tabs.onActivated.addListener(function () {
705
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
706
            if (master_password) {
707
                createIconForTab(tabs[0]);
708
            } else {
709
                displayLogoutIcons();
710
            }
711
        });
712
    });
713
714
    displayLogoutIcons();
715
716
717
    storage.get('master_password').then(function (password) {
718
        if (password) {
719
            master_password = password;
720
            API.api.browserAction.setBadgeBackgroundColor({
721
                color: defaultColor
722
            });
723
        }
724
        getSettings();
725
    }).error(function (error) {
726
        if (error === "Data not found") {
727
            getSettings();
728
        }
729
    });
730
    return _window;
731
}());
732
733