image_vise 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 037b10ccb40b25cf245f7751d99085e67cca8552
4
- data.tar.gz: 43a7c0779a8ee07bc6a1da19bf98e1ca41c091c1
3
+ metadata.gz: '0790cd6968b8c71bdb827b446d0505169e6d6254'
4
+ data.tar.gz: 7b98094a9cffaf0f518ef1e1b2e851fbce962fa2
5
5
  SHA512:
6
- metadata.gz: 9b9e8e91a62ccabe54fe2fa12c24dc2ef6e2b8c64f63e2f7d90d62ace373e7cef776c3fab84d0823ab3577340a6eb202e4077acc8ebbbabb26e2f5dc168b6138
7
- data.tar.gz: 51b7013be378b9e72faa23f6d9c94895ce11915622509459406da9814ba5388d1c1ef99d8d0b183c5ab9c04a2359d01ce40fc350fe9738d18cd4f5c9b09dadbd
6
+ metadata.gz: 1806c4f6da54b58dc7b4b38f286c6afd411a3a770c51b7cc6e258407e6fd1339063b43a1c9372b09d32345a5a4e13dc8f2e77a382b887eac798b4a7d113700a2
7
+ data.tar.gz: cb9a32c8dabe2261ff291d1d0ba778d79056f0fc8c41cba459f635a52fb76cfb99db13d24e08ea979525a3a8c6f2b73e3e8c275b66bdf096ccdc0fd84c3d75ac
data/README.md CHANGED
@@ -7,11 +7,11 @@ framework. The main uses are:
7
7
  * Applying image filters
8
8
 
9
9
  It is implemented as a Rack application that responds to any URL and accepts the following two _last_ path
10
- compnents, internally named `q` and `sig`:
10
+ compnents, internally named `request` and `signature`:
11
11
 
12
- * `q` - Base64 encoded JSON object with `src_url` and `pipeline` properties
12
+ * `request` - Base64 encoded JSON object with `src_url` and `pipeline` properties
13
13
  (the source URL of the image and processing steps to apply)
14
- * `sig` - the HMAC signature, computed over the JSON in `q` before it gets Base64-encoded
14
+ * `signature` - the HMAC signature, computed over the JSON in `q` before it gets Base64-encoded
15
15
 
16
16
  A request to `ImageVise` might look like this:
17
17
 
@@ -90,11 +90,6 @@ def thumb_url(source_image_url)
90
90
  '/images' + path
91
91
  end
92
92
  ```
93
- ## Path decoding and SCRIPT_NAME
94
-
95
- `ImageVise::RenderEngine` _must_ be mounted under a `SCRIPT_NAME` (using either `mount` in Rails
96
- or using `map` in Rack). That is so since we may have more than 1 path component that we have to
97
- decode (when the Base64 payload contains slashes).
98
93
 
99
94
  ## Processing files on the local filesystem instead of remote ones
100
95
 
@@ -8,10 +8,7 @@ class ImageVise::ImageRequest < Ks.strict(:src_url, :pipeline)
8
8
 
9
9
  # Initializes a new ParamsChecker from given HTTP server framework
10
10
  # params. The params can be symbol- or string-keyed, does not matter.
11
- def self.from_params(qs_params:, secrets:)
12
- base64_encoded_params = qs_params.fetch(:q) rescue qs_params.fetch('q')
13
- given_signature = qs_params.fetch(:sig) rescue qs_params.fetch('sig')
14
-
11
+ def self.from_params(base64_encoded_params:, given_signature:, secrets:)
15
12
  # Unmask slashes and equals signs (if they are present)
16
13
  base64_encoded_params = base64_encoded_params.tr('-', '/').tr('_', '+')
17
14
 
@@ -20,8 +17,8 @@ class ImageVise::ImageRequest < Ks.strict(:src_url, :pipeline)
20
17
  raise SignatureError, "Invalid or missing signature"
21
18
  end
22
19
 
23
- # Decode the JSON
24
- # (only AFTER the signature has been validated, so we can use symbol keys)
20
+ # Decode the JSON - only AFTER the signature has been validated,
21
+ # so we can use symbol keys
25
22
  decoded_json = Base64.decode64(base64_encoded_params)
26
23
  params = JSON.parse(decoded_json, symbolize_names: true)
27
24
 
@@ -29,21 +26,17 @@ class ImageVise::ImageRequest < Ks.strict(:src_url, :pipeline)
29
26
  source_url_str = params.fetch(:src_url).to_s
30
27
  raise URLError, "the :src_url parameter must be non-empty" if source_url_str.empty?
31
28
  pipeline_definition = params.fetch(:pipeline)
32
- new(src_url: URI(source_url_str), pipeline: ImageVise::Pipeline.from_param(pipeline_definition))
29
+ new(src_url: URI(source_url_str), pipeline: ImageVise::Pipeline.from_array_of_operator_params(pipeline_definition))
33
30
  rescue KeyError => e
34
31
  raise InvalidRequest.new(e.message)
35
32
  end
36
33
 
37
- def to_path_params(signed_with_secret)
38
- qs = to_query_string_params(signed_with_secret)
39
- q_masked = qs.fetch(:q).tr('/', '-').tr('+', '_')
40
- '/%s/%s' % [q_masked, qs[:sig]]
41
- end
42
-
43
- def to_query_string_params(signed_with_secret)
34
+ def to_path_params(signing_secret)
44
35
  payload = JSON.dump(to_h)
45
- base64_enc = Base64.strict_encode64(payload).gsub(/\=+$/, '')
46
- {q: base64_enc, sig: OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, signed_with_secret, base64_enc)}
36
+ req_base64_enc = Base64.strict_encode64(payload).gsub(/\=+$/, '')
37
+ req_masked = req_base64_enc.tr('/', '-').tr('+', '_')
38
+ sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, signing_secret, req_base64_enc)
39
+ '/%s/%s' % [req_masked, sig]
47
40
  end
48
41
 
49
42
  def to_h
@@ -3,7 +3,7 @@ class ImageVise::Pipeline
3
3
  operator = ImageVise.operator_from(name) or raise "Unknown operator #{name}"
4
4
  end
5
5
 
6
- def self.from_param(array_of_operator_names_to_operator_params)
6
+ def self.from_array_of_operator_params(array_of_operator_names_to_operator_params)
7
7
  operators = array_of_operator_names_to_operator_params.map do |(operator_name, operator_params)|
8
8
  operator_class = operator_by_name(operator_name)
9
9
  if operator_params && operator_params.any? && operator_class.method(:new).arity.nonzero?
@@ -57,8 +57,4 @@ class ImageVise::Pipeline
57
57
  operator.apply!(magick_image, image_metadata)
58
58
  end
59
59
  end
60
-
61
- def each(&b)
62
- @ops.each(&b)
63
- end
64
60
  end
@@ -72,9 +72,13 @@
72
72
 
73
73
  req = parse_env_into_request(env)
74
74
  bail(405, 'Only GET supported') unless req.get?
75
- params = extract_params_from_request(req)
75
+ encoded_request, signature = extract_params_from_request(req)
76
76
 
77
- image_request = ImageVise::ImageRequest.from_params(qs_params: params, secrets: ImageVise.secret_keys)
77
+ image_request = ImageVise::ImageRequest.from_params(
78
+ base64_encoded_params: encoded_request,
79
+ given_signature: signature,
80
+ secrets: ImageVise.secret_keys
81
+ )
78
82
  render_destination_file, render_file_type, etag = process_image_request(image_request)
79
83
  image_rack_response(render_destination_file, render_file_type, etag)
80
84
  rescue *permanent_failures => e
@@ -104,7 +108,7 @@
104
108
  # Extracts the image params from the Rack::Request
105
109
  #
106
110
  # @param rack_request[#path_info] an object that has a path info
107
- # @return [Hash] the params hash with `:q` and `:sig` keys
111
+ # @return [String, String] the Base64-encoded image request and the signature
108
112
  def extract_params_from_request(rack_request)
109
113
  # Prevent cache bypass DOS attacks by only permitting :sig and :q
110
114
  bail(400, 'Query strings are not supported') if rack_request.params.any?
@@ -121,7 +125,7 @@
121
125
  nothing_recovered = [q_from_path, sig_from_path].all?{|v| v.nil? || v.empty? }
122
126
  bail(400, 'Need 2 usable path components') if nothing_recovered
123
127
 
124
- {q: q_from_path, sig: sig_from_path}
128
+ [q_from_path, sig_from_path]
125
129
  end
126
130
 
127
131
  # Processes the ImageRequest object created from the request parameters,
@@ -1,3 +1,3 @@
1
1
  class ImageVise
2
- VERSION = '0.2.6'
2
+ VERSION = '0.3.0'
3
3
  end
data/lib/image_vise.rb CHANGED
@@ -83,25 +83,6 @@ class ImageVise
83
83
  keys or raise "No keys set, add a key using `ImageVise.add_secret_key!(key)'"
84
84
  end
85
85
 
86
- # Generate a set of querystring params for a resized image. Yields a Pipeline object that
87
- # will receive method calls for adding image operations to a stack.
88
- #
89
- # ImageVise.image_params(src_url: image_url_on_s3, secret: '...') do |p|
90
- # p.center_fit width: 128, height: 128
91
- # p.elliptic_stencil
92
- # end #=> {q: '...', sig: '...'}
93
- #
94
- # The query string elements can be then passed on to RenderEngine for validation and execution.
95
- #
96
- # @yield {ImageVise::Pipeline}
97
- # @return [Hash]
98
- def image_params(src_url:, secret:)
99
- p = Pipeline.new
100
- yield(p)
101
- raise ArgumentError, "Image pipeline has no steps defined" if p.empty?
102
- ImageRequest.new(src_url: URI(src_url), pipeline: p).to_query_string_params(secret)
103
- end
104
-
105
86
  # Generate a path for a resized image. Yields a Pipeline object that
106
87
  # will receive method calls for adding image operations to a stack.
107
88
  #
@@ -6,23 +6,26 @@ describe ImageVise::ImageRequest do
6
6
  img_params_json = JSON.dump(img_params)
7
7
 
8
8
  q = Base64.encode64(img_params_json)
9
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
10
- params = {q: q, sig: signature}
9
+ sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
11
10
 
12
- image_request = described_class.from_params(qs_params: params, secrets: ['this is a secret'])
13
- request_qs_params = image_request.to_query_string_params('this is a secret')
14
- expect(request_qs_params).to be_kind_of(Hash)
15
-
16
- image_request_roundtrip = described_class.from_params(qs_params: request_qs_params, secrets: ['this is a secret'])
11
+ image_request = described_class.from_params(
12
+ base64_encoded_params: q,
13
+ given_signature: sig,
14
+ secrets: ['this is a secret']
15
+ )
16
+ expect(image_request).to be_kind_of(described_class)
17
17
  end
18
18
 
19
- it 'converts a file:// URL into a URI objectlist' do
19
+ it 'converts a file:// URL into a URI object' do
20
20
  img_params = {src_url: 'file:///etc/passwd', pipeline: [[:auto_orient, {}]]}
21
21
  img_params_json = JSON.dump(img_params)
22
22
  q = Base64.encode64(img_params_json)
23
- signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
24
- params = {q: q, sig: signature}
25
- image_request = described_class.from_params(qs_params: params, secrets: ['this is a secret'])
23
+ sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'this is a secret', q)
24
+ image_request = described_class.from_params(
25
+ base64_encoded_params: q,
26
+ given_signature: sig,
27
+ secrets: ['this is a secret']
28
+ )
26
29
  expect(image_request.src_url).to be_kind_of(URI)
27
30
  end
28
31
 
@@ -40,7 +43,7 @@ describe ImageVise::ImageRequest do
40
43
  (1..12).each do |num_chars_in_url|
41
44
  uri = URI('http://ex.com/%s' % ('i' * num_chars_in_url))
42
45
  image_request = described_class.new(src_url: uri, pipeline: parametrized)
43
- q = image_request.to_query_string_params('password').fetch(:q)
46
+ q = image_request.to_path_params('password')
44
47
  expect(q).not_to include('=')
45
48
  end
46
49
  end
@@ -52,10 +55,9 @@ describe ImageVise::ImageRequest do
52
55
  img_params_json = JSON.dump(img_params)
53
56
  enc = Base64.encode64(img_params_json)
54
57
  signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, 'a', enc)
55
- params = {q: enc, sig: signature}
56
58
 
57
59
  expect {
58
- described_class.from_params(qs_params: params, secrets: ['b'])
60
+ described_class.from_params(base64_encoded_params: enc, given_signature: signature, secrets: ['b'])
59
61
  }.to raise_error(/Invalid or missing signature/)
60
62
  end
61
63
  end
@@ -12,7 +12,7 @@ describe ImageVise::Pipeline do
12
12
  ["auto_orient", {}],
13
13
  ["fit_crop", {:width=>10, :height=>32, :gravity=>"c"}]
14
14
  ]
15
- pipeline = described_class.from_param(params)
15
+ pipeline = described_class.from_array_of_operator_params(params)
16
16
  expect(pipeline).not_to be_empty
17
17
  end
18
18
 
@@ -29,7 +29,7 @@ describe ImageVise::Pipeline do
29
29
  ["fit_crop", {:width=>10, :height=>32, :gravity=>"c"}]
30
30
  ])
31
31
 
32
- pipeline = described_class.from_param(operator_list)
32
+ pipeline = described_class.from_array_of_operator_params(operator_list)
33
33
  expect(pipeline).not_to be_empty
34
34
  end
35
35
 
@@ -196,8 +196,11 @@ describe ImageVise::RenderEngine do
196
196
  'cmljb246L0NQR1BfRmlyZWJhbGw-Yz1kOWM4ZTMzO'+
197
197
  'TZmNjMwYzM1MjM0MTYwMmM2YzJhYmQyZjAzNTcxMTF'+
198
198
  'jIn0'
199
- params = {q: q, sig: sig}
200
- req = ImageVise::ImageRequest.from_params(qs_params: params, secrets: ['this is fab'])
199
+ req = ImageVise::ImageRequest.from_params(
200
+ base64_encoded_params: q,
201
+ given_signature: sig,
202
+ secrets: ['this is fab']
203
+ )
201
204
 
202
205
  # We do a check based on the raised exception - the request will fail
203
206
  # at the fetcher lookup stage. That stage however takes place _after_ the
@@ -216,7 +219,6 @@ describe ImageVise::RenderEngine do
216
219
 
217
220
  p = ImageVise::Pipeline.new.geom(geometry_string: '512x335').fit_crop(width: 10, height: 10, gravity: 'c')
218
221
  image_request = ImageVise::ImageRequest.new(src_url: uri.to_s, pipeline: p)
219
- params = image_request.to_query_string_params('l33tness')
220
222
 
221
223
  expect(app).to receive(:parse_env_into_request).and_call_original
222
224
  expect(app).to receive(:process_image_request).and_call_original
@@ -80,17 +80,6 @@ describe ImageVise do
80
80
  end
81
81
  end
82
82
 
83
- describe '.image_params' do
84
- it 'generates a Hash with paremeters for processing the resized image' do
85
- params = ImageVise.image_params(src_url: 'http://host.com/image.jpg', secret: 'l33t') do |pipe|
86
- pipe.fit_crop width: 128, height: 256, gravity: 'c'
87
- end
88
- expect(params).to be_kind_of(Hash)
89
- expect(params[:q]).not_to be_empty
90
- expect(params[:sig]).not_to be_empty
91
- end
92
- end
93
-
94
83
  describe 'methods dealing with fetchers' do
95
84
  it 'returns the fetchers for the default schemes' do
96
85
  http = ImageVise.fetcher_for('http')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: image_vise
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-03 00:00:00.000000000 Z
11
+ date: 2018-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: patron