Module: RPlatform::Rails::ControllerExtensions

Defined in:
lib/rplatform_rails/controller_extensions.rb

Constant Summary collapse

FACEBOOK_SIGNATURE_TIME_SLACK =

signatures are allowed at most a 30 minute delta in the sig_time

48*3600

Instance Method Summary collapse

Instance Method Details

#added_facebook_application?Boolean

returns true if the user has added (installed) the current application

Returns:

  • (Boolean)


214
215
216
217
# File 'lib/rplatform_rails/controller_extensions.rb', line 214

def added_facebook_application?
  # TODO: make this check fbparams instead (signature is validated there)
  return (params and params["fb_sig_added"] == "1")
end

#facebook_api_keyObject

Facebook API key, as parsed from the YAML file



64
65
66
# File 'lib/rplatform_rails/controller_extensions.rb', line 64

def facebook_api_key
  NETWORKS[get_network]["key"]
end

#facebook_api_secretObject

Facebook API secret, as parsed from the YAML file



69
70
71
# File 'lib/rplatform_rails/controller_extensions.rb', line 69

def facebook_api_secret
  NETWORKS[get_network]["secret"]
end

#facebook_callback_pathObject

Facebook callback path, as parsed from the YAML file (may be nil if this application is an external app)



79
80
81
# File 'lib/rplatform_rails/controller_extensions.rb', line 79

def facebook_callback_path
  NETWORKS[get_network]["callback_path"]
end

#facebook_canvas_pathObject

Facebook canvas path, as parsed from the YAML file (may be nil if this application is an external app)



74
75
76
# File 'lib/rplatform_rails/controller_extensions.rb', line 74

def facebook_canvas_path
  NETWORKS[get_network]["canvas_path"]
end

#facebook_debug_panelObject

returns HTML containing information about the current environment (API key, API secret, etc.)



309
310
311
312
313
# File 'lib/rplatform_rails/controller_extensions.rb', line 309

def facebook_debug_panel
  templatePath = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'debug_panel.rhtml')
  template = File.read(templatePath)
  return ERB.new(template).result(Proc.new{})
end

#facebook_platform_signature_verified?Boolean

returns true if the fb_sig_* parameters have been verified with a correct signature

Returns:

  • (Boolean)


227
228
229
# File 'lib/rplatform_rails/controller_extensions.rb', line 227

def facebook_platform_signature_verified?
  return (fbparams.size != 0)
end

#facebook_status_managerObject

used for the debug panel, runs a series of tests to determine what might be wrong with your particular environment



317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/rplatform_rails/controller_extensions.rb', line 317

def facebook_status_manager
  checks = [
    SessionStatusCheck.new(self),
    (FacebookParamsStatusCheck.new(self) unless (!in_facebook_canvas? and !in_facebook_frame?)),
    InCanvasStatusCheck.new(self),
    InFrameStatusCheck.new(self),
    (CanvasPathStatusCheck.new(self) unless (!in_facebook_canvas? or !in_facebook_frame?)),
    (CallbackPathStatusCheck.new(self) unless (!in_facebook_canvas? or !in_facebook_frame?)),
    (FinishFacebookLoginStatusCheck.new(self) unless (in_facebook_canvas? or in_facebook_frame?)),
    APIKeyStatusCheck.new(self),
    APISecretStatusCheck.new(self)
    ].compact
  return StatusManager.new(checks)
end

#fbparamsObject

Accessor for all params beginning with “fb_sig_”. The signature is verified to prevent replay attacks and other calls that don’t originate from Facebook. (the “fb_sig_” prefix is removed from the parameter name)



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/rplatform_rails/controller_extensions.rb', line 91

def fbparams
  # check to see if we have parsed the fb_sig_ params yet
  if @fbparams.nil?
    # first, look in the params hash
    sourceParams = (params || {}).dup
    @fbparams = parse_fb_sig_params(sourceParams)
    
    # second, look in the cookies hash
    if @fbparams.size == 0
      sourceParams = (cookies || {}).dup
      @fbparams = parse_fb_sig_params(sourceParams)
    end
    
    # ensure that these parameters aren't being replayed
    sigTime = @fbparams["time"] ? @fbparams["time"].to_i : nil
    if (sigTime.nil? or (sigTime > 0 and Time.now.to_i > (sigTime + FACEBOOK_SIGNATURE_TIME_SLACK)))
      # signature expired, fbparams are not valid
      @fbparams = {}
    end
    
    # ensure that signature validates properly from Facebook
    expectedSignature =  fbsession_holder.signature(@fbparams)
    actualSignature = sourceParams["fb_sig"]
    if (actualSignature.nil? or expectedSignature != actualSignature)
      # signatures didn't match, fbparams are not valid
      @fbparams = {}
    end
  end
  
  # as a last resort, if we are an iframe app, we might have saved the
  # fbparams to the session previously
  if @fbparams.size == 0
    @fbparams ||= session[:_rfacebook_fbparams] || {}
  end
  
  # return fbparams (may or may not be populated)
  return @fbparams
