active_elastic_job 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/active_elastic_job/md5_message_digest_calculation.rb +17 -24
- data/lib/active_elastic_job/message_verifier.rb +9 -7
- data/lib/active_elastic_job/rack/sqs_message_consumer.rb +14 -5
- data/lib/active_elastic_job/version.rb +1 -1
- data/lib/active_job/queue_adapters/active_elastic_job_adapter.rb +27 -23
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7f80373d7f57286083c91bfb62632d463df9b7a
|
4
|
+
data.tar.gz: 30ad4c79acf1d9a8393092bdf430f7416b606239
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72d3f884abb6f6b16d0c43fe2c537ba64d80acba7e65ff2a97faaea99d16b4e6b45666aeb33f232b95cf227b8d16af172385bc9dfcc5d978720fe97f3ea6742a
|
7
|
+
data.tar.gz: b5f4204e84cc39de88f67f321fbc758021b1fbcbb698cc7c991e1a1e0e14b0b9d6d177f92f92191840a01e9e72e29e4b5796886dfa4d2482e310889b89eafda1
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'openssl'
|
2
2
|
module ActiveElasticJob
|
3
3
|
# This module provides methods that calculate the MD5 digest for Amazon
|
4
4
|
# SQS message bodies and message attributes.
|
@@ -29,35 +29,30 @@ module ActiveElasticJob
|
|
29
29
|
'Number' => 1
|
30
30
|
}
|
31
31
|
|
32
|
-
|
32
|
+
NORMALIZED_ENCODING = Encoding::UTF_8
|
33
33
|
|
34
|
-
# Returns MD5 digest of +message_body+.
|
35
34
|
def md5_of_message_body(message_body)
|
36
35
|
OpenSSL::Digest::MD5.hexdigest(message_body)
|
37
36
|
end
|
38
37
|
|
39
|
-
# Returns MD5 digest of +message_attributes+.
|
40
|
-
#
|
41
|
-
# The calculation follows the official algorithm which
|
42
|
-
# is specified by Amazon.
|
43
38
|
def md5_of_message_attributes(message_attributes)
|
44
|
-
encoded =
|
45
|
-
|
46
|
-
|
39
|
+
encoded = { }
|
40
|
+
message_attributes.each do |name, attribute|
|
41
|
+
name = name.to_s
|
42
|
+
encoded[name] = String.new
|
43
|
+
encoded[name] << encode_length_and_bytes(name) <<
|
44
|
+
encode_length_and_bytes(attribute[:data_type]) <<
|
45
|
+
[TRANSPORT_TYPE_ENCODINGS[attribute[:data_type]]].pack('C'.freeze)
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if string_value = v['string_value'] || v[:string_value]
|
53
|
-
hash[name.to_s] << encode_length_and_string(string_value)
|
54
|
-
elsif binary_value = v['binary_value'] || v[:binary_value]
|
55
|
-
hash[name.to_s] << encode_length_and_bytes(binary_value)
|
47
|
+
if string_value = attribute[:string_value]
|
48
|
+
encoded[name] << encode_length_and_string(string_value)
|
49
|
+
elsif binary_value = attribute[:binary_value]
|
50
|
+
encoded[name] << encode_length_and_bytes(binary_value)
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
59
|
-
buffer = encoded.keys.sort.reduce(
|
60
|
-
|
54
|
+
buffer = encoded.keys.sort.reduce(String.new) do |string, name|
|
55
|
+
string << encoded[name]
|
61
56
|
end
|
62
57
|
OpenSSL::Digest::MD5.hexdigest(buffer)
|
63
58
|
end
|
@@ -65,15 +60,13 @@ module ActiveElasticJob
|
|
65
60
|
private
|
66
61
|
|
67
62
|
def encode_length_and_string(string)
|
68
|
-
return '' if string.nil?
|
69
63
|
string = String.new(string)
|
70
|
-
string.encode!(
|
64
|
+
string.encode!(NORMALIZED_ENCODING)
|
71
65
|
encode_length_and_bytes(string)
|
72
66
|
end
|
73
67
|
|
74
68
|
def encode_length_and_bytes(bytes)
|
75
|
-
|
76
|
-
[ bytes.bytesize, bytes ].pack("L>a#{bytes.bytesize}")
|
69
|
+
[bytes.bytesize, bytes].pack('L>a*'.freeze)
|
77
70
|
end
|
78
71
|
end
|
79
72
|
end
|
@@ -13,20 +13,22 @@ module ActiveElasticJob
|
|
13
13
|
@secret = secret
|
14
14
|
end
|
15
15
|
|
16
|
-
def verify
|
16
|
+
def verify(message, digest)
|
17
17
|
if message.nil? || message.blank? || digest.nil? || digest.blank?
|
18
|
-
|
18
|
+
return false
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
return ActiveSupport::SecurityUtils.secure_compare(
|
22
|
+
digest, generate_digest(message))
|
23
|
+
end
|
24
|
+
|
25
|
+
def verify!(message, digest)
|
26
|
+
raise InvalidDigest unless verify(message, digest)
|
25
27
|
end
|
26
28
|
|
27
29
|
def generate_digest(message)
|
28
30
|
require 'openssl' unless defined?(OpenSSL)
|
29
|
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest
|
31
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, @secret, message)
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -5,8 +5,8 @@ module ActiveElasticJob
|
|
5
5
|
# This middleware intercepts requests which are sent by the SQS daemon
|
6
6
|
# running in {Amazon Elastic Beanstalk worker environments}[http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html].
|
7
7
|
# It does this by looking at the +User-Agent+ header.
|
8
|
-
# Furthermore, it verifies the digest which is sent along with a legit SQS
|
9
|
-
# and passed as an HTTP header in the resulting request.
|
8
|
+
# Furthermore, it verifies the digest which is sent along with a legit SQS
|
9
|
+
# message, and passed as an HTTP header in the resulting request.
|
10
10
|
# The digest is based on Rails' +secrets.secret_key_base+.
|
11
11
|
# Therefore, the application running in the web environment, which generates
|
12
12
|
# the digest, and the application running in the worker
|
@@ -36,11 +36,20 @@ module ActiveElasticJob
|
|
36
36
|
job = JSON.load(request.body)
|
37
37
|
ActiveJob::Base.execute(job)
|
38
38
|
rescue ActiveElasticJob::MessageVerifier::InvalidDigest => e
|
39
|
-
return [
|
39
|
+
return [
|
40
|
+
'403',
|
41
|
+
{CONTENT_TYPE_HEADER_NAME => 'text/plain' },
|
42
|
+
["incorrect digest"]]
|
40
43
|
rescue StandardError => e
|
41
|
-
return [
|
44
|
+
return [
|
45
|
+
'500',
|
46
|
+
{CONTENT_TYPE_HEADER_NAME => 'text/plain' },
|
47
|
+
[e.message]]
|
42
48
|
end
|
43
|
-
return [
|
49
|
+
return [
|
50
|
+
OK_RESPONSE_CODE ,
|
51
|
+
{CONTENT_TYPE_HEADER_NAME => CONTENT_TYPE },
|
52
|
+
[ '' ]]
|
44
53
|
end
|
45
54
|
@app.call(env)
|
46
55
|
end
|
@@ -8,9 +8,11 @@ module ActiveJob
|
|
8
8
|
# environments}[http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features-managing-env-tiers.html].
|
9
9
|
#
|
10
10
|
# This adapter serializes job objects and sends them as a message to an
|
11
|
-
# Amazon SQS queue specified by the job's queue name,
|
11
|
+
# Amazon SQS queue specified by the job's queue name,
|
12
|
+
# see <tt>ActiveJob::Base.queue_as</tt>
|
12
13
|
#
|
13
|
-
# To use Active Elastic Job, set the queue_adapter config
|
14
|
+
# To use Active Elastic Job, set the queue_adapter config
|
15
|
+
# to +:active_elastic_job+.
|
14
16
|
#
|
15
17
|
# Rails.application.config.active_job.queue_adapter = :active_elastic_job
|
16
18
|
class ActiveElasticJobAdapter
|
@@ -34,9 +36,9 @@ which exceeds the allowed maximum of #{MAX_MESSAGE_SIZE} bytes imposed by Amazon
|
|
34
36
|
end
|
35
37
|
|
36
38
|
# Raised when job queue does not exist. The job queue is determined by
|
37
|
-
# <tt>ActiveJob::Base.queue_as</tt>. You can either: (1) create a new
|
38
|
-
# SQS queue and attach a worker environment to it, or (2) select a
|
39
|
-
# queue for your jobs.
|
39
|
+
# <tt>ActiveJob::Base.queue_as</tt>. You can either: (1) create a new
|
40
|
+
# Amazon SQS queue and attach a worker environment to it, or (2) select a
|
41
|
+
# different queue for your jobs.
|
40
42
|
#
|
41
43
|
# Example:
|
42
44
|
# * Open your AWS console and create an SQS queue named +high_priority+ in
|
@@ -49,12 +51,12 @@ which exceeds the allowed maximum of #{MAX_MESSAGE_SIZE} bytes imposed by Amazon
|
|
49
51
|
# end
|
50
52
|
class NonExistentQueue < Error
|
51
53
|
def initialize(queue_name)
|
52
|
-
msg =
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
msg = "The job is bound to queue at #{queue_name}. " <<
|
55
|
+
"Unfortunately a queue with this name does not exist in this " <<
|
56
|
+
"region. Either create an Amazon SQS queue named #{queue_name} - " <<
|
57
|
+
"you can do this in AWS console, make sure to select region " <<
|
58
|
+
"'#{ENV['AWS_REGION']}' - or you select another queue for your jobs."
|
59
|
+
|
58
60
|
super msg
|
59
61
|
end
|
60
62
|
end
|
@@ -62,11 +64,12 @@ region '#{ENV['AWS_REGION']}' - or you select another queue for your jobs.
|
|
62
64
|
# Raised when calculated MD5 digest does not match the MD5 Digest
|
63
65
|
# of the response from Amazon SQS.
|
64
66
|
class MD5MismatchError < Error
|
65
|
-
def initialize(
|
66
|
-
msg =
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
def initialize(message_id)
|
68
|
+
msg = "MD5 returned by Amazon SQS does not match the calculation " <<
|
69
|
+
"on the original request. The message with Message ID " <<
|
70
|
+
"#{message_id} sent to SQS might be corrupted."
|
71
|
+
|
72
|
+
super msg
|
70
73
|
end
|
71
74
|
end
|
72
75
|
|
@@ -128,13 +131,14 @@ The message with Message ID #{message_id} sent to SQS might be corrupted.
|
|
128
131
|
def calculate_delay(timestamp)
|
129
132
|
delay = (timestamp - Time.current.to_f).to_i + 1
|
130
133
|
if delay > MAX_DELAY_IN_MINUTES.minutes
|
131
|
-
msg
|
132
|
-
|
133
|
-
See http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html
|
134
|
-
for further details!
|
135
|
-
|
136
|
-
raise RangeError
|
134
|
+
msg = "Jobs cannot be scheduled more than " <<
|
135
|
+
"#{MAX_DELAY_IN_MINUTES} minutes into the future. " <<
|
136
|
+
"See http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html" <<
|
137
|
+
" for further details!"
|
138
|
+
|
139
|
+
raise RangeError, msg
|
137
140
|
end
|
141
|
+
delay = 0 if delay < 0
|
138
142
|
delay
|
139
143
|
end
|
140
144
|
|
@@ -158,7 +162,7 @@ for further details!
|
|
158
162
|
@verifier.generate_digest(messsage_body)
|
159
163
|
end
|
160
164
|
|
161
|
-
def verify_md5_digests!(response, messsage_body, message_attributes
|
165
|
+
def verify_md5_digests!(response, messsage_body, message_attributes)
|
162
166
|
if md5_of_message_body(messsage_body) != response.md5_of_message_body
|
163
167
|
raise MD5MismatchError, response.message_id
|
164
168
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_elastic_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tawan Sierek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|