Completed
Push — master ( 053a36...0a386d )
by Sander
03:38
created

js/app/services/credentialservice.js   B

Complexity

Conditions 1
Paths 6

Size

Total Lines 336

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
nc 6
nop 0
dl 0
loc 336
rs 8.2857
c 1
b 0
f 0

1 Function

Rating   Name   Duplication   Size   Complexity  
B angular.service(ꞌCredentialServiceꞌ) 0 325 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
	 * @ngdoc service
27
	 * @name passmanApp.CredentialService
28
	 * @description
29
	 * # CredentialService
30
	 * Service in the passmanApp.
31
	 */
32
	angular.module('passmanApp')
33
		.service('CredentialService', ['$http', 'EncryptService', 'VaultService', 'FileService', function ($http, EncryptService, VaultService, FileService) {
34
			var credential = {
35
				'credential_id': null,
36
				'guid': null,
37
				'vault_id': null,
38
				'label': null,
39
				'description': null,
40
				'created': null,
41
				'changed': null,
42
				'tags': [],
43
				'email': null,
44
				'username': null,
45
				'password': null,
46
				'url': null,
47
				'favicon': null,
48
				'renew_interval': null,
49
				'expire_time': 0,
50
				'delete_time': 0,
51
				'files': [],
52
				'custom_fields': [],
53
				'otp': {},
54
				'hidden': false
55
			};
56
			var _encryptedFields = ['description', 'username', 'password', 'files', 'custom_fields', 'otp', 'email', 'tags', 'url'];
57
58
59
			return {
60
				newCredential: function () {
61
					return angular.copy(credential);
62
				},
63
				createCredential: function (credential) {
64
					var _credential = angular.copy(credential);
65
					for (var i = 0; i < _encryptedFields.length; i++) {
66
						var field = _encryptedFields[i];
67
						var fieldValue = angular.copy(credential[field]);
68
						_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue));
69
					}
70
71
					_credential.expire_time = new Date(angular.copy(credential.expire_time)).getTime() / 1000;
72
73
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials');
74
					return $http.post(queryUrl, _credential).then(function (response) {
75
						if (response.data) {
76
							return response.data;
77
						} else {
78
							return response;
79
						}
80
					});
81
				},
82
				getEncryptedFields: function () {
83
					return _encryptedFields;
84
				},
85
				updateCredential: function (credential, skipEncryption, key) {
86
					var _credential = angular.copy(credential);
87
					if (!skipEncryption) {
88
						for (var i = 0; i < _encryptedFields.length; i++) {
89
							var field = _encryptedFields[i];
90
							var fieldValue = angular.copy(credential[field]);
91
							_credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue), key);
92
						}
93
					}
94
					_credential.expire_time = new Date(angular.copy(credential.expire_time)).getTime() / 1000;
95
96
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + credential.guid);
97
					return $http.patch(queryUrl, _credential).then(function (response) {
98
						if (response.data) {
99
							return response.data;
100
						} else {
101
							return response;
102
						}
103
					});
104
				},
105
				getCredential: function (guid) {
106
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + guid);
107
					return $http.get(queryUrl).then(function (response) {
108
						if (response.data) {
109
							return response.data;
110
						} else {
111
							return response;
112
						}
113
					});
114
				},
115
				destroyCredential: function (guid) {
116
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + guid);
117
					return $http.delete(queryUrl).then(function (response) {
118
						if (response.data) {
119
							return response.data;
120
						} else {
121
							return response;
122
						}
123
					});
124
				},
125
				encryptCredential: function (credential, key) {
126
					for (var i = 0; i < _encryptedFields.length; i++) {
127
						var field = _encryptedFields[i];
128
						var fieldValue = angular.copy(credential[field]);
129
						credential[field] = EncryptService.encryptString(JSON.stringify(fieldValue), key);
130
					}
131
					return credential;
132
				},
133
				decryptCredential: function (credential, key) {
134
					for (var i = 0; i < _encryptedFields.length; i++) {
135
						var field = _encryptedFields[i];
136
						var fieldValue = angular.copy(credential[field]);
137
						var field_decrypted_value;
138
						try {
139
							field_decrypted_value = EncryptService.decryptString(fieldValue, key);
140
						} catch (e) {
141
							throw e;
142
						}
143
						try {
144
							credential[field] = JSON.parse(field_decrypted_value);
145
						} catch (e) {
146
							console.warn('Field' + field + ' in ' + credential.label + ' could not be parsed! Value:' + fieldValue);
147
148
						}
149
150
					}
151
					return credential;
152
				},
