swift_client 0.1.2 → 0.1.3

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44973422875ba91bde545267d984f7ad5ce67f5f
4
- data.tar.gz: 044594bc8d651dde398221808252ba417a1292bf
3
+ metadata.gz: 4555d9e6b744150b80127f8a144e358666a4d70c
4
+ data.tar.gz: f6da1e97fa79dc31ee538f5c3fa08981d9e76c06
5
5
  SHA512:
6
- metadata.gz: 51f7033388ebd75a8f83d62900880cd5de518ad08e94fd9c64e8420a8ca0d24eb3e41fc29fa96d4dff4001c2b88ed79831f733fb2b1da80245cc01ceb446c5a1
7
- data.tar.gz: 5a5199435b62f2b318aacf1728150894d0a242adc8030a167d22b25656c5a925a5472242768b633164359133a346de9d26852b6f221bc551ab4e402d0f259e6c
6
+ metadata.gz: 34f5162ea5b2b2fcfa3f5647eaf8c2d31dbf8947678a32cb8f6c0134f299c63a6c1b89b5c85a19c82d660bb0510ac48bbdd25e5247aa41b00a632b99e9d29ec2
7
+ data.tar.gz: 570eaee9130d6c3bd43af82dabe763ab8af06b60add3fc577d805ddeb28623889c50de51c1bd28efad0e6edc2f2e04b8dd6461e760a4eb0a6674cb20e92d5b7c
data/README.md CHANGED
@@ -67,7 +67,32 @@ swift_client = SwiftClient.new(
67
67
  :storage_url => "https://storage.example.com/v1/AUTH_account",
68
68
  :username => "username",
69
69
  :password => "password",
70
- :domain => "example.com" # :domain_id => "..." is valid as well
70
+ :user_domain => "example.com" # :user_domain_id => "..." is valid as well
71
+ )
72
+
73
+ # OR
74
+
75
+ # project scoped authentication
76
+
77
+ swift_client = SwiftClient.new(
78
+ :auth_url => "https://auth.example.com/v3",
79
+ :username => "username",
80
+ :password => "password",
81
+ :user_domain => "example.com", # :user_domain_id => "..." is valid as well
82
+ :project_id => "p-123456", # :project_name => "..." is valid as well
83
+ :project_domain_id => "d-123456" # :project_domain_name => "..." is valid as well
84
+ )
85
+
86
+ # OR
87
+
88
+ # domain scoped authentication
89
+
90
+ swift_client = SwiftClient.new(
91
+ :auth_url => "https://auth.example.com/v3",
92
+ :username => "username",
93
+ :password => "password",
94
+ :user_domain => "example.com", # :user_domain_id => "..." is valid as well
95
+ :domain_id => "d-123456" # :domain_name => "..." is valid as well
71
96
  )
72
97
 
73
98
  # OR
@@ -120,7 +145,7 @@ SwiftClient offers the following requests:
120
145
  * get_objects(container_name, query = {}) -> HTTParty::Response
121
146
  * paginate_objects(container_name, query = {}) -> Enumerator
122
147
  * public_url(object_name, container_name) -> HTTParty::Response
123
- * temp_url(object_name, container_name) -> HTTParty::Response
148
+ * temp_url(object_name, container_name, options = {}) -> HTTParty::Response
124
149
 
125
150
  ## Contributing
126
151
 
data/lib/swift_client.rb CHANGED
@@ -28,6 +28,8 @@ class SwiftClient
28
28
  attr_accessor :options, :auth_token, :storage_url
29
29
 
30
30
  def initialize(options = {})
31
+ raise(OptionError, "Setting expires_in connection wide is deprecated") if options[:expires_in]
32
+
31
33
  self.options = options
32
34
 
33
35
  authenticate
@@ -148,7 +150,7 @@ class SwiftClient
148
150
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
149
151
  raise(TempUrlKeyMissing) unless options[:temp_url_key]
150
152
 
151
- expires = (Time.now + (options[:expires_in] || 3600).to_i).to_i
153
+ expires = (Time.now + (opts[:expires_in] || 3600).to_i).to_i
152
154
  path = URI.parse("#{storage_url}/#{container_name}/#{object_name}").path
153
155
 
154
156
  signature = OpenSSL::HMAC.hexdigest("sha1", options[:temp_url_key], "GET\n#{expires}\n#{path}")
@@ -234,16 +236,15 @@ class SwiftClient
234
236
  end
235
237
 
236
238
  def authenticate_v3
237
- [:auth_url, :storage_url].each do |key|
238
- raise(AuthenticationError, "#{key} missing") unless options[key]
239
- end
239
+ raise(AuthenticationError, "auth_url missing") unless options[:auth_url]
240
+ raise(AuthenticationError, "username in combination with domain/domain_id is deprecated, please use user_domain/user_domain_id instead") if options[:username] && (options[:domain] || options[:domain_id]) && !options[:user_domain] && !options[:user_domain_id]
240
241
 
