swift_client 0.1.2 → 0.1.3

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