---
layout: manual_3.4
title: Access Control
---
Access Control
Authentication
To enable HTTP Basic authentication on image and information endpoints, set the following configuration keys:
{% highlight properties %}
endpoint.public.auth.basic.enabled = true
endpoint.public.auth.basic.username = myusername
endpoint.public.auth.basic.secret = mypassword
{% endhighlight %}
Authorization
A custom delegate method can be used to implement authorization logic ranging from simple to complex. The method will be invoked upon every image request. Depending on its return value, it can either:
- Authorize the request (by returning
HTTP 200 OK
);
- Forbid the request (by returning
HTTP 403 Forbidden
); or
- Redirect to a different URL via a response code returned from the method.
The delegate method is named authorized?()
. A skeleton with documented parameters and return values is present in the delegates.rb.sample file. By default, it just returns true
, authorizing all requests.
Notes
- The authorization method will be called on every image request and should therefore be written to be efficient.
- The authorization method will be called upon requests to all image endpoints, but not information endpoints.
- Implementations should assume that the underlying source image exists, even though it may not. The image server will check for existence, but the check may occur after the delegate method is invoked.
Examples
Inspect the parameters
This method will print the method arguments to the console.
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
puts "identifier: #{identifier}"
puts "full_size: #{full_size}"
puts "operations: #{operations}"
puts "resulting_size: #{resulting_size}"
puts "output_format: #{output_format}"
puts "request_uri: #{request_uri}"
puts "request_headers: #{request_headers}"
puts "client_ip: #{client_ip}"
puts "cookies: #{cookies}"
true
end
end
{% endhighlight %}
Allow only requests for half-scale images or smaller
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
scale = operations.select{ |op| op['class'] == 'Scale' }.first
if scale
max_scale = 0.5
return scale['width'] <= full_size['width'] * max_scale and
scale['height'] <= full_size['height'] * max_scale
end
false
end
end
{% endhighlight %}
Allow only requests for identifiers matching a certain pattern
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
# Allow only identifiers that don't include "_restricted"
return !identifier.include?('_restricted')
# Allow only identifiers that start with "_public"
return identifier.start_with?('public_')
# Allow only identifiers matching a regex
return identifier.match(/^image[5-9][0-9]/)
end
end
{% endhighlight %}
Allow only requests for images set as "public" in a MySQL database
(You will need to install the MySQL JDBC driver first.)
Note: The parameters passed to authorized?
are not guaranteed to be safe. identifier
, for example, will be exactly as the application receives it. Prefer prepared statements over string concatenation in order to reduce vulnerability to injection attacks.
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
require "rubygems"
require "jdbc/mysql"
require "java"
authorized = false
Java::com.mysql.jdbc.Driver
url = "jdbc:mysql://HOST/DATABASE"
conn = java.sql.DriverManager.get_connection(url, "USERNAME", "PASSWORD")
stmt = conn.create_statement
begin
query = %q{SELECT is_public
FROM image
WHERE identifier = ?
LIMIT 1}
stmt = conn.prepare_statement(query)
stmt.setString(1, identifier);
result_set = stmt.execute_query
while result_set.next do
authorized = result_set.getBoolean(1)
end
ensure
stmt.close
conn.close
end
authorized
end
end
{% endhighlight %}
Allow only JPEG output
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
output_format['media_type'] == 'image/jpeg'
end
end
{% endhighlight %}
Allow only certain user agents
This is not foolproof—if a client knows what User-Agent you are checking for, they can spoof it.
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
agent = request_headers.select{ |h, v| h.downcase == 'user-agent' }.first
agent.start_with?('MyAllowedUserAgent/')
end
end
{% endhighlight %}
Allow only requests that supply a valid token in a header
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
headers['X-MyToken'] == ... # write code to authorize the token
end
end
{% endhighlight %}
Restrict a region of an image
In this example, requests for images containing any part of the bottom right quadrant of the source image will be denied.
(Also see redaction.)
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
crop = operations.select{ |op| op['class'] == 'Crop' }.first
if crop
max_x = full_size['width'] / 2
max_y = full_size['height'] / 2
return !(crop['x'] + crop['width'] > max_x and
crop['y'] + crop['height'] > max_y)
end
false
end
end
{% endhighlight %}
Redirect to another URL
{% highlight ruby %}
module Cantaloupe
def self.authorized?(identifier, full_size, operations, resulting_size,
output_format, request_uri, request_headers, client_ip,
cookies)
{
'location' => 'http://example.org/some-other-url',
'status_code' => 302
}
end
end
{% endhighlight %}