241
242
  auth = { "auth" => { "identity" => {} } }
242
243
 
243
- if options[:username] && options[:password] && (options[:domain] || options[:domain_id])
244
+ if options[:username] && options[:password] && (options[:user_domain] || options[:user_domain_id])
244
245
  auth["auth"]["identity"]["methods"] = ["password"]
245
246
  auth["auth"]["identity"]["password"] = { "user" => { "name" => options[:username], "password" => options[:password] } }
246
- auth["auth"]["identity"]["password"]["user"]["domain"] = options[:domain] ? { "name" => options[:domain] } : { "id" => options[:domain_id] }
247
+ auth["auth"]["identity"]["password"]["user"]["domain"] = options[:user_domain] ? { "name" => options[:user_domain] } : { "id" => options[:user_domain_id] }
247
248
  elsif options[:user_id] && options[:password]
248
249
  auth["auth"]["identity"]["methods"] = ["password"]
249
250
  auth["auth"]["identity"]["password"] = { "user" => { "id" => options[:user_id], "password" => options[:password] } }
@@ -254,12 +255,46 @@ class SwiftClient
254
255
  raise AuthenticationError, "Unknown authentication method"
255
256
  end
256
257
 
258
+ # handle project authentication scope
259
+
260
+ if (options[:project_id] || options[:project_name]) && (options[:project_domain_name] || options[:project_domain_id])
261
+ auth["auth"]["scope"] = { "project" => { "domain" => {} } }
262
+ auth["auth"]["scope"]["project"]["name"] = options[:project_name] if options[:project_name]
263
+ auth["auth"]["scope"]["project"]["id"] = options[:project_id] if options[:project_id]
264
+ auth["auth"]["scope"]["project"]["domain"]["name"] = options[:project_domain_name] if options[:project_domain_name]
265
+ auth["auth"]["scope"]["project"]["domain"]["id"] = options[:project_domain_id] if options[:project_domain_id]
266
+ end
267
+
268
+ # handle domain authentication scope
269
+
270
+ if options[:domain_name] || options[:domain_id]
271
+ auth["auth"]["scope"] = { "domain" => {} }
272
+ auth["auth"]["scope"]["domain"]["name"] = options[:domain_name] if options[:domain_name]
273
+ auth["auth"]["scope"]["domain"]["id"] = options[:domain_id] if options[:domain_id]
274
+ end
275
+
257
276
  response = HTTParty.post("#{options[:auth_url].gsub(/\/+$/, "")}/auth/tokens", :body => JSON.dump(auth), :headers => { "Content-Type" => "application/json" })
258
277
 
259
278
  raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?
260
279
 
261
280
  self.auth_token = response.headers["X-Subject-Token"]
262
- self.storage_url = options[:storage_url]
281
+ self.storage_url = options[:storage_url] || storage_url_from_v3_response(response)
282
+
283
+ raise(AuthenticationError, "storage_url missing") unless storage_url
284
+ end
285
+
286
+ def storage_url_from_v3_response(response)
287
+ swift_services = Array(response.parsed_response["token"]["catalog"]).select { |service| service["type"] == "object-store" }
288
+ swift_service = swift_services.first
289
+
290
+ return unless swift_services.size == 1
291
+
292
+ swift_endpoints = swift_service["endpoints"].select { |endpoint| endpoint["interface"] == "public" }
293
+ swift_endpoint = swift_endpoints.first
294
+
295
+ return unless swift_endpoints.size == 1
296
+
297
+ swift_endpoint["url"]
263
298
  end
264
299
 
265
300
  def paginate(method, *args, query)
@@ -1,5 +1,5 @@
1
1
 
2
2
  class SwiftClient
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
5
5
 
data/swift_client.gemspec CHANGED
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rake"
26
26
  spec.add_development_dependency "minitest"
27
27
  spec.add_development_dependency "webmock"
28
+ spec.add_development_dependency "mocha"
28
29
  end
@@ -11,15 +11,58 @@ class SwiftClientTest < MiniTest::Test
11
11
  assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
12
12
  end
13
13
 
14
- def test_v3_authentication_with_password
14
+ def test_v3_authentication_unscoped_with_password
15
15
  stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => "..."), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
16
16
 
17
- @swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v3", :username => "username", :domain => "example.com", :password => "secret")
17
+ @swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret")
18
18
 
19
19
  assert_equal "Token", @swift_client.auth_token
20
20
  assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
21
21
  end
22
22
 