153
				getSharedKeyFromCredential: function (credential) {
154
					var key = null;
155
					if (!credential.hasOwnProperty('acl') && credential.hasOwnProperty('shared_key')) {
156
						if (credential.shared_key) {
157
							key = EncryptService.decryptString(angular.copy(credential.shared_key));
158
						}
159
					}
160
					if (credential.hasOwnProperty('acl')) {
161
						key = EncryptService.decryptString(angular.copy(credential.acl.shared_key));
162
					}
163
					return key;
164
				},
165
				getRevisions: function (guid) {
166
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + guid + '/revision');
167
					return $http.get(queryUrl).then(function (response) {
168
						if (response.data) {
169
							return response.data;
170
						} else {
171
							return response;
172
						}
173
					});
174
				},
175
				updateRevision: function (revision) {
176
					var _revision = angular.copy(revision);
177
					_revision.credential_data = window.btoa(JSON.stringify(_revision.credential_data));
178
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + revision.credential_data.guid + '/revision/' + revision.revision_id);
179
					return $http.patch(queryUrl, _revision).then(function (response) {
180
						if (response.data) {
181
							return response.data;
182
						} else {
183
							return response;
184
						}
185
					});
186
				},
187
				deleteRevision: function (credential_guid, revision_id) {
188
					var queryUrl = OC.generateUrl('apps/passman/api/v2/credentials/' + credential_guid + '/revision/' + revision_id);
189
					return $http.delete(queryUrl).then(function (response) {
190
						if (response.data) {
191
							return response.data;
192
						} else {
193
							return response;
194
						}
195
					});
196
				},
197
				reencryptCredential: function (credential_guid, old_password, new_password, skipSharingKey) {
198
199
					var service = this;
200
201
					var progress_datatype = function (current, total, process) {
202
						this.process = process;
203
						this.current = current;
204
						this.total = total;
205
						this.calculated = current / total * 100;
206
					};
207
208
					var promise_credential_update = function () {
209
						service.getCredential(credential_guid).then((function (credential) {
210
							this.parent.plain_credential = service.decryptCredential(credential, this.parent.old_password);
211
							var tmp = angular.copy(this.parent.plain_credential);
212
213
							if (tmp.hasOwnProperty('shared_key') && tmp.shared_key !== null && !skipSharingKey) {
214
								var shared_key = EncryptService.decryptString(angular.copy(tmp.shared_key)).trim();
215
								tmp.shared_key = EncryptService.encryptString(angular.copy(shared_key), this.parent.new_password);
216
								tmp.set_share_key = true;
217
								tmp.skip_revision = true;
218
								this.parent.new_password = shared_key;
219
							}
220
221
							this.parent.new_credential_cryptogram = service.encryptCredential(tmp, this.parent.new_password);
222
							this.call_progress(new progress_datatype(1, 2, 'credential'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
223
224
							// Save data
225
							this.parent.new_credential_cryptogram.skip_revision = true;
226
							service.updateCredential(this.parent.new_credential_cryptogram, true).then((function () {
227
								this.call_progress(new progress_datatype(2, 2, 'credential'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
228
								this.call_then({
229
									plain_text: this.parent.plain_credential,
230
									cryptogram: this.parent.new_credential_cryptogram
231
								});
232
							}).bind(this));
233
						}).bind(this));
234
					};
235
236
					var promise_files_update = function () {
237
						// Add the double of the files so we take encryption phase and upload to the server into the math
238
						this.total = this.parent.plain_credential.files.length * 2;	 // Binded on credential finish upload
239
						this.current = 0;
240
241
						for (var i = 0; i < this.parent.plain_credential.files.length; i++) {
242
							var _file = this.parent.plain_credential.files[i];
243
							/* jshint ignore:start */
244
							FileService.getFile(_file).then((function (fileData) {
245
								//Decrypt with old key
246
								fileData.filename = EncryptService.decryptString(fileData.filename, this.parent.old_password);
247
								fileData.file_data = EncryptService.decryptString(fileData.file_data, this.parent.old_password);
248
249
								this.current++;
250
251
								this.call_progress(new progress_datatype(this.current, this.total, 'files'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
252
253
								FileService.updateFile(fileData, this.parent.new_password).then((function () {
254
									this.current++;
255
									this.call_progress(new progress_datatype(this.current, this.total, 'files'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
256
									if (this.current === this.total) {
257
										this.call_then('All files has been updated');
258
									}
259
								}).bind(this));
260
							}).bind(this));
261
							/* jshint ignore:end */
262
						}
263
						if (this.parent.plain_credential.files.length === 0) {
264
							this.call_progress(new progress_datatype(0, 0, 'files'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
265
							this.call_then("No files to update");
266
						}
267
					};
268
269
					var promise_revisions_update = function () {
270
						service.getRevisions(this.parent.plain_credential.guid).then((function (revisions) {
271
							// Double, so we include the actual upload of the data back to the server
272
							this.total = revisions.length * 2;
273
							this.upload = 0;
274
							this.current = 0;
275
							this.revisions = revisions;
276
277
							var revision_workload = function () {
278
								if (this.revisions.length === 0) {
279
									this.call_progress(new progress_datatype(0, 0, 'revisions'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
280
									this.call_then("No history to update");
281
									return;
282
								}
283
								var _revision = revisions[this.current];
284
								//Decrypt!
285
								_revision.credential_data = service.decryptCredential(_revision.credential_data, this.parent.old_password);
286
								_revision.credential_data = service.encryptCredential(_revision.credential_data, this.parent.new_password);
287
								this.current++;
288
289
								this.call_progress(new progress_datatype(this.current + this.upload, this.total, 'revisions'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
290
291
								service.updateRevision(_revision).then((function () {
292
									this.upload++;
293
									this.call_progress(new progress_datatype(this.upload + this.current, this.total, 'revisions'));
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like progress_datatype should be capitalized.
Loading history...
294
									if (this.current + this.upload === this.total) {
295
										this.call_then("History updated");
296
									}
297
								}).bind(this));
298
299
								if (this.current !== (this.total / 2)) {
300
									setTimeout(revision_workload.bind(this), 1);
301
								}
302
							};
303
							setTimeout(revision_workload.bind(this), 1);
304
						}).bind(this));
305
					};
306
307
					var promise_workload = function () {
308
						this.old_password = angular.copy(old_password);
309
						this.new_password = angular.copy(new_password);
310
						this.promises = 0;
311
312
						var master_promise = this;
313
314
						var password_data = function () {
315
							this.old_password = master_promise.old_password;
316
							this.new_password = master_promise.new_password;
317
							this.plain_credential = master_promise.plain_credential;
318
						};
319
						this.credential_data = {};
320
						/** global: C_Promise */
321
						(new C_Promise(promise_credential_update, new password_data())).progress(function (data) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like password_data should be capitalized.
Loading history...
322
							master_promise.call_progress(data);
323
						}).then(function (data) {
324
							console.warn("End credential update");
325
							master_promise.plain_credential = data.plain_text;
326
							master_promise.promises++;
327
328
							master_promise.credential_data = data;
329
							/** global: C_Promise */
330
							(new C_Promise(promise_files_update, new password_data())).progress(function (data) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like password_data should be capitalized.
Loading history...
331
								master_promise.call_progress(data);
332
							}).then(function () {
333
								console.warn("End files update");
334
								master_promise.promises--;
335
								if (master_promise.promises === 0) {
336
									master_promise.call_then(master_promise.credential_data);
337
								}
338
							});
339
340
							master_promise.promises++;
341
							/** global: C_Promise */
342
							(new C_Promise(promise_revisions_update, new password_data())).progress(function (data) {
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like password_data should be capitalized.
Loading history...
343
								master_promise.call_progress(data);
344
							}).then(function () {
345
								console.warn("End revisions update");
346
								master_promise.promises--;
347
								if (master_promise.promises === 0) {
348
									master_promise.call_then(master_promise.credential_data);
349
								}
350
							});
351
						});
352
					};
353
					/** global: C_Promise */
354
					return new C_Promise(promise_workload);
355
				}
356
			};
357
		}]);
358
}());