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 +4 -4
- data/README.md +27 -2
- data/lib/swift_client.rb +42 -7
- data/lib/swift_client/version.rb +1 -1
- data/swift_client.gemspec +1 -0
- data/test/swift_client_test.rb +55 -3
- data/test/test_helper.rb +2 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4555d9e6b744150b80127f8a144e358666a4d70c
|
4
|
+
data.tar.gz: f6da1e97fa79dc31ee538f5c3fa08981d9e76c06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
:
|
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 + (
|
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
|
238
|
-
|
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[:
|
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[:
|
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)
|
data/lib/swift_client/version.rb
CHANGED
data/swift_client.gemspec
CHANGED
data/test/swift_client_test.rb
CHANGED
@@ -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
|
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", :
|
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
|
-
|
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
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.
|
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-
|
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
|