Skip to content

Commit

Permalink
Add sts sign url (#29)
Browse files Browse the repository at this point in the history
* Support sign object url with STS

* Add STS env

* Fix incompatibility of ruby-1.9.x caused by simplecov

* Remove .cise.yml
  • Loading branch information
rockuw authored Jul 19, 2016
1 parent c906df0 commit ca5ba5a
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 27 deletions.
12 changes: 0 additions & 12 deletions .cise.yml

This file was deleted.

5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ env:
- secure: iMjPztZoWaRxrgI/buZZj8SliuOYGEcOyO++xskr0O6x8+3qkT7JRqozwu/PK3Au77Py9+LTpZqNrjy3ZZx/KBzYUX4+g+k5YcjiUAnlX3/CEdj5Apti6Qb0CwtOgHY7V+y205vZeh0x83RuPUY1ZZ0kOS2C7Frd0S7rMWSTDbTnVxpUn46f1Pgnjin5ohtlUKiOuP4txEWiJgJhVJQkgDo749GanX7Wg9KOd4PnUdjXVVVWsYt1x9LXTUXyCpq8jwnOFFw0NKKUVyjVv+6b75KokTU/3QCr2AMnaPRTbkn89244ZLM1mr0Per0LJmTowpBGKF2K2PWMuJ8uDW3U/GgsG2OqwarGFKsE2JZsezctZRAhQGVDtLcG/SLY90THtskgIG4nXykfin49RfCyEPAKXk+VJ1QtevogX4Bw5qOvoCen3Uww5LBQYS3cjjTJDmTiJyqqOblMCZibRjwf8+Lvw20VRkrYqYPqFr/VxRddWVjeK/ktjJS/cnAZyoCOl6PRP9Rrdf7MmdJGYT9RhfTC/nGpPqGv7CUM7n/wnmRx3tmE0ohHW8phwRTBlXT412EzMZS6Heo5tSlTFkTWHXoYz840gqok/APS6Y8h7idXmav4PbgJO7PVfiIGl8koGT2yM1XEmmjX7/T2vJprdO6zr5xRXu4tx2CHnA/yQCI=
- secure: a4zvMhM/Ml19tWAM+HJxOqr/T7KdNJX4bfdSAv37oXwYzsaBYXhUYKzyGfRum1DDFPpdC4X5uRLnC8eXRxKCLJfDVmMX9atui20v6QWLTXiyy1jajdnTeyCRuuVfclwAT1JMja8J+qaaB7eZoTJgT3dVKuoRpueCodSpLL70hK0gWxtdFnBRqgDZR/4oxfqEryZbIEjiC2zxOxBJSWrqpIJdO4kR39fsc+ThMbpM8Ii7yyDYZmBbHF2AMiB96pLCOr3uEk0l8smg/6iu0BcgXY6A+0bKzI5nmM0Nm261+7nLok5VLAThxnvzio6ybeaMp5/VhYowA/RyeyUTcnNZqhyc8dbssOBaZpeJVdVJiNA/lCaR/jHbx7i25H2BhKvboEypW9aaCAclWbkMLg9p03CQuXHProViNDL6jvu+rQJVpqNDDozKxCyxIbf6yOS71qQFDeXoJpurokgyV/FCN4bbPkBkJnQdNaF5Zeyoc5fX8yipv0fL8SrFZi2ZGSaRKn6WChZwl1eUi0xAeqbXdJsGaf5P0VxUSc7I/bIR6IlXlSu0SpA2KqQvpc4QMNhzaHbhoquNJ9SK4FwCjhdFhGrfRNsePH6tpK9Po2PQoFBqT1WEFZogA5QcdC/3axlfpsXPL8RRHw+YqgplVpR43N2f0ug6rTq2hL8/YM6Dn7Y=
- secure: Bh0qS85guNrnHwGTAqD0TY0xWJ5VSMO9Qh7//oBxT+eqWNmp7jmH7etDGQ6EzlnbXE7MUOBLarlnzH8uONhpz7RsU2R+OaQ4Z1i0npAS7t9sivEK8mHlUpX5iMhbnPvi70LQf8Xh/JEtOis/aYS/J86DM6S4tkFaSgbKAFFpyaxCHconHCLcRTXLRl8PgK+k8VAhlUBIbI2u7v1uYhjefy3eHf/I0ZnUBvM5VOtjflddOH607fbo65SzaQiUrxg7Ed2oy/Vdhgksq4HPSLjS5afsF7EifW2eVq/EjdXeyF8K3XTdMiV9oYj7KkLXVL21jA5LWCiM5rvkG+Zz/ATHZodOgBw/Wdim/Tbni+E3cLkhN3LEVUqx/W4/PKK3bWiIuaf37T7UuZC2+tEpRt1UHCc83w9uX+4XPUjGkH3N5rXrTAhz82z455nSl6lVi8zvIkXECP05/HJFU83ottc7GyWX2a7rpuLVe3iq8D6BCGRpjI3nKsfnCAjbs5UR/ED8/xie1/RylqpBbgQc3yLB/3m9LVV5mZxBYLQGKS4yfYpwJN7sqFZn9dFGqhLTGFfOVyPPmSnjjL7x1R5WgfNHEtwfGFXZN97kt4xd8TkR0UwW4opqePsqX1+JQQODJixxm3CfZVE4P8mQaZWHz7OMGTpnrTSZPqLZPnwte/6pAUk=
- secure: kZBO0KL9ZHTOwDsJPFt/iF0B0JgaDOfO2pX9xwbhhnS8muXAbqt85TM9d5RxLgzKthxgdcxUPQbOewvrkdlrGe+g5rLhDjTXJzX8z6BB583/iXzkXci4PIT0lXmi5XcGVepIez3lhM1g4iutpxeKklYu/h7z9J2LMlNRiAqU0P5tsSIwFYbUazutP1ZrHyL8IC8yOYyJ9QR/EvFmr3T31mndios0MKfGofnQiuBnChWRZEPxa1aqF2bv+NEbCDnaavPq7zbZ3X2EY7OfbloqRM93cTg0Jmudfn6KoROSyWEMOMHHPEWCVYidjRQrcCSjcDiAe9NBZ4i8QcnLFGGy4FBKW4OLR08iI4F0UfQ0ugz3XQFVSneySiGxNAt6RwwFei/vJSyfMJRIcaIQbpxeKZ574HXpRDfV/kk4YByyYR4JtK4JI1h+K+sPsZDjXvC8ufYmMKZOithbULymIIBa2VJj+ukcoCmFPQbnYLkX2vZGBxtwF+RJGdZAcC2OYRhXBloPirNFB+XCQ4mXdX/gmaITwOOk5RIpVPVsHR+xcxWlLUsk9ngIFXM7TfIOVwsMJ8N1QYEickKZ9sBAGv2vS5miw+JPyLqY6Gq3KnQL99gx8+Lqd3NTrVPGLoqv7b1My/TSkWGqpopnj3X1HdV3BsfF/ArxlClv3SE3feB8LaI=
- secure: v/mne8fMeGLw67LxVmKyychnGHuA38OGarsB/oUs1ehTIgj7Uiom6Z2YGVbWsoVxGzGuRlN+pcgaE/oHoRA5D7m+aO0oyCDs2O3MeGfcFk1vg+LMwZ2wi3GDKJ5P4kRGuB6YIdQ1jONZWvu55I5KoDOODHNpWHUxx1BGGS8bFCWLA4/u+4DkORrFtQ27weIN5y7Tw0Pm73pJlHl82Pc33s3RKI0bErOqdCD9amltrmYm5/2Bnsy/b5TMdZMTjQE9j//aTB0b5Dy70R7i9fXnC3bNSB/MK0FEHiR+3azb4V0qdiQhrdYfWnR0/VRRfgab12SJLcMtTbMREgvpv+tWQdZuyW2Jp2fGdQIUMIaoG/FdFx+vOmNfSUEK3RuGiCWHm3el8DdFQMHTS9fT2Qf4kO2QRP5/z2rqKEm0J3MmL9NPyGhD4+d32v8HBBfqEGiElCBFfPNQqH6uA2ooJn/hVLFE4Bx4HlOdReHpExOZlF4VnrLf/cH1ugpGDrLPmDKwXdtjvnOJsy+0tEfE8RjOA87eLNAu0s3KWifgMYodNn5hDRRg7R3+d13v7hZSu5Ub7FMTu5VpsLyZiB1JCrVR725TczDGuR3BvZftpQxqA9Wbr/KFzTtz2UBgSEoxzoEKYiNSLr/aj2zY94jZr1cxp0Hw2kVjx7dbmQAuKuIDtbU=
- secure: qnz4m9DmbH+Ar/BxQgmnsclxoUTY5+BAgZw9yfpPkh+n1iJc/ppOfvETNpbelty0k4Vf+0Cq4xojJszEs56SwCCrNByHuKT8D8aCJMwlNNuUt6pcgF4JNZCexyQZIYiyniwiS91BQvsMYfCq2qF3328xdl2VUz9g6TMBq8BCde/wRQFRn9HABQuIwE4J+RKLMvwP2+aHoAA04/Bpk/iq6wv8xhMEU/cAI+/QSuFQCHUvK/FzjFO85cXAmR+ColQPxIh/OBdpD1ZtBQPD9kFjEC5zahIX9Vr83weoOlUoYAdYTfdLjgEbhE4812s3L/meVK/yW3ceW6qIkqS8C6ZbcvqzbfRcNal+co6AVUMs+fkH6KOuj6dU8LUGouD0KorApwWpfLv2DbmAmxZl3zitKtb5IeBfdwLfWGjhMDp2H3LJ0ck1Y/556OamfLLHX25zzFz6F/fWCZWH0+6ADKz1HM7ogfn4nQCPk8+hVrkjZgLCeX3PcfBeFHG1eHIp69XE3P5L8fvF5gGXU8wVbSie8gOrU2VwP4Tb3mZODTh3Nx9ZBru8K6lzBN1c20SVZnKpjrKK7uKTFMwjkh3ypVDw6Djm2yjIwctCnsQ3YlAVr4maHa+5N0YMuyH+k/a7xFFy5TRXaA3wxf9Pou26dKjOmNxbFinRH3uLvtKPl9l90fw=
- secure: FffdE4JDaJoBpbL4WvZNY8uwTVZ7wKIeDx2EUhIguFTa2NGXNLVJ7TamOhHpIjtiu8FjvlC5EZvYmct5nB7DHPwAxM7937p0gW2Wb4w58dZqU1+Ak86lo2P0oaI9oqMwfR+h9HUN4O4UrfSeuHVajSLEn8EuQEhAbD3HhYCiMLfCHZ8z4xTd8Nt+EH3y52AAGAYtR4OzJuhA9DjlPPhpOPg2A7wd0UStHfWRpehhJ/qqMTU5tEuqP4YcOYuLAGtHHPVYxnDge32FqGO4aUbJQf54D8nShrLh7XsAEDuvQV6yVOHI8ITopgnVJyZt6A8yYTbji+m4VP8EUgp7E4QC5enZXqlsNVMRWpaz82BJHy+mCeIYQ0Efqe055FfSir367fbp/Qrne/WnWYeOF/P1PU+NicZVCHVxtO6uSYAxaErdDnGwzQyyZc9IMwS70G7fzUGJI7IOB12zq7lZEIdGWhk3qUPknTKtZ6ig4qyPA8G4KMlcSkb9tjfKYayQ4RrmIY5JUyh1fTPGVmS4hqxA8pHzAK+9RUlwVmDnw7eMTxmb9u+AscVPEOQ3EVRumcJNOfJvNK2+qQbi5/K77/cbXR7Au7xuLiPA65RTL0QDdSONEELn1E9GnIKBaF6XvcmTyNLeI1fnW4FF73BgNQKNWEfswEeoA8rxlCdJnD1XDXg=
- secure: VZuRhCu8kvJrMsTPZVJZVPbeJs6AJK8RVBvbNAhYGhiAO4wHUQ5ZcpO6IZVJ8c+pVL0WzGRogZg95sO7Ul1zpjxIWdla6XVqhU8YY+5RdcOUZK14qhM4XE5qnUSGAHwpY3jyRdFUOVbA6N/jus+AplQRsbbYNOZTMYKpnduyPqLaUfxeH6BvdPUiuSSG0CW+RZDsrbCTsQsVBk2QI6T5Y1yIKWl60zXYBelyb9X7RdCKg0i6xYMav18puHatozvJkOpLTXeOGxbZbclFwagH+GhJ7DFIs/FbLqDymS/+eLdzU8NSIqe9mUnRKul0jVWiruMR4a12f/ANCEuSvOcqeO1GuDAHyykmbWEdnKRlhZ3xmVhuIpKnRIlKenf2/WImsew1w07mnmLjjKVoxY+VghC06jSfvWDTqdRiJY1pSuUomuAmGRY0Ap6KehFgPJYAhI61US26hHolr6aVGdxB5ggipf/9UMWLWmn74YT4Ao6Bim+PqRVSRCX3v30t1TTrPvCpoa43PfVWLonDuJWSGD5aLaOWfZCtnemZYcR81Q/HE3MCGtSSLgQcAGRcHxxJ4wzG/GivP6c5bIHkjzTud4GJDQthuguErXkIxK6QyxXzpNzm4Va1ZXhuMkb29KgR9LIHeHXyJ8blGZC2Onmq1qGLtiv259kFsuxOVO9TxLA=
script: bundle exec rake smart_test
before_install: gem install bundler
2 changes: 1 addition & 1 deletion aliyun-sdk.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rake', '~> 10.4'
spec.add_development_dependency 'rspec', '~> 3.3'
spec.add_development_dependency 'webmock', '~> 1.22'
spec.add_development_dependency 'simplecov', '~> 0.10'
spec.add_development_dependency 'simplecov', '~> 0.10.0'
spec.add_development_dependency 'minitest', '~> 5.8'

spec.required_ruby_version = '>= 1.9.3'
Expand Down
36 changes: 26 additions & 10 deletions lib/aliyun/oss/bucket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -595,17 +595,33 @@ def object_url(key, sign = true, expiry = 60)
return url unless sign

expires = Time.now.to_i + expiry
string_to_sign = "GET\n" +
"\n\n" +
"#{expires}\n" +
"/#{name}/#{key}"
signature = sign(string_to_sign)

query_string = {
query = {
'Expires' => expires.to_s,
'OSSAccessKeyId' => CGI.escape(access_key_id),
'Signature' => CGI.escape(signature)
}.map { |k, v| "#{k}=#{v}" }.join('&')
'OSSAccessKeyId' => CGI.escape(access_key_id)
}

sub_res = []
if @protocol.get_sts_token
sub_res << "security-token=#{@protocol.get_sts_token}"
query['security-token'] = CGI.escape(@protocol.get_sts_token)
end

resource = "/#{name}/#{key}"
unless sub_res.empty?
resource << "?#{sub_res.join('&')}"
end

string_to_sign = "" <<
"GET\n" << # method
"\n" << # Content-MD5
"\n" << # Content-Type
"#{expires}\n" <<
"#{resource}"

signature = sign(string_to_sign)
query_string =
query.merge('Signature' => CGI.escape(signature))
.map { |k, v| "#{k}=#{v}" }.join('&')

[url, query_string].join('?')
end
Expand Down
6 changes: 6 additions & 0 deletions lib/aliyun/oss/protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,12 @@ def get_access_key_id
@config.access_key_id
end

# Get user's STS token
# @return [String] the STS token
def get_sts_token
@config.sts_token
end

# Sign a string using the stored access key secret
# @param [String] string_to_sign the string to sign
# @return [String] the signature
Expand Down
4 changes: 2 additions & 2 deletions lib/aliyun/oss/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ def get_signature(key, verb, headers, resources)
"#{verb}\n#{content_md5}\n#{content_type}\n#{date}\n" +
"#{cano_headers}#{cano_res}"

logger.debug("String to sign: #{string_to_sign}")

Util.sign(key, string_to_sign)
end

Expand All @@ -51,6 +49,8 @@ def get_signature(key, verb, headers, resources)
# @param [String] string_to_sign the string to sign
# @return [String] the signature
def sign(key, string_to_sign)
logger.debug("String to sign: #{string_to_sign}")

Base64.strict_encode64(
OpenSSL::HMAC.digest('sha1', key, string_to_sign))
end
Expand Down
3 changes: 2 additions & 1 deletion lib/aliyun/sts/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ module STS
# timeout, retry mechanism, etc
class Config < Common::Struct::Base

attrs :access_key_id, :access_key_secret
attrs :access_key_id, :access_key_secret, :endpoint

def initialize(opts = {})
super(opts)

@access_key_id = @access_key_id.strip if @access_key_id
@access_key_secret = @access_key_secret.strip if @access_key_secret
@endpoint = @endpoint.strip if @endpoint
end
end # Config

Expand Down
2 changes: 1 addition & 1 deletion lib/aliyun/sts/protocol.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def do_request(params)

r = RestClient::Request.execute(
:method => 'POST',
:url => ENDPOINT,
:url => @config.endpoint || ENDPOINT,
:payload => query
) do |response, request, result, &blk|

Expand Down
24 changes: 24 additions & 0 deletions spec/aliyun/oss/client/bucket_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,30 @@ def err(msg, reqid = '0000')
sig = Util.sign('yyy', string_to_sign)
expect(signature).to eq(sig)
end

it "should get object url with STS" do
sts_bucket = Client.new(
:endpoint => @endpoint,
:access_key_id => 'xxx',
:access_key_secret => 'yyy',
:sts_token => 'zzz').get_bucket(@bucket_name)

object_url = 'http://rubysdk-bucket.oss-cn-hangzhou.aliyuncs.com/yeah'

url = sts_bucket.object_url('yeah')
path = url[0, url.index('?')]
expect(path).to eq(object_url)

query = {}
url[url.index('?') + 1, url.size].split('&')
.each { |s| k, v = s.split('='); query[k] = v }

expect(query.key?('Expires')).to be true
expect(query.key?('Signature')).to be true
expect(query['OSSAccessKeyId']).to eq('xxx')
expect(query['security-token']).to eq('zzz')
end

end # object operations

context "multipart operations" do
Expand Down
16 changes: 16 additions & 0 deletions tests/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,21 @@ def creds
def bucket
ENV['RUBY_SDK_OSS_BUCKET']
end

def sts_creds
{
access_key_id: ENV['RUBY_SDK_STS_ID'],
access_key_secret: ENV['RUBY_SDK_STS_KEY'],
endpoint: ENV['RUBY_SDK_STS_ENDPOINT']
}
end

def sts_role
ENV['RUBY_SDK_STS_ROLE']
end

def sts_bucket
ENV['RUBY_SDK_STS_BUCKET']
end
end
end
69 changes: 69 additions & 0 deletions tests/test_object_url.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
require 'minitest/autorun'
require 'yaml'
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
require 'aliyun/oss'
require 'aliyun/sts'
require 'rest-client'
require_relative 'config'

class TestObjectUrl < Minitest::Test
def setup
Aliyun::Common::Logging.set_log_level(Logger::DEBUG)
client = Aliyun::OSS::Client.new(TestConf.creds)
@bucket = client.get_bucket(TestConf.bucket)

@prefix = "tests/object_url/"
end

def get_key(k)
"#{@prefix}#{k}"
end

def test_signed_url_for_get
key = get_key('object-for-get')

@bucket.put_object(key, acl: Aliyun::OSS::ACL::PRIVATE)

plain_url = @bucket.object_url(key, false)
begin
r = RestClient.get(plain_url)
assert false, 'GET plain object url should receive 403'
rescue => e
assert_equal 403, e.response.code
end

signed_url = @bucket.object_url(key)
r = RestClient.get(signed_url)

assert_equal 200, r.code
end

def test_signed_url_with_sts
key = get_key('object-with-sts')

sts_client = Aliyun::STS::Client.new(TestConf.sts_creds)
token = sts_client.assume_role(TestConf.sts_role, 'app')

bucket = Aliyun::OSS::Client.new(
:endpoint => TestConf.creds[:endpoint],
:sts_token => token.security_token,
:access_key_id => token.access_key_id,
:access_key_secret => token.access_key_secret)
.get_bucket(TestConf.sts_bucket)

bucket.put_object(key, acl: Aliyun::OSS::ACL::PRIVATE)

plain_url = bucket.object_url(key, false)
begin
r = RestClient.get(plain_url)
assert false, 'GET plain object url should receive 403'
rescue => e
assert_equal 403, e.response.code
end

signed_url = bucket.object_url(key)
r = RestClient.get(signed_url)

assert_equal 200, r.code
end
end

0 comments on commit ca5ba5a

Please sign in to comment.