zanzibar 0.1.27 → 0.2.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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OGUxZDk3YjZhOTljMGI5NGRjMjIyZGI2M2ZjN2Y3ZGM1NmY5ODllZg==
4
+ YWI3MWY3ZGUyNzM2ZWI1MjU1NzBmNzE3YTdkYzdmZWE0ZDA3YTg0ZA==
5
5
  data.tar.gz: !binary |-
6
- MTkwY2NhNDVkNmE3Y2I0NDk2NmNmZWZlNzE2MDUzYzFhNDA0OTBlOQ==
6
+ NDBmNGY5OGUwZDhkYzZjMjY0ZTJlYmE0ZDAyMzU3Yjk1MWQyYzEwMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZDliNjVkMzA2NDU4NDAwYmJhNDliMGI1ZTEwYzZjYmM2ZmIwYTE4NjUyZTQ2
10
- NTQzMTdkZDRmNjE0Mzg5NDk3NmM3YmQzYTJhNDdjZDlhMTQ1NGQ4ZTJjNjVk
11
- Y2NjOTY0MDQwYTNhZWI5ZmY1NGNmMTU5NDdjYmFiYjc4MDMxYzQ=
9
+ ZWM3MzZhM2MwYjI5MzY2ZTg1Y2YxMzk1Y2NkYTk2NmM3MzJjNDIyYjc1NDI3
10
+ ZWZiZTIxOGUxYmRkZWZjMmVjYzMzMzAyMzQ3MjI5MDBkMzkxNGQ2OGRiOGY4
11
+ ZmU3OTRlNWYxYjcxN2I0ZjU5OWNlNDYzZDM3YjNlZTBhM2FhY2Y=
12
12
  data.tar.gz: !binary |-
13
- YTU2N2E5YzM0Zjc1NDY0NjZiNmM1MzdhMjIyYmI5ODY1YjI5N2E1MzM5NmE2
14
- MmM5YmFkYzlmMzlhNDQzNTY3NWQ3NmM4NWJiNzQ5Zjk5NjcwNjFhNjQ2OGJj
15
- MzNiOGNhMGJjNzc5MWJkZWYyYzk1ZGRiNWI2OGQxZWVmNThlMjQ=
13
+ ZThhZjY3YzM3ZjBjMTUwNjYwMjQzOTkwNTNkZmY0ZTNiZDY4NDVhOWIzZjRj
14
+ Mjk1Njg4OTJmZmVhNGFjZjAwYTFhM2VmOTE1NzU5MDU3ZDkwOTE0ODZkMGNm
15
+ YTA0NDc5ZTlhODY0Nzc1NmI3Y2I0YmJlYzEyNjA5ZmUwYWY2MjA=
@@ -0,0 +1,21 @@
1
+ ---
2
+ engines:
3
+ bundler-audit:
4
+ enabled: false
5
+ duplication:
6
+ enabled: true
7
+ config:
8
+ languages:
9
+ - ruby
10
+ fixme:
11
+ enabled: true
12
+ rubocop:
13
+ enabled: true
14
+ ratings:
15
+ paths:
16
+ - Gemfile.lock
17
+ - "**.rb"
18
+ exclude_paths:
19
+ - spec/
20
+ - templates/
21
+ - doc/
@@ -3,3 +3,7 @@ Metrics/ClassLength:
3
3
 
4
4
  Metrics/LineLength:
5
5
  Max: 175
6
+
7
+ AllCops:
8
+ Exclude:
9
+ - 'spec/**/*'
@@ -0,0 +1,24 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.2.0] - 2016-05-18
8
+ ### Changed
9
+ - Upgraded dependencies
10
+ - `rubyntlm`: ~>0.4.0 to ~>0.6.0
11
+ - `savon`: ~>2.10.0 to ~>2.11.0
12
+ - `rubocop`: ~>0.28.0 to ~>0.39.0
13
+ - Lots of rubocop-y code cleanup
14
+ - Converted from code climate classic to code climate platform
15
+ - Added rake task to run code climate platform locally
16
+ - Broke low-level secret server operations into `zanzibar/client`
17
+
18
+ ## [0.1.27] - 2016-04-15
19
+ ### Added
20
+ - `zanzibar get` can fetch field values for fields other than password
21
+ - This ability has not been added to Zanzifiles yet.
22
+
23
+ [0.2.0]: https://github.com/Cimpress-MCP/Zanzibar/compare/v0.1.27...v0.2.0
24
+ [Unreleased]: https://github.com/Cimpress-MCP/Zanzibar/compare/v0.2.0...HEAD
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Zanzibar
2
2
  [![Gem Version](https://badge.fury.io/rb/zanzibar.svg)](http://badge.fury.io/rb/zanzibar)
3
+ [![Code Climate](https://codeclimate.com/github/Cimpress-MCP/zanzibar/badges/gpa.svg?1=1)](https://codeclimate.com/github/Cimpress-MCP/zanzibar)
4
+ [![Test Coverage](https://codeclimate.com/github/Cimpress-MCP/zanzibar/badges/coverage.svg)](https://codeclimate.com/github/Cimpress-MCP/zanzibar/coverage)
5
+ [![Dependency Status](https://gemnasium.com/badges/github.com/Cimpress-MCP/zanzibar.svg)](https://gemnasium.com/github.com/Cimpress-MCP/zanzibar)
6
+ [![Inline docs](http://inch-ci.org/github/cimpress-mcp/zanzibar.svg?branch=master)](http://inch-ci.org/github/cimpress-mcp/zanzibar)
3
7
 
4
8
  Zanzibar is a utility to retrieve secrets from a Secret Server installation. It supports retrieval of a password, public/private key, or secret attachment.
5
9
 
@@ -98,7 +102,7 @@ When it downloads a file, it gets added to `Zanzifile.resolved`. And next time
98
102
  `resolved` file, it will not attempt to re-download. `zanzibar update` will attempt
99
103
  to re-download all secrets.
100
104
 
101
- Subdirectories under the root directory `secret_dir` can be created for individual keys by specifying a `prefix` path for that secret. Secrets will default to be downloaded to the root `secret_dir` directory otherwise.
105
+ Subdirectories under the root directory `secret_dir` can be created for individual keys by specifying a `prefix` path for that secret. Secrets will default to be downloaded to the root `secret_dir` directory otherwise.
102
106
 
103
107
  Note: `zanzibar get` can fetch passwords or files, but `zanzibar bundle` can
104
108
  only operate on secret files.
data/Rakefile CHANGED
@@ -7,5 +7,15 @@ require 'rubocop/rake_task'
7
7
  task default: [:test]
8
8
 
9
9
  RSpec::Core::RakeTask.new(:test)
10
-
11
10
  RuboCop::RakeTask.new
11
+
12
+ task :cc_local do
13
+ command = 'docker run '
14
+ command << '--interactive --tty --rm '
15
+ command << '--env CODECLIMATE_CODE="$PWD" '
16
+ command << '--volume "$PWD":/code '
17
+ command << '--volume /var/run/docker.sock:/var/run/docker.sock '
18
+ command << '--volume /tmp/cc:/tmp/cc '
19
+ command << 'codeclimate/codeclimate analyze'
20
+ sh command
21
+ end
@@ -3,216 +3,175 @@ require 'savon'
3
3
  require 'io/console'
4
4
  require 'fileutils'
5
5
  require 'yaml'
6
+ require 'zanzibar/client'
6
7
 
7
8
  module Zanzibar
8
9
  ##
9
- # Class for interacting with Secret Server
10
+ # High-level operations for downloading things from Secret Server
10
11
  class Zanzibar
11
12
  ##
12
13
  # @param args{:domain, :wsdl, :pwd, :username, :globals{}}
13
-
14
14
  def initialize(args = {})
15
- if args[:username]
16
- @@username = args[:username]
17
- elsif ENV['ZANZIBAR_USER']
18
- @@username = ENV['ZANZIBAR_USER']
19
- else
20
- @@username = ENV['USER']
21
- end
22
-
23
- if args[:wsdl]
24
- @@wsdl = args[:wsdl]
25
- else
26
- @@wsdl = get_wsdl_location
27
- end
28
-
29
- if args[:pwd]
30
- @@password = args[:pwd]
31
- elsif ENV['ZANZIBAR_PASSWORD']
32
- @@password = ENV['ZANZIBAR_PASSWORD']
33
- else
34
- @@password = prompt_for_password
35
- end
36
-
37
- if args[:domain]
38
- @@domain = args[:domain]
39
- else
40
- @@domain = prompt_for_domain
41
- end
15
+ @username = resolve_username(args)
16
+ @wsdl = resolve_wsdl(args)
17
+ @password = resolve_password(args)
18
+ @domain = resolve_domain(args)
42
19
  args[:globals] = {} unless args[:globals]
43
- init_client(args[:globals])
44
- end
45
-
46
- def get_client_username
47
- @@username
48
- end
49
-
50
- def get_client_password
51
- @@password
20
+ @client = Client.new(@username, @password, @domain, @wsdl, args[:globals])
52
21
  end
53
22
 
54
- ## Initializes the Savon client class variable with the wdsl document location and optional global variables
55
- # @param globals{}, optional
56
-
57
- def init_client(globals = {})
58
- globals = {} if globals.nil?
59
- @@client = Savon.client(globals) do
60
- wsdl @@wsdl
61
- end
62
- end
63
-
64
- ## Gets the user's password if none is provided in the constructor.
23
+ ##
24
+ # Gets the user's password if none is provided in the constructor.
65
25
  # @return [String] the password for the current user
66
-
67
26
  def prompt_for_password
68
- puts "Please enter password for #{@@username}:"
27
+ puts "Please enter password for #{@username}:"
69
28
  STDIN.noecho(&:gets).chomp.tap do
70
- puts "Using password to login..."
29
+ puts 'Using password to login...'
71
30
  end
72
31
  end
73
32
 
74
- ## Gets the wsdl document location if none is provided in the constructor
33
+ ##
34
+ # Gets the wsdl document location if none is provided in the constructor
75
35
  # @return [String] the location of the WDSL document
76
-
77
36
  def prompt_for_wsdl_location
78
37
  puts 'Enter the URL of the Secret Server WSDL:'
79
38
  STDIN.gets.chomp
80
39
  end
81
40
 
82
- ## Gets the domain of the Secret Server installation if none is provided in the constructor
41
+ ##
42
+ # Gets the domain of the Secret Server installation if none is provided in the constructor
83
43
  # @return [String] the domain of the secret server installation
84
-
85
44
  def prompt_for_domain
86
45
  puts 'Enter the domain of your Secret Server:'
87
46
  STDIN.gets.chomp
88
47
  end
89
48
 
90
- ## Get an authentication token for interacting with Secret Server. These are only good for about 10 minutes so just get a new one each time.
91
- # Will raise an error if there is an issue with the authentication.
92
- # @return the authentication token for the current user.
93
-
94
- def get_token
95
- response = @@client.call(:authenticate, message: { username: @@username, password: @@password, organization: '', domain: @@domain })
96
- .hash[:envelope][:body][:authenticate_response][:authenticate_result]
97
- fail "Error generating the authentication token for user #{@@username}: #{response[:errors][:string]}" if response[:errors]
98
- response[:token]
99
- rescue Savon::Error => err
100
- raise "There was an error generating the authentiaton token for user #{@@username}: #{err}"
101
- end
102
-
103
- ## Get a secret returned as a hash
104
- # Will raise an error if there was an issue getting the secret
105
- # @param [Integer] the secret id
106
- # @return [Hash] the secret hash retrieved from the wsdl
107
-
108
- def get_secret(scrt_id, token = nil)
109
- secret = @@client.call(:get_secret, message: { token: token || get_token, secretId: scrt_id }).hash[:envelope][:body][:get_secret_response][:get_secret_result]
110
- fail "There was an error getting secret #{scrt_id}: #{secret[:errors][:string]}" if secret[:errors]
111
- return secret
112
- rescue Savon::Error => err
113
- raise "There was an error getting the secret with id #{scrt_id}: #{err}"
114
- end
115
-
116
- ## Retrieve the value from a field label of a secret
49
+ ##
50
+ # Retrieve the value from a field label of a secret
117
51
  # Will raise an error if there are any issues
118
52
  # @param [Integer] the secret id
119
53
  # @param [String] the field label to get, defaults to Password
120
54
  # @return [String] the value for the given field label
121
55
  def get_fieldlabel_value(scrt_id, fieldlabel = 'Password')
122
- secret = get_secret(scrt_id)
56
+ secret = @client.get_secret(scrt_id)
123
57
  secret_items = secret[:secret][:items][:secret_item]
124
- return get_secret_item_by_field_name(secret_items, fieldlabel)[:value]
58
+ return @client.get_secret_item_by_field_name(secret_items, fieldlabel)[:value]
125
59
  rescue Savon::Error => err
126
60
  raise "There was an error getting '#{fieldlabel}' for secret #{scrt_id}: #{err}"
127
61
  end
128
62
 
129
- ## Retrieve a simple password from a secret
63
+ ##
64
+ # Retrieve a simple password from a secret
130
65
  # Calls get get_fieldlabel_value()
131
66
  # @param [Integer] the secret id
132
67
  # @return [String] the password for the given secret
133
68
  def get_password(scrt_id)
134
- return get_fieldlabel_value(scrt_id)
69
+ get_fieldlabel_value(scrt_id)
135
70
  end
136
71
 
137
- ## Get the password, save it to a file, and return the path to the file.
72
+ ##
73
+ # Get the password, save it to a file, and return the path to the file.
138
74
  def get_username_and_password_and_save(scrt_id, path, name)
139
- secret_items = get_secret(scrt_id)[:secret][:items][:secret_item]
140
- password = get_secret_item_by_field_name(secret_items, 'Password')[:value]
141
- username = get_secret_item_by_field_name(secret_items, 'Username')[:value]
75
+ secret_items = @client.get_secret(scrt_id)[:secret][:items][:secret_item]
76
+ password = @client.get_secret_item_by_field_name(secret_items, 'Password')[:value]
77
+ username = @client.get_secret_item_by_field_name(secret_items, 'Username')[:value]
142
78
  save_username_and_password_to_file(password, username, path, name)
143
- return File.join(path, name)
144
- end
145
-
146
- def write_secret_to_file(path, secret_response)
147
- File.open(File.join(path, secret_response[:file_name]), 'wb') do |file|
148
- file.puts Base64.decode64(secret_response[:file_attachment])
149
- end
79
+ File.join(path, name)
150
80
  end
151
81
 
152
- ## Write the password to a file. Intended for use with a Zanzifile
82
+ ##
83
+ # Write the password to a file. Intended for use with a Zanzifile
153
84
  def save_username_and_password_to_file(password, username, path, name)
154
- user_pass = {'username' => username.to_s, 'password' => password.to_s}.to_yaml
85
+ user_pass = { 'username' => username.to_s, 'password' => password.to_s }.to_yaml
155
86
  File.open(File.join(path, name), 'wb') do |file|
156
87
  file.print user_pass
157
88
  end
158
89
  end
159
90
 
160
- def get_secret_item_by_field_name(secret_items, field_name)
161
- secret_items.each do |item|
162
- return item if item[:field_name] == field_name
163
- end
164
- end
165
-
166
- ## Get the secret item id that relates to a key file or attachment.
167
- # Will raise on error
168
- # @param [Integer] the secret id
169
- # @param [String] the type of secret item to get, one of privatekey, publickey, attachment
170
- # @return [Integer] the secret item id
171
-
172
- def get_scrt_item_id(scrt_id, type, token)
173
- secret = get_secret(scrt_id, token)
174
- secret_items = secret[:secret][:items][:secret_item]
175
- begin
176
- return get_secret_item_by_field_name(secret_items, type)[:id]
177
- rescue
178
- raise "Unknown type, #{type}."
179
- end
180
- end
181
-
182
- ## Downloads a file for a secret and places it where Zanzibar is running, or :path if specified
91
+ ##
92
+ # Downloads a file for a secret and places it where Zanzibar is running, or :path if specified
183
93
  # Raise on error
184
94
  # @param [Hash] args, :scrt_id, :type (one of "Private Key", "Public Key", "Attachment"), :scrt_item_id - optional, :path - optional
185
-
186
95
  def download_secret_file(args = {})
187
- token = get_token
188
- FileUtils.mkdir_p(args[:path]) if args[:path]
189
- path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
190
- begin
191
- response = @@client.call(:download_file_attachment_by_item_id, message:
192
- { token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], args[:type], token) })
193
- .hash[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result]
194
- fail "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{response[:errors][:string]}" if response[:errors]
195
- write_secret_to_file(path, response)
196
- return File.join(path, response[:file_name])
197
- rescue Savon::Error => err
198
- raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{err}"
199
- end
96
+ response = @client.download_file_attachment_by_item_id(args[:scrt_id], args[:scrt_item_id], args[:type])
97
+ raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{response[:errors][:string]}" if response[:errors]
98
+ return write_secret_to_file(args[:path], response)
99
+ rescue Savon::Error => err
100
+ raise "There was an error getting the #{args[:type]} for secret #{args[:scrt_id]}: #{err}"
200
101
  end
201
102
 
202
- ## Methods to maintain backwards compatibility
103
+ ##
104
+ # Download a private key secret
105
+ # @deprecated
203
106
  def download_private_key(args = {})
204
107
  args[:type] = 'Private Key'
205
108
  download_secret_file(args)
206
109
  end
207
110
 
111
+ ##
112
+ # Download a public key secret
113
+ # @deprecated
208
114
  def download_public_key(args = {})
209
115
  args[:type] = 'Public Key'
210
116
  download_secret_file(args)
211
117
  end
212
118
 
119
+ ##
120
+ # Download an arbitrary secret attachment
121
+ # @deprecated
213
122
  def download_attachment(args = {})
214
123
  args[:type] = 'Attachment'
215
124
  download_secret_file(args)
216
125
  end
126
+
127
+ private
128
+
129
+ def make_or_find_path(path = nil)
130
+ FileUtils.mkdir_p(path) if path
131
+ path || '.'
132
+ end
133
+
134
+ def resolve_username(args = {})
135
+ if args[:username]
136
+ args[:username]
137
+ elsif ENV['ZANZIBAR_USER']
138
+ ENV['ZANZIBAR_USER']
139
+ else
140
+ ENV['USER']
141
+ end
142
+ end
143
+
144
+ def resolve_wsdl(args = {})
145
+ args[:wsdl]
146
+ end
147
+
148
+ def resolve_password(args = {})
149
+ if args[:pwd]
150
+ args[:pwd]
151
+ elsif ENV['ZANZIBAR_PASSWORD']
152
+ ENV['ZANZIBAR_PASSWORD']
153
+ else
154
+ prompt_for_password
155
+ end
156
+ end
157
+
158
+ def resolve_domain(args = {})
159
+ if args[:domain]
160
+ args[:domain]
161
+ else
162
+ prompt_for_domain
163
+ end
164
+ end
165
+
166
+ def write_secret_to_file(path, secret_response)
167
+ path = make_or_find_path(path)
168
+ filepath = File.join(path, secret_response[:file_name])
169
+
170
+ File.open(filepath, 'wb') do |file|
171
+ file.puts Base64.decode64(secret_response[:file_attachment])
172
+ end
173
+
174
+ filepath
175
+ end
217
176
  end
218
177
  end
@@ -2,12 +2,18 @@ module Zanzibar
2
2
  module Actions
3
3
  # Basic plumbing for all actions
4
4
  class Base
5
+ ##
6
+ # The options passed in from the Thor action
5
7
  attr_accessor :options
6
8
  private :options=
7
9
 
10
+ ##
11
+ # The logger that Thor is using for this run
8
12
  attr_accessor :logger
9
13
  private :logger=
10
14
 
15
+ ##
16
+ # Initialize the basic components used by all actions
11
17
  def initialize(logger, options = {})
12
18
  self.logger = logger
13
19
  self.options = options
@@ -6,17 +6,35 @@ module Zanzibar
6
6
  module Actions
7
7
  # Download or verify the secrets in a Zanzifile
8
8
  class Bundle < Base
9
+ ##
10
+ # The settings defined in the Zanzifile
9
11
  attr_accessor :settings
12
+
13
+ ##
14
+ # The unresolved secrets from the Zanzifile
10
15
  attr_accessor :remote_secrets
16
+
17
+ ##
18
+ # The resolved secrets from the Zanzifile.resolved
11
19
  attr_accessor :local_secrets
20
+
21
+ ##
22
+ # Whether to disregard local secrets and re-download regardness
12
23
  attr_accessor :update
24
+
25
+ ##
26
+ # Our Zanzibar client
13
27
  attr_accessor :zanzibar
14
28
 
29
+ ##
30
+ # An action that will fetch secrets defined in a Zanzifile
15
31
  def initialize(ui, options, args = {})
16
32
  super(ui, options)
17
33
  @update = args[:update]
18
34
  end
19
35
 
36
+ ##
37
+ # Coordinate downloading to secrets (or skipping ones we already have)
20
38
  def run
21
39
  ensure_zanzifile
22
40
  load_required_secrets
@@ -39,7 +57,7 @@ module Zanzibar
39
57
  end
40
58
 
41
59
  def ensure_zanzifile
42
- fail Error, NO_ZANZIFILE_ERROR unless File.exist? ZANZIFILE_NAME
60
+ raise Error, NO_ZANZIFILE_ERROR unless File.exist? ZANZIFILE_NAME
43
61
  debug { "#{ZANZIFILE_NAME} located..." }
44
62
  end
45
63
 
@@ -47,7 +65,7 @@ module Zanzibar
47
65
  ## Make sure the directory exists and that a .gitignore is there to ignore it
48
66
  if @settings['secret_dir']
49
67
  FileUtils.mkdir_p(@settings['secret_dir'])
50
- if !File.exist? "#{@settings['secret_dir']}/.gitignore"
68
+ unless File.exist? "#{@settings['secret_dir']}/.gitignore"
51
69
  File.open("#{@settings['secret_dir']}/.gitignore", 'w') do |file|
52
70
  file.puts '*'
53
71
  file.puts '!.gitignore'
@@ -69,7 +87,7 @@ module Zanzibar
69
87
 
70
88
  def validate_environment
71
89
  return unless @settings.empty? || @remote_secrets.empty?
72
- fail Error, INVALID_ZANZIFILE_ERROR
90
+ raise Error, INVALID_ZANZIFILE_ERROR
73
91
  end
74
92
 
75
93
  def load_resolved_secrets
@@ -94,12 +112,9 @@ module Zanzibar
94
112
 
95
113
  downloaded_secrets = {}
96
114
  remote_secrets.each do |key, secret|
97
- full_path = secret.has_key?('prefix') ? File.join(@settings['secret_dir'], secret['prefix']) : @settings['secret_dir']
98
- downloaded_secrets[key] = download_one_secret(secret['id'],
99
- secret['label'],
100
- full_path,
101
- args,
102
- secret['name'] || "#{secret['id']}_password")
115
+ full_path = secret.key?('prefix') ? File.join(@settings['secret_dir'], secret['prefix']) : @settings['secret_dir']
116
+ downloaded_secrets[key] = download_one_secret(secret['id'], secret['label'], full_path,
117
+ args, secret_filename(secret))
103
118
 
104
119
  debug { "Downloaded secret: #{key} to #{@settings['secret_dir']}..." }
105
120
  end
@@ -110,13 +125,13 @@ module Zanzibar
110
125
  def download_one_secret(scrt_id, label, path, args, name = nil)
111
126
  if label == 'Password'
112
127
  path = zanzibar(args).get_username_and_password_and_save(scrt_id, path, name)
113
- { path: path, hash: Digest::MD5.file(path).hexdigest }
114
128
  else
115
129
  path = zanzibar(args).download_secret_file(scrt_id: scrt_id,
116
- type: label,
117
- path: path)
118
- { path: path, hash: Digest::MD5.file(path).hexdigest }
130
+ type: label,
131
+ path: path)
119
132
  end
133
+
134
+ { path: path, hash: Digest::MD5.file(path).hexdigest }
120
135
  end
121
136
 
122
137
  def update_resolved_file(new_secrets)
@@ -134,6 +149,10 @@ module Zanzibar
134
149
  domain: @settings['domain'],
135
150
  globals: args)
136
151
  end
152
+
153
+ def secret_filename(secret)
154
+ secret['name'] || "#{secret['id']}_password"
155
+ end
137
156
  end
138
157
  end
139
158
  end
@@ -7,15 +7,24 @@ module Zanzibar
7
7
  module Actions
8
8
  # Fetch a single secret
9
9
  class Get < Base
10
+ ##
11
+ # The options to use when initializing our Zanzibar client
10
12
  attr_accessor :zanibar_options
13
+
14
+ ##
15
+ # The id of the secret to download
11
16
  attr_accessor :scrt_id
12
17
 
18
+ ##
19
+ # Initialize the action
13
20
  def initialize(ui, options, scrt_id)
14
21
  super(ui, options)
15
22
  @scrt_id = scrt_id
16
23
  @zanzibar_options = {}
17
24
  end
18
25
 
26
+ ##
27
+ # Ensure we have the options we need and download the secret
19
28
  def run
20
29
  construct_options
21
30
  ensure_options
@@ -23,6 +32,8 @@ module Zanzibar
23
32
  fetch_secret(@scrt_id)
24
33
  end
25
34
 
35
+ ##
36
+ # Actually download the secret
26
37
  def fetch_secret(scrt_id)
27
38
  scrt = ::Zanzibar::Zanzibar.new(@zanzibar_options)
28
39
 
@@ -32,9 +43,10 @@ module Zanzibar
32
43
  else
33
44
  scrt.get_fieldlabel_value(scrt_id, @zanzibar_options[:fieldlabel])
34
45
  end
35
-
36
46
  end
37
47
 
48
+ ##
49
+ # Coalesce our options and some defaults to ensure we are ready to run
38
50
  def construct_options
39
51
  @zanzibar_options[:wsdl] = construct_wsdl
40
52
  @zanzibar_options[:globals] = { ssl_verify_mode: :none } if options['ignoressl']
@@ -45,6 +57,8 @@ module Zanzibar
45
57
  @zanzibar_options[:filelabel] = options['filelabel'] if options['filelabel']
46
58
  end
47
59
 
60
+ ##
61
+ # Construct a WSDL URL from the server hostname if necessary
48
62
  def construct_wsdl
49
63
  if options['wsdl'].nil? && options['server']
50
64
  DEFAULT_WSDL % options['server']
@@ -53,9 +67,11 @@ module Zanzibar
53
67
  end
54
68
  end
55
69
 
70
+ ##
71
+ # Make sure a proper WSDL was constructed
56
72
  def ensure_options
57
73
  return if @zanzibar_options[:wsdl]
58
- fail Error, NO_WSDL_ERROR
74
+ raise Error, NO_WSDL_ERROR
59
75
  end
60
76
  end
61
77
  end
@@ -8,6 +8,8 @@ module Zanzibar
8
8
  module Actions
9
9
  # Create a new Zanzifile
10
10
  class Init < Base
11
+ ##
12
+ # Make sure we don't already have a Zanzifile, then template one
11
13
  def run
12
14
  check_for_zanzifile
13
15
  write_template
@@ -17,7 +19,7 @@ module Zanzibar
17
19
 
18
20
  def check_for_zanzifile
19
21
  return unless File.exist?(ZANZIFILE_NAME) && !options['force']
20
- fail Error, ALREADY_EXISTS_ERROR
22
+ raise Error, ALREADY_EXISTS_ERROR
21
23
  end
22
24
 
23
25
  def write_template
@@ -28,9 +30,12 @@ module Zanzibar
28
30
  end
29
31
  end
30
32
 
33
+ ##
31
34
  # Allows us to easily feed our options hash
32
35
  # to an ERB
33
36
  class TemplateRenderer < OpenStruct
37
+ ##
38
+ # Render an ERB template to a string
34
39
  def render(template)
35
40
  ERB.new(template).result(binding)
36
41
  end
@@ -8,12 +8,18 @@ require 'zanzibar/error'
8
8
  require 'zanzibar/defaults'
9
9
 
10
10
  module Zanzibar
11
- # The `zanzibar` binay/thor application main class
11
+ ##
12
+ # The `zanzibar` binary/thor application main class.
13
+ # See http://whatisthor.com/ for information on syntax.
12
14
  class Cli < Thor
13
15
  include Thor::Actions
14
16
 
17
+ ##
18
+ # The stream to which we are writing messages (usually stdout)
15
19
  attr_accessor :ui
16
20
 
21
+ ##
22
+ # Initialize the application and set some logging defaults
17
23
  def initialize(*)
18
24
  super
19
25
  the_shell = (options['no-color'] ? Thor::Shell::Basic.new : shell)
@@ -24,11 +30,15 @@ module Zanzibar
24
30
  debug_header
25
31
  end
26
32
 
33
+ ##
34
+ # Print the version of the application
27
35
  desc 'version', 'Display your Zanzibar verion'
28
36
  def version
29
37
  say "#{APPLICATION_NAME} Version: #{VERSION}"
30
38
  end
31
39
 
40
+ ##
41
+ # Generate a new blank Zanzifile
32
42
  desc 'init', "Create an empty #{ZANZIFILE_NAME} in the current directory."
33
43
  option 'verbose', type: :boolean, default: false, aliases: :v
34
44
  option 'wsdl', type: :string, aliases: :w,
@@ -46,21 +56,29 @@ module Zanzibar
46
56
  run_action { init! }
47
57
  end
48
58
 
59
+ ##
60
+ # Fetch secrets declared in a local Zanzifle
61
+
49
62
  desc 'bundle', "Fetch secrets declared in your #{ZANZIFILE_NAME}"
50
63
  option 'verbose', type: :boolean, default: false, aliases: :v
64
+
51
65
  def bundle
52
66
  run_action { bundle! }
53
67
  end
54
68
 
55
- desc 'plunder', "Alias to `#{APPLICATION_NAME} bundle`", :hide => true
69
+ desc 'plunder', "Alias to `#{APPLICATION_NAME} bundle`", hide: true
56
70
  option 'verbose', type: :boolean, default: false, aliases: :v
57
- alias_method :plunder, :bundle
71
+ alias plunder bundle
58
72
 
59
73
  desc 'install', "Alias to `#{APPLICATION_NAME} bundle`"
60
- alias_method :install, :bundle
74
+ alias install bundle
75
+
76
+ ##
77
+ # Redownload Zazifile secrets
61
78
 
62
79
  desc 'update', "Redownload all secrets in your #{ZANZIFILE_NAME}"
63
80
  option 'verbose', type: :boolean, default: false, aliases: :v
81
+
64
82
  def update
65
83
  run_action { update! }
66
84
  end
@@ -80,6 +98,8 @@ module Zanzibar
80
98
  desc: 'Specify a field (by label) to get'
81
99
  option 'username', type: :string, aliases: :u
82
100
  option 'password', type: :string, aliases: :p
101
+ ##
102
+ # Fetch a single secret specified on the commandline
83
103
  def get(scrt_id)
84
104
  run_action { get! scrt_id }
85
105
  end
@@ -93,6 +113,7 @@ module Zanzibar
93
113
  @ui.debug { "#{APPLICATION_NAME} Version: #{VERSION}" }
94
114
  end
95
115
 
116
+ ##
96
117
  # Run the specified action and rescue errors we
97
118
  # explicitly send back to format them
98
119
  def run_action(&_block)
@@ -0,0 +1,91 @@
1
+ require 'zanzibar/version'
2
+ require 'savon'
3
+ require 'io/console'
4
+ require 'fileutils'
5
+ require 'yaml'
6
+
7
+ module Zanzibar
8
+ ##
9
+ # Class for performing low-level WSDL actions against Secret Server
10
+ class Client
11
+ ##
12
+ # Initializes the Savon client class variable with the wdsl document location and optional global variables
13
+ # @param globals{}, optional
14
+ def initialize(username, password, domain, wsdl, globals = {})
15
+ @username = username
16
+ @password = password
17
+ @domain = domain
18
+
19
+ globals = {} if globals.nil?
20
+
21
+ wsdl_loc = wsdl
22
+ @client = Savon.client(globals) do
23
+ wsdl wsdl_loc
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Get an authentication token for interacting with Secret Server. These are only good for about 10 minutes so just get a new one each time.
29
+ # Will raise an error if there is an issue with the authentication.
30
+ # @return the authentication token for the current user.
31
+ def generate_token
32
+ response = @client.call(:authenticate, message: { username: @username, password: @password, organization: '', domain: @domain })
33
+ .hash[:envelope][:body][:authenticate_response][:authenticate_result]
34
+ raise "Error generating the authentication token for user #{@username}: #{response[:errors][:string]}" if response[:errors]
35
+ response[:token]
36
+ rescue Savon::Error => err
37
+ raise "There was an error generating the authentiaton token for user #{@username}: #{err}"
38
+ end
39
+
40
+ ##
41
+ # Get a secret returned as a hash
42
+ # Will raise an error if there was an issue getting the secret
43
+ # @param [Integer] the secret id
44
+ # @return [Hash] the secret hash retrieved from the wsdl
45
+ def get_secret(scrt_id, token = nil)
46
+ secret = @client.call(:get_secret, message: { token: token || generate_token, secretId: scrt_id }).hash[:envelope][:body][:get_secret_response][:get_secret_result]
47
+ raise "There was an error getting secret #{scrt_id}: #{secret[:errors][:string]}" if secret[:errors]
48
+ return secret
49
+ rescue Savon::Error => err
50
+ raise "There was an error getting the secret with id #{scrt_id}: #{err}"
51
+ end
52
+
53
+ ##
54
+ # Get the secret item id that relates to a key file or attachment.
55
+ # Will raise on error
56
+ # @param [Integer] the secret id
57
+ # @param [String] the type of secret item to get, one of privatekey, publickey, attachment
58
+ # @return [Integer] the secret item id
59
+ def get_scrt_item_id(scrt_id, type, token)
60
+ secret = get_secret(scrt_id, token)
61
+ secret_items = secret[:secret][:items][:secret_item]
62
+ begin
63
+ return get_secret_item_by_field_name(secret_items, type)[:id]
64
+ rescue => e
65
+
66
+ raise "Unknown type, #{type}. #{e}"
67
+ end
68
+ end
69
+
70
+ ##
71
+ # Get an "Attachment"-type file from a secret
72
+ # @param [Integer] the id of the secret
73
+ # @param [Integer] the id of the attachment on the secret
74
+ # @param [String] the type of the item being downloaded
75
+ # @return [Hash] contents and metadata of the downloaded file
76
+ def download_file_attachment_by_item_id(scrt_id, secret_item_id, item_type, token = nil)
77
+ token = generate_token unless token
78
+ @client.call(:download_file_attachment_by_item_id, message:
79
+ { token: token, secretId: scrt_id, secretItemId: secret_item_id || get_scrt_item_id(scrt_id, item_type, token) })
80
+ .hash[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result]
81
+ end
82
+
83
+ ##
84
+ # Extract an item from a secret based on field name
85
+ def get_secret_item_by_field_name(secret_items, field_name)
86
+ secret_items.each do |item|
87
+ return item if item[:field_name] == field_name
88
+ end
89
+ end
90
+ end
91
+ end
@@ -2,15 +2,25 @@ require 'pathname'
2
2
 
3
3
  # Definitions for various strings used throughout the gem
4
4
  module Zanzibar
5
+ # The name of the binstub that invoked this code
5
6
  APPLICATION_NAME = Pathname.new($PROGRAM_NAME).basename
6
- ZANZIFILE_NAME = 'Zanzifile'
7
- RESOLVED_NAME = 'Zanzifile.resolved'
8
- TEMPLATE_NAME = 'templates/Zanzifile.erb'
9
- DEFAULT_SERVER = 'secret.example.com'
10
- DEFAULT_WSDL = 'https://%s/webservices/sswebservice.asmx?wsdl'
7
+ # The filename of the Zanzifile
8
+ ZANZIFILE_NAME = 'Zanzifile'.freeze
9
+ # The filename of the resolved Zanzifile
10
+ RESOLVED_NAME = 'Zanzifile.resolved'.freeze
11
+ # The template to use when writing the Zanzifile
12
+ TEMPLATE_NAME = 'templates/Zanzifile.erb'.freeze
13
+ # The default value of the server when writing the Zanzifile
14
+ DEFAULT_SERVER = 'secret.example.com'.freeze
15
+ # The default WSDL location for the Zanzifile template
16
+ DEFAULT_WSDL = 'https://%s/webservices/sswebservice.asmx?wsdl'.freeze
11
17
 
12
- ALREADY_EXISTS_ERROR = "#{ZANZIFILE_NAME} already exists! Aborting..."
13
- NO_WSDL_ERROR = 'Could not construct WSDL URL. Please provide either --server or --wsdl'
14
- NO_ZANZIFILE_ERROR = "You don't have a #{ZANZIFILE_NAME}! Run `#{APPLICATION_NAME} init` first!"
15
- INVALID_ZANZIFILE_ERROR = "Unable to load your #{ZANZIFILE_NAME}. Please ensure it is valid YAML."
18
+ # Error thrown when trying to overwrite an existing Zanzifile
19
+ ALREADY_EXISTS_ERROR = "#{ZANZIFILE_NAME} already exists! Aborting...".freeze
20
+ # Error thrown when unable to construct the WSDL location
21
+ NO_WSDL_ERROR = 'Could not construct WSDL URL. Please provide either --server or --wsdl'.freeze
22
+ # Error thrown when trying to download secrets from a Zanzifile that doesn't exist
23
+ NO_ZANZIFILE_ERROR = "You don't have a #{ZANZIFILE_NAME}! Run `#{APPLICATION_NAME} init` first!".freeze
24
+ # Error thrown when a Zanzifile is missing necessary information
25
+ INVALID_ZANZIFILE_ERROR = "Unable to load your #{ZANZIFILE_NAME}. Please ensure it is valid YAML.".freeze
16
26
  end
@@ -1,40 +1,59 @@
1
1
  require 'rubygems/user_interaction'
2
2
 
3
3
  module Zanzibar
4
+ ##
4
5
  # Prints messages out to stdout
5
6
  class Shell
7
+ ##
8
+ # The stream to write log messages (usually stdout)
6
9
  attr_writer :shell
7
10
 
11
+ ##
12
+ # Logging options and initializing stream
8
13
  def initialize(shell)
9
14
  @shell = shell
10
15
  @quiet = false
11
16
  @debug = ENV['DEBUG']
12
17
  end
13
18
 
19
+ ##
20
+ # Write a debug message if debug is enabled
14
21
  def debug(message = nil)
15
22
  @shell.say(message || yield) if @debug && !@quiet
16
23
  end
17
24
 
25
+ ##
26
+ # Write an info message unless we have silenced output
18
27
  def info(message = nil)
19
28
  @shell.say(message || yield) unless @quiet
20
29
  end
21
30
 
31
+ ##
32
+ # Ask the user for confirmation unless we have silenced output
22
33
  def confirm(message = nil)
23
34
  @shell.say(message || yield, :green) unless @quiet
24
35
  end
25
36
 
37
+ ##
38
+ # Print a warning
26
39
  def warn(message = nil)
27
40
  @shell.say(message || yield, :yellow)
28
41
  end
29
42
 
43
+ ##
44
+ # Print an error
30
45
  def error(message = nil)
31
46
  @shell.say(message || yield, :red)
32
47
  end
33
48
 
49
+ ##
50
+ # Enable silent mode
34
51
  def be_quiet!
35
52
  @quiet = true
36
53
  end
37
54
 
55
+ ##
56
+ # Enable debug mode
38
57
  def debug!
39
58
  @debug = true
40
59
  end
@@ -1,4 +1,5 @@
1
1
  # The version of the gem
2
2
  module Zanzibar
3
- VERSION = '0.1.27'
3
+ # The Version of the application
4
+ VERSION = '0.2.0'.freeze
4
5
  end
@@ -24,12 +24,12 @@ describe Zanzibar::Cli do
24
24
  FakeFS::FileSystem.clone files
25
25
 
26
26
  stub_request(:any, 'https://www.zanzitest.net/webservices/sswebservice.asmx')
27
- .to_return({body: AUTH_XML, status: 200}).then
28
- .to_return({body: SECRET_WITH_KEY_XML, status: 200}).then
29
- .to_return({body: PRIVATE_KEY_XML, status: 200}).then
30
- .to_return({body: AUTH_XML, status: 200}).then
31
- .to_return({body: SECRET_WITH_KEY_XML, status: 200}).then
32
- .to_return({body: PRIVATE_KEY_XML, status: 200})
27
+ .to_return(body: AUTH_XML, status: 200).then
28
+ .to_return(body: SECRET_WITH_KEY_XML, status: 200).then
29
+ .to_return(body: PRIVATE_KEY_XML, status: 200).then
30
+ .to_return(body: AUTH_XML, status: 200).then
31
+ .to_return(body: SECRET_WITH_KEY_XML, status: 200).then
32
+ .to_return(body: PRIVATE_KEY_XML, status: 200)
33
33
 
34
34
  Dir.chdir File.join(source_root, 'spec', 'files')
35
35
  end
@@ -87,9 +87,9 @@ describe Zanzibar::Cli do
87
87
 
88
88
  WebMock.reset!
89
89
  stub_request(:any, 'https://www.zanzitest.net/webservices/sswebservice.asmx')
90
- .to_return({body: AUTH_XML, status: 200}).then
91
- .to_return({body: SECRET_WITH_KEY_XML, status: 200}).then
92
- .to_return({body: PRIVATE_KEY_XML, status: 200}).then
90
+ .to_return(body: AUTH_XML, status: 200).then
91
+ .to_return(body: SECRET_WITH_KEY_XML, status: 200).then
92
+ .to_return(body: PRIVATE_KEY_XML, status: 200).then
93
93
  .to_return(body: AUTH_XML, status: 200).then
94
94
  .to_return(body: SECRET_WITH_KEY_XML, status: 200).then
95
95
  .to_return(body: PRIVATE_KEY_XML, status: 200)
@@ -13,7 +13,7 @@ describe 'Zanzibar Test' do
13
13
  stub_request(:any, 'https://www.zanzitest.net/webservices/sswebservice.asmx')
14
14
  .to_return(body: AUTH_XML, status: 200)
15
15
 
16
- expect(client.get_token).to eq('imatoken')
16
+ expect(client.instance_variable_get(:@client).generate_token).to eq('imatoken')
17
17
  end
18
18
 
19
19
  it 'should get a secret' do
@@ -21,7 +21,7 @@ describe 'Zanzibar Test' do
21
21
  .to_return(body: AUTH_XML, status: 200).then
22
22
  .to_return(body: SECRET_XML, status: 200)
23
23
 
24
- expect(client.get_secret(1234)[:secret][:name]).to eq('Zanzi Test Secret')
24
+ expect(client.instance_variable_get(:@client).get_secret(1234)[:secret][:name]).to eq('Zanzi Test Secret')
25
25
  end
26
26
 
27
27
  it 'should get a password' do
@@ -109,18 +109,18 @@ describe 'Zanzibar Test' do
109
109
  .to_return(body: AUTH_XML, status: 200).then
110
110
  .to_return(body: SECRET_XML, status: 200)
111
111
 
112
- client.get_username_and_password_and_save(1234, '.', 'zanziTestCreds')
113
- expect(File.exist? 'zanziTestCreds')
114
- expect(File.read('zanziTestCreds')).to eq({'username' => 'ZanziUser', 'password' => 'zanziUserPassword'}.to_yaml)
115
- File.delete('zanziTestCreds')
112
+ client.get_username_and_password_and_save(1234, '.', 'zanziTestCreds')
113
+ expect(File.exist? 'zanziTestCreds')
114
+ expect(File.read('zanziTestCreds')).to eq({ 'username' => 'ZanziUser', 'password' => 'zanziUserPassword' }.to_yaml)
115
+ File.delete('zanziTestCreds')
116
116
  end
117
117
 
118
118
  it 'should use environment variables for credentials' do
119
119
  ENV['ZANZIBAR_USER'] = 'environment_user'
120
120
  ENV['ZANZIBAR_PASSWORD'] = 'environment_password'
121
121
  client = Zanzibar::Zanzibar.new(domain: 'zanzitest.net', wsdl: 'spec/scrt.wsdl')
122
- expect(client.get_client_username).to eq(ENV['ZANZIBAR_USER'])
123
- expect(client.get_client_password).to eq(ENV['ZANZIBAR_PASSWORD'])
122
+ expect(client.instance_variable_get(:@username)).to eq(ENV['ZANZIBAR_USER'])
123
+ expect(client.instance_variable_get(:@password)).to eq(ENV['ZANZIBAR_PASSWORD'])
124
124
  ENV.delete 'ZANZIBAR_PASSWORD'
125
125
  ENV.delete 'ZANZIBAR_USER'
126
126
  end
@@ -14,13 +14,13 @@ Gem::Specification.new do |spec|
14
14
  spec.license = 'Apache 2.0'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
- spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(/^(test|spec|features)\//)
17
+ spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)\/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.7'
22
22
  spec.add_development_dependency 'rake', '~> 10.0'
23
- spec.add_development_dependency 'rubocop', '~> 0.28.0'
23
+ spec.add_development_dependency 'rubocop', '~> 0.39.0'
24
24
  spec.add_development_dependency 'savon_spec', '~> 0.1.6'
25
25
  spec.add_development_dependency 'rspec', '~> 3.1.0'
26
26
  spec.add_development_dependency 'webmock', '~> 1.20.4'
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'fakefs', '~> 0.6.4'
29
29
  spec.add_development_dependency 'simplecov', '~> 0.9.1'
30
30
 
31
- spec.add_runtime_dependency 'savon', '~> 2.10.0'
32
- spec.add_runtime_dependency 'rubyntlm', '~> 0.4.0'
31
+ spec.add_runtime_dependency 'savon', '~> 2.11.0'
32
+ spec.add_runtime_dependency 'rubyntlm', '~> 0.6.0'
33
33
  spec.add_runtime_dependency 'thor', '~> 0.19.0'
34
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zanzibar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.27
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Davis-Cooke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-15 00:00:00.000000000 Z
11
+ date: 2016-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: 0.28.0
47
+ version: 0.39.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 0.28.0
54
+ version: 0.39.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: savon_spec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -142,28 +142,28 @@ dependencies:
142
142
  requirements:
143
143
  - - ~>
144
144
  - !ruby/object:Gem::Version
145
- version: 2.10.0
145
+ version: 2.11.0
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - ~>
151
151
  - !ruby/object:Gem::Version
152
- version: 2.10.0
152
+ version: 2.11.0
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: rubyntlm
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - ~>
158
158
  - !ruby/object:Gem::Version
159
- version: 0.4.0
159
+ version: 0.6.0
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - ~>
165
165
  - !ruby/object:Gem::Version
166
- version: 0.4.0
166
+ version: 0.6.0
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: thor
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -187,10 +187,12 @@ executables:
187
187
  extensions: []
188
188
  extra_rdoc_files: []
189
189
  files:
190
+ - .codeclimate.yml
190
191
  - .gitignore
191
192
  - .rspec
192
193
  - .rubocop.yml
193
194
  - .travis.yml
195
+ - CHANGELOG.md
194
196
  - Gemfile
195
197
  - LICENSE.txt
196
198
  - README.md
@@ -204,6 +206,7 @@ files:
204
206
  - lib/zanzibar/actions/get.rb
205
207
  - lib/zanzibar/actions/init.rb
206
208
  - lib/zanzibar/cli.rb
209
+ - lib/zanzibar/client.rb
207
210
  - lib/zanzibar/defaults.rb
208
211
  - lib/zanzibar/error.rb
209
212
  - lib/zanzibar/ui.rb