zanzibar 0.0.8

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.
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'savon'
4
+ gem 'savon_spec'
5
+ gem 'rspec'
6
+ gem 'webmock'
7
+
8
+ # Specify your gem's dependencies in zanzibar.gemspec
9
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Cimpress
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Zanzibar
2
+
3
+
4
+ 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
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'zanzibar'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install zanzibar
21
+
22
+ ## Usage
23
+
24
+ In your ruby project, rakefile, etc., create a new Zanzibar object. The constructor takes a hash of optional parameters for the WSDL location, the domain of the Secret Server, a hash of global variables to pass to savon (necessary for windows environments with self-signed certs) and a password for the current user (intended to be passed in through some encryption method, unless you really want a plaintext password there.). All of these parameters are optional and the user will be prompted to enter them if they are missing.
25
+
26
+ ```ruby
27
+ my_object = Zanzibar::Zanzibar.new(:domain => 'my.domain.net', :wsdl => 'my.scrt.srvr.com/webservices/sswebservice.asmx?wdsl', :pwd => get_encrypted_password_from_somewhere)
28
+ ```
29
+
30
+ Example:
31
+
32
+ ```ruby
33
+ require 'zanzibar'
34
+
35
+ ## Constructor takes hash as argument, all optional :domain, :wsdl, :pwd, :globals
36
+ secrets = Zanzibar::Zanzibar.new(:domain => 'mydomain.net', :wsdl => "https://my.scrt.server/webservices/sswebservice.asmx?wsdl")
37
+ # On windows with self-signed certs,
38
+ # Zanzibar::Zanzibar.new(:domain => 'mydomain.net', :wsdl => "https://my.scrt.server/webservices/sswebservice.asmx?wsdl", :globals => {:ssl_verify_mode => :none})
39
+
40
+ ## Simple password -> takes secret id as argument
41
+ secrets.get_secret(1234)
42
+
43
+ ## Private Key -> takes hash as argument, requires :scrt_id, optional :scrt_item_id, :path
44
+ secrets.download_private_key(:scrt_id => 2345, :path => 'secrets/')
45
+
46
+ ## Public Key -> takes hash as argument, requires :scrt_id, optional :scrt_item_id, :path
47
+ secrets.download_public_key(:scrt_id => 2345, :path => 'secrets/')
48
+
49
+ ## Attachment; only supports secrets with single attachment -> takes hash as argument, requires :scrt_id, optional :scrt_item_id, :path
50
+ secrets.download_attachment(:scrt_id => 3456, :path => 'secrets/')
51
+
52
+ ```
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it ( https://github.com/Cimpress-MCP/zanzibar/fork )
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task 'test' do
4
+ Dir.chdir('test')
5
+ system("rspec zanzibar_spec.rb")
6
+ end
7
+
8
+ task 'install_dependencies' do
9
+ system('bundle install')
10
+ end
data/lib/zanzibar.rb ADDED
@@ -0,0 +1,209 @@
1
+ require "zanzibar/version"
2
+ require 'savon'
3
+ require 'io/console'
4
+ require 'fileutils'
5
+
6
+ module Zanzibar
7
+
8
+ ##
9
+ # Class for interacting with Secret Server
10
+ class Zanzibar
11
+
12
+ ##
13
+ # @param args{:domain, :wsdl, :pwd, :globals{}}
14
+
15
+ def initialize(args = {})
16
+ if args[:wsdl]
17
+ @@wsdl = args[:wsdl]
18
+ else
19
+ @@wsdl = get_wsdl_location
20
+ end
21
+ if args[:pwd]
22
+ @@password = args[:pwd]
23
+ else
24
+ @@password = prompt_for_password
25
+ end
26
+ if args[:domain]
27
+ @@domain = args[:domain]
28
+ else
29
+ @@domain = prompt_for_domain
30
+ end
31
+ args[:globals] = {} unless args[:globals]
32
+ init_client(args[:globals])
33
+ end
34
+
35
+ ## Initializes the Savon client class variable with the wdsl document location and optional global variables
36
+ # @param globals{}, optional
37
+
38
+ def init_client(globals = {})
39
+ globals = {} if globals == nil
40
+ @@client = Savon.client(globals) do
41
+ wsdl @@wsdl
42
+ end
43
+ end
44
+
45
+ ## Gets the user's password if none is provided in the constructor.
46
+ # @return [String] the password for the current user
47
+
48
+ def prompt_for_password
49
+ puts "Please enter password for #{ENV['USER']}:"
50
+ return STDIN.noecho(&:gets).chomp
51
+ end
52
+
53
+ ## Gets the wsdl document location if none is provided in the constructor
54
+ # @return [String] the location of the WDSL document
55
+
56
+ def get_wsdl_location
57
+ puts "Enter the URL of the Secret Server WSDL:"
58
+ return STDIN.gets.chomp
59
+ end
60
+
61
+ ## Gets the domain of the Secret Server installation if none is provided in the constructor
62
+ # @return [String] the domain of the secret server installation
63
+
64
+ def prompt_for_domain
65
+ puts "Enter the domain of your Secret Server:"
66
+ return STDIN.gets.chomp
67
+ end
68
+
69
+
70
+ ## 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.
71
+ # Will raise an error if there is an issue with the authentication.
72
+ # @return the authentication token for the current user.
73
+
74
+ def get_token
75
+ begin
76
+ response = @@client.call(:authenticate, message: { username: ENV['USER'], password: @@password, organization: "", domain: @@domain }).hash
77
+ if response[:envelope][:body][:authenticate_response][:authenticate_result][:errors]
78
+ raise "Error generating the authentication token for user #{ENV['USER']}: #{response[:envelope][:body][:authenticate_response][:authenticate_result][:errors][:string]}"
79
+ end
80
+ response[:envelope][:body][:authenticate_response][:authenticate_result][:token]
81
+ rescue Savon::Error => err
82
+ raise "There was an error generating the authentiaton token for user #{ENV['USER']}: #{err}"
83
+ end
84
+ end
85
+
86
+ ## Get a secret returned as a hash
87
+ # Will raise an error if there was an issue getting the secret
88
+ # @param [Integer] the secret id
89
+ # @return [Hash] the secret hash retrieved from the wsdl
90
+
91
+ def get_secret(scrt_id, token = nil)
92
+ begin
93
+ secret = @@client.call(:get_secret, message: { token: token || get_token, secretId: scrt_id}).hash
94
+ if secret[:envelope][:body][:get_secret_response][:get_secret_result][:errors]
95
+ raise "There was an error getting secret #{scrt_id}: #{secret[:envelope][:body][:get_secret_response][:get_secret_result][:errors][:string]}"
96
+ end
97
+ return secret
98
+ rescue Savon::Error => err
99
+ raise "There was an error getting the secret with id #{scrt_id}: #{err}"
100
+ end
101
+ end
102
+
103
+ ## Retrieve a simple password from a secret
104
+ # Will raise an error if there are any issues
105
+ # @param [Integer] the secret id
106
+ # @return [String] the password for the given secret
107
+
108
+ def get_password(scrt_id)
109
+ begin
110
+ secret = get_secret(scrt_id)
111
+ return secret[:envelope][:body][:get_secret_response][:get_secret_result][:secret][:items][:secret_item][1][:value]
112
+ rescue Savon::Error => err
113
+ raise "There was an error getting the password for secret #{scrt_id}: #{err}"
114
+ end
115
+ end
116
+
117
+ ## Get the secret item id that relates to a key file or attachment.
118
+ # Will raise on error
119
+ # @param [Integer] the secret id
120
+ # @param [String] the type of secret item to get, one of privatekey, publickey, attachment
121
+ # @return [Integer] the secret item id
122
+
123
+ def get_scrt_item_id(scrt_id, type, token)
124
+ secret = get_secret(scrt_id, token)
125
+ case type
126
+ when 'privatekey'
127
+ ## Get private key item id
128
+ secret[:envelope][:body][:get_secret_response][:get_secret_result][:secret][:items][:secret_item].each do |item|
129
+ return item[:id] if item[:field_name] == 'Private Key'
130
+ end
131
+ when 'publickey'
132
+ ## Get public key item id
133
+ secret[:envelope][:body][:get_secret_response][:get_secret_result][:secret][:items][:secret_item].each do |item|
134
+ return item[:id] if item[:field_name] == 'Public Key'
135
+ end
136
+ when 'attachment'
137
+ ## Get attachment item id. This currently only supports secrets with one attachment.
138
+ secret[:envelope][:body][:get_secret_response][:get_secret_result][:secret][:items][:secret_item].each do |item|
139
+ return item[:id] if item[:field_name] == 'Attachment'
140
+ end
141
+ else
142
+ raise "Unknown type, #{type}."
143
+ end
144
+ end
145
+
146
+ ## Downloads the private key for a secret and places it where Zanzibar is running, or :path if specified
147
+ # Raise on error
148
+ # @param [Hash] args, :scrt_id, :scrt_item_id - optional, :path - optional
149
+
150
+ def download_private_key(args = {})
151
+ token = get_token
152
+ FileUtils.mkdir_p(args[:path]) if args[:path]
153
+ path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
154
+ begin
155
+ response = @@client.call(:download_file_attachment_by_item_id, message: { token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], 'privatekey', token)}).hash
156
+ if response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:errors]
157
+ raise "There was an error getting the private key for secret #{args[:scrt_id]}: #{response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:string]}"
158
+ end
159
+ File.open(File.join(path, response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_name]), 'wb') do |file|
160
+ file.puts Base64.decode64(response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_attachment])
161
+ end
162
+ rescue Savon::Error => err
163
+ raise "There was an error getting the private key for secret #{args[:scrt_id]}: #{err}"
164
+ end
165
+ end
166
+
167
+ ## Downloads the public key for a secret and places it where Zanzibar is running, or :path if specified
168
+ # Raise on error
169
+ # @param [Hash] args, :scrt_id, :scrt_item_id - optional, :path - optional
170
+
171
+ def download_public_key(args = {})
172
+ token = get_token
173
+ FileUtils.mkdir_p(args[:path]) if args[:path]
174
+ path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
175
+ begin
176
+ response = @@client.call(:download_file_attachment_by_item_id, message: { token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], 'publickey', token)}).hash
177
+ if response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:errors]
178
+ raise "There was an error getting the public key for secret #{args[:scrt_id]}: #{response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:string]}"
179
+ end
180
+ File.open(File.join(path, response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_name]), 'wb') do |file|
181
+ file.puts Base64.decode64(response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_attachment])
182
+ end
183
+ rescue Savon::Error => err
184
+ raise "There was an error getting the public key for secret #{args[:scrt_id]}: #{err}"
185
+ end
186
+ end
187
+
188
+ ## Downloads an attachment for a secret and places it where Zanzibar is running, or :path if specified
189
+ # Raise on error
190
+ # @param [Hash] args, :scrt_id, :scrt_item_id - optional, :path - optional
191
+
192
+ def download_attachment(args = {})
193
+ token = get_token
194
+ FileUtils.mkdir_p(args[:path]) if args[:path]
195
+ path = args[:path] ? args[:path] : '.' ## The File.join below doesn't handle nils well, so let's take that possibility away.
196
+ begin
197
+ response = @@client.call(:download_file_attachment_by_item_id, message: { token: token, secretId: args[:scrt_id], secretItemId: args[:scrt_item_id] || get_scrt_item_id(args[:scrt_id], 'attachment', token)}).hash
198
+ if response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:errors]
199
+ raise "There was an error getting the attachment for secret #{args[:scrt_id]}: #{response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:string]}"
200
+ end
201
+ File.open(File.join(path, response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_name]), 'wb') do |file|
202
+ file.puts Base64.decode64(response[:envelope][:body][:download_file_attachment_by_item_id_response][:download_file_attachment_by_item_id_result][:file_attachment])
203
+ end
204
+ rescue Savon::Error => err
205
+ raise "There was an error getting the attachment from secret #{args[:scrt_id]}: #{err}"
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,3 @@
1
+ module Zanzibar
2
+ VERSION = "0.0.8"
3
+ end
data/test/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <soap:Body>
4
+ <DownloadFileAttachmentByItemIdResponse xmlns="urn:thesecretserver.com">
5
+ <DownloadFileAttachmentByItemIdResult>
6
+ <Errors />
7
+ <FileAttachment>SSBhbSBhIHNlY3JldCBhdHRhY2htZW50</FileAttachment>
8
+ <FileName>attachment.txt</FileName>
9
+ </DownloadFileAttachmentByItemIdResult>
10
+ </DownloadFileAttachmentByItemIdResponse>
11
+ </soap:Body>
12
+ </soap:Envelope>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
3
+ <soap:Body>
4
+ <AuthenticateResponse xmlns="urn:thesecretserver.com">
5
+ <AuthenticateResult>
6
+ <Errors>
7
+ </Errors>
8
+ <Token>imatoken</Token>
9
+ </AuthenticateResult>
10
+ </AuthenticateResponse>
11
+ </soap:Body>
12
+ </soap:Envelope>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <soap:Body>
4
+ <DownloadFileAttachmentByItemIdResponse xmlns="urn:thesecretserver.com">
5
+ <DownloadFileAttachmentByItemIdResult>
6
+ <Errors />
7
+ <FileAttachment>LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVkgLS0tLS0KemFuemliYXJUZXN0UGFzc3dvcmQKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0=</FileAttachment>
8
+ <FileName>zanzi_key</FileName>
9
+ </DownloadFileAttachmentByItemIdResult>
10
+ </DownloadFileAttachmentByItemIdResponse>
11
+ </soap:Body>
12
+ </soap:Envelope>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <soap:Body>
4
+ <DownloadFileAttachmentByItemIdResponse xmlns="urn:thesecretserver.com">
5
+ <DownloadFileAttachmentByItemIdResult>
6
+ <Errors />
7
+ <FileAttachment>MTIzNFB1YmxpY0tleTU2Nzg9PQ==</FileAttachment>
8
+ <FileName>zanzi_key.pub</FileName>
9
+ </DownloadFileAttachmentByItemIdResult>
10
+ </DownloadFileAttachmentByItemIdResponse>
11
+ </soap:Body>
12
+ </soap:Envelope>
@@ -0,0 +1,57 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
+ <soap:Body>
4
+ <GetSecretResponse xmlns="urn:thesecretserver.com">
5
+ <GetSecretResult>
6
+ <Errors />
7
+ <Secret>
8
+ <Name>Zanzi Test Secret</Name>
9
+ <Items>
10
+ <SecretItem>
11
+ <Value>ZanziUser</Value>
12
+ <Id>15391</Id>
13
+ <FieldId>284</FieldId>
14
+ <FieldName>Username</FieldName>
15
+ <IsFile>false</IsFile>
16
+ <IsNotes>false</IsNotes>
17
+ <IsPassword>false</IsPassword>
18
+ <FieldDisplayName>Username</FieldDisplayName>
19
+ </SecretItem>
20
+ <SecretItem>
21
+ <Value>zanziUserPassword</Value>
22
+ <Id>15392</Id>
23
+ <FieldId>285</FieldId>
24
+ <FieldName>Password</FieldName>
25
+ <IsFile>false</IsFile>
26
+ <IsNotes>false</IsNotes>
27
+ <IsPassword>true</IsPassword>
28
+ <FieldDisplayName>Password</FieldDisplayName>
29
+ </SecretItem>
30
+ <SecretItem>
31
+ <Value />
32
+ <Id>15395</Id>
33
+ <FieldId>287</FieldId>
34
+ <FieldName>Attachment</FieldName>
35
+ <IsFile>true</IsFile>
36
+ <IsNotes>false</IsNotes>
37
+ <IsPassword>false</IsPassword>
38
+ <FieldDisplayName>Attachment</FieldDisplayName>
39
+ </SecretItem>
40
+ </Items>
41
+ <Id>1234</Id>
42
+ <SecretTypeId>6028</SecretTypeId>
43
+ <FolderId>106</FolderId>
44
+ <IsWebLauncher>false</IsWebLauncher>
45
+ <Active>true</Active>
46
+ <CheckOutMinutesRemaining xsi:nil="true" />
47
+ <IsCheckedOut xsi:nil="true" />
48
+ <CheckOutUserDisplayName />
49
+ <CheckOutUserId xsi:nil="true" />
50
+ <IsOutOfSync xsi:nil="true" />
51
+ <IsRestricted>false</IsRestricted>
52
+ <OutOfSyncReason />
53
+ </Secret>
54
+ </GetSecretResult>
55
+ </GetSecretResponse>
56
+ </soap:Body>
57
+ </soap:Envelope>