mirror of
				https://github.com/nextcloud/passman.git
				synced 2025-11-01 00:06:00 +08:00 
			
		
		
		
	Working passman import
This commit is contained in:
		
							parent
							
								
									b916ce4290
								
							
						
					
					
						commit
						a991302777
					
				
					 14 changed files with 229 additions and 23 deletions
				
			
		|  | @ -371,6 +371,13 @@ | |||
|     padding-left: 0px; | ||||
|     padding-right: 15px; } | ||||
| 
 | ||||
| .import_log { | ||||
|   max-height: 600px; | ||||
|   overflow-y: auto; } | ||||
|   .import_log textarea { | ||||
|     width: 90%; | ||||
|     height: 200px; } | ||||
| 
 | ||||
| #app-settings-content:not(.ng-hide) { | ||||
|   height: 60px; | ||||
|   display: inherit !important; | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -88,7 +88,7 @@ jQuery(document).ready(function () { | |||
| 		angular.element('#app-content-wrapper').scope().deleteCredential(credential); | ||||
| 		angular.element('#app-content-wrapper').scope().$apply(); | ||||
| 	}); | ||||
| 	var adjustControlsWidth = function() { | ||||
| 	var adjustControlsWidth = function(r) { | ||||
| 		if($('#controls').length) { | ||||
| 			var controlsWidth; | ||||
| 			// if there is a scrollbar …
 | ||||
|  | @ -111,12 +111,17 @@ jQuery(document).ready(function () { | |||
| 					controlsWidth = $('#content').width(); | ||||
| 				} | ||||
| 			} | ||||
| 			$('#controls').css('width', controlsWidth); | ||||
| 			$('#controls').css('min-width', controlsWidth); | ||||
| 			if(r){ | ||||
| 				var magic = 0; | ||||
| 			} else { | ||||
| 				var magic = 85; | ||||
| 			} | ||||
| 			$('#controls').css('width', controlsWidth+magic); | ||||
| 			$('#controls').css('min-width', controlsWidth+magic); | ||||
| 		} | ||||
| 	}; | ||||
| 	$(window).resize(_.debounce(adjustControlsWidth, 256)); | ||||
| 	$(window).resize(_.debounce(adjustControlsWidth, 400)); | ||||
| 	setTimeout(function(){ | ||||
| 		adjustControlsWidth() | ||||
| 		adjustControlsWidth(true) | ||||
| 	},200) | ||||
| }); | ||||
|  | @ -8,11 +8,12 @@ | |||
|  * Controller of the passmanApp | ||||
|  */ | ||||
| angular.module('passmanApp') | ||||
| 	.controller('ImportCtrl', ['$scope', '$window', function ($scope, $window) { | ||||
| 	.controller('ImportCtrl', ['$scope', '$window', 'CredentialService', 'VaultService', function ($scope, $window, CredentialService, VaultService) { | ||||
| 		//@TODo read the available importers from $window.PassmanImporter
 | ||||
| 		$scope.available_importers = [ | ||||
| 
 | ||||
| 		]; | ||||
| 		$scope.active_vault = VaultService.getActiveVault(); | ||||
| 
 | ||||
| 
 | ||||
| 		$scope.$watch(function(){ | ||||
|  | @ -25,16 +26,21 @@ angular.module('passmanApp') | |||
| 				} | ||||
| 			} | ||||
| 		}, true); | ||||
| 
 | ||||
| 		$scope.log = []; | ||||
| 		$scope.setImporter = function (importer) { | ||||
| 			importer = JSON.parse(importer); | ||||
| 			$scope.selectedImporter = importer; | ||||
| 		}; | ||||
| 		var _log = function(str){ | ||||
| 			$scope.log.push(str); | ||||
| 		}; | ||||
| 
 | ||||
| 		var file_data; | ||||
| 		$scope.fileLoaded = function (file) { | ||||
| 			file_data = file.data.split(','); | ||||
| 			file_data = decodeURIComponent(escape(window.atob( file_data[1] ))); //window.atob();
 | ||||
| 			_log('File read successfully!') | ||||
| 			$scope.$apply(); | ||||
| 		}; | ||||
| 
 | ||||
| 		$scope.fileLoadError = function (file) { | ||||
|  | @ -44,11 +50,49 @@ angular.module('passmanApp') | |||
| 
 | ||||
| 		}; | ||||
| 
 | ||||
| 		var parsed_data; | ||||
| 		$scope.current_import_index = 0; | ||||
| 		$scope.current_import_length = 0; | ||||
| 		var addCredential = function(parsed_data_index){ | ||||
| 			if(!parsed_data[parsed_data_index]){ | ||||
| 				return; | ||||
| 			} | ||||
| 			var _credential = parsed_data[parsed_data_index]; | ||||
| 			if(!_credential.label){ | ||||
| 				if(parsed_data[ parsed_data_index +1]) { | ||||
| 					_log('Credential has no label, skipping'); | ||||
| 					addCredential(parsed_data_index +1) | ||||
| 				} | ||||
| 				return | ||||
| 			} | ||||
| 			_log('Adding  '+ _credential.label); | ||||
| 			$scope.current_import_index = parsed_data_index; | ||||
| 			_credential.vault_id = $scope.active_vault.vault_id; | ||||
| 			CredentialService.createCredential(_credential).then(function (result) { | ||||
| 				if(result.credential_id){ | ||||
| 					_log('Added  '+ _credential.label); | ||||
| 					if(parsed_data[ parsed_data_index +1]) { | ||||
| 						addCredential(parsed_data_index +1) | ||||
| 					} else { | ||||
| 						_log('DONE!'); | ||||
| 					} | ||||
| 				} | ||||
| 			}) | ||||
| 		}; | ||||
| 
 | ||||
| 		$scope.startImport = function(){ | ||||
| 			if(file_data){ | ||||
| 				var parsed_data = $window.PassmanImporter[$scope.selectedImporter.id].readFile(file_data); | ||||
| 				console.log('Data parsed!', parsed_data); | ||||
| 				parsed_data = $window.PassmanImporter[$scope.selectedImporter.id].readFile(file_data); | ||||
| 				_log('Parsed '+ parsed_data.length + ' credentials, starting to import'); | ||||
| 				$scope.current_import_length = parsed_data.length; | ||||
| 				if( parsed_data.length > 0){ | ||||
| 					addCredential(0); | ||||
| 				} else { | ||||
| 					// @TODO Show message no data found
 | ||||
| 				} | ||||
| 
 | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 	}]); | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										22
									
								
								js/app/directives/autoscroll.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								js/app/directives/autoscroll.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| /** | ||||
|  * @ngdoc directive | ||||
|  * @name passmanApp.directive:autoScroll | ||||
|  * @description | ||||
|  * # autoScroll | ||||
|  */ | ||||
| angular.module('passmanApp') | ||||
| 	.directive('autoScroll', function () { | ||||
| 		return { | ||||
| 			restrict: 'A', | ||||
| 			scope: { | ||||
| 				autoScroll: '=' | ||||
| 			}, | ||||
| 			link: function postLink(scope, element, attrs) { | ||||
| 				scope.$watch('autoScroll', function () { | ||||
| 					$('#import_log').scrollTop($('#import_log')[0].scrollHeight); | ||||
| 				}, true); | ||||
| 			} | ||||
| 		}; | ||||
| 	}); | ||||
|  | @ -24,7 +24,11 @@ PassmanImporter.parseRow_ = function(row, isHeading) { | |||
| 	} | ||||
| 	return row; | ||||
| }; | ||||
| 
 | ||||
| PassmanImporter.htmlDecode = function(input){ | ||||
| 	var e = document.createElement('div'); | ||||
| 	e.innerHTML = input; | ||||
| 	return e.childNodes[0].nodeValue; | ||||
| }; | ||||
| PassmanImporter.toObject_ = function(headings, row) { | ||||
| 	var result = {}; | ||||
| 	for (var i = 0, ii = row.length; i < ii; i++) { | ||||
|  | @ -87,4 +91,8 @@ PassmanImporter.readCsv = function( csv, hasHeadings ){ | |||
| 		} | ||||
| 	} | ||||
| 	return lines; | ||||
| }; | ||||
| 
 | ||||
| PassmanImporter.readJson = function (string){ | ||||
| 	return JSON.parse(string); | ||||
| }; | ||||
|  | @ -31,7 +31,9 @@ PassmanImporter.dashLaneCsv.readFile = function (file_data) { | |||
| 		_credential.password = row_data[row_data.length - 2]; | ||||
| 		_credential.url = row_data[0]; | ||||
| 		_credential.description = row_data[row_data.length - 1]; | ||||
| 		credential_list.push(_credential); | ||||
| 		if(_credential.label){ | ||||
| 			credential_list.push(_credential); | ||||
| 		} | ||||
| 	} | ||||
| 	return credential_list; | ||||
| }; | ||||
|  | @ -17,13 +17,15 @@ PassmanImporter.lastpassCsv.readFile = function (file_data) { | |||
| 	for (var i = 0; i < parsed_csv.length; i++) { | ||||
| 		var row = parsed_csv[i]; | ||||
| 		var _credential = PassmanImporter.newCredential(); | ||||
| 		_credential.label = row.name; | ||||
| 		_credential.label = PassmanImporter.htmlDecode(row.name); | ||||
| 		_credential.username = row.username; | ||||
| 		_credential.password = row.password; | ||||
| 		_credential.url = row.url; | ||||
| 		_credential.tags = [{text: row.grouping}]; | ||||
| 		_credential.description = row.extra; | ||||
| 		credential_list.push(_credential); | ||||
| 		if(_credential.label){ | ||||
| 			credential_list.push(_credential); | ||||
| 		} | ||||
| 	} | ||||
| 	return credential_list; | ||||
| }; | ||||
							
								
								
									
										60
									
								
								js/importers/importer-passmanjson.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								js/importers/importer-passmanjson.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| // Importers should always start with this
 | ||||
| if (!window['PassmanImporter']) { | ||||
| 	var PassmanImporter = {} | ||||
| } | ||||
| // Define the importer
 | ||||
| PassmanImporter.passmanJson = { | ||||
| 	info: { | ||||
| 		name: 'Passman JSON', | ||||
| 		id: 'passmanJson', | ||||
| 		description: 'Export the item in passman as passman json, with all fields enabled' | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| PassmanImporter.passmanJson.readFile = function (file_data) { | ||||
| 	var parsed_json = PassmanImporter.readJson(file_data); | ||||
| 	var credential_list = []; | ||||
| 	for (var i = 0; i < parsed_json.length; i++) { | ||||
| 		var item = parsed_json[i]; | ||||
| 		var _credential = PassmanImporter.newCredential(); | ||||
| 		_credential.label = item.label; | ||||
| 		_credential.username = item.account; | ||||
| 		_credential.password = item.password; | ||||
| 		_credential.email = item.email; | ||||
| 		_credential.url = item.url; | ||||
| 		_credential.tags = item.tags; | ||||
| 		//Check for custom fields
 | ||||
| 		if (item.hasOwnProperty('customFields')) { | ||||
| 			//Check for otp
 | ||||
| 			if (item.customFields.length > 0) { | ||||
| 				for (var cf = 0; cf < item.customFields.length; cf++) { | ||||
| 					_credential.custom_fields.push( | ||||
| 						{ | ||||
| 							'label': item.customFields[cf].label, | ||||
| 							'value': item.customFields[cf].value, | ||||
| 							'secret': (item.customFields[cf].clicktoshow == '1') | ||||
| 						} | ||||
| 					) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if (item.hasOwnProperty('otpsecret')) { | ||||
| 			if (item.otpsecret) { | ||||
| 				_credential.otp = { | ||||
| 					'issuer': item.otpsecret.issuer, | ||||
| 					'label': item.otpsecret.label, | ||||
| 					'qr_uri': { | ||||
| 						'image': item.otpsecret.qrCode, | ||||
| 						'qrData': '' | ||||
| 					}, | ||||
| 					'secret': item.otpsecret.secret, | ||||
| 					'type': item.otpsecret.type | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if(_credential.label){ | ||||
| 			credential_list.push(_credential); | ||||
| 		} | ||||
| 	} | ||||
| 	return credential_list; | ||||
| }; | ||||
							
								
								
									
										30
									
								
								js/importers/importer-zohocsv.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								js/importers/importer-zohocsv.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| // Importers should always start with this
 | ||||
| if (!window['PassmanImporter']) { | ||||
| 	var PassmanImporter = {} | ||||
| } | ||||
| // Define the importer
 | ||||
| PassmanImporter.zohoCsv = { | ||||
| 	info: { | ||||
| 		name: 'ZOHO csv', | ||||
| 		id: 'zohoCsv', | ||||
| 		description: 'Create an csv export. Go to Tools ->  Export secrets -> Select "General CSV" and click "Export Secrets"' | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| PassmanImporter.zohoCsv.readFile = function (file_data) { | ||||
| 	var parsed_csv = PassmanImporter.readCsv(file_data, false); | ||||
| 	var credential_list = []; | ||||
| 	for (var i = 0; i < parsed_csv.length; i++) { | ||||
| 		var row = parsed_csv[i]; | ||||
| 		var _credential = PassmanImporter.newCredential(); | ||||
| 		_credential.label = row[0]; | ||||
| 		_credential.username = row[3]; | ||||
| 		_credential.password = row[4]; | ||||
| 		_credential.url = row[1]; | ||||
| 		_credential.description = row[2]; | ||||
| 		if(_credential.label){ | ||||
| 			credential_list.push(_credential); | ||||
| 		} | ||||
| 	} | ||||
| 	return credential_list; | ||||
| }; | ||||
|  | @ -57,7 +57,7 @@ angular.module('views/partials/forms/settings/general_settings.html', []).run([' | |||
| angular.module('views/partials/forms/settings/import.html', []).run(['$templateCache', function($templateCache) { | ||||
|   'use strict'; | ||||
|   $templateCache.put('views/partials/forms/settings/import.html', | ||||
|     '<div ng-controller="ImportCtrl"><div class="row"><div class="col-xs-12"><label>Import type<select ng-init="importerType" ng-model="importerType" ng-change="setImporter(importerType)"><option ng-repeat="importer in available_importers" value="{{importer}}">{{importer.name}}</option></select></label><div><b>{{selectedImporter.description}}</b></div><input ng-if="selectedImporter" type="file" file-select success="fileLoaded" error="fileLoadError" progress="fileSelectProgress"><br><button class="button" ng-click="startImport()" ng-if="selectedImporter">Import</button></div></div></div>'); | ||||
|     '<div ng-controller="ImportCtrl"><div class="row"><div class="col-xs-6"><label>Import type<select ng-init="importerType" ng-model="importerType" ng-change="setImporter(importerType)"><option ng-repeat="importer in available_importers" value="{{importer}}">{{importer.name}}</option></select></label><div><b>{{selectedImporter.description}}</b></div><input ng-if="selectedImporter" type="file" file-select success="fileLoaded" error="fileLoadError" progress="fileSelectProgress"><br><button class="button" ng-click="startImport()" ng-if="selectedImporter">Import</button><div ng-if="current_import_length">{{ current_import_index }} / {{ current_import_length - 1}}</div></div><div class="col-xs-6"><div ng-if="log" class="import_log"><textarea id="import_log" auto-scroll="log">{{log.join(\'\\n\')}}</textarea></div></div></div></div>'); | ||||
| }]); | ||||
| 
 | ||||
| angular.module('views/partials/forms/settings/tool.html', []).run(['$templateCache', function($templateCache) { | ||||
|  |  | |||
|  | @ -4,4 +4,13 @@ | |||
|     padding-left: 0px; | ||||
|     padding-right: 15px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .import_log { | ||||
|   max-height: 600px; | ||||
|   overflow-y: auto; | ||||
|   textarea{ | ||||
|     width: 90%; | ||||
|     height: 200px; | ||||
|   } | ||||
| } | ||||
|  | @ -58,10 +58,13 @@ script('passman', 'app/directives/tooltip'); | |||
| script('passman', 'app/directives/use-theme'); | ||||
| script('passman', 'app/directives/credentialfield'); | ||||
| script('passman', 'app/directives/ngenter'); | ||||
| script('passman', 'app/directives/autoscroll'); | ||||
| script('passman', 'importers/import-main'); | ||||
| script('passman', 'importers/importer-keepasscsv'); | ||||
| script('passman', 'importers/importer-lastpasscsv'); | ||||
| script('passman', 'importers/importer-dashlanecsv'); | ||||
| script('passman', 'importers/importer-zohocsv'); | ||||
| script('passman', 'importers/importer-passmanjson'); | ||||
| 
 | ||||
| /* | ||||
|  * Styles | ||||
|  |  | |||
|  | @ -1,15 +1,29 @@ | |||
| <div ng-controller="ImportCtrl"> | ||||
| 	<div class="row"> | ||||
| 		<div class="col-xs-12" > | ||||
| 		<div class="col-xs-6"> | ||||
| 			<label>Import type | ||||
| 			<select ng-init="importerType" ng-model="importerType" ng-change="setImporter(importerType)"> | ||||
| 				<option ng-repeat="importer in available_importers" value="{{importer}}"> | ||||
| 					{{importer.name}} | ||||
| 				</option> | ||||
| 			</select></label> | ||||
| 				<select ng-init="importerType" ng-model="importerType" | ||||
| 						ng-change="setImporter(importerType)"> | ||||
| 					<option ng-repeat="importer in available_importers" | ||||
| 							value="{{importer}}"> | ||||
| 						{{importer.name}} | ||||
| 					</option> | ||||
| 				</select></label> | ||||
| 			<div><b>{{selectedImporter.description}}</b></div> | ||||
| 			<input ng-if="selectedImporter" type="file" file-select success="fileLoaded" error="fileLoadError" progress="fileSelectProgress"><br /> | ||||
| 			<button class="button" ng-click="startImport()" ng-if="selectedImporter">Import</button> | ||||
| 			<input ng-if="selectedImporter" type="file" file-select | ||||
| 				   success="fileLoaded" error="fileLoadError" | ||||
| 				   progress="fileSelectProgress"><br/> | ||||
| 			<button class="button" ng-click="startImport()" | ||||
| 					ng-if="selectedImporter">Import | ||||
| 			</button> | ||||
| 			<div ng-if="current_import_length"> | ||||
| 				{{ current_import_index }} / {{ current_import_length - 1}} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="col-xs-6"> | ||||
| 			<div ng-if="log" class="import_log"> | ||||
| 				<textarea id="import_log" auto-scroll="log">{{log.join('\n')}}</textarea> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
		Loading…
	
	Add table
		
		Reference in a new issue