one_pass 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7774961c176b2d6ebc5729b68030132c93d9d9df
4
+ data.tar.gz: 1e670ffa2e34cd27a9db1b1b947eac101a67763a
5
+ SHA512:
6
+ metadata.gz: 1026704202367e43c49d6da76355dc65c48b5559b17097ab39482b60d7c99fe7bdc2556878a32021f81831cb946e91d6c7f211d38ca45629bc3e6ea0b4e10c37
7
+ data.tar.gz: 9c5d4b84d154cb5013b1839b946665c1dd445be8b379ad294922c4879b038acb5cff4845a12300922506c596b75ca10bdb6c111091644b2aeec0696f098c012f
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.0
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'pry'
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ one_pass (0.1.0)
5
+ dispel (~> 0.0)
6
+ thor (~> 0.19)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ bump (0.5.3)
12
+ coderay (1.1.1)
13
+ curses (1.0.2)
14
+ diff-lcs (1.2.5)
15
+ dispel (0.0.7)
16
+ curses
17
+ method_source (0.8.2)
18
+ pry (0.10.3)
19
+ coderay (~> 1.1.0)
20
+ method_source (~> 0.8.1)
21
+ slop (~> 3.4)
22
+ rake (10.5.0)
23
+ rspec (3.4.0)
24
+ rspec-core (~> 3.4.0)
25
+ rspec-expectations (~> 3.4.0)
26
+ rspec-mocks (~> 3.4.0)
27
+ rspec-core (3.4.4)
28
+ rspec-support (~> 3.4.0)
29
+ rspec-expectations (3.4.0)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.4.0)
32
+ rspec-mocks (3.4.1)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.4.0)
35
+ rspec-support (3.4.1)
36
+ slop (3.6.0)
37
+ thor (0.19.1)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ bump (~> 0.5)
44
+ bundler (~> 1.11)
45
+ one_pass!
46
+ pry
47
+ rake (~> 10.0)
48
+ rspec (~> 3.0)
49
+
50
+ BUNDLED WITH
51
+ 1.11.2
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Seth Voltz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,28 @@
1
+ # OnePass
2
+
3
+ 1Password command line client
4
+
5
+ ## Installation
6
+
7
+
8
+ ```ruby
9
+ gem install one_pass
10
+ ```
11
+
12
+ ## Usage
13
+
14
+
15
+ ## Development
16
+
17
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
18
+
19
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
20
+
21
+ ## Contributing
22
+
23
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sethvoltz/one_pass.
24
+
25
+
26
+ ## License
27
+
28
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "bump/tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "OnePass"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ Secure Delete Variable in Memory
2
+ ================================
3
+
4
+ ```
5
+ pass = ""
6
+ $stdin.sysread(256, pass) # assuming a line-buffered terminal
7
+ io = StringIO.new("\0" * pass.bytesize)
8
+ io.read(pass.bytesize, pass)
9
+ ```
10
+
11
+ _Must_ use sysread as userspace buffering will likely leak one or more additional copies of the string.
data/exe/one-pass ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'OnePass'
5
+ require 'OnePass/cli'
6
+
7
+ app = OnePass::CLI.start ARGV
data/lib/OnePass.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'OnePass/version'
2
+ require 'OnePass/op_vault'
@@ -0,0 +1,146 @@
1
+ require 'dispel'
2
+
3
+ module OnePass
4
+ # OnePass Application
5
+ class Application
6
+ CONFIG_PATH='~/.one_pass'
7
+
8
+ def initialize(vault_path = nil)
9
+ @vault_path = get_vault vault_path
10
+ @vault = OpVault.new @vault_path
11
+
12
+ @vault.unlock password_entry
13
+ @vault.load_items
14
+ end
15
+
16
+ def password_entry
17
+ accumulator = []
18
+ Dispel::Screen.open do |screen|
19
+ map = Dispel::StyleMap.new(screen.lines)
20
+ config = {
21
+ map: map,
22
+ width: screen.columns,
23
+ height: screen.lines,
24
+ mode: :password
25
+ }
26
+ screen.draw *draw_password_box(accumulator.length, config)
27
+
28
+ Dispel::Keyboard.output do |key|
29
+ case key
30
+ when :enter
31
+ case config[:mode]
32
+ when :cancel
33
+ raise 'Cancel'
34
+ else
35
+ break
36
+ end
37
+ when :backspace
38
+ accumulator.pop
39
+ when :down
40
+ config[:mode] = config[:mode] == :password ? :ok : config[:mode]
41
+ when :up
42
+ config[:mode] = :password
43
+ when :left
44
+ config[:mode] = config[:mode] == :password ? config[:mode] : :ok
45
+ when :right
46
+ config[:mode] = config[:mode] == :password ? config[:mode] : :cancel
47
+ else
48
+ accumulator.push key if config[:mode] == :password
49
+ end
50
+ screen.draw *draw_password_box(accumulator.length, config)
51
+ end
52
+ end
53
+ accumulator.join
54
+ end
55
+
56
+ def draw_password_box(pw_length, config)
57
+ message = 'Please enter your 1Password master password for the following vault:'
58
+ min_width = [message, @vault_path].max_by(&:length).length
59
+ prefix = 'Master Password: '
60
+
61
+ offset_left = (config[:width] - (min_width + 4)) / 2
62
+ offset_top = (config[:height] - 8) / 2
63
+ left = ' ' * offset_left
64
+ third = min_width / 3
65
+
66
+ password_string = ("*" * pw_length).ljust(min_width - prefix.length, '_')
67
+ buttons = '<OK>'.center(third) + ' ' * (min_width - (third * 2)) + '<Cancel>'.center(third)
68
+
69
+ box = ("\n" * offset_top)
70
+ box += "#{left}┏#{'━' * (min_width + 2)}┓\n"
71
+ box += "#{left}┃ #{message.ljust(min_width)} ┃\n"
72
+ box += "#{left}┃ #{@vault_path.ljust(min_width)} ┃\n"
73
+ box += "#{left}┃ #{' ' * min_width} ┃\n"
74
+ box += "#{left}┃ #{prefix}#{password_string} ┃\n"
75
+ box += "#{left}┃#{' ' * (min_width + 2)}┃\n"
76
+ box += "#{left}┃ #{buttons} ┃\n"
77
+ box += "#{left}┗#{'━' * (min_width + 2)}┛\n"
78
+
79
+ cursor_top = offset_top + (config[:mode] == :password ? 4 : 6)
80
+ cursor_left = offset_left
81
+ case config[:mode]
82
+ when :ok
83
+ cursor_left += third / 2
84
+ when :cancel
85
+ cursor_left += min_width - third + third / 2 - 2
86
+ else
87
+ cursor_left += prefix.length + pw_length + 2
88
+ end
89
+
90
+ [box, config[:map], [cursor_top, cursor_left]]
91
+ end
92
+
93
+ def get_vault(vault_path = nil)
94
+ return vault_path if vault_path
95
+ path = File.expand_path CONFIG_PATH
96
+ raise 'Config file missing, please log in' unless File.exist? path
97
+
98
+ config = File.read path
99
+ raise 'Config file error' unless config.start_with? 'path='
100
+ config[5..-1].strip
101
+ end
102
+
103
+ def show(query, reply_type)
104
+ item = @vault.find(/#{query}/i).first
105
+ data = @vault.item_overview item
106
+ unless %i( uuid url title ).include? reply_type
107
+ data.merge!(@vault.item_detail item)
108
+ end
109
+
110
+ case reply_type
111
+ when *%i( uuid url title )
112
+ data[reply_type.to_s]
113
+ when :username
114
+ data['fields'].find({}) { |field| field['designation'] == 'username' }['value']
115
+ when :password
116
+ data['fields'].find({}) { |field| field['designation'] == 'password' }['value']
117
+ else
118
+ data
119
+ end
120
+ end
121
+
122
+ def search(query)
123
+ @vault.find(/#{query}/i).collect do |item|
124
+ data = (@vault.item_overview item).merge(@vault.item_detail item)
125
+ {
126
+ uuid: data['uuid'],
127
+ title: data['title'],
128
+ username: data['fields'].find({}) { |field| field['designation'] == 'username' }['value']
129
+ }
130
+ end
131
+ end
132
+
133
+ def self.save(vault_path = nil)
134
+ app = self.new vault_path # if succeeds, path & pw is good
135
+ path = File.expand_path CONFIG_PATH
136
+ File.open path, File::CREAT|File::TRUNC|File::RDWR do |file|
137
+ file.write "path=#{File.expand_path vault_path}\n"
138
+ end
139
+ end
140
+
141
+ def self.forget
142
+ path = File.expand_path CONFIG_PATH
143
+ File.delete path if File.exist? path
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,56 @@
1
+ require 'io/console'
2
+ require 'thor'
3
+
4
+ require 'OnePass/application'
5
+
6
+ module OnePass
7
+ # OnePass CLI
8
+ class CLI < Thor
9
+ SHOW_OPTIONS = %w( all username password url uuid title )
10
+
11
+ desc 'login', 'Save a 1Password vault and verify password'
12
+ option :vault, aliases: '-v', type: :string, banner: 'Specify a vault path'
13
+ def login
14
+ OnePass::Application.save options.vault
15
+ end
16
+
17
+ desc 'logout', 'Forget any saved 1Password vault'
18
+ def logout
19
+ OnePass::Application.forget
20
+ end
21
+
22
+ desc 'show [type] {NAME|UUID}', 'Get a single item from your vault, use only one type'
23
+ option :clip, aliases: '-c', type: :boolean, banner: 'Copy value to clipboard instead of stdout'
24
+ SHOW_OPTIONS.each do |switch|
25
+ option switch.to_sym, type: :boolean
26
+ end
27
+ def show(name)
28
+ # Check for multiple mutex args
29
+ type = SHOW_OPTIONS.each_with_object(Hash.new) do |k, hash|
30
+ hash[k] = options[k] if options.has_key?(k)
31
+ end
32
+ if type.length > 1
33
+ puts "Use only one of #{SHOW_OPTIONS.collect { |switch| '--' + switch }.join ', '}"
34
+ exit 1
35
+ end
36
+
37
+ # TODO: Check if name looks like a UUID
38
+ # otherwise, search for title by substring, return first
39
+ app = OnePass::Application.new
40
+ reply_type = type.keys.first.to_sym
41
+ reply = app.show name, reply_type
42
+ puts reply_type == :all ? JSON.pretty_generate(reply) : reply
43
+ end
44
+
45
+ desc 'search QUERY', 'Perform fuzzy search for items in your vault, shows uuid, title and username'
46
+ def search(query)
47
+ app = OnePass::Application.new
48
+ puts JSON.pretty_generate(app.search query)
49
+ end
50
+
51
+ map ls: :list
52
+ desc 'list FOLDER', 'List the contents of a folder, shows title and username'
53
+ def list(folder)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,166 @@
1
+ # STD Libs
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'json'
5
+
6
+ module OnePass
7
+ # Handle all the 1Password OpVault operations
8
+ class OpVault
9
+ FIELD_TYPES = {
10
+ password: 'P',
11
+ text: 'T',
12
+ email: 'E',
13
+ number: 'N',
14
+ radio: 'R',
15
+ telephone: 'TEL',
16
+ checkbox: 'C',
17
+ url: 'U'
18
+ }
19
+
20
+ DESIGNATION_TYPES = {
21
+ username: 'username',
22
+ password: 'password',
23
+ none: ''
24
+ }
25
+
26
+ def initialize(vault_path)
27
+ check_and_set_vault vault_path
28
+ check_and_set_profile File.join(@vault_path, 'default', 'profile.js')
29
+ @items = {}
30
+ @item_index = {}
31
+ end
32
+
33
+ def check_and_set_vault(vault_path)
34
+ @vault_path = File.expand_path(vault_path)
35
+ unless File.exist? @vault_path
36
+ raise ArgumentError.new, 'Vault file does not exist'
37
+ end
38
+ end
39
+
40
+ def check_and_set_profile(profile_path)
41
+ unless File.exist? profile_path
42
+ raise ArgumentError.new, 'Vault profile does not exist'
43
+ end
44
+
45
+ profile = File.read profile_path
46
+ unless profile.start_with?('var profile=') && profile.end_with?(';')
47
+ raise 'Vault profile format incorrect'
48
+ end
49
+
50
+ @profile = JSON.parse profile[12..-2]
51
+ rescue
52
+ raise 'Unable to parse vault profile'
53
+ end
54
+
55
+ def unlock(master_password = nil)
56
+ master_password = yield if block_given?
57
+
58
+ salt = Base64.decode64(@profile['salt'])
59
+ iterations = @profile['iterations']
60
+ key, mac_key = derive_keys master_password, salt, iterations
61
+ @master_key, @master_mac_key = master_keys key, mac_key
62
+ @overview_key, @overview_mac_key = overview_keys key, mac_key
63
+ rescue
64
+ raise 'Incorrect password'
65
+ end
66
+
67
+ def lock
68
+ @master_key = @master_mac_key = nil
69
+ end
70
+
71
+ def check_hmac(data, hmac_key, desired_hmac)
72
+ digest = OpenSSL::Digest::SHA256.new
73
+ computed_hmac = OpenSSL::HMAC.digest digest, hmac_key, data
74
+ raise ArgumentError.new, 'Invalid HMAC' if computed_hmac != desired_hmac
75
+ end
76
+
77
+ def load_items
78
+ file_glob = File.join(@vault_path, 'default', 'band_*.js')
79
+ Dir.glob(file_glob) do |file|
80
+ band = JSON.parse File.read(file)[3..-3]
81
+ @items.merge! band
82
+ end
83
+
84
+ @items.each_pair do |uuid, item|
85
+ overview = item_overview item
86
+ @item_index[overview['title']] = uuid
87
+ end
88
+ end
89
+
90
+ def find(search)
91
+ @items.values_at *@item_index.values_at(*@item_index.keys.grep(search))
92
+ end
93
+
94
+ def decrypt_data(key, iv, data)
95
+ cipher = OpenSSL::Cipher::AES256.new(:CBC).decrypt
96
+ cipher.key = key
97
+ cipher.iv = iv
98
+ cipher.padding = 0
99
+ cipher.update(data) + cipher.final
100
+ end
101
+
102
+ def decrypt_opdata(cipher_text, cipher_key, cipher_mac_key)
103
+ key_data = cipher_text[0..-33]
104
+ mac_data = cipher_text[-32..-1]
105
+
106
+ check_hmac key_data, cipher_mac_key, mac_data
107
+ plaintext = decrypt_data cipher_key, key_data[16..31], key_data[32..-1]
108
+
109
+ plaintext_size = key_data[8..15].unpack('Q')[0]
110
+ plaintext[-plaintext_size..-1]
111
+ end
112
+
113
+ def derive_keys(master_password, salt, iterations)
114
+ digest = OpenSSL::Digest::SHA512.new
115
+ derived_key = OpenSSL::PKCS5.pbkdf2_hmac(
116
+ master_password, salt, iterations, digest.digest_length, digest
117
+ )
118
+
119
+ # => key, hmac
120
+ [derived_key[0..31], derived_key[32..63]]
121
+ end
122
+
123
+ def master_keys(derived_key, derived_mac_key)
124
+ encrypted = Base64.decode64(@profile['masterKey'])
125
+ decrypt_keys encrypted, derived_key, derived_mac_key
126
+ end
127
+
128
+ def overview_keys(derived_key, derived_mac_key)
129
+ encrypted = Base64.decode64(@profile['overviewKey'])
130
+ decrypt_keys encrypted, derived_key, derived_mac_key
131
+ end
132
+
133
+ def decrypt_keys(encrypted_key, derived_key, derived_mac_key)
134
+ key_base = decrypt_opdata encrypted_key, derived_key, derived_mac_key
135
+ keys = OpenSSL::Digest::SHA512.new.digest key_base
136
+
137
+ # => key, hmac
138
+ [keys[0..31], keys[32..63]]
139
+ end
140
+
141
+ def item_keys(item)
142
+ item_key = Base64.decode64 item['k']
143
+ key_data = item_key[0..-33]
144
+ key_hmac = item_key[-32..-1]
145
+
146
+ check_hmac key_data, @master_mac_key, key_hmac
147
+ plaintext = decrypt_data @master_key, key_data[0..15], key_data[16..-1]
148
+
149
+ # => key, hmac
150
+ [plaintext[0..31], plaintext[32..63]]
151
+ end
152
+
153
+ def item_overview(item)
154
+ data = Base64.decode64(item['o'])
155
+ overview = decrypt_opdata data, @overview_key, @overview_mac_key
156
+ { 'uuid' => item['uuid'] }.merge JSON.parse overview
157
+ end
158
+
159
+ def item_detail(item)
160
+ data = Base64.decode64(item['d'])
161
+ item_key, item_mac_key = item_keys item
162
+ detail = decrypt_opdata data, item_key, item_mac_key
163
+ { 'uuid' => item['uuid'] }.merge JSON.parse detail
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,3 @@
1
+ module OnePass
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/one_pass.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'OnePass/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "one_pass"
8
+ spec.version = OnePass::VERSION
9
+ spec.authors = ["Seth Voltz"]
10
+ spec.email = ["seth@designgods.net"]
11
+
12
+ spec.summary = "A simple command line client for your 1Password vault"
13
+ spec.description = "Retrieve passwords from your 1Password OpVault-encoded vault via the command line"
14
+ spec.homepage = "https://sethvoltz.github.com/one_pass"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_runtime_dependency "dispel", "~> 0.0"
31
+ spec.add_runtime_dependency "thor", "~> 0.19"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.11"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "rspec", "~> 3.0"
36
+ spec.add_development_dependency "bump", "~> 0.5"
37
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: one_pass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Seth Voltz
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dispel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.19'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.19'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bump
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.5'
97
+ description: Retrieve passwords from your 1Password OpVault-encoded vault via the
98
+ command line
99
+ email:
100
+ - seth@designgods.net
101
+ executables:
102
+ - one-pass
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".ruby-version"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE.txt
113
+ - README.markdown
114
+ - Rakefile
115
+ - bin/console
116
+ - bin/setup
117
+ - doc/notes.markdown
118
+ - exe/one-pass
119
+ - lib/OnePass.rb
120
+ - lib/OnePass/application.rb
121
+ - lib/OnePass/cli.rb
122
+ - lib/OnePass/op_vault.rb
123
+ - lib/OnePass/version.rb
124
+ - one_pass.gemspec
125
+ homepage: https://sethvoltz.github.com/one_pass
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.5.1
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: A simple command line client for your 1Password vault
149
+ test_files: []