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.
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