blobsterix 0.0.34 → 0.0.35
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.
- 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.
|