Completed
Push — master ( 640d6a...83cbed )
by Sander
13s
created

api.js ➔ ... ➔ setTimeout   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
1
/* global sjcl */
2
3
window.PAPI = (function () {
4
    var _encryptedFields = ['description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url'];
5
    var encryption_config = {
6
        adata: "",
7
        iter: 1000,
8
        ks: 256,
9
        mode: 'ccm',
10
        ts: 64
11
    };
12
    var _API = {
13
        username: '',
14
        password: '',
15
        host: '',
16
17
        getVaults: function (callback) {
18
            api_request({}, '/api/v2/vaults', 'GET', null, callback);
19
        },
20
        getVault: function (account, callback) {
21
            api_request(account, '/api/v2/vaults/' + account.vault.guid, 'GET', null, callback);
22
        },
23
        credentialsSet: function () {
24
            var hostSet = (typeof this.host !== 'undefined');
25
            var usernameSet = (this.username !== 'undefined');
26
            var passwordSet = (typeof this.password !== 'undefined');
27
            return (hostSet && usernameSet && passwordSet);
28
        },
29
        decryptString: function (ciphertext, _key) {
30
            if(!ciphertext || !_key){
31
                return '';
32
            }
33
            ciphertext = window.atob(ciphertext);
34
            var rp = {};
35
            try {
36
                /** global: sjcl */
37
                return sjcl.decrypt(_key, ciphertext, encryption_config, rp);
38
            } catch (e) {
39
                throw e;
40
            }
41
        },
42
        decryptCredential: function (credential, key) {
43
            for (var i = 0; i < _encryptedFields.length; i++) {
44
                var field = _encryptedFields[i];
45
                var fieldValue = credential[field];
46
                var field_decrypted_value;
47
                try {
48
                    field_decrypted_value = this.decryptString(fieldValue, key);
49
                } catch (e) {
50
                    console.warn('Field' + field + ' in ' + credential.label + ' could not be parsed! Value:' + fieldValue);
51
                    //throw e;
52
                }
53
                try {
54
                    credential[field] = JSON.parse(field_decrypted_value);
55
                } catch (e) {
56
                    console.warn('Field' + field + ' in ' + credential.label + ' could not be parsed! Value:' + fieldValue);
57
                }
58
59
            }
60
            return credential;
61
62
        },
63
        encryptString: function (string, _key) {
64
            var rp = {};
65
            /** global: sjcl */
66
            var ct = sjcl.encrypt(_key, string, encryption_config, rp);
67
            return window.btoa(ct);
68
        },
69
        newCredential: function () {
70
            return {
71
                'credential_id': null,
72
                'guid': null,
73
                'vault_id': null,
74
                'label': null,
75
                'description': null,
76
                'created': null,
77
                'changed': null,
78
                'tags': [],
79
                'email': null,
80
                'username': null,
81
                'password': null,
82
                'url': null,
83
                'favicon': null,
84
                'renew_interval': null,
85
                'expire_time': 0,
86
                'delete_time': 0,
87
                'files': [],
88
                'custom_fields': [],
89
                'otp': {},
90
                'hidden': false
91
            };
92
        },
93
        encryptCredential: function (credential, _key) {
94
            for (var i = 0; i < _encryptedFields.length; i++) {
95
                var field = _encryptedFields[i];
96
                var fieldValue = credential[field];
97
                credential[field] = this.encryptString(JSON.stringify(fieldValue), _key);
98
            }
99
            return credential;
100
        },
101
        createCredential: function (account, credential, _key, callback) {
102
            credential = this.encryptCredential(credential, _key);
103
104
            credential.expire_time = new Date(credential.expire_time).getTime() / 1000;
105
            var _that = this;
106
107
            api_request(account, '/api/v2/credentials', 'POST', credential, function (r) {
108
                credential.credential_id = r.credential_id;
109
                credential.guid = r.guid;
110
                credential = _that.decryptCredential(credential, _key);
111
                callback(credential);
112
            });
113
        },
114
        encryptSharedCredential: function (credential, sharedKey, origKey) {
115
            var _credential = credential;
116
            _credential.shared_key = this.encryptString(sharedKey, origKey);
117
            var encrypted_fields = _encryptedFields;
118
            for (var i = 0; i < encrypted_fields.length; i++) {
119
                var field = encrypted_fields[i];
120
                var fieldValue = credential[field];
121
                _credential[field] = this.encryptString(JSON.stringify(fieldValue), sharedKey);
122
            }
123
            return _credential;
124
        },
125
        getCredendialsSharedWithUs: function (account, vault_guid, callback) {
126
            api_request(account, '/api/v2/sharing/vault/' + vault_guid + '/get', 'GET', null, callback);
127
        },
128
        decryptSharedCredential: function (credential, sharedKey) {
129
            var encrypted_fields = _encryptedFields;
130
            for (var i = 0; i < encrypted_fields.length; i++) {
131
                var field = encrypted_fields[i];
132
                var fieldValue = credential[field];
133
                var field_decrypted_value;
134
                if (credential.hasOwnProperty(field)) {
135
                    try {
136
                        field_decrypted_value = this.decryptString(fieldValue, sharedKey);
137
                    } catch (e) {
138
                        throw e;
139
                    }
140
                    try {
141
                        credential[field] = JSON.parse(field_decrypted_value);
142
                    } catch (e) {
143
                        console.warn('Field' + field + ' in ' + _credential.label + ' could not be parsed! Value:' + fieldValue);
0 ignored issues
show
Bug introduced by brantje
The variable _credential seems to be never declared. If this is a global, consider adding a /** global: _credential */ 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...
144
                        throw e;
145
                    }
146
                }
147
            }
148
            return credential;
149
            //console.log(this.decryptCredential(credental, decrypted_key));
150
        },
151
        updateCredential: function (account, credential, key, callback) {
152
            var origKey = key;
153
            var _credential, _key;
154
155
            if (!credential.hasOwnProperty('acl') && credential.hasOwnProperty('shared_key')) {
156
                if (credential.shared_key) {
157
                    _key = this.decryptString(credential.shared_key, key);
158
                }
159
            }
160
161
            if (credential.hasOwnProperty('acl')) {
162
                _key = this.decryptString(credential.acl.shared_key, key);
163
            }
164
165
            var regex = /(<([^>]+)>)/ig;
166
            if(credential.description && credential.description !== "") {
167
                credential.description = credential.description.replace(regex, "");
168
            }
169
170
171
            if (_key) {
172
                _credential = this.encryptSharedCredential(JSON.parse(JSON.stringify(credential)), _key, origKey);
173
            } else {
174
                _credential = this.encryptCredential(JSON.parse(JSON.stringify(credential)), key);
175
            }
176
            delete _credential.shared_key;
177
178
179
            credential.expire_time = new Date(credential.expire_time).getTime() / 1000;
180
181
            api_request(account, '/api/v2/credentials/' + credential.guid, 'PATCH', _credential, function () {
182
                callback(credential);
183
            });
184
        }
185
    };
186
187
    var api_request = function (account, endpoint, method, data, callback) {
188
189
        var host = (account.hasOwnProperty('nextcloud_host')) ? account.nextcloud_host : _API.host;
190
        var username = (account.hasOwnProperty('nextcloud_username')) ? account.nextcloud_username : _API.username;
191
        var password = (account.hasOwnProperty('nextcloud_password')) ? account.nextcloud_password : _API.password;
192
193
        var encodedLogin = btoa(username + ":" + password);
194
195
        var headers = new Headers();
0 ignored issues
show
Bug introduced by brantje
The variable Headers seems to be never declared. If this is a global, consider adding a /** global: Headers */ 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...
196
        headers.append('Authorization', 'Basic ' + encodedLogin);
197
        headers.append("Accept", " application/json, text/plain, */*");
198
        var opts = {
199
            method: method,
200
            headers: headers
201
202
        };
203
204
        if(data){
205
            // Prevent leakage of account data;
206
            if(data.hasOwnProperty('account')){
207
                delete data.account;
208
            }
209
        }
210
211
        if(method.toLowerCase() !== 'get'){
212
            headers.append('content-type','application/json;charset=UTF-8');
213
            opts.body = JSON.stringify(data);
214
        }
215
216
        var request = new Request(host + '/index.php/apps/passman' + endpoint, opts);
0 ignored issues
show
Bug introduced by brantje
The variable Request seems to be never declared. If this is a global, consider adding a /** global: Request */ 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...
217
218
        var timeoutTimer = setTimeout(function () {
219
            API.notifications.create('Error', 'Error connecting to server (Error: Connection timeout)');
220
            callback({error: true, result: {statusText: 'Connection timeout', status: 0}});
221
        }, 10000);
222
223
        fetch(request).then(function(response){
224
            clearTimeout(timeoutTimer);
225
            if(response.status !== 200){
226
                callback({error: true, result: {statusText: response.statusText, status: response.status}});
227
                return;
228
            }
229
230
            var contentType = response.headers.get("content-type");
231
            if(contentType && contentType.indexOf("application/json") !== -1) {
232
                return response.json().then(function(json) {
233
                    if(json){
234
                        callback(json);
235
                    } else {
236
                        callback({error: true, result: {statusText: 'Empty reply from server', status: 0}});
237
                    }
238
239
                });
240
            } else {
241
                callback({error: true, result: {statusText: 'Invalid reply from server', status: 0}});
0 ignored issues
show
Best Practice introduced by brantje
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
242
            }
243
244
        }).catch(function (e) {
245
            clearTimeout(timeoutTimer);
246
            API.notifications.create('Error', 'Error connecting to server (Error: '+ e +')');
247
            callback({error: true, result: {statusText: e, status: 0}});
248
        });
249
    };
250
251
    return _API;
252
}());