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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/README.md +28 -4
  4. data/examples/aliyun/oss/bucket.rb +1 -1
  5. data/examples/aliyun/oss/object.rb +1 -1
  6. data/examples/aliyun/oss/resumable_download.rb +1 -1
  7. data/examples/aliyun/oss/resumable_upload.rb +1 -1
  8. data/examples/aliyun/oss/streaming.rb +1 -1
  9. data/examples/aliyun/oss/using_sts.rb +48 -0
  10. data/examples/aliyun/sts/assume_role.rb +59 -0
  11. data/lib/aliyun/common.rb +6 -0
  12. data/lib/aliyun/common/exception.rb +18 -0
  13. data/lib/aliyun/{oss → common}/logging.rb +3 -3
  14. data/lib/aliyun/common/struct.rb +56 -0
  15. data/lib/aliyun/oss.rb +1 -2
  16. data/lib/aliyun/oss/bucket.rb +1 -1
  17. data/lib/aliyun/oss/client.rb +2 -2
  18. data/lib/aliyun/oss/config.rb +3 -2
  19. data/lib/aliyun/oss/download.rb +3 -0
  20. data/lib/aliyun/oss/exception.rb +2 -14
  21. data/lib/aliyun/oss/http.rb +3 -1
  22. data/lib/aliyun/oss/multipart.rb +2 -4
  23. data/lib/aliyun/oss/object.rb +1 -1
  24. data/lib/aliyun/oss/protocol.rb +1 -1
  25. data/lib/aliyun/oss/struct.rb +5 -54
  26. data/lib/aliyun/oss/upload.rb +3 -0
  27. data/lib/aliyun/oss/util.rb +1 -1
  28. data/lib/aliyun/sts.rb +9 -0
  29. data/lib/aliyun/sts/client.rb +38 -0
  30. data/lib/aliyun/sts/config.rb +21 -0
  31. data/lib/aliyun/sts/exception.rb +53 -0
  32. data/lib/aliyun/sts/protocol.rb +130 -0
  33. data/lib/aliyun/sts/struct.rb +64 -0
  34. data/lib/aliyun/sts/util.rb +48 -0
  35. data/lib/aliyun/{oss/version.rb → version.rb} +1 -3
  36. data/spec/aliyun/oss/client/client_spec.rb +21 -1
  37. data/spec/aliyun/sts/client_spec.rb +150 -0
  38. data/spec/aliyun/sts/util_spec.rb +39 -0
  39. metadata +21 -4
@@ -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)
@@ -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
 
@@ -6,7 +6,7 @@ module Aliyun
6
6
  ##
7
7
  # Object表示OSS存储的一个对象
8
8
  #
9
- class Object < Struct::Base
9
+ class Object < Common::Struct::Base
10
10
 
11
11
  attrs :key, :type, :size, :etag, :metas, :last_modified, :content_type
12
12
 
@@ -15,7 +15,7 @@ module Aliyun
15
15
 
16
16
  STREAM_CHUNK_SIZE = 16 * 1024
17
17
 
18
- include Logging
18
+ include Common::Logging
19
19
 
20
20
  def initialize(config)
21
21
  @config = config
@@ -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
@@ -7,6 +7,9 @@ module Aliyun
7
7
  # A multipart upload transaction
8
8
  #
9
9
  class Upload < Transaction
10
+
11
+ include Common::Logging
12
+
10
13
  PART_SIZE = 10 * 1024 * 1024
11
14
  READ_SIZE = 16 * 1024
12
15
  NUM_THREAD = 10
@@ -18,7 +18,7 @@ module Aliyun
18
18
 
19
19
  class << self
20
20
 
21
- include Logging
21
+ include Common::Logging
22
22
 
23
23
  # Calculate request signatures
24
24
  def get_signature(key, verb, headers, resources)
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require_relative 'common'
4
+ require_relative 'sts/util'
5
+ require_relative 'sts/exception'
6
+ require_relative 'sts/struct'
7
+ require_relative 'sts/config'
8
+ require_relative 'sts/protocol'
9
+ require_relative 'sts/client'
@@ -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