blobsterix 0.0.34 → 0.0.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/blobsterix/s3/s3_auth_v4.rb +82 -10
- data/lib/blobsterix/version.rb +1 -1
- data/spec/lib/s3/s3_auth_spec.rb +60 -4
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 549a1162916756237d9df4372dbce37432d2cbb2
|
4
|
+
data.tar.gz: 85b118a8cb57a4140e922dbae3caf6c768b17cd3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00f4d1abc5b5b64469090a20b5f6c6d303ba13d361a723961f606e79723fb23eb56755d8f5ee91d548d51f69466beabeab8fd6f01f4042b6ec718f621714a773
|
7
|
+
data.tar.gz: c79acbb55211463bb151cc0ed731aa85a7673b8c4936842d839a39a28b8cce7656c3efd8e9b6f829daee35897ecf3d9008260cf964b01eb4fab7aaf988dec342
|
@@ -3,30 +3,102 @@ module Blobsterix
|
|
3
3
|
class V4
|
4
4
|
include ::Blobsterix::S3UrlHelper
|
5
5
|
include ::Blobsterix::UrlHelper
|
6
|
-
|
7
6
|
|
8
7
|
V4_REGEX = /AWS4-HMAC-SHA256 Credential=(\w+\/\d+\/.+\/\w+\/aws4_request),.*SignedHeaders=(.+),.*Signature=(\w+)/
|
9
8
|
|
10
9
|
def self.create(env)
|
11
|
-
|
12
|
-
matcher = V4_REGEX.match(auth_string)
|
10
|
+
matcher = V4_REGEX.match(env["HTTP_AUTHORIZATION"])
|
13
11
|
matcher ? V4.new(env, matcher[1], matcher[2], matcher[3]) : nil
|
14
12
|
end
|
15
13
|
|
16
|
-
attr_reader :env, :credential, :signed_headers, :signature
|
14
|
+
attr_reader :env, :credential, :signed_headers, :signature, :access_key, :scope_date, :scope_region, :scope_service
|
15
|
+
|
17
16
|
def initialize(env, credential, signed_headers, signature)
|
18
17
|
@env = env
|
19
18
|
@credential = credential
|
20
19
|
@signed_headers = signed_headers
|
21
20
|
@signature = signature
|
21
|
+
|
22
|
+
@access_key, @scope_date, @scope_region, @scope_service = @credential.split("/")
|
23
|
+
end
|
24
|
+
|
25
|
+
def check(secret_key_store)
|
26
|
+
return false if is_expired?
|
27
|
+
|
28
|
+
secret_key = secret_key_store.get_key(@access_key)
|
29
|
+
gen_signature(secret_key) == @signature
|
30
|
+
end
|
31
|
+
|
32
|
+
def gen_signature(secret_key)
|
33
|
+
signing_key = hmac256("AWS4"+secret_key, @scope_date)
|
34
|
+
signing_key = hmac256(signing_key, @scope_region)
|
35
|
+
signing_key = hmac256(signing_key, @scope_service)
|
36
|
+
signing_key = hmac256(signing_key, "aws4_request")
|
37
|
+
|
38
|
+
hmac256(signing_key, string_to_sign).unpack('H*').first
|
39
|
+
end
|
40
|
+
|
41
|
+
def string_to_sign
|
42
|
+
[
|
43
|
+
"AWS4-HMAC-SHA256",
|
44
|
+
header_datetime_iso8601,
|
45
|
+
[@scope_date, @scope_region, @scope_service, "aws4_request"].join("/"),
|
46
|
+
Digest::SHA256.hexdigest(canonical_request)
|
47
|
+
].join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def canonical_request
|
51
|
+
[
|
52
|
+
@env["REQUEST_METHOD"].to_s.upcase,
|
53
|
+
@env["REQUEST_PATH"],
|
54
|
+
canonical_query,
|
55
|
+
canonical_headers,
|
56
|
+
@signed_headers,
|
57
|
+
@env["HTTP_X_AMZ_CONTENT_SHA256"] || Digest::SHA256.hexdigest("")
|
58
|
+
].join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def canonical_query
|
62
|
+
@env["QUERY_STRING"].to_s.split("&").sort.map do |param|
|
63
|
+
escaped = param.split("=").map { |k_or_v| escape(k_or_v) }.join("=")
|
64
|
+
escaped["="] ? escaped : "#{escaped}="
|
65
|
+
end.join("&")
|
66
|
+
end
|
67
|
+
|
68
|
+
def canonical_headers
|
69
|
+
canonicalized = @env.select { |key,value| key.is_a?(String) }.inject({}) do |memo, (key,value)|
|
70
|
+
memo[key.gsub("HTTP_","").downcase.gsub("_","-")] = value.to_s.strip
|
71
|
+
memo
|
72
|
+
end
|
73
|
+
|
74
|
+
headers = ""
|
75
|
+
|
76
|
+
@signed_headers.split(";").each do |key|
|
77
|
+
raise StandardError.new("Missing signed header") unless canonicalized.key?(key)
|
78
|
+
headers << key + ":" + canonicalized[key] + "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
headers
|
82
|
+
end
|
83
|
+
|
84
|
+
def hmac256(key, data)
|
85
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), key, data)
|
86
|
+
end
|
87
|
+
|
88
|
+
def header_datetime
|
89
|
+
Time.parse(@env["HTTP_X_AMZ_DATE"] || @env["HTTP_DATE"] || Time.now.to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def header_datetime_iso8601
|
93
|
+
header_datetime.utc.strftime('%Y%m%dT%H%M%SZ')
|
94
|
+
end
|
95
|
+
|
96
|
+
def is_expired?
|
97
|
+
::Blobsterix::S3Auth.current_time > header_datetime+15*60
|
22
98
|
end
|
23
|
-
def getSignatureKey(key, dateStamp, regionName, serviceName)
|
24
|
-
kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
|
25
|
-
kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
|
26
|
-
kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
|
27
|
-
kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
|
28
99
|
|
29
|
-
|
100
|
+
def escape(string)
|
101
|
+
string.gsub(/([^a-zA-Z0-9_.\-~]+)/) { "%" + $1.unpack("H2" * $1.bytesize).join("%").upcase }
|
30
102
|
end
|
31
103
|
end
|
32
104
|
end
|
data/lib/blobsterix/version.rb
CHANGED
data/spec/lib/s3/s3_auth_spec.rb
CHANGED
@@ -63,7 +63,7 @@ describe Blobsterix::S3Auth do
|
|
63
63
|
:path=>"/photos/puppy.jpg",
|
64
64
|
:query=>"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&Expires=1175139620",
|
65
65
|
:head => {
|
66
|
-
"host"=>"johnsmith.s3.amazonaws.com"
|
66
|
+
"host"=>"johnsmith.s3.amazonaws.com"
|
67
67
|
}
|
68
68
|
}
|
69
69
|
}
|
@@ -97,12 +97,12 @@ describe Blobsterix::S3Auth do
|
|
97
97
|
"HTTP_PROXY_CONNECTION"=>"Keep-Alive",
|
98
98
|
"HTTP_DATE"=>"Fri, 24 May 2013 00:00:00 GMT",
|
99
99
|
"HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41",
|
100
|
-
"HTTP_HOST"=>"examplebucket.s3.amazonaws.com
|
100
|
+
"HTTP_HOST"=>"examplebucket.s3.amazonaws.com",
|
101
101
|
"HTTP_TE"=>"trailers, deflate, gzip",
|
102
102
|
"HTTP_CONNECTION"=>"TE, close",
|
103
103
|
"CONTENT_LENGTH"=>"0",
|
104
104
|
"REQUEST_METHOD"=>"GET",
|
105
|
-
"REQUEST_URI"=>"http://examplebucket.s3.amazonaws.com
|
105
|
+
"REQUEST_URI"=>"http://examplebucket.s3.amazonaws.com/test.txt",
|
106
106
|
"QUERY_STRING"=>nil,
|
107
107
|
"HTTP_VERSION"=>"1.1",
|
108
108
|
"SCRIPT_NAME"=>"",
|
@@ -117,6 +117,42 @@ describe Blobsterix::S3Auth do
|
|
117
117
|
}
|
118
118
|
}
|
119
119
|
|
120
|
+
let(:v4_req_env_with_query_param) {
|
121
|
+
{
|
122
|
+
"HTTP_HOST"=>"firefox.s3.amazonaws.com",
|
123
|
+
"HTTP_ACCEPT_ENCODING"=>"identity",
|
124
|
+
"HTTP_X_AMZ_CONTENT_SHA256"=>"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
125
|
+
"HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20160217/US/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=10681a4fd1254dce4d80d7c254ae521dd8031b5ef871d77b7ffb28b260ee8372",
|
126
|
+
"HTTP_X_AMZ_DATE"=>"20160217T134245Z",
|
127
|
+
"CONTENT_LENGTH"=>"0",
|
128
|
+
"REQUEST_METHOD"=>"GET",
|
129
|
+
"REQUEST_URI"=>"http://firefox.s3.amazonaws.com/?delimiter=/",
|
130
|
+
"QUERY_STRING"=>"delimiter=/",
|
131
|
+
"HTTP_VERSION"=>"1.1",
|
132
|
+
"SCRIPT_NAME"=>"",
|
133
|
+
"REQUEST_PATH"=>"/",
|
134
|
+
"PATH_INFO"=>"/"
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
let(:v4_req_env_with_query_param_2) {
|
139
|
+
{
|
140
|
+
"HTTP_HOST"=>"firefox.s3.amazonaws.com",
|
141
|
+
"HTTP_ACCEPT_ENCODING"=>"identity",
|
142
|
+
"HTTP_X_AMZ_CONTENT_SHA256"=>"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
143
|
+
"HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20160217/US/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=d8fe8bcca8ada96996626125bec12258ed4ba0462f7c277d7412a8b9c230430d",
|
144
|
+
"HTTP_X_AMZ_DATE"=>"20160217T134245Z",
|
145
|
+
"CONTENT_LENGTH"=>"0",
|
146
|
+
"REQUEST_METHOD"=>"GET",
|
147
|
+
"REQUEST_URI"=>"http://firefox.s3.amazonaws.com/?location",
|
148
|
+
"QUERY_STRING"=>"location",
|
149
|
+
"HTTP_VERSION"=>"1.1",
|
150
|
+
"SCRIPT_NAME"=>"",
|
151
|
+
"REQUEST_PATH"=>"/",
|
152
|
+
"PATH_INFO"=>"/"
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
120
156
|
before :all do
|
121
157
|
Blobsterix.secret_key_store = Blobsterix::S3Auth::KeyStore.new(
|
122
158
|
"AKIAIOSFODNN7EXAMPLE" => "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
@@ -174,8 +210,28 @@ describe Blobsterix::S3Auth do
|
|
174
210
|
auth.class.should eql Blobsterix::S3Auth::V4
|
175
211
|
end
|
176
212
|
|
213
|
+
it "should authenticate v4 request if the signature match and request time is within 15minutes window" do
|
214
|
+
Blobsterix::S3Auth.current_time=lambda{Time.parse("Fri, 24 May 2013 00:14:59 GMT")}
|
215
|
+
Blobsterix::S3Auth.authenticate(v4_req_env).check(Blobsterix.secret_key_store).should be_true
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should not authenticate v4 request if the signature match but request time is older than 15 minutes" do
|
219
|
+
Blobsterix::S3Auth.current_time=lambda{Time.parse("Fri, 24 May 2013 00:15:01 GMT")}
|
220
|
+
Blobsterix::S3Auth.authenticate(v4_req_env).check(Blobsterix.secret_key_store).should be_false
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should authenticate v4 request with query params" do
|
224
|
+
Blobsterix::S3Auth.current_time=lambda{Time.parse("20160217T135245Z")}
|
225
|
+
Blobsterix::S3Auth.authenticate(v4_req_env_with_query_param).check(Blobsterix.secret_key_store).should be_true
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should authenticate v4 request with query param without equal character" do
|
229
|
+
Blobsterix::S3Auth.current_time=lambda{Time.parse("20160217T135245Z")}
|
230
|
+
Blobsterix::S3Auth.authenticate(v4_req_env_with_query_param_2).check(Blobsterix.secret_key_store).should be_true
|
231
|
+
end
|
232
|
+
|
177
233
|
it "should at least recognize aws v2" do
|
178
234
|
auth = Blobsterix::S3Auth.authenticate(v2_req_env)
|
179
235
|
auth.class.should eql Blobsterix::S3Auth::V2
|
180
236
|
end
|
181
|
-
end
|
237
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blobsterix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.35
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Sudmann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -317,7 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
317
317
|
version: '0'
|
318
318
|
requirements: []
|
319
319
|
rubyforge_project:
|
320
|
-
rubygems_version: 2.
|
320
|
+
rubygems_version: 2.5.1
|
321
321
|
signing_key:
|
322
322
|
specification_version: 4
|
323
323
|
summary: Blobsterix is a transcoding, caching, storage server.
|