end

#fbsessionObject

Gives direct access to a Facebook session (of type RFacebook::FacebookWebSession) for this user. An attempt will be made to activate this session (either using canvas params or an auth_token for external apps), but if the user has not been forced to log in to Facebook, the session will NOT be ready for usage. To double-check this, simply call ‘ready?’ to see if the session is okay to use.



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/rplatform_rails/controller_extensions.rb', line 136

def fbsession
  
  # do a check to ensure that we nil out the fbsession_holder in case there is a new user visiting
  if session[:_rfacebook_fbsession_holder] and fbparams["session_key"] and session[:_rfacebook_fbsession_holder].session_key != fbparams["session_key"]
    session[:_rfacebook_fbsession_holder] = nil
  end
  
  # if we have verified fb_sig_* params, we should be able to activate the session here
  if (!fbsession_holder.ready? and facebook_platform_signature_verified?)
    # then try to activate the session somehow (or retrieve from previous state)
    # these might be nil
    facebookUserId = fbparams["user"]
    facebookSessionKey = fbparams["session_key"]
    expirationTime = fbparams["expires"]
          
    # activate the session if we got all the pieces of information we needed
    if (facebookUserId and facebookSessionKey and expirationTime)
      fbsession_holder.activate_with_previous_session(facebookSessionKey, facebookUserId, expirationTime)
      RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK INFO: Activated session from inside the canvas (user=#{facebookUserId}, session_key=#{facebookSessionKey}, expires=#{expirationTime})"
      
    # warn that we couldn't get a valid Facebook session since we were missing data
    else
      RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK WARNING: Tried to get a valid Facebook session from POST params, but failed"
    end
  end
  
  # if we still don't have a session, check the Rails session
  # (used for external and iframe apps when fb_sig POST params weren't present)
  if (!fbsession_holder.ready? and session[:_rfacebook_fbsession_holder] and session[:_rfacebook_fbsession_holder].ready?)
    RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK INFO: grabbing Facebook session from Rails session"
    @fbsession_holder = session[:_rfacebook_fbsession_holder]
    @fbsession_holder.logger = RAILS_DEFAULT_LOGGER
  end
  
  # set the network that should be used for this session REVIEW There is likely a better way to do this
  fbsession_holder.network = get_network
  
  # if all went well, we should definitely have a valid Facebook session object
  return fbsession_holder
end

#finish_facebook_loginObject

this is a callback method for EXTERNAL web applications, you should define this method to do something (for example, redirect the user to your main page, etc.)



233
234
235
# File 'lib/rplatform_rails/controller_extensions.rb', line 233

def 
  # do nothing by default
end

#get_networkObject



42
43
44
45
46
# File 'lib/rplatform_rails/controller_extensions.rb', line 42

def get_network
  network = get_network_from_yml
  # check to see if there's a network configured for the given value. If not then default to facebook
  NETWORKS[network].nil? ? 'facebook' : network
end

#get_network_from_ymlObject



48
49
50
51
52
53
54
55
56
# File 'lib/rplatform_rails/controller_extensions.rb', line 48

def get_network_from_yml
  # host_parts = request.host.split('.') 
  # network = host_parts[0]
  
  # TODO: finish this
  # networks = NETWORKS
  # networks.each {|n| n.value?()}
  return 'facebook'
end

#in_ajax?Boolean

returns true if the current request is an FBJS ajax request

Returns:

  • (Boolean)


201
202
203
204
# File 'lib/rplatform_rails/controller_extensions.rb', line 201

def in_ajax?
  # TODO: make this check fbparams instead (signature is validated there)
  return (params and params["fb_sig_is_ajax"] == "1")
end

#in_external_app?Boolean

returns true if the user is viewing the page from an external website

Returns:

  • (Boolean)


207
208
209
210
211
# File 'lib/rplatform_rails/controller_extensions.rb', line 207

def in_external_app?
  # FIXME: once you click away in an iframe app, you are considered to be an external app
  # TODO: read up on the hacks for avoiding nested iframes
  return (params and params["fb_sig"] == nil and !in_facebook_frame?)
end

#in_facebook_canvas?Boolean

returns true if the user is viewing the page in the canvas

Returns:

  • (Boolean)


183
184
185
186
# File 'lib/rplatform_rails/controller_extensions.rb', line 183

def in_facebook_canvas?
  # TODO: make this check fbparams instead (signature is validated there)
  return (params and params["fb_sig_in_canvas"] == "1")
end

#in_facebook_frame?Boolean

returns true if the user is viewing the page in an iframe

Returns:

  • (Boolean)


189
190
191
192
# File 'lib/rplatform_rails/controller_extensions.rb', line 189

def in_facebook_frame?
  # TODO: make this check fbparams instead (signature is validated there)
  return (params and params["fb_sig_in_iframe"] == "1")
end

#in_mock_ajax?Boolean

returns true if the current request is a mock-ajax request

Returns:

  • (Boolean)


195
196
197
198
# File 'lib/rplatform_rails/controller_extensions.rb', line 195

def in_mock_ajax?
  # TODO: make this check fbparams instead (signature is validated there)
  return (params and params["fb_sig_is_mockajax"] == "1")
end

#log_out_of_facebookObject

clear the current session so that a new user can log in



220
221
222
223
224
# File 'lib/rplatform_rails/controller_extensions.rb', line 220

def log_out_of_facebook
  session[:_rfacebook_fbsession_holder] = nil
  session[:_rfacebook_fbparams] = nil
  @fbsession_holder = nil
end

#render_with_facebook_debug_panel(options = {}) ⇒ Object

special rendering method to use when debugging



299
300
301
302
303
304
305
306
# File 'lib/rplatform_rails/controller_extensions.rb', line 299

def render_with_facebook_debug_panel(options={})
  begin
    renderedOutput = render_to_string(options)
  rescue Exception => e
    renderedOutput = facebook_canvas_backtrace(e)
  end
  render :text =>  "#{facebook_debug_panel}#{renderedOutput}"
end

#require_facebook_install(urlOptions = {}) ⇒ Object

force the user to install your Facebook application



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/rplatform_rails/controller_extensions.rb', line 277

def require_facebook_install(urlOptions={})
  #   if in_facebook_frame? and not added_facebook_application?
  #     render :text => %Q(<script language="javascript">top.location.href="#{fbsession.get_install_url}&next=#{request.path.gsub(/#{facebook_callback_path}/, "")}"</script>)
  #   end
  if (in_facebook_canvas? or in_facebook_frame?)
    if (!fbsession.ready? or !added_facebook_application?)
      redirect_to fbsession.get_install_url(urlOptions)
      return false
    end
  else
    RAILS_DEFAULT_LOGGER.info "** RFACEBOOK WARNING: require_facebook_install is not intended for external applications, using require_facebook_login instead"
    return (urlOptions)
  end
  return true
end

#require_facebook_login(urlOptions = {}) ⇒ Object

force the user to log in to Facebook



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/rplatform_rails/controller_extensions.rb', line 243

def (urlOptions={})
  # check to be sure we haven't already performed a redirect or other action
  if !performed?
    
    # handle invalid sessions by forcing the user to log in      
    if !fbsession.ready?
    
      # external applications need to be redirected
      if in_external_app?    
        RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK INFO: Redirecting to login for external app"
        redirect_to fbsession.(urlOptions)
        return false
        
      # iframe and canvas apps need *validated* fbparams, otherwise session activation cannot happen
      elsif !facebook_platform_signature_verified?
        RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK WARNING: Failed to verified canvas parameters from Facebook (probably due to a bad API key or API secret)"
        render :text => facebook_debug_panel
        return false
      
      else
        RAILS_DEFAULT_LOGGER.debug "** RFACEBOOK INFO: Redirecting to login for canvas app"
        urlOptions.merge!({:canvas=>true})
        redirect_to fbsession.(urlOptions)
        return false
        
      end
    end
  end
  
  # by default, the filter passes
  return true
end