23
+ def test_v3_authentication_project_scoped_with_password
24
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}, "scope"=> { "project" => { "domain" => { "id" => "domain1" }, "id" => "project1" }}})).to_return(:status => 200, :body => JSON.dump("token" => "..."), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
25
+
26
+ @swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret", :project_id => 'project1', :project_domain_id => 'domain1')
27
+
28
+ assert_equal "Token", @swift_client.auth_token
29
+ assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
30
+ end
31
+
32
+ def test_v3_authentication_domain_scoped_with_password
33
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}, "scope"=> { "domain" => { "id" => "domain1" }}})).to_return(:status => 200, :body => JSON.dump("token" => "..."), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
34
+
35
+ @swift_client = SwiftClient.new(:storage_url => "https://example.com/v1/AUTH_account", :auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret", :domain_id => 'domain1')
36
+
37
+ assert_equal "Token", @swift_client.auth_token
38
+ assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
39
+ end
40
+
41
+ def test_v3_authentication_storage_url_from_catalog
42
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => { "catalog"=> [{ "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }] }] }), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
43
+
44
+ @swift_client = SwiftClient.new(:auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret")
45
+
46
+ assert_equal "Token", @swift_client.auth_token
47
+ assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
48
+ end
49
+
50
+ def test_v3_authentication_without_storage_url_and_multiple_swifts
51
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => { "catalog"=> [{ "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }] }, { "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://example.com/v1/AUTH_account" }] }] }), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
52
+
53
+ assert_raises SwiftClient::AuthenticationError do
54
+ SwiftClient.new :auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret"
55
+ end
56
+ end
57
+
58
+ def test_v3_authentication_without_storage_url_and_multiple_endpoints
59
+ stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["password"], "password" => { "user" => { "name" => "username", "password" => "secret", "domain" => { "name" => "example.com" }}}}})).to_return(:status => 200, :body => JSON.dump("token" => { "catalog"=> [{ "type" => "object-store", "endpoints" => [{ "interface"=>"public", "url"=> "https://first.example.com/v1/AUTH_account" }, { "interface"=>"public", "url"=> "https://second.example.com/v1/AUTH_account" }] }] }), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
60
+
61
+ assert_raises SwiftClient::AuthenticationError do
62
+ SwiftClient.new :auth_url => "https://auth.example.com/v3", :username => "username", :user_domain => "example.com", :password => "secret"
63
+ end
64
+ end
65
+
23
66
  def test_v3_authentication_with_token
24
67
  stub_request(:post, "https://auth.example.com/v3/auth/tokens").with(:body => JSON.dump("auth" => { "identity" => { "methods" => ["token"], "token" => { "id" => "Token" }}})).to_return(:status => 200, :body => JSON.dump("token" => "..."), :headers => { "X-Subject-Token" => "Token", "Content-Type" => "application/json" })
25
68
 
@@ -29,6 +72,12 @@ class SwiftClientTest < MiniTest::Test
29
72
  assert_equal "https://example.com/v1/AUTH_account", @swift_client.storage_url
30
73
  end
31
74
 
75
+ def test_v3_authentication_username_domain_deprecation
76
+ assert_raises SwiftClient::AuthenticationError do
77
+ SwiftClient.new :auth_url => "https://auth.example.com/v3", :username => "username", :domain => "example.com", :password => "secret"
78
+ end
79
+ end
80
+
32
81
  def test_v2_authentication_with_password
33
82
  stub_request(:post, "https://auth.example.com/v2.0/tokens").with(:body => JSON.dump("auth" => { "tenantName" => "Tenant", "passwordCredentials" => { "username" => "Username", :password => "Password" }})).to_return(:status => 200, :body => JSON.dump("access" => { "token" => { "id" => "Token" }}), :headers => { "Content-Type" => "application/json" })
34
83
 
@@ -243,7 +292,10 @@ class SwiftClientTest < MiniTest::Test
243
292
  end
244
293
 
245
294
  def test_temp_url
246
- assert @swift_client.temp_url("object", "container", :expires_in => 3600) =~ %r{https://example.com/v1/AUTH_account/container/object\?temp_url_sig=[a-f0-9]{40}&temp_url_expires=[0-9]+}
295
+ Time.expects(:now).at_least_once.returns(1_000_000)
296
+
297
+ assert @swift_client.temp_url("object", "container") =~ %r{https://example.com/v1/AUTH_account/container/object\?temp_url_sig=[a-f0-9]{40}&temp_url_expires=1003600}
298
+ assert @swift_client.temp_url("object", "container", :expires_in => 86400) =~ %r{https://example.com/v1/AUTH_account/container/object\?temp_url_sig=[a-f0-9]{40}&temp_url_expires=1086400}
247
299
  end
248
300
  end
249
301
 
data/test/test_helper.rb CHANGED
@@ -3,4 +3,5 @@ require "swift_client"
3
3
  require "minitest"
4
4
  require "minitest/autorun"
5
5
  require "webmock/minitest"
6
-
6
+ require "minitest/unit"
7
+ require "mocha/mini_test"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swift_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-23 00:00:00.000000000 Z
11
+ date: 2016-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mocha
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: Small but powerful client to interact with OpenStack Swift
98
112
  email:
99
113
  - vetter@plainpicture.de