Completed
Push — master ( a4e08b...140437 )
by Sander
11s
created

js/app/controllers/edit_credential.js   D

Complexity

Total Complexity 61
Complexity/F 2.44

Size

Lines of Code 326
Function Count 25

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 512
dl 0
loc 326
rs 4.9583
c 0
b 0
f 0
wmc 61
mnd 3
bc 56
fnc 25
bpm 2.24
cpm 2.44
noi 5

1 Function

Rating   Name   Duplication   Size   Complexity  
D angular.controller(ꞌCredentialEditCtrlꞌ) 0 312 8

How to fix   Complexity   

Complexity

Complex classes like js/app/controllers/edit_credential.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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 function
29
	 * @name passmanApp.controller:MainCtrl
30
	 * @description
31
	 * # MainCtrl
32
	 * Controller of the passmanApp
33
	 */
34
	angular.module('passmanApp')
35
		.controller('CredentialEditCtrl', ['$scope', 'VaultService', 'CredentialService', 'SettingsService', '$location', '$routeParams', 'FileService', 'EncryptService', 'TagService', 'NotificationService', 'ShareService', '$translate',
36
			function ($scope, VaultService, CredentialService, SettingsService, $location, $routeParams, FileService, EncryptService, TagService, NotificationService, ShareService, $translate) {
37
				$scope.active_vault = VaultService.getActiveVault();
38
				if (!SettingsService.getSetting('defaultVault') || !SettingsService.getSetting('defaultVaultPass')) {
39
					if (!$scope.active_vault) {
40
						$location.path('/');
41
						return;
42
					}
43
				} else {
44
					if (SettingsService.getSetting('defaultVault') && SettingsService.getSetting('defaultVaultPass')) {
45
						var _vault = angular.copy(SettingsService.getSetting('defaultVault'));
46
						_vault.vaultKey = SettingsService.getSetting('defaultVaultPass');
47
						VaultService.setActiveVault(_vault);
48
						$scope.active_vault = _vault;
49
					}
50
				}
51
52
				VaultService.getVault($scope.active_vault).then(function (vault) {
53
					vault.vaultKey = VaultService.getActiveVault().vaultKey;
54
					delete vault.credentials;
55
					VaultService.setActiveVault(vault);
56
					$scope.pwSettings = VaultService.getVaultSetting('pwSettings',
57
						{
58
							'length': 12,
59
							'useUppercase': true,
60
							'useLowercase': true,
61
							'useDigits': true,
62
							'useSpecialChars': true,
63
							'minimumDigitCount': 3,
64
							'avoidAmbiguousCharacters': false,
65
							'requireEveryCharType': true,
66
							'generateOnCreate': true
67
						});
68
				});
69
70
				$scope.currentTab = {
71
					title: $translate.instant('general'),
72
					url: 'views/partials/forms/edit_credential/basics.html',
73
					color: 'blue'
74
				};
75
76
				$translate(['general', 'password', 'custom.fields','files','otp']).then(function (translations) {
77
					$scope.tabs = [{
78
						title: translations.general,
79
						url: 'views/partials/forms/edit_credential/basics.html',
80
						color: 'blue'
81
					}, {
82
						title: translations.password,
83
						url: 'views/partials/forms/edit_credential/password.html',
84
						color: 'green'
85
					}, {
86
						title:translations['custom.fields'],
87
						url: 'views/partials/forms/edit_credential/custom_fields.html',
88
						color: 'orange'
89
					}, {
90
						title: translations.files,
91
						url: 'views/partials/forms/edit_credential/files.html',
92
						color: 'yellow'
93
					}, {
94
						title: translations.otp,
95
						url: 'views/partials/forms/edit_credential/otp.html',
96
						color: 'purple'
97
					}];
98
					$scope.currentTab = $scope.tabs[0];
99
				});
100
101
				if ($scope.active_vault) {
102
					$scope.$parent.selectedVault = true;
103
				}
104
				var storedCredential = SettingsService.getSetting('edit_credential');
105
106
				if (!storedCredential) {
107
					CredentialService.getCredential($routeParams.credential_id).then(function (result) {
108
						$scope.storedCredential = CredentialService.decryptCredential(angular.copy(result));
109
					});
110
				} else {
111
					$scope.storedCredential = CredentialService.decryptCredential(angular.copy(storedCredential));
112
					$scope.storedCredential.password_repeat = angular.copy($scope.storedCredential.password);
113
					$scope.storedCredential.expire_time = $scope.storedCredential.expire_time * 1000;
114
				}
115
116
				$scope.getTags = function ($query) {
117
					return TagService.searchTag($query);
118
				};
119
120
121
				$scope.onClickTab = function (tab) {
122
					$scope.currentTab = tab;
123
				};
124
125
				$scope.isActiveTab = function (tab) {
126
					return tab.url === $scope.currentTab.url;
127
				};
128
129
				/**
130
				 * Below general edit functions
131
				 */
132
133
				$scope.pwGenerated = function (pass) {
134
					$scope.storedCredential.password_repeat = pass;
135
				};
136
137
138
				var _customField = {
139
					label: '',
140
					value: '',
141
					secret: false,
142
					field_type: 'text'
143
				};
144
				$scope.selected_field_type = 'text';
145
				$scope.new_custom_field = angular.copy(_customField);
146
147
				$scope.addCustomField = function () {
148
					var _field = angular.copy($scope.new_custom_field);
149
					if (!_field.label) {
150
						NotificationService.showNotification($translate.instant('error.no.label'), 3000);
151
					}
152
					if (!_field.value) {
153
						NotificationService.showNotification($translate.instant('error.no.value'), 3000);
154
					}
155
					if (!_field.label || !_field.value) {
156
						return;
157
					}
158
					$scope.selected_field_type = 'text';
159
					_field.secret = (_field.field_type === 'password');
160
					if(_field.field_type === 'file'){
161
						var key = false;
162
						var _file = $scope.new_custom_field.value;
163
						if (!$scope.storedCredential.hasOwnProperty('acl') && $scope.storedCredential.hasOwnProperty('shared_key')) {
164
165
							if ($scope.storedCredential.shared_key) {
166
								key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
167
							}
168
						}
169
170
						if ($scope.storedCredential.hasOwnProperty('acl')) {
171
							key = EncryptService.decryptString(angular.copy($scope.storedCredential.acl.shared_key));
172
						}
173
174
						FileService.uploadFile(_file, key).then(function (result) {
175
							delete result.file_data;
176
							result.filename = EncryptService.decryptString(result.filename, key);
177
							_field.value = result;
178
							$scope.storedCredential.custom_fields.push(_field);
179
							$scope.new_custom_field = angular.copy(_customField);
180
						});
181
					} else {
182
						$scope.storedCredential.custom_fields.push(_field);
183
						$scope.new_custom_field = angular.copy(_customField);
184
					}
185
186
				};
187
188
				$scope.addFileToCustomField = function (file) {
189
					var _file = {
190
						filename: file.name,
191
						size: file.size,
192
						mimetype: file.type,
193
						data: file.data
194
					};
195
					$scope.new_custom_field.value = _file;
196
					$scope.$digest();
197
				};
198
199
				$scope.deleteCustomField = function (field) {
200
					if(field.hasOwnProperty('field_type')) {
201
						if (field.field_type === 'file') {
202
							FileService.deleteFile(field.value);
203
						}
204
					}
205
					var idx = $scope.storedCredential.custom_fields.indexOf(field);
206
					$scope.storedCredential.custom_fields.splice(idx, 1);
207
				};
208
209
				$scope.new_file = {
210
					name: '',
211
					data: null
212
				};
213
214
				$scope.deleteFile = function (file) {
215
					var idx = $scope.storedCredential.files.indexOf(file);
216
					FileService.deleteFile(file).then(function () {
217
						$scope.storedCredential.files.splice(idx, 1);
218
					});
219
				};
220
221
				$scope.fileLoaded = function (file) {
222
					var key;
223
					var _file = {
224
						filename: file.name,
225
						size: file.size,
226
						mimetype: file.type,
227
						data: file.data
228
					};
229
230
					if (!$scope.storedCredential.hasOwnProperty('acl') && $scope.storedCredential.hasOwnProperty('shared_key')) {
231
232
						if ($scope.storedCredential.shared_key) {
233
							key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
234
						}
235
					}
236
237
					if ($scope.storedCredential.hasOwnProperty('acl')) {
238
						key = EncryptService.decryptString(angular.copy($scope.storedCredential.acl.shared_key));
239
					}
240
241
242
					FileService.uploadFile(_file, key).then(function (result) {
243
						delete result.file_data;
244
						result.filename = EncryptService.decryptString(result.filename, key);
245
						$scope.storedCredential.files.push(result);
246
					});
247
248
249
					$scope.$digest();
250
				};
251
252
				$scope.fileLoadError = function (error) {
253
					console.log($translate.instant('error.loading.file'), error);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
254
				};
255
256
				$scope.selected_file = '';
257
				$scope.fileprogress = [];
258
				$scope.fileSelectProgress = function (progress) {
259
					if (progress) {
260
						$scope.fileprogress = progress;
261
						$scope.$digest();
262
263
					}
264
				};
265
				$scope.renewIntervalValue = 0;
266
				$scope.renewIntervalModifier = '0';
267
268
				$scope.updateInterval = function (renewIntervalValue, renewIntervalModifier) {
269
					var value = parseInt(renewIntervalValue);
270
					var modifier = parseInt(renewIntervalModifier);
271
					if (value && modifier) {
272
						$scope.storedCredential.renew_interval = value * modifier;
273
					}
274
				};
275
276
				$scope.parseQR = function (QRCode) {
277
					var re = /otpauth:\/\/(totp|hotp)\/(.*)\?(secret|issuer)=(.*)&(issuer|secret)=(.*)/, parsedQR, qrInfo;
278
					parsedQR = (QRCode.qrData.match(re));
279
					if (parsedQR)
280
						qrInfo = {
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
281
							type: parsedQR[1],
282
							label: decodeURIComponent(parsedQR[2]),
283
							qr_uri: QRCode
284
						};
285
					qrInfo[parsedQR[3]] = parsedQR[4];
0 ignored issues
show
Bug introduced by
The variable qrInfo does not seem to be initialized in case parsedQR on line 279 is false. Are you sure this can never be the case?
Loading history...
286
					qrInfo[parsedQR[5]] = parsedQR[6];
287
					$scope.storedCredential.otp = qrInfo;
288
					$scope.$digest();
289
				};
290
291
				$scope.saveCredential = function () {
292
293
294
					if ($scope.new_custom_field.label && $scope.new_custom_field.value) {
295
						$scope.storedCredential.custom_fields.push(angular.copy($scope.new_custom_field));
296
					}
297
298
299
					//@TODO  validation
300
					//@TODO When credential is expired and has renew interval set, calc new expire time.
301
					delete $scope.storedCredential.password_repeat;
302
					
303
					if (!$scope.storedCredential.credential_id) {
304
						$scope.storedCredential.vault_id = $scope.active_vault.vault_id;
305
						CredentialService.createCredential($scope.storedCredential).then(function () {
306
							$location.path('/vault/' + $routeParams.vault_id);
307
							NotificationService.showNotification($translate.instant('credential.created'), 5000);
308
						});
309
					} else {
310
311
						var key, _credential;
312
						if (!$scope.storedCredential.hasOwnProperty('acl') && $scope.storedCredential.hasOwnProperty('shared_key')) {
313
314
							if ($scope.storedCredential.shared_key) {
315
								key = EncryptService.decryptString(angular.copy($scope.storedCredential.shared_key));
316
							}
317
						}
318
319
						if ($scope.storedCredential.hasOwnProperty('acl')) {
320
							key = EncryptService.decryptString(angular.copy($scope.storedCredential.acl.shared_key));
321
						}
322
323
						if (key) {
324
							_credential = ShareService.encryptSharedCredential($scope.storedCredential, key);
325
						} else {
326
							_credential = angular.copy($scope.storedCredential);
327
						}
328
329
						delete _credential.shared_key;
330
						var _useKey = (key != null);
0 ignored issues
show
Bug introduced by
The variable key seems to not be initialized for all possible execution paths.
Loading history...
Coding Style introduced by
It is recommended to use !== to compare with null.

Generally, it is recommended to use strict comparison whenever possible and not to rely on the weaker type-juggling comparison operator.

Read more about comparison operations.

Loading history...
331
						var regex = /(<([^>]+)>)/ig;
332
						if(_credential.description && _credential.description !== "") {
333
							_credential.description = _credential.description.replace(regex, "");
334
						}
335
						CredentialService.updateCredential(_credential, _useKey).then(function () {
336
							SettingsService.setSetting('edit_credential', null);
337
							$location.path('/vault/' + $routeParams.vault_id);
338
							NotificationService.showNotification($translate.instant('credential.updated'), 5000);
339
						});
340
					}
341
342
				};
343
344
				$scope.cancel = function () {
345
					$location.path('/vault/' + $routeParams.vault_id);
346
				};
347
			}]);
348
}());