aliyun-sdk 0.1.8 → 0.2.0
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/CHANGELOG.md +5 -0
- data/README.md +28 -4
- data/examples/aliyun/oss/bucket.rb +1 -1
- data/examples/aliyun/oss/object.rb +1 -1
- data/examples/aliyun/oss/resumable_download.rb +1 -1
- data/examples/aliyun/oss/resumable_upload.rb +1 -1
- data/examples/aliyun/oss/streaming.rb +1 -1
- data/examples/aliyun/oss/using_sts.rb +48 -0
- data/examples/aliyun/sts/assume_role.rb +59 -0
- data/lib/aliyun/common.rb +6 -0
- data/lib/aliyun/common/exception.rb +18 -0
- data/lib/aliyun/{oss → common}/logging.rb +3 -3
- data/lib/aliyun/common/struct.rb +56 -0
- data/lib/aliyun/oss.rb +1 -2
- data/lib/aliyun/oss/bucket.rb +1 -1
- data/lib/aliyun/oss/client.rb +2 -2
- data/lib/aliyun/oss/config.rb +3 -2
- data/lib/aliyun/oss/download.rb +3 -0
- data/lib/aliyun/oss/exception.rb +2 -14
- data/lib/aliyun/oss/http.rb +3 -1
- data/lib/aliyun/oss/multipart.rb +2 -4
- data/lib/aliyun/oss/object.rb +1 -1
- data/lib/aliyun/oss/protocol.rb +1 -1
- data/lib/aliyun/oss/struct.rb +5 -54
- data/lib/aliyun/oss/upload.rb +3 -0
- data/lib/aliyun/oss/util.rb +1 -1
- data/lib/aliyun/sts.rb +9 -0
- data/lib/aliyun/sts/client.rb +38 -0
- data/lib/aliyun/sts/config.rb +21 -0
- data/lib/aliyun/sts/exception.rb +53 -0
- data/lib/aliyun/sts/protocol.rb +130 -0
- data/lib/aliyun/sts/struct.rb +64 -0
- data/lib/aliyun/sts/util.rb +48 -0
- data/lib/aliyun/{oss/version.rb → version.rb} +1 -3
- data/spec/aliyun/oss/client/client_spec.rb +21 -1
- data/spec/aliyun/sts/client_spec.rb +150 -0
- data/spec/aliyun/sts/util_spec.rb +39 -0
- metadata +21 -4
data/lib/aliyun/oss/http.rb
CHANGED
@@ -32,6 +32,7 @@ module Aliyun
|
|
32
32
|
class HTTP
|
33
33
|
|
34
34
|
DEFAULT_CONTENT_TYPE = 'application/octet-stream'
|
35
|
+
STS_HEADER = 'x-oss-security-token'
|
35
36
|
OPEN_TIMEOUT = 10
|
36
37
|
READ_TIMEOUT = 120
|
37
38
|
|
@@ -124,7 +125,7 @@ module Aliyun
|
|
124
125
|
|
125
126
|
end
|
126
127
|
|
127
|
-
include Logging
|
128
|
+
include Common::Logging
|
128
129
|
|
129
130
|
def initialize(config)
|
130
131
|
@config = config
|
@@ -209,6 +210,7 @@ module Aliyun
|
|
209
210
|
headers['User-Agent'] = get_user_agent
|
210
211
|
headers['Date'] = Time.now.httpdate
|
211
212
|
headers['Content-Type'] ||= DEFAULT_CONTENT_TYPE
|
213
|
+
headers[STS_HEADER] = @config.sts_token if @config.sts_token
|
212
214
|
|
213
215
|
if body = http_options[:body]
|
214
216
|
if body.respond_to?(:read)
|
data/lib/aliyun/oss/multipart.rb
CHANGED
@@ -14,9 +14,7 @@ module Aliyun
|
|
14
14
|
##
|
15
15
|
# A multipart transaction. Provide the basic checkpoint methods.
|
16
16
|
#
|
17
|
-
class Transaction < Struct::Base
|
18
|
-
|
19
|
-
include Logging
|
17
|
+
class Transaction < Common::Struct::Base
|
20
18
|
|
21
19
|
attrs :id, :object, :bucket, :creation_time, :options
|
22
20
|
|
@@ -65,7 +63,7 @@ module Aliyun
|
|
65
63
|
##
|
66
64
|
# A part in a multipart uploading transaction
|
67
65
|
#
|
68
|
-
class Part < Struct::Base
|
66
|
+
class Part < Common::Struct::Base
|
69
67
|
|
70
68
|
attrs :number, :etag, :size, :last_modified
|
71
69
|
|
data/lib/aliyun/oss/object.rb
CHANGED
data/lib/aliyun/oss/protocol.rb
CHANGED
data/lib/aliyun/oss/struct.rb
CHANGED
@@ -51,55 +51,6 @@ module Aliyun
|
|
51
51
|
end
|
52
52
|
end # KeyEncoding
|
53
53
|
|
54
|
-
##
|
55
|
-
# Common structs used. It provides a 'attrs' helper method for
|
56
|
-
# subclass to define its attributes. 'attrs' is based on
|
57
|
-
# attr_reader and provide additional functionalities for classes
|
58
|
-
# that inherits Struct::Base :
|
59
|
-
# * the constuctor is provided to accept options and set the
|
60
|
-
# corresponding attibute automatically
|
61
|
-
# * the #to_s method is rewrite to concatenate the defined
|
62
|
-
# attributes keys and values
|
63
|
-
# @example
|
64
|
-
# class X < Struct::Base
|
65
|
-
# attrs :foo, :bar
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# x.new(:foo => 'hello', :bar => 'world')
|
69
|
-
# x.foo # == "hello"
|
70
|
-
# x.bar # == "world"
|
71
|
-
# x.to_s # == "foo: hello, bar: world"
|
72
|
-
module Struct
|
73
|
-
class Base
|
74
|
-
module AttrHelper
|
75
|
-
def attrs(*s)
|
76
|
-
define_method(:attrs) {s}
|
77
|
-
attr_reader(*s)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
extend AttrHelper
|
82
|
-
|
83
|
-
def initialize(opts = {})
|
84
|
-
extra_keys = opts.keys - attrs
|
85
|
-
unless extra_keys.empty?
|
86
|
-
fail ClientError, "Unexpected extra keys: #{extra_keys.join(', ')}"
|
87
|
-
end
|
88
|
-
|
89
|
-
attrs.each do |attr|
|
90
|
-
instance_variable_set("@#{attr}", opts[attr])
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def to_s
|
95
|
-
attrs.map do |attr|
|
96
|
-
v = instance_variable_get("@#{attr}")
|
97
|
-
"#{attr.to_s}: #{v}"
|
98
|
-
end.join(", ")
|
99
|
-
end
|
100
|
-
end # Base
|
101
|
-
end # Struct
|
102
|
-
|
103
54
|
##
|
104
55
|
# Bucket Logging setting. See: {http://help.aliyun.com/document_detail/oss/product-documentation/function/logging.html OSS Bucket logging}
|
105
56
|
# Attributes:
|
@@ -111,7 +62,7 @@ module Aliyun
|
|
111
62
|
# :enable => true, :target_bucket => 'log_bucket', :target_prefix => 'my-log')
|
112
63
|
# @example Disable bucket logging
|
113
64
|
# bucket.logging = BucketLogging.new(:enable => false)
|
114
|
-
class BucketLogging < Struct::Base
|
65
|
+
class BucketLogging < Common::Struct::Base
|
115
66
|
attrs :enable, :target_bucket, :target_prefix
|
116
67
|
|
117
68
|
def enabled?
|
@@ -125,7 +76,7 @@ module Aliyun
|
|
125
76
|
# * enable [Boolean] whether to enable website hosting for the bucket
|
126
77
|
# * index [String] the index object as the index page for the website
|
127
78
|
# * error [String] the error object as the error page for the website
|
128
|
-
class BucketWebsite < Struct::Base
|
79
|
+
class BucketWebsite < Common::Struct::Base
|
129
80
|
attrs :enable, :index, :error
|
130
81
|
|
131
82
|
def enabled?
|
@@ -138,7 +89,7 @@ module Aliyun
|
|
138
89
|
# Attributes:
|
139
90
|
# * allow_empty [Boolean] whether to allow requests with empty "Referer"
|
140
91
|
# * whitelist [Array<String>] the allowed origins for requests
|
141
|
-
class BucketReferer < Struct::Base
|
92
|
+
class BucketReferer < Common::Struct::Base
|
142
93
|
attrs :allow_empty, :whitelist
|
143
94
|
|
144
95
|
def allow_empty?
|
@@ -171,7 +122,7 @@ module Aliyun
|
|
171
122
|
# :prefix => 'foo/',
|
172
123
|
# :expiry => 15)
|
173
124
|
# @note the expiry date is treated as UTC time
|
174
|
-
class LifeCycleRule < Struct::Base
|
125
|
+
class LifeCycleRule < Common::Struct::Base
|
175
126
|
|
176
127
|
attrs :id, :enable, :prefix, :expiry
|
177
128
|
|
@@ -188,7 +139,7 @@ module Aliyun
|
|
188
139
|
# * allowed_headers [Array<String>] the allowed headers
|
189
140
|
# * expose_headers [Array<String>] the expose headers
|
190
141
|
# * max_age_seconds [Integer] the max age seconds
|
191
|
-
class CORSRule < Struct::Base
|
142
|
+
class CORSRule < Common::Struct::Base
|
192
143
|
|
193
144
|
attrs :allowed_origins, :allowed_methods, :allowed_headers,
|
194
145
|
:expose_headers, :max_age_seconds
|
data/lib/aliyun/oss/upload.rb
CHANGED
data/lib/aliyun/oss/util.rb
CHANGED
data/lib/aliyun/sts.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module STS
|
5
|
+
|
6
|
+
# STS服务的客户端,用于向STS申请临时token。
|
7
|
+
# @example 创建Client
|
8
|
+
# client = Client.new(
|
9
|
+
# :access_key_id => 'access_key_id',
|
10
|
+
# :access_key_secret => 'access_key_secret')
|
11
|
+
# token = client.assume_role('role:arn', 'app')
|
12
|
+
#
|
13
|
+
# policy = Policy.new
|
14
|
+
# policy.allow(['oss:Get*'], ['acs:oss:*:*:my-bucket/*'])
|
15
|
+
# token = client.assume_role('role:arn', 'app', policy, 60)
|
16
|
+
# puts token.to_s
|
17
|
+
class Client
|
18
|
+
|
19
|
+
def initialize(opts)
|
20
|
+
@config = Config.new(opts)
|
21
|
+
@protocol = Protocol.new(@config)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Assume a role
|
25
|
+
# @param role [String] the role arn
|
26
|
+
# @param session [String] the session name
|
27
|
+
# @param policy [STS::Policy] the policy
|
28
|
+
# @param duration [Fixnum] the duration seconds for the
|
29
|
+
# requested token
|
30
|
+
# @return [STS::Token] the sts token
|
31
|
+
def assume_role(role, session, policy = nil, duration = 3600)
|
32
|
+
@protocol.assume_role(role, session, policy, duration)
|
33
|
+
end
|
34
|
+
|
35
|
+
end # Client
|
36
|
+
|
37
|
+
end # STS
|
38
|
+
end # Aliyun
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
module STS
|
5
|
+
|
6
|
+
# A place to store various configurations: credentials, api
|
7
|
+
# timeout, retry mechanism, etc
|
8
|
+
class Config < Common::Struct::Base
|
9
|
+
|
10
|
+
attrs :access_key_id, :access_key_secret
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
super(opts)
|
14
|
+
|
15
|
+
@access_key_id.strip! if @access_key_id
|
16
|
+
@access_key_secret.strip! if @access_key_secret
|
17
|
+
end
|
18
|
+
end # Config
|
19
|
+
|
20
|
+
end # STS
|
21
|
+
end # Aliyun
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Aliyun
|
6
|
+
module STS
|
7
|
+
|
8
|
+
# ServerError represents exceptions from the STS
|
9
|
+
# service. i.e. Client receives a HTTP response whose status is
|
10
|
+
# NOT OK. #message provides the error message and #to_s gives
|
11
|
+
# detailed information probably including the STS request id.
|
12
|
+
class ServerError < Common::Exception
|
13
|
+
|
14
|
+
attr_reader :http_code, :error_code, :message, :request_id
|
15
|
+
|
16
|
+
def initialize(response)
|
17
|
+
@http_code = response.code
|
18
|
+
@attrs = {}
|
19
|
+
|
20
|
+
doc = Nokogiri::XML(response.body) do |config|
|
21
|
+
config.options |= Nokogiri::XML::ParseOptions::NOBLANKS
|
22
|
+
end rescue nil
|
23
|
+
|
24
|
+
if doc and doc.root
|
25
|
+
doc.root.children.each do |n|
|
26
|
+
@attrs[n.name] = n.text
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@error_code = @attrs['Code']
|
31
|
+
@message = @attrs['Message']
|
32
|
+
@request_id = @attrs['RequestId']
|
33
|
+
end
|
34
|
+
|
35
|
+
def message
|
36
|
+
msg = @attrs['Message'] || "UnknownError[#{http_code}]."
|
37
|
+
"#{msg} RequestId: #{request_id}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@attrs.merge({'HTTPCode' => @http_code}).map do |k, v|
|
42
|
+
[k, v].join(": ")
|
43
|
+
end.join(", ")
|
44
|
+
end
|
45
|
+
end # ServerError
|
46
|
+
|
47
|
+
# ClientError represents client exceptions caused mostly by
|
48
|
+
# invalid parameters.
|
49
|
+
class ClientError < Common::Exception
|
50
|
+
end # ClientError
|
51
|
+
|
52
|
+
end # STS
|
53
|
+
end # Aliyun
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rest-client'
|
4
|
+
require 'nokogiri'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module Aliyun
|
8
|
+
module STS
|
9
|
+
|
10
|
+
# Protocol implements the STS Open API which is low-level. User
|
11
|
+
# should refer to {STS::Client} for normal use.
|
12
|
+
class Protocol
|
13
|
+
|
14
|
+
ENDPOINT = 'https://sts.aliyuncs.com'
|
15
|
+
FORMAT = 'XML'
|
16
|
+
API_VERSION = '2015-04-01'
|
17
|
+
SIGNATURE_METHOD = 'HMAC-SHA1'
|
18
|
+
SIGNATURE_VERSION = '1.0'
|
19
|
+
|
20
|
+
include Common::Logging
|
21
|
+
|
22
|
+
def initialize(config)
|
23
|
+
@config = config
|
24
|
+
end
|
25
|
+
|
26
|
+
# Assume a role
|
27
|
+
# @param role [String] the role arn
|
28
|
+
# @param session [String] the session name
|
29
|
+
# @param policy [STS::Policy] the policy
|
30
|
+
# @param duration [Fixnum] the duration seconds for the
|
31
|
+
# requested token
|
32
|
+
# @return [STS::Token] the sts token
|
33
|
+
def assume_role(role, session, policy = nil, duration = 3600)
|
34
|
+
logger.info("Begin assume role, role: #{role}, session: #{session}, "\
|
35
|
+
"policy: #{policy}, duration: #{duration}")
|
36
|
+
|
37
|
+
params = {
|
38
|
+
'Action' => 'AssumeRole',
|
39
|
+
'RoleArn' => role,
|
40
|
+
'RoleSessionName' => session,
|
41
|
+
'DurationSeconds' => duration.to_s
|
42
|
+
}
|
43
|
+
params.merge!({'Policy' => policy.serialize}) if policy
|
44
|
+
|
45
|
+
body = do_request(params)
|
46
|
+
doc = parse_xml(body)
|
47
|
+
|
48
|
+
creds_node = doc.at_css("Credentials")
|
49
|
+
creds = {
|
50
|
+
session_name: session,
|
51
|
+
access_key_id: get_node_text(creds_node, 'AccessKeyId'),
|
52
|
+
access_key_secret: get_node_text(creds_node, 'AccessKeySecret'),
|
53
|
+
security_token: get_node_text(creds_node, 'SecurityToken'),
|
54
|
+
expiration: get_node_text(
|
55
|
+
creds_node, 'Expiration') { |x| Time.parse(x) },
|
56
|
+
}
|
57
|
+
|
58
|
+
logger.info("Done assume role, creds: #{creds}")
|
59
|
+
|
60
|
+
Token.new(creds)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
# Generate a random signature nonce
|
65
|
+
# @return [String] a random string
|
66
|
+
def signature_nonce
|
67
|
+
(rand * 1_000_000_000).to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
# Do HTTP POST request with specified params
|
71
|
+
# @param params [Hash] the parameters to STS
|
72
|
+
# @return [String] the response body
|
73
|
+
# @raise [ServerError] raise errors if the server responds with errors
|
74
|
+
def do_request(params)
|
75
|
+
query = params.merge(
|
76
|
+
{'Format' => FORMAT,
|
77
|
+
'Version' => API_VERSION,
|
78
|
+
'AccessKeyId' => @config.access_key_id,
|
79
|
+
'SignatureMethod' => SIGNATURE_METHOD,
|
80
|
+
'SignatureVersion' => SIGNATURE_VERSION,
|
81
|
+
'SignatureNonce' => signature_nonce,
|
82
|
+
'Timestamp' => Time.now.utc.iso8601})
|
83
|
+
|
84
|
+
signature = Util.get_signature('POST', query, @config.access_key_secret)
|
85
|
+
query.merge!({'Signature' => signature})
|
86
|
+
|
87
|
+
r = RestClient::Request.execute(
|
88
|
+
:method => 'POST',
|
89
|
+
:url => ENDPOINT,
|
90
|
+
:payload => query
|
91
|
+
) do |response, request, result, &blk|
|
92
|
+
|
93
|
+
if response.code >= 300
|
94
|
+
e = ServerError.new(response)
|
95
|
+
logger.error(e.to_s)
|
96
|
+
raise e
|
97
|
+
else
|
98
|
+
response.return!(request, result, &blk)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
logger.debug("Received HTTP response, code: #{r.code}, headers: "\
|
103
|
+
"#{r.headers}, body: #{r.body}")
|
104
|
+
r.body
|
105
|
+
end
|
106
|
+
|
107
|
+
# Parse body content to xml document
|
108
|
+
# @param content [String] the xml content
|
109
|
+
# @return [Nokogiri::XML::Document] the parsed document
|
110
|
+
def parse_xml(content)
|
111
|
+
doc = Nokogiri::XML(content) do |config|
|
112
|
+
config.options |= Nokogiri::XML::ParseOptions::NOBLANKS
|
113
|
+
end
|
114
|
+
|
115
|
+
doc
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get the text of a xml node
|
119
|
+
# @param node [Nokogiri::XML::Node] the xml node
|
120
|
+
# @param tag [String] the node tag
|
121
|
+
# @yield [String] the node text is given to the block
|
122
|
+
def get_node_text(node, tag, &block)
|
123
|
+
n = node.at_css(tag) if node
|
124
|
+
value = n.text if n
|
125
|
+
block && value ? yield(value) : value
|
126
|
+
end
|
127
|
+
|
128
|
+
end # Protocol
|
129
|
+
end # STS
|
130
|
+
end # Aliyun
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module Aliyun
|
7
|
+
module STS
|
8
|
+
|
9
|
+
# STS Policy. Referer to
|
10
|
+
# https://help.aliyun.com/document_detail/ram/ram-user-guide/policy_reference/struct_def.html for details.
|
11
|
+
class Policy < Common::Struct::Base
|
12
|
+
VERSION = '1'
|
13
|
+
|
14
|
+
attrs :rules
|
15
|
+
|
16
|
+
# Add an 'Allow' rule
|
17
|
+
# @param actions [Array<String>] actions of the rule. e.g.:
|
18
|
+
# oss:GetObject, oss:Get*, oss:*
|
19
|
+
# @param resources [Array<String>] resources of the rule. e.g.:
|
20
|
+
# acs:oss:*:*:my-bucket, acs:oss:*:*:my-bucket/*, acs:oss:*:*:*
|
21
|
+
def allow(actions, resources)
|
22
|
+
add_rule(true, actions, resources)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add an 'Deny' rule
|
26
|
+
# @param actions [Array<String>] actions of the rule. e.g.:
|
27
|
+
# oss:GetObject, oss:Get*, oss:*
|
28
|
+
# @param resources [Array<String>] resources of the rule. e.g.:
|
29
|
+
# acs:oss:*:*:my-bucket, acs:oss:*:*:my-bucket/*, acs:oss:*:*:*
|
30
|
+
def deny(actions, resources)
|
31
|
+
add_rule(false, actions, resources)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Serialize to rule to string
|
35
|
+
def serialize
|
36
|
+
{'Version' => VERSION, 'Statement' => @rules}.to_json
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def add_rule(allow, actions, resources)
|
41
|
+
@rules ||= []
|
42
|
+
@rules << {
|
43
|
+
'Effect' => allow ? 'Allow' : 'Deny',
|
44
|
+
'Action' => actions,
|
45
|
+
'Resource' => resources
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# STS token. User may use the credentials included to access
|
51
|
+
# Alicloud resources(OSS, OTS, etc).
|
52
|
+
# Attributes:
|
53
|
+
# * access_key_id [String] the AccessKeyId
|
54
|
+
# * access_key_secret [String] the AccessKeySecret
|
55
|
+
# * security_token [String] the SecurityToken
|
56
|
+
# * expiration [Time] the time when the token will be expired
|
57
|
+
# * session_name [String] the session name for this token
|
58
|
+
class Token < Common::Struct::Base
|
59
|
+
attrs :access_key_id, :access_key_secret,
|
60
|
+
:security_token, :expiration, :session_name
|
61
|
+
end
|
62
|
+
|
63
|
+
end # STS
|
64
|
+
end # Aliyun
|