I’m not sure if these were all changes in how 1Password exports 1PIF files, or if I just had a messy vault with lots of edge cases, but these were changes I needed to make to run the importer on a .1pif file exported by 1Password 6.5.1 on macOS 10.12.1.
Thanks!
>From e846c9add61cf974e1817f259fb86ed9795c00b0 Mon Sep 17 00:00:00 2001 From: Alex Dunn <[email protected]> Date: Tue, 29 Nov 2016 09:12:26 -0800 Subject: [PATCH 1/3] 1password2pass: fix 1PIF conversion to JSON --- contrib/importers/1password2pass.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/contrib/importers/1password2pass.rb b/contrib/importers/1password2pass.rb index 79318ee..4a11e6b 100755 --- a/contrib/importers/1password2pass.rb +++ b/contrib/importers/1password2pass.rb @@ -2,13 +2,13 @@ # Copyright (C) 2014 Tobias V. Langhoff <[email protected]>. All Rights Reserved. # This file is licensed under GPLv2+. Please see COPYING for more information. -# +# # 1Password Importer -# +# # Reads files exported from 1Password and imports them into pass. Supports comma # and tab delimited text files, as well as logins (but not other items) stored # in the 1Password Interchange File (1PIF) format. -# +# # Supports using the title (default) or URL as pass-name, depending on your # preferred organization. Also supports importing metadata, adding them with # `pass insert --multiline`; the username and URL are compatible with @@ -44,7 +44,7 @@ options.meta = meta end - begin + begin opts.parse! rescue OptionParser::InvalidOption $stderr.puts optparse @@ -95,15 +95,14 @@ elsif File.extname(filename) =~ /.1pif/i require "json" - # 1PIF is almost JSON, but not quite - pif = "[#{File.open(filename).read}]" - pif.gsub!(/^\*\*\*.*\*\*\*$/, ",") - pif = JSON.parse(pif, {symbolize_names: true}) - options.name = :location if options.name == :url + # 1PIF is almost JSON, but not quite. Remove the ***...*** lines + # separating records, and then remove the trailing comma + pif = File.open(filename).read.gsub(/^\*\*\*.*\*\*\*$/, ",").chomp.chomp(",") + # Import 1PIF - pif.each do |entry| + JSON.parse("[#{pif}]", symbolize_names: true).each do |entry| next unless entry[:typeName] == "webforms.WebForm" pass = {} pass[:name] = "#{(options.group + "/") if options.group}#{entry[options.name]}" >From 4c49ca898a9da5708cec6c617d7451f44d5b5c74 Mon Sep 17 00:00:00 2001 From: Alex Dunn <[email protected]> Date: Tue, 29 Nov 2016 09:50:19 -0800 Subject: [PATCH 2/3] 1password2pass: more reliable parsing of 1PIF JSON - skip entries without secureContents fields - use :designation instead of :name - handle empty username fields --- contrib/importers/1password2pass.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/contrib/importers/1password2pass.rb b/contrib/importers/1password2pass.rb index 4a11e6b..f44bab7 100755 --- a/contrib/importers/1password2pass.rb +++ b/contrib/importers/1password2pass.rb @@ -104,15 +104,24 @@ # Import 1PIF JSON.parse("[#{pif}]", symbolize_names: true).each do |entry| next unless entry[:typeName] == "webforms.WebForm" + next if entry[:secureContents][:fields].nil? + pass = {} + pass[:name] = "#{(options.group + "/") if options.group}#{entry[options.name]}" + pass[:title] = entry[:title] + pass[:password] = entry[:secureContents][:fields].detect do |field| - field[:name] == "password" - end[:value] - pass[:login] = entry[:secureContents][:fields].detect do |field| - field[:name] == "username" + field[:designation] == "password" end[:value] + + username = entry[:secureContents][:fields].detect do |field| + field[:designation] == "username" + end + # might be nil + pass[:login] = username[:value] if username + pass[:url] = entry[:location] pass[:notes] = entry[:secureContents][:notesPlain] passwords << pass >From 645e5dfdaf603782ff92c733564936e6da24845f Mon Sep 17 00:00:00 2001 From: Alex Dunn <[email protected]> Date: Tue, 29 Nov 2016 09:52:57 -0800 Subject: [PATCH 3/3] 1password2pass: put pass[:name] in doublequotes It's more likely that there'll be a single quote in the name (e.g., "O'Reilly"), so handle those cases. --- contrib/importers/1password2pass.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/importers/1password2pass.rb b/contrib/importers/1password2pass.rb index f44bab7..e0ca39b 100755 --- a/contrib/importers/1password2pass.rb +++ b/contrib/importers/1password2pass.rb @@ -133,7 +133,7 @@ errors = [] # Save the passwords passwords.each do |pass| - IO.popen("pass insert #{"-f " if options.force}-m '#{pass[:name]}' > /dev/null", "w") do |io| + IO.popen("pass insert #{"-f " if options.force}-m \"#{pass[:name]}\" > /dev/null", "w") do |io| io.puts pass[:password] if options.meta io.puts "login: #{pass[:login]}" unless pass[:login].to_s.empty?
_______________________________________________ Password-Store mailing list [email protected] https://lists.zx2c4.com/mailman/listinfo/password-store
