dagi3d.net v5.0

API authentication

I have started developing an API service for an application I'm working on and one of the requisites is that all the requests need to be authenticated through the server in order to control the applications that access to it. I did some research to learn which mechanisms are using other services to authorize third party applications and faced there are plenty of them, like for example, OAuth, basic digest or sending the auth tokens inside the request headers - one interesting project I think is worth to have a look at is Fog, a ruby library that provides a common interface to access to different cloud computing providers.

Pat Allan recently wrote a very well explained article about how to implement the authentication through the headers. The only problem I see with this approach is that the application tokens are exposed in plain text inside the request and could be read by a sniffer, so using SSL would be recommended. In case you can't or don't want to use a secure connection at the endpoint, you can always add some extra security layers without much effort like sending a signed header in the request(by the way, no solution is 100% perfect):

Say we have the following spec:

users_controller_spec.rb
require File.expand_path('../../spec_helper', __FILE__)

describe UsersController do

  let(:api_key) { "myapp" }
  let(:api_token) { "12345" }
  let(:app) { mock(Application, :api_key => api_key, :api_token => api_token) }

  def signature(key, data)
    digest = OpenSSL::Digest::Digest.new('sha256')
    Base64.encode64(OpenSSL::HMAC.digest(digest, key, data)).chomp
  end

  it "returns a 403 http code when authentication fails" do
    get :index, :format => :json
    response.response_code.should == 403
  end

  it "authenticates the requests through the headers" do
    Application.stub(:find_by_api_key).and_return(app)

    now = Time.now.rfc2822
    request.env['X-Auth-ApiKey'] = api_key
    request.env['X-Auth-Signature'] = signature(api_token, now)
    request.env['X-Auth-Date'] = now

    get :index, :format => :json
    response.response_code.should == 200
  end

end

What we are doing here is to use the api token as the key to encode a text, in this case, the current date and time. Then, in our controller we will obtain the registered application with the given api key, get its token and sign the value given in the X-Auth-Date header using the same algorithm we used on the client. If the value set in X-Auth-Signature is equal to the new signed value, the access will be granted:

class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :authorize

  private
  def authorize
    api_key = request.headers['X-Auth-ApiKey']
    signature = request.headers['X-Auth-Signature']
    date = request.headers['X-Auth-Date']

    app = Application.find_by_api_key(api_key)

    render :text => "Forbidden access", :status => 403 unless app && 
        signature == sign_request(app.api_token, date)
  end

  def sign_request(api_token, date)
    digest = OpenSSL::Digest::Digest.new('sha256')
    Base64.encode64(OpenSSL::HMAC.digest(digest, api_token, date)).chomp
  end
end

note: the headers names are totally arbitrary

This approach has the problem that a valid signature could get stolen and be used by unauthorized users on successive requests. One possible solution could be to set an expiration time limit and use the passed date to control it:

it "denies access to requests more than five minutes old" do
  Application.stub(:find_by_api_key).and_return(app)

  now = (Time.now - 5.minutes - 1.second).rfc2822

  request.env['X-Auth-ApiKey'] = api_key
  request.env['X-Auth-Signature'] = signature(api_token, now)
  request.env['X-Auth-Date'] = now

  get :index, :format => :json
  response.response_code.should == 403
end
class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :authorize

  private
  def authorize
    api_key = request.headers['X-Auth-ApiKey']
    signature = request.headers['X-Auth-Signature']
    date = request.headers['X-Auth-Date']

    app = Application.find_by_api_key(api_key)

    render :text => "Forbidden access", :status => 403 unless app &&
        valid_date(date) &&
        signature == sign_request(app.api_token, date)
  end


  def sign_request(api_token, date)
    digest = OpenSSL::Digest::Digest.new('sha256')
    Base64.encode64(OpenSSL::HMAC.digest(digest, api_token, date)).chomp
  end

  def valid_date(request_date)
    now = Time.now
    request_date = Time.parse(request_date)
    expiration_date = now - 5.minutes

    request_date >= expiration_date && request_date <= now
  end

end

Eventually, we could go even further and check that the same signature isn't used twice within the allowed time frame maybe by saving them in a key-value datastore like Redis, which allows to set their own timeouts so that they expire automatically: http://redis.io/commands/expire

Simple private gem server with a rack middleware

In my job we have developed some ruby gems to reuse in our internal applications and found the need to set up a private gem server so that every developer could install them in an easy way. To configure an own gem server without using the gem server command is really simple: we just need to create a directory with a folder called gems, place our packaged gems on it and run the command gem generate_index on the base directory:

dagi3d:/www/gemserver# tree
.
`-- gems
    `-- foo-0.0.1.gem
dagi3d:/www/gemserver# gem generate_index
Loading 1 gems from .
.
Loaded all gems
loaded: 0.003s
Generating Marshal quick index gemspecs for 1 gems
.
Complete
Generated Marshal quick index gemspecs: 0.001s
Generating Marshal master index
Generated Marshal master index: 0.000s
Generating specs index
Generated specs index: 0.000s
Generating latest specs index
Generated latest specs index: 0.000s
Generating prerelease specs index
Generated prerelease specs index: 0.000s
Compressing indicies
Compressed indicies: 0.002s
dagi3d:/www/gemserver# tree
.
|-- Marshal.4.8
|-- Marshal.4.8.Z
|-- gems
|   `-- foo-0.0.1.gem
|-- latest_specs.4.8
|-- latest_specs.4.8.gz
|-- prerelease_specs.4.8
|-- prerelease_specs.4.8.gz
|-- quick
|   `-- Marshal.4.8
|       `-- foo-0.0.1.gemspec.rz
|-- specs.4.8
`-- specs.4.8.gz

