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