require 'cgi' require 'uri' require 'rest-graph/core' if Rails::VERSION::MAJOR >= 3 class RestGraph class Railtie < Rails::Railtie initializer 'rest-graph' do |app| RestGraph::RailsUtil.init(app) end end end end # this cannot be put here because of load order, # so put in the bottom of this file to load up for rails2. # if Rails::VERSION::MAJOR == 2 # ::RestGraph::RailsUtil.init(Rails) # end class RestGraph module DefaultAttributes def default_log_method ; Rails.logger.method(:debug); end def default_cache ; Rails.cache ; end def default_canvas ; '' ; end def default_iframe ; false; end def default_auto_authorize ; false; end def default_auto_authorize_options; {} ; end def default_auto_authorize_scope ; '' ; end def default_ensure_authorized ; false; end def default_write_session ; false; end def default_write_cookies ; false; end def default_write_handler ; nil; end def default_check_handler ; nil; end end module RailsCache def [] key ; read(key) ; end def []= key, value; write(key, value) ; end def store key, value, options={}; write(key, value, options); end end end module RestGraph::RailsUtil def self.init app=Rails ActiveSupport::Cache::Store.send(:include, RestGraph::RailsCache) RestGraph::ConfigUtil.load_config_for_rails(app) end module Helper def rest_graph controller.send(:rest_graph) end end def self.included controller # skip if included already, any better way to detect this? return if controller.respond_to?(:rest_graph, true) controller.rescue_from(RestGraph::Error::AccessToken, :with => :rest_graph_on_access_token_error) controller.helper(RestGraph::RailsUtil::Helper) controller.instance_methods.select{ |method| method.to_s =~ /^rest_graph/ }.each{ |method| controller.send(:protected, method) } end def rest_graph_setup options={} rest_graph_options_ctl.merge!(rest_graph_extract_options(options, :reject)) rest_graph_options_new.merge!(rest_graph_extract_options(options, :select)) # we'll need to reinitialize rest_graph with the new options, # otherwise if you're calling rest_graph before rest_graph_setup, # you'll end up with default options without the ones you've passed # into rest_graph_setup. rest_graph.send(:initialize, rest_graph_options_new) rest_graph_check_params_signed_request # canvas rest_graph_check_params_session # i think it would be deprecated rest_graph_check_cookie # for js sdk (canvas or not) rest_graph_check_code # oauth api # there are above 4 ways to check the user identity! # if nor of them passed, then we can suppose the user # didn't authorize for us, but we can check if user has authorized # before, in that case, the fbs would be inside session, # as we just saved it there rest_graph_check_rg_fbs # check rest-graph storage if rest_graph_oget(:ensure_authorized) && !rest_graph.authorized? rest_graph_authorize('ensure authorized') false # action halt, redirect to do authorize, # eagerly, as opposed to auto_authorize else true # keep going end end # override this if you need different app_id and secret def rest_graph @rest_graph ||= RestGraph.new(rest_graph_options_new) end def rest_graph_on_access_token_error error=nil rest_graph_authorize(error, false) end alias_method :rest_graph_on_error, # backward compatibility :rest_graph_on_access_token_error def rest_graph_authorize error=nil, force_redirect=true logger.warn("WARN: RestGraph: #{error.inspect}") if force_redirect || rest_graph_auto_authorize? @rest_graph_authorize_url = rest_graph.authorize_url( {:redirect_uri => rest_graph_normalized_request_uri, :scope => rest_graph_oget(:auto_authorize_scope)}. merge(rest_graph_oget(:auto_authorize_options))) logger.debug("DEBUG: RestGraph: redirect to #{@rest_graph_authorize_url}") rest_graph_cleanup rest_graph_authorize_redirect end end # override this if you want the simple redirect_to def rest_graph_authorize_redirect unless rest_graph_in_canvas? redirect_to @rest_graph_authorize_url else rest_graph_js_redirect(@rest_graph_authorize_url, rest_graph_authorize_body) end end def rest_graph_js_redirect redirect_url, body='' render :inline => <<-HTML #{body} HTML end def rest_graph_authorize_body redirect_url=@rest_graph_authorize_url <<-HTML
Please authorize if this page is not automatically redirected.
HTML end module_function # ==================== begin options utility ======================= def rest_graph_oget key if rest_graph_options_ctl.has_key?(key) rest_graph_options_ctl[key] else RestGraph.send("default_#{key}") end end def rest_graph_options_ctl @rest_graph_options_ctl ||= {} end def rest_graph_options_new @rest_graph_options_new ||= {} end # ==================== end options utility ======================= # ==================== begin facebook check ====================== def rest_graph_check_params_signed_request return if rest_graph.authorized? || !params[:signed_request] rest_graph.parse_signed_request!(params[:signed_request]) logger.debug("DEBUG: RestGraph: detected signed_request, parsed:" \ " #{rest_graph.data.inspect}") if rest_graph.authorized? rest_graph_write_rg_fbs else logger.warn( "WARN: RestGraph: bad signed_request: #{params[:signed_request]}") end end # if the code is bad or not existed, # check if there's one in session, # meanwhile, there the sig and access_token is correct, # that means we're in the context of canvas def rest_graph_check_params_session return if rest_graph.authorized? || !params[:session] rest_graph.parse_json!(params[:session]) logger.debug("DEBUG: RestGraph: detected session, parsed:" \ " #{rest_graph.data.inspect}") if rest_graph.authorized? rest_graph_write_rg_fbs else logger.warn("WARN: RestGraph: bad session: #{params[:session]}") end end # if we're not in canvas nor code passed, # we could check out cookies as well. def rest_graph_check_cookie return if rest_graph.authorized? || (!cookies["fbsr_#{rest_graph.app_id}"] && !cookies["fbs_#{rest_graph.app_id}"]) rest_graph.parse_cookies!(cookies) logger.debug("DEBUG: RestGraph: detected cookies, parsed:" \ " #{rest_graph.data.inspect}") end # exchange the code with access_token def rest_graph_check_code return if rest_graph.authorized? || !params[:code] rest_graph.authorize!(:code => params[:code], :redirect_uri => rest_graph_normalized_request_uri) logger.debug( "DEBUG: RestGraph: detected code with " \ "#{rest_graph_normalized_request_uri}, " \ "parsed: #{rest_graph.data.inspect}") rest_graph_write_rg_fbs if rest_graph.authorized? end # ==================== end facebook check ====================== # ==================== begin check ================================ def rest_graph_storage_key "rest_graph_fbs_#{rest_graph_oget(:app_id)}" end def rest_graph_check_rg_fbs rest_graph_check_rg_handler # custom method to store fbs rest_graph_check_rg_session # prefered way to store fbs rest_graph_check_rg_cookies # in canvas, session might not work.. end def rest_graph_check_rg_handler handler=rest_graph_oget(:check_handler) return if rest_graph.authorized? || !handler rest_graph.parse_fbs!(handler.call) logger.debug("DEBUG: RestGraph: called check_handler, parsed:" \ " #{rest_graph.data.inspect}") end def rest_graph_check_rg_session return if rest_graph.authorized? || !rest_graph_oget(:write_session) || !(fbs = session[rest_graph_storage_key]) rest_graph.parse_fbs!(fbs) logger.debug("DEBUG: RestGraph: detected rest-graph session, parsed:" \ " #{rest_graph.data.inspect}") end def rest_graph_check_rg_cookies return if rest_graph.authorized? || !rest_graph_oget(:write_cookies) || !(fbs = cookies[rest_graph_storage_key]) rest_graph.parse_fbs!(fbs) logger.debug("DEBUG: RestGraph: detected rest-graph cookies, parsed:" \ " #{rest_graph.data.inspect}") end # ==================== end check ================================ # ==================== begin write ================================ def rest_graph_write_rg_fbs cookies.delete("fbs_#{rest_graph.app_id}") rest_graph_write_rg_handler rest_graph_write_rg_session rest_graph_write_rg_cookies end def rest_graph_write_rg_handler handler=rest_graph_oget(:write_handler) return if !handler handler.call(fbs = rest_graph.fbs) logger.debug("DEBUG: RestGraph: called write_handler: fbs => #{fbs}") end def rest_graph_write_rg_session return if !rest_graph_oget(:write_session) session[rest_graph_storage_key] = fbs = rest_graph.fbs logger.debug("DEBUG: RestGraph: wrote session: fbs => #{fbs}") end def rest_graph_write_rg_cookies return if !rest_graph_oget(:write_cookies) cookies[rest_graph_storage_key] = fbs = rest_graph.fbs logger.debug("DEBUG: RestGraph: wrote cookies: fbs => #{fbs}") end # ==================== end write ================================ # ==================== begin misc ================================ def rest_graph_cleanup cookies.delete("fbs_#{rest_graph.app_id}") cookies.delete("fbsr_#{rest_graph.app_id}") cookies.delete(rest_graph_storage_key) session.delete(rest_graph_storage_key) end def rest_graph_normalized_request_uri uri = if rest_graph_in_canvas? # rails 3 uses newer rack which has fullpath "http://apps.facebook.com/#{rest_graph_oget(:canvas)}" + (request.respond_to?(:fullpath) ? request.fullpath : request.request_uri) else request.url end rest_graph_filter_uri(uri) end def rest_graph_filter_uri uri URI.parse(URI.encode(uri)).tap{ |uri| uri.query = uri.query.split('&').reject{ |q| q =~ /^(code|session|signed_request)\=/ }.join('&') if uri.query uri.query = nil if uri.query.blank? }.to_s end def rest_graph_in_canvas? !rest_graph_oget(:canvas).blank? end def rest_graph_auto_authorize? !rest_graph_oget(:auto_authorize_scope) .blank? || !rest_graph_oget(:auto_authorize_options).blank? || rest_graph_oget(:auto_authorize) end def rest_graph_extract_options options, method # Hash[] is for ruby 1.8.7 Hash[options.send(method){ |(k, v)| RestGraph::Attributes.member?(k) }] end # ==================== end misc ================================ end if Rails::VERSION::MAJOR == 2 RestGraph::RailsUtil.init(Rails) end