$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__))) require 'rubygems' require 'cgi' require 'yaml' require 'rest_client' require 'addressable/uri' require "digest/sha1" require 'ezcrypto' module Enigma VERSION = '0.0.1' KEY_LENGTH = 32 SECRET_LENGTH = 32 class Query < Hash def initialize(key, secret, params) self[:key] = key self[:params] = Enigma.encode_params(secret, params) end def to_params params = '' stack = [] each do |k, v| if v.is_a?(Hash) stack << [k,v] else params << "#{k}=#{v}&" end end stack.each do |parent, hash| hash.each do |k, v| if v.is_a?(Hash) stack << ["#{parent}[#{k}]", v] else params << "#{parent}[#{k}]=#{v}&" end end end params.chop! # trailing & params end end class KeyGenerator def self.generate(length = 10) Digest::SHA1.hexdigest(Time.now.to_s + rand(12341234).to_s)[1..length] end end def self.generate_key KeyGenerator.generate(KEY_LENGTH) end def self.generate_secret KeyGenerator.generate(SECRET_LENGTH) end def self.find_secret(&block) @find_secret = block end def self.authenticate(params={}) if @find_secret.nil? raise "Pass a block to #{self.name}.find_secret which returns secret for the given key. See documentation for details." end self.decode_params(@find_secret.call(params[:key]), params[:params]) end def self.encrypt(secret, data) key = EzCrypto::Key.with_password(secret, "salt") key.encrypt(data) end def self.decrypt(secret, data) key = EzCrypto::Key.with_password(secret, "salt") key.decrypt(data) end def self.encode_params(secret, params) CGI::escape( encrypt( secret, [ YAML::dump(params.sort {|a,b| a[0].to_s <=> b[0].to_s}) ]. pack('m') ) ) end def self.decode_params(secret, signed_params) begin params = {} YAML::load(decrypt(secret, CGI::unescape(signed_params)).unpack('m').first).each do |key, value| params[key] = value end return params rescue return nil end end class Client def initialize(key, secret) @key = key @secret = secret end def get(url, params={}) query = Query.new(@key, @secret, params) RestClient.get("#{url}?#{query.to_params}") end end end