1
|
|
|
/** |
2
|
|
|
* Nextcloud - passman |
3
|
|
|
* |
4
|
|
|
* @copyright Copyright (c) 2016, Sander Brand ([email protected]) |
5
|
|
|
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel ([email protected]) |
6
|
|
|
* @license GNU AGPL version 3 or any later version |
7
|
|
|
* |
8
|
|
|
* This program is free software: you can redistribute it and/or modify |
9
|
|
|
* it under the terms of the GNU Affero General Public License as |
10
|
|
|
* published by the Free Software Foundation, either version 3 of the |
11
|
|
|
* License, or (at your option) any later version. |
12
|
|
|
* |
13
|
|
|
* This program is distributed in the hope that it will be useful, |
14
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16
|
|
|
* GNU Affero General Public License for more details. |
17
|
|
|
* |
18
|
|
|
* You should have received a copy of the GNU Affero General Public License |
19
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
20
|
|
|
* |
21
|
|
|
*/ |
22
|
|
|
|
23
|
|
|
(function () { |
24
|
|
|
'use strict'; |
25
|
|
|
|
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @ngdoc service |
29
|
|
|
* @name passmanApp.ShareService |
30
|
|
|
* @description |
31
|
|
|
* # ShareService |
32
|
|
|
* Service in the passmanApp. |
33
|
|
|
*/ |
34
|
|
|
angular.module('passmanApp') |
35
|
|
|
.service('ShareService', ['$http', 'VaultService', 'EncryptService', 'CredentialService', function ($http, VaultService, EncryptService, CredentialService) { |
36
|
|
|
// Setup sjcl random engine to max paranoia level and start collecting data |
37
|
|
|
var paranoia_level = 10; |
38
|
|
|
/** global: sjcl */ |
39
|
|
|
sjcl.random.setDefaultParanoia(paranoia_level); |
40
|
|
|
sjcl.random.startCollectors(); |
41
|
|
|
|
42
|
|
|
return { |
43
|
|
|
search: function (string) { |
44
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/search'); |
45
|
|
|
return $http.post(queryUrl, {search: string}).then(function (response) { |
46
|
|
|
if (response.data) { |
47
|
|
|
return response.data; |
48
|
|
|
} else { |
49
|
|
|
return response; |
50
|
|
|
} |
51
|
|
|
}); |
52
|
|
|
}, |
53
|
|
|
shareWithUser: function (credential, target_user_data) { |
54
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/share'); |
55
|
|
|
return $http.post(queryUrl, |
56
|
|
|
{ |
57
|
|
|
item_id: credential.credential_id, |
58
|
|
|
item_guid: credential.guid, |
59
|
|
|
permissions: target_user_data.accessLevel, |
60
|
|
|
vaults: target_user_data.vaults, |
61
|
|
|
} |
62
|
|
|
); |
63
|
|
|
}, |
64
|
|
|
getVaultsByUser: function (userId) { |
65
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/vaults/' + userId); |
66
|
|
|
return $http.get(queryUrl, {search: userId}).then(function (response) { |
67
|
|
|
if (response.data) { |
68
|
|
|
for (var i = 0; i < response.data.length; i++) { |
69
|
|
|
/** global: forge */ |
70
|
|
|
response.data[i].public_sharing_key = forge.pki.publicKeyFromPem(response.data[i].public_sharing_key); |
71
|
|
|
} |
72
|
|
|
return response.data; |
73
|
|
|
} else { |
74
|
|
|
return response; |
75
|
|
|
} |
76
|
|
|
}); |
77
|
|
|
}, |
78
|
|
|
getPendingRequests: function () { |
79
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/pending'); |
80
|
|
|
return $http.get(queryUrl).then(function (response) { |
81
|
|
|
if (response.data) { |
|
|
|
|
82
|
|
|
return response.data; |
83
|
|
|
} |
84
|
|
|
}); |
85
|
|
|
}, |
86
|
|
|
saveSharingRequest: function (request, crypted_shared_key) { |
87
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/save'); |
88
|
|
|
return $http.post(queryUrl, { |
89
|
|
|
item_guid: request.item_guid, |
90
|
|
|
target_vault_guid: request.target_vault_guid, |
91
|
|
|
final_shared_key: crypted_shared_key |
92
|
|
|
}).then(function (response) { |
93
|
|
|
return response.data; |
94
|
|
|
}); |
95
|
|
|
}, |
96
|
|
|
declineSharingRequest: function (request) { |
97
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/decline/' + request.req_id); |
98
|
|
|
return $http.delete(queryUrl).then(function (response) { |
99
|
|
|
return response.data; |
100
|
|
|
}); |
101
|
|
|
}, |
102
|
|
|
unshareCredential: function (credential) { |
103
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid); |
104
|
|
|
return $http.delete(queryUrl).then(function (response) { |
105
|
|
|
return response.data; |
106
|
|
|
}); |
107
|
|
|
}, |
108
|
|
|
|
109
|
|
|
unshareCredentialFromUser: function (credential, user_id) { |
110
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/' + user_id); |
111
|
|
|
return $http.delete(queryUrl).then(function (response) { |
112
|
|
|
return response.data; |
113
|
|
|
}); |
114
|
|
|
}, |
115
|
|
|
|
116
|
|
|
createPublicSharedCredential: function (shareObj) { |
117
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/public'); |
118
|
|
|
return $http.post(queryUrl, shareObj).then(function (response) { |
119
|
|
|
return response.data; |
120
|
|
|
}); |
121
|
|
|
}, |
122
|
|
|
getPublicSharedCredential: function (credential_guid) { |
123
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential_guid + '/public'); |
124
|
|
|
return $http.get(queryUrl).then(function (response) { |
125
|
|
|
if (response.data) { |
126
|
|
|
return response; |
127
|
|
|
} else { |
128
|
|
|
return response; |
129
|
|
|
} |
130
|
|
|
}, |
131
|
|
|
function (result) { |
132
|
|
|
return result; |
133
|
|
|
}); |
134
|
|
|
}, |
135
|
|
|
getSharedCredentialACL: function (credential) { |
136
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/acl'); |
137
|
|
|
return $http.get(queryUrl).then(function (response) { |
138
|
|
|
if (response.data) { |
139
|
|
|
return response.data; |
140
|
|
|
} else { |
141
|
|
|
return response; |
142
|
|
|
} |
143
|
|
|
}, |
144
|
|
|
function (result) { |
145
|
|
|
return result; |
146
|
|
|
}); |
147
|
|
|
}, |
148
|
|
|
updateCredentialAcl: function (credential, acl) { |
149
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/acl'); |
150
|
|
|
return $http.patch(queryUrl, acl).then(function (response) { |
151
|
|
|
return response.data; |
152
|
|
|
}); |
153
|
|
|
}, |
154
|
|
|
getCredendialsSharedWithUs: function (vault_guid) { |
155
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/vault/' + vault_guid + '/get'); |
156
|
|
|
return $http.get(queryUrl).then(function (response) { |
157
|
|
|
if (response.data) { |
|
|
|
|
158
|
|
|
return response.data; |
159
|
|
|
} |
160
|
|
|
}); |
161
|
|
|
}, |
162
|
|
|
downloadSharedFile: function (credential, file) { |
163
|
|
|
var queryUrl = OC.generateUrl('apps/passman/api/v2/sharing/credential/' + credential.guid + '/file/' + file.guid); |
164
|
|
|
return $http.get(queryUrl).then(function (response) { |
165
|
|
|
if (response.data) { |
|
|
|
|
166
|
|
|
return response.data; |
167
|
|
|
} |
168
|
|
|
}); |
169
|
|
|
}, |
170
|
|
|
encryptSharedCredential: function (credential, sharedKey) { |
171
|
|
|
var _credential = angular.copy(credential); |
172
|
|
|
_credential.shared_key = EncryptService.encryptString(sharedKey); |
173
|
|
|
var encrypted_fields = CredentialService.getEncryptedFields(); |
174
|
|
|
for (var i = 0; i < encrypted_fields.length; i++) { |
175
|
|
|
var field = encrypted_fields[i]; |
176
|
|
|
var fieldValue = angular.copy(credential[field]); |
177
|
|
|
_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue), sharedKey); |
178
|
|
|
} |
179
|
|
|
return _credential; |
180
|
|
|
}, |
181
|
|
|
decryptSharedCredential: function (credential, sharedKey) { |
182
|
|
|
var _credential = angular.copy(credential); |
183
|
|
|
var encrypted_fields = CredentialService.getEncryptedFields(); |
184
|
|
|
for (var i = 0; i < encrypted_fields.length; i++) { |
185
|
|
|
var field = encrypted_fields[i]; |
186
|
|
|
var fieldValue = angular.copy(_credential[field]); |
187
|
|
|
var field_decrypted_value; |
188
|
|
|
if (_credential.hasOwnProperty(field)) { |
189
|
|
|
try { |
190
|
|
|
field_decrypted_value = EncryptService.decryptString(fieldValue, sharedKey); |
191
|
|
|
} catch (e) { |
192
|
|
|
throw e; |
193
|
|
|
} |
194
|
|
|
try { |
195
|
|
|
_credential[field] = JSON.parse(field_decrypted_value); |
196
|
|
|
} catch (e) { |
197
|
|
|
console.warn('Field' + field + ' in ' + _credential.label + ' could not be parsed! Value:' + fieldValue); |
198
|
|
|
throw e; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
return _credential; |
203
|
|
|
}, |
204
|
|
|
|
205
|
|
|
generateRSAKeys: function (key_length) { |
206
|
|
|
/** global: C_Promise */ |
207
|
|
|
var p = new C_Promise(function () { |
208
|
|
|
/** global: forge */ |
209
|
|
|
var state = forge.pki.rsa.createKeyPairGenerationState(key_length, 0x10001); |
210
|
|
|
var step = function () { |
211
|
|
|
// run for 100 ms |
212
|
|
|
/** global: forge */ |
213
|
|
|
if (!forge.pki.rsa.stepKeyPairGenerationState(state, 100)) { |
214
|
|
|
if (state.p !== null) { |
215
|
|
|
// progress(50); |
216
|
|
|
this.call_progress(50); |
217
|
|
|
} |
218
|
|
|
else { |
219
|
|
|
// progress(0); |
220
|
|
|
this.call_progress(0); |
221
|
|
|
} |
222
|
|
|
setTimeout(step.bind(this), 1); |
223
|
|
|
} |
224
|
|
|
else { |
225
|
|
|
// callback(state.keys); |
226
|
|
|
this.call_then(state.keys); |
227
|
|
|
} |
228
|
|
|
}; |
229
|
|
|
setTimeout(step.bind(this), 100); |
230
|
|
|
}); |
231
|
|
|
return p; |
232
|
|
|
}, |
233
|
|
|
generateSharedKey: function (size) { |
234
|
|
|
size = size || 20; |
235
|
|
|
/** global: C_Promise */ |
236
|
|
|
return new C_Promise(function () { |
237
|
|
|
var t = this; |
238
|
|
|
/** global: CRYPTO */ |
239
|
|
|
CRYPTO.PASSWORD.generate(size, |
240
|
|
|
function (pass) { |
241
|
|
|
t.call_then(pass); |
242
|
|
|
}, |
243
|
|
|
function (progress) { |
244
|
|
|
t.call_progress(progress); |
245
|
|
|
} |
246
|
|
|
); |
247
|
|
|
}); |
248
|
|
|
}, |
249
|
|
|
rsaKeyPairToPEM: function (keypair) { |
250
|
|
|
return { |
251
|
|
|
'publicKey': forge.pki.publicKeyToPem(keypair.publicKey), |
252
|
|
|
'privateKey': forge.pki.privateKeyToPem(keypair.privateKey) |
253
|
|
|
}; |
254
|
|
|
}, |
255
|
|
|
getSharingKeys: function () { |
256
|
|
|
var vault = VaultService.getActiveVault(); |
257
|
|
|
return { |
258
|
|
|
'private_sharing_key': EncryptService.decryptString(angular.copy(vault.private_sharing_key)), |
259
|
|
|
'public_sharing_key': vault.public_sharing_key |
260
|
|
|
}; |
261
|
|
|
}, |
262
|
|
|
rsaPrivateKeyFromPEM: function (private_pem) { |
263
|
|
|
/** global: forge */ |
264
|
|
|
return forge.pki.privateKeyFromPem(private_pem); |
265
|
|
|
}, |
266
|
|
|
rsaPublicKeyFromPEM: function (public_pem) { |
267
|
|
|
/** global: forge */ |
268
|
|
|
return forge.pki.publicKeyFromPem(public_pem); |
269
|
|
|
}, |
270
|
|
|
/** |
271
|
|
|
* Cyphers an array of string in a non-blocking way |
272
|
|
|
* @param vaults[] An array of vaults with the processed public keys |
|
|
|
|
273
|
|
|
* @param string The string to cypher |
274
|
|
|
*/ |
275
|
|
|
cypherRSAStringWithPublicKeyBulkAsync: function (vaults, string) { |
276
|
|
|
var workload = function () { |
277
|
|
|
if (this.current_index < this.vaults.length > 0 && this.vaults.length > 0) { |
278
|
|
|
var _vault = angular.copy(this.vaults[this.current_index]); |
279
|
|
|
/** global: forge */ |
280
|
|
|
_vault.key = forge.util.encode64( |
281
|
|
|
_vault.public_sharing_key.encrypt(this.string) |
282
|
|
|
); |
283
|
|
|
this.data.push( |
284
|
|
|
_vault |
285
|
|
|
); |
286
|
|
|
this.current_index++; |
287
|
|
|
|
288
|
|
|
this.call_progress(this.current_index); |
289
|
|
|
setTimeout(workload.bind(this), 1); |
290
|
|
|
} |
291
|
|
|
else { |
292
|
|
|
this.call_then(this.data); |
293
|
|
|
} |
294
|
|
|
}; |
295
|
|
|
/** global: C_Promise */ |
296
|
|
|
return new C_Promise(function () { |
297
|
|
|
this.data = []; |
298
|
|
|
this.vaults = vaults; |
299
|
|
|
this.string = string; |
300
|
|
|
this.current_index = 0; |
301
|
|
|
|
302
|
|
|
setTimeout(workload.bind(this), 0); |
303
|
|
|
}); |
304
|
|
|
} |
305
|
|
|
}; |
306
|
|
|
}]); |
307
|
|
|
}()); |
This check looks for functions where a
return
statement is found in some execution paths, but not in all.Consider this little piece of code
The function
isBig
will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly returnundefined
.This behaviour may not be what you had intended. In any case, you can add a
return undefined
to the other execution path to make the return value explicit.