Eventually, we have to configure our webserver by setting a virtual host with this directory as the document root:

server {
   listen 80;
   server_name rubygems.dagi3d.net;
   root /www/gemserver;
}

The problem with this approach is that we have to upload the gem on the server manually and regenerate the index every time, so I came up with the idea of writing a simple rack application that could handle the gems upload and generate the index automatically:

gem_server.rb
require 'fileutils'
require 'rubygems/user_interaction'
require 'rubygems/indexer'

class GemServer

  def call(env)
    request = Rack::Request.new(env)
    case request.path_info
    when "/"
      list_gems(request)
    when "/push"
      upload_gem(request)
    else
      [404, {"Content-Type" => "text/html"}, ["Page not found"]]
    end
  end

  private
  def gem_indexer
    Gem::Indexer.new("#{APP_ROOT}/public")
  end

  def upload_gem(request)
    return [500, {"Content-Type" => "text/html"}, ["Invalid method"]] unless request.post?

    file = request.params['file']
    return [400, {"Content-Type" => "text/html"}, ["No file was given"]] if file.nil?

    gem_format = Gem::Format.from_file_by_path(file[:tempfile])
    return [400, {"Content-Type" => "text/html"}, ["Invalid gem"]] if gem_format.nil?

    dst_dir = "#{APP_ROOT}/public/gems"
    FileUtils.mkdir(dst_dir) unless File.exists?(dst_dir)
    FileUtils.mv file[:tempfile], "#{dst_dir}/#{file[:filename]}"

    gem_indexer.generate_index

    [200, {"Content-Type" => "text/html"}, ["OK!"]]
  end

  def list_gems(request)
    out = <<-HTML
    <html>
      <body>
      <ul>
        #{gem_indexer.gem_file_list.map {|gem| '<li>' + File.basename(gem) + '</li>'}.join}
      </ul>
      </body>
    </html>
    HTML

    [200, {"Content-Type" => "text/html"}, [out]]
  end

end
config.ru
APP_ROOT = File.expand_path(".")
require File.expand_path("../gem_server", __FILE__)
run GemServer.new
nginx.conf
server {
   listen 80;
   server_name rubygems.dagi3d.net;
   root /www/gemserver/public;
   passenger_enabled on;
 }

Now we can send a POST request to the /push path of our server and the gem will be copied to the public/gems directory and the index will be generated automatically:

client-test.rb
require 'rest_client'
exit if ARGV.size == 0

file = File.new(ARGV[0])
host = ARGV[1]

begin
  response = RestClient.post(host, :file => file)
  puts response.inspect
rescue Exception => e
  puts e.inspect
end
dagi3d:rack-gem-server dagi3d$ ruby client-test.rb foo-0.0.1.gem http://rubygems.dagi3d.net/push
"OK!"
dagi3d:rack-gem-server dagi3d$

Obviously this is a really naive implementation and has some security flaws as there is no user validation and everyone could publish a gem to the server but I think it can work on certain environments where the users access is restricted and under control.

I've uploaded the code on my github account: https://github.com/dagi3d/rack-gem-server

Generating QR code images with rqrcode

I was playing with the rqrcod gem to generate QR codes when I realized it wasn't prepared to output an image. This library provides an easy way to iterate over the generated matrix and detect the black modules, so it should be a straightforward task:

require "rqrcode"
require "chunky_png"

module RQRCode
  class QRCode

    # to_image
    #
    # the size parameter indicates the width and height in pixels for each module
    def to_image(size)
      image = ChunkyPNG::Image.new(size * @modules.size, size * @modules.size,
              ChunkyPNG::Color::WHITE)

      @modules.each_index do |x|
        @modules.each_index do |y|
          if (self.dark?(x,y))
            image.rect(x * size, y * size, (x * size) + size, (y * size) + size, 
                       ChunkyPNG::Color::BLACK, ChunkyPNG::Color::BLACK)
          end
        end
      end

      image
    end
  end
end

Then we could write a simple qr-code generator with a rack middleware:

class RackQR
  def call(env)
    text  = Rack::Request.new(env).GET["text"]
    qr    = RQRCode::QRCode.new(text)
    image = qr.to_image(5)    

    [200, {"Content-type" => "image/png"}, [image.to_blob]]
  end
end

Paperclip::FaceCrop

On a recent project I was working on, we needed to collect a bunch of images and generate the thumbnails for them automatically with Paperclip. The problem we found was that on most of the collected pictures people did appear on them and their heads where cut out off the photo on the generated thumbnails because Paperclip uses the center of the image to calculate the new area.

In order to solve this issue, I came with the idea of writing a Paperclip processor to try to recognize all the faces located in the picture, calculate a surrounding area and generate the thumbnail with the desired width and height by using this new area as the center point. The faces are found by using the OpenCV library so it only has to calculate the new area and write the thumbnail image.

Original image:
Paperclip::Thumbnail processor
class Image < ActiveRecord::Base
  has_attached_file :attachment,
      :styles => {:thumbnail => "200x125"}
end

Paperclip::Facecrop processor:
class Image < ActiveRecord::Base
  has_attached_file :attachment,
      :styles => {:thumbnail => "200x125"},
      :processors => [:face_crop]
end

The solution is far from being perfect because sometimes the faces aren't recognized but I guess it could generate more accurate results by using more trained filters sets

Source code and usage instructions can be found at github: [https://github.com/dagi3d/paperclip-facecrop]

Hello World!

Hello World!