zanzibar 0.1.27 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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