image_vise 0.2.6 → 0.3.0

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 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