facepalm2 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: df7444ea0447bf5fa7110a3c899a5ce233cf629e
4
+ data.tar.gz: fae1374d10c29e9d34a77e6a1af909e688b9b54e
5
+ SHA512:
6
+ metadata.gz: b3a57d325e731bdb1bd6c2ac64675640971d127c880cf34e0846fec704bdc078e336f8cc3e513bde1e884a39bfaefff21e96ca433689053ecc2ab7e79de8a58f
7
+ data.tar.gz: 7d995775af09763d5cf6aa9ca55c4f1c47b9843233c49733d653bbe35406a5046d2f980cdbd284354e3c3081e7135c58b99d12f534a37679f64f20c05e9c3732
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source :rubygems
2
+
3
+ group :dev do # not development, otherwise would add unneeded development dependencies in gemspec
4
+ gem 'rails', '2.3.11'
5
+ gem 'redgreen'
6
+ gem 'rake'
7
+ gem 'jeweler'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,72 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionmailer (2.3.11)
5
+ actionpack (= 2.3.11)
6
+ actionpack (2.3.11)
7
+ activesupport (= 2.3.11)
8
+ rack (~> 1.1.0)
9
+ activerecord (2.3.11)
10
+ activesupport (= 2.3.11)
11
+ activeresource (2.3.11)
12
+ activesupport (= 2.3.11)
13
+ activesupport (2.3.11)
14
+ addressable (2.3.8)
15
+ builder (3.2.2)
16
+ descendants_tracker (0.0.4)
17
+ thread_safe (~> 0.3, >= 0.3.1)
18
+ faraday (0.9.1)
19
+ multipart-post (>= 1.2, < 3)
20
+ git (1.2.9.1)
21
+ github_api (0.12.3)
22
+ addressable (~> 2.3)
23
+ descendants_tracker (~> 0.0.4)
24
+ faraday (~> 0.8, < 0.10)
25
+ hashie (>= 3.3)
26
+ multi_json (>= 1.7.5, < 2.0)
27
+ nokogiri (~> 1.6.3)
28
+ oauth2
29
+ hashie (3.4.1)
30
+ highline (1.7.1)
31
+ httpauth (0.2.1)
32
+ jeweler (2.0.1)
33
+ builder
34
+ bundler (>= 1.0)
35
+ git (>= 1.2.5)
36
+ github_api
37
+ highline (>= 1.6.15)
38
+ nokogiri (>= 1.5.10)
39
+ rake
40
+ rdoc
41
+ json (1.8.2)
42
+ mini_portile (0.6.2)
43
+ multi_json (1.11.0)
44
+ multipart-post (2.0.0)
45
+ nokogiri (1.6.6.2)
46
+ mini_portile (~> 0.6.0)
47
+ oauth2 (0.6.1)
48
+ faraday (~> 0.7)
49
+ httpauth (~> 0.1)
50
+ multi_json (~> 1.3)
51
+ rack (1.1.6)
52
+ rails (2.3.11)
53
+ actionmailer (= 2.3.11)
54
+ actionpack (= 2.3.11)
55
+ activerecord (= 2.3.11)
56
+ activeresource (= 2.3.11)
57
+ activesupport (= 2.3.11)
58
+ rake (>= 0.8.3)
59
+ rake (10.4.2)
60
+ rdoc (4.2.0)
61
+ json (~> 1.4)
62
+ redgreen (1.2.2)
63
+ thread_safe (0.3.5)
64
+
65
+ PLATFORMS
66
+ ruby
67
+
68
+ DEPENDENCIES
69
+ jeweler
70
+ rails (= 2.3.11)
71
+ rake
72
+ redgreen
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ LICENSE
2
+
3
+ The MIT License
4
+
5
+ Copyright (c) 2008 Jon Yurek and thoughtbot, inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in
15
+ all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ Facepalm
2
+ ===========
3
+
4
+ Provides a set of classes, methods, and helpers to ease development of Facebook applications with Rails.
5
+
6
+ Installation
7
+ ------------
8
+
9
+ In order to install Facepalm you should add it to your Gemfile:
10
+
11
+ gem 'facepalm'
12
+
13
+ Usage
14
+ -----
15
+
16
+ **Requesting Permissions**
17
+
18
+ Facepalm makes it simple to require Facebook authentication to access certain controller actions. You can require user to provide certain permissions to your application:
19
+
20
+ class PostsController < ApplicationController
21
+ facepalm_authentication :email, :publish_actions, :only => :index
22
+
23
+ def index
24
+ ...
25
+ end
26
+ end
27
+
28
+ This code will redirect user to Facebook permission request page if user wasn't authenticated yet.
29
+
30
+ You can also check user authentication right in your action code and request certain permission if necessary:
31
+
32
+ class PostsController < ApplicationController
33
+ facepalm_authentication :email, :publish_actions, :only => :index
34
+
35
+ def edit
36
+ if facepalm_require_authentication(:email)
37
+ ...
38
+ end
39
+ end
40
+ end
41
+
42
+ **Accessing Current User**
43
+
44
+ Current Facebook user data can be accessed using the ```current_facebook_user``` method:
45
+
46
+ class UsersController < ApplicationController
47
+ def profile
48
+ @user = User.find_by_facebook_id(current_facebook_user.uid)
49
+ end
50
+ end
51
+
52
+ This method is also accessible as a view helper.
53
+
54
+ **Application Configuration**
55
+
56
+ In order to use Facepalm you should set a default configuration for your Facebook application. The config file should be placed at RAILS_ROOT/config/facebook.yml
57
+
58
+ Sample config file:
59
+
60
+ development:
61
+ app_id: ...
62
+ secret: ...
63
+ namespace: your-app-namespace
64
+ callback_domain: yourdomain.com
65
+
66
+ test:
67
+ app_id: ...
68
+ secret: ...
69
+ namespace: test
70
+ callback_domain: callback.url
71
+
72
+ All these attributes attributes can be obtained or defined in your application settings at the [Facebook developer page](https://developers.facebook.com/apps).
73
+
74
+ Default configuration will be automatically loaded at the first request and won't reload. If you want to change application settings you should restart your application server instance.
75
+
76
+ Default configuration can be accessed from any parts of your code:
77
+
78
+ Facepalm::Config.default # => #<Facepalm::Config:0x108c03f38 @config={:secret=>"...", :namespace=>"...", :callback_domain=>"...", :app_id=>...}>
79
+
80
+ Current configuration is also accessible as a ```facepalm``` method in your controller. You can override this method to provide application configuration on a per-request basis:
81
+
82
+ class PostsController < ApplicationController
83
+ def current_fb_app
84
+ FbApp.find_by_app_id(params[:app_id])
85
+ end
86
+
87
+ def facepalm
88
+ if current_fb_app
89
+ Facepalm::Config.new(current_fb_app.attributes)
90
+ else
91
+ Facepalm::Config.default
92
+ end
93
+ end
94
+ end
95
+
96
+ Credits
97
+ -------
98
+
99
+ Big thanks to authors of the Facebooker2 plugin for inspiring me to develop this piece of software. I also copied some code snippets from this project so big thanks to everyone who put their effort to develop Facebooker2.
100
+
101
+
102
+ Copyright & License
103
+ -------------------
104
+
105
+ Copyright (c) 2011-2012 Aleksey V. Dmitriev.It is free software, and may be redistributed under the terms specified in the LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new(:test) do |test|
4
+ test.libs << 'lib'
5
+ test.pattern = 'test/**/*_test.rb'
6
+ test.verbose = true
7
+ end
8
+
9
+ task :default do
10
+ sh "RAILS=2.3.12 && (bundle || bundle install) && bundle exec rake test"
11
+ sh "RAILS=3.0.10 && (bundle || bundle install) && exec rake test"
12
+ sh "RAILS=3.1.2 && (bundle || bundle install) && exec rake test"
13
+ sh "git checkout Gemfile.lock"
14
+ end
15
+
16
+ begin
17
+ require 'jeweler'
18
+
19
+ Jeweler::Tasks.new do |gem|
20
+ gem.name = 'facepalm2'
21
+ gem.summary = "Facebook v2 integration for Rack & Rails application"
22
+ gem.email = "vk.stelmakh@gmail.com"
23
+ gem.homepage = "http://github.com/stelmakh/facepalm"
24
+ gem.authors = ["Aleksey V. Dmitriev", "Volodymyr Stelmakh"]
25
+
26
+ gem.add_dependency "ie_iframe_cookies", '~> 0.1.2'
27
+ gem.add_dependency "koala", '~> 2.0'
28
+ end
29
+
30
+ Jeweler::GemcutterTasks.new
31
+ rescue LoadError
32
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: gem install jeweler"
33
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/app/.gitkeep ADDED
@@ -0,0 +1 @@
1
+ This folder is necessary to correctly include routes in Rails 2.3. Without this folder routes won't be loaded correctly.
@@ -0,0 +1,30 @@
1
+ module Facepalm
2
+ class EndpointController < ActionController::Base
3
+ # OAuth 2.0 endpoint action added to ApplicationController and mounted to /facebook_oauth
4
+ def show
5
+ if params[:error]
6
+ raise Facepalm::OAuthException.new(params[:error_description])
7
+ else
8
+ # this is where you get a code for requesting an access_token to do additional OAuth requests
9
+ # outside of using the FB JavaScript library (see Authenticating Users in a Web Application
10
+ # under the Authentication docs at http://developers.facebook.com/docs/authentication/)
11
+ if params[:code]
12
+ begin
13
+ # Decrypting return URL and redirecting to it
14
+ return_url = URI.unescape(params[:fb_return_to].to_s)
15
+ ::Rails.logger.fatal("Return URL: #{return_url}")
16
+ redirect_to(facebook_canvas_page_url + facepalm_url_encryptor.decrypt(return_url))
17
+ rescue ActiveSupport::MessageEncryptor::InvalidMessage
18
+ ::Rails.logger.fatal "ERROR: Failed to decrypt return URL: #{ params[:fb_return_to] }"
19
+
20
+ redirect_to facebook_canvas_page_url
21
+ end
22
+
23
+ false
24
+ else
25
+ raise Facepalm::OAuthException.new('No code returned.')
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,16 @@
1
+ if Rails::VERSION::MAJOR < 3
2
+
3
+ ActionController::Routing::Routes.draw do |map|
4
+ # OAuth 2.0 endpoint for facebook authentication
5
+ map.facepalm_endpoint '/facepalm/endpoint',
6
+ :controller => "facepalm/endpoint",
7
+ :action => "show"
8
+ end
9
+
10
+ else
11
+
12
+ Rails.application.routes.draw do
13
+ match '/facepalm/endpoint' => 'facepalm/endpoint#show', :as => :facepalm_endpoint
14
+ end
15
+
16
+ end
data/facepalm.gemspec ADDED
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: facepalm 0.2.6.b3 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "facepalm"
9
+ s.version = "0.2.6.b3"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Aleksey V. Dmitriev", "Volodymyr Stelmakh"]
14
+ s.date = "2015-04-09"
15
+ s.email = "vk.stelmakh@gmail.com"
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "app/.gitkeep",
28
+ "app/controllers/facepalm/endpoint_controller.rb",
29
+ "config/routes.rb",
30
+ "facepalm.gemspec",
31
+ "init.rb",
32
+ "install",
33
+ "lib/facepalm.rb",
34
+ "lib/facepalm/config.rb",
35
+ "lib/facepalm/engine.rb",
36
+ "lib/facepalm/rack/post_canvas_middleware.rb",
37
+ "lib/facepalm/rails/controller.rb",
38
+ "lib/facepalm/rails/controller/oauth_access.rb",
39
+ "lib/facepalm/rails/controller/redirects.rb",
40
+ "lib/facepalm/rails/controller/url_rewriting.rb",
41
+ "lib/facepalm/rails/helpers.rb",
42
+ "lib/facepalm/rails/helpers/javascript_helper.rb",
43
+ "lib/facepalm/rails/helpers/url_helper.rb",
44
+ "lib/facepalm/user.rb"
45
+ ]
46
+ s.homepage = "http://github.com/stelmakh/facepalm"
47
+ s.rubygems_version = "2.2.2"
48
+ s.summary = "Facebook integration for Rack & Rails application"
49
+
50
+ if s.respond_to? :specification_version then
51
+ s.specification_version = 4
52
+
53
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
+ s.add_runtime_dependency(%q<ie_iframe_cookies>, ["~> 0.1.2"])
55
+ s.add_runtime_dependency(%q<koala>, ["~> 2.0"])
56
+ else
57
+ s.add_dependency(%q<ie_iframe_cookies>, ["~> 0.1.2"])
58
+ s.add_dependency(%q<koala>, ["~> 2.0"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<ie_iframe_cookies>, ["~> 0.1.2"])
62
+ s.add_dependency(%q<koala>, ["~> 2.0"])
63
+ end
64
+ end
65
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "lib", "facepalm")
data/install ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env sh
2
+
3
+ bundle exec rake gemspec:generate build
4
+ cd pkg
5
+ gem uninstall facepalm
6
+ gem install --local facepalm
7
+ cd ..
@@ -0,0 +1,66 @@
1
+ module Facepalm
2
+ # Facebook application configuration class
3
+ class Config
4
+ attr_accessor :config
5
+
6
+ class << self
7
+ # A shortcut to access default configuration stored in RAILS_ROOT/config/facebook.yml
8
+ def default
9
+ @@default ||= self.new(load_default_config_from_file)
10
+ end
11
+
12
+ def load_default_config_from_file
13
+ config_data = YAML.load(
14
+ ERB.new(
15
+ File.read(::Rails.root.join("config", "facebook.yml"))
16
+ ).result
17
+ )[::Rails.env]
18
+
19
+ raise NotConfigured.new("Unable to load configuration for #{ ::Rails.env } from config/facebook.yml") unless config_data
20
+
21
+ config_data
22
+ end
23
+ end
24
+
25
+ def initialize(options = {})
26
+ self.config = options.to_options
27
+ end
28
+
29
+ # Defining methods for quick access to config values
30
+ %w{app_id secret namespace callback_domain}.each do |attribute|
31
+ class_eval %{
32
+ def #{ attribute }
33
+ config[:#{ attribute }]
34
+ end
35
+ }
36
+ end
37
+
38
+ def oauth_client
39
+ @oauth_client ||= Koala::Facebook::OAuth.new(app_id, secret)
40
+ end
41
+
42
+ # Koala Facebook API client instantiated with application access token
43
+ def api_client
44
+ @api_client ||= Koala::Facebook::API.new(app_access_token)
45
+ end
46
+
47
+ # Fetches application access token
48
+ def app_access_token
49
+ @app_access_token ||= oauth_client.get_app_access_token
50
+ end
51
+
52
+ def subscription_token
53
+ Digest::MD5.hexdigest(secret)
54
+ end
55
+
56
+ # URL of the application canvas page
57
+ def canvas_page_url(protocol)
58
+ "#{ protocol }apps.facebook.com/#{ namespace }"
59
+ end
60
+
61
+ # Application callback URL
62
+ def callback_url(protocol)
63
+ protocol + callback_domain
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ if Rails::VERSION::MAJOR > 2
2
+
3
+ module Facepalm
4
+ class Engine < ::Rails::Engine
5
+ initializer "facepalm.middleware" do |app|
6
+ app.middleware.insert_after(ActionDispatch::ParamsParser, Facepalm::Rack::PostCanvasMiddleware)
7
+ end
8
+
9
+ initializer "facepalm.controller_extension" do
10
+ ActiveSupport.on_load :action_controller do
11
+ ActionController::Base.send(:include, Facepalm::Rails::Controller)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ else
18
+ ActionController::Routing::Routes.add_configuration_file(File.expand_path('../../../config/routes.rb', __FILE__))
19
+
20
+ ActionController::Dispatcher.middleware.insert_after(ActionController::ParamsParser, Facepalm::Rack::PostCanvasMiddleware)
21
+
22
+ ActionController::Base.send(:include, Facepalm::Rails::Controller)
23
+
24
+ # Loading plugin controllers manually because the're not loaded automatically from gems
25
+ Dir[File.expand_path('../../../app/controllers/**/*.rb', __FILE__)].each do |file|
26
+ require file
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Facepalm
2
+ module Rack
3
+
4
+ # This rack middleware converts POST requests from Facebook to GET requests.
5
+ # It's necessary to make RESTful routes work as expected without any changes
6
+ # in the application.
7
+ class PostCanvasMiddleware
8
+ def initialize(app, options = {})
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ request = ::Rack::Request.new(env)
14
+
15
+ if request.POST['signed_request'] && request.post? && request.params['_method'].blank?
16
+ env['REQUEST_METHOD'] = 'GET'
17
+ end
18
+
19
+ # Put signed_request parameter to the same place where HTTP header X-Signed-Request come.
20
+ # This let us work both with params and HTTP headers in the same way. Very useful for AJAX.
21
+ env['HTTP_SIGNED_REQUEST'] ||= request.POST['signed_request']
22
+
23
+ @app.call(env)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,108 @@
1
+ module Facepalm
2
+ class OAuthException < StandardError; end
3
+
4
+ module Rails
5
+ module Controller
6
+
7
+ # OAuth 2.0 authentication module
8
+ module OauthAccess
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ # A filter class for Rails
14
+ class AccessFilter
15
+ def initialize(*permissions)
16
+ @permissions = permissions
17
+ end
18
+
19
+ def filter(controller)
20
+ controller.send(:facepalm_require_authentication, *@permissions)
21
+ end
22
+ end
23
+
24
+ module ClassMethods
25
+ # Requires Facebook authentication for the whole set of controller actions.
26
+ # Use it to setup a given set of permissions for the whole controller
27
+ #
28
+ # @param permissions An array of permissions to require
29
+ # @param options A hash of options to control filter application, similar to
30
+ # options hash for before_filter
31
+ #
32
+ # @example
33
+ # class MyController < ApplicationController
34
+ # facepalm_authentication :email, :publish_actions, :only => :index
35
+ # end
36
+ def facepalm_authentication(*permissions)
37
+ options = permissions.extract_options!
38
+
39
+ cattr_accessor :facepalm_authentication_filter
40
+ self.facepalm_authentication_filter = AccessFilter.new(*permissions)
41
+
42
+ before_filter(facepalm_authentication_filter, options)
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ # Requires a given set of permissions in context of the current action.
49
+ # Use it to require permissions in a single action or custom filter.
50
+ #
51
+ # NOTE: Facepalm doesn't check if user provided all required permissions.
52
+ # It only checks if user was authenticated and redirects to permission
53
+ # request page with a given set of permissions.
54
+ #
55
+ # @param permissions An array of permissions to require
56
+ #
57
+ # @return true if user authorized the application, false otehrwise
58
+ #
59
+ # @example
60
+ # class MyController < ApplicationController
61
+ # before_filter :my_custom_filter, :only => :show
62
+ #
63
+ # def my_custom_filter
64
+ # my_custom_condition? and facepalm_require_authentication(:publish_actions)
65
+ # end
66
+ #
67
+ # def index
68
+ # if facepalm_require_authentication(:email)
69
+ # ... do what you need ...
70
+ # end
71
+ # end
72
+ # end
73
+ def facepalm_require_authentication(*permissions)
74
+ if current_facebook_user.try(:authenticated?)
75
+ true
76
+ else
77
+ redirect_to = facepalm.oauth_client.url_for_oauth_code(
78
+ :permissions => permissions,
79
+ :callback => facepalm_endpoint_url(
80
+ :fb_return_to => facepalm_auth_return_code
81
+ )
82
+ )
83
+ ::Rails.logger.fatal("User #{current_facebook_user} is not authenticated\nRedirecting to #{redirect_to}")
84
+ redirect_from_iframe(redirect_to)
85
+
86
+ false
87
+ end
88
+ end
89
+
90
+ # Encrypting return URL to pass it to Facebook
91
+ def facepalm_auth_return_code
92
+ facepalm_url_encryptor.encrypt(
93
+ url_for(params_without_facebook_data.merge(:canvas => false, :only_path => true))
94
+ )
95
+ end
96
+
97
+ # Internally used to encrypt return URL for authentication endpoint
98
+ #
99
+ # @return ActiveSupport::MessageEncryptor
100
+ #
101
+ # @private
102
+ def facepalm_url_encryptor
103
+ @facebook_url_encryptor ||= ActiveSupport::MessageEncryptor.new(facepalm.secret)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,74 @@
1
+ module Facepalm
2
+ module Rails
3
+ module Controller
4
+ module Redirects
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method_chain :redirect_to, :signed_request
8
+ end
9
+ end
10
+
11
+ protected
12
+
13
+ # Overrides ActionController::Base#redirect_to to pass signed_request in flash[]
14
+ def redirect_to_with_signed_request(*args)
15
+ flash[:signed_request] = fb_signed_request if fb_canvas?
16
+
17
+ redirect_to_without_signed_request(*args)
18
+ end
19
+
20
+ # Redirects user to a definite URL with JavaScript code that overwrites
21
+ # top frame location. Use it to redirect user from within an iframe.
22
+ def redirect_from_iframe(url_options)
23
+ redirect_url = url_options.is_a?(String) ? url_options : url_for(url_options)
24
+
25
+ logger.info "Redirecting from IFRAME to #{ redirect_url }"
26
+
27
+ respond_to do |format|
28
+ format.html do
29
+ render(
30
+ :text => iframe_redirect_html_code(redirect_url),
31
+ :layout => false
32
+ )
33
+ end
34
+
35
+ format.js do
36
+ render(
37
+ :text => iframe_redirect_js_code(redirect_url),
38
+ :layout => false
39
+ )
40
+ end
41
+ end
42
+ end
43
+
44
+ # Generates HTML and JavaScript code to redirect user with top frame location
45
+ # overwrite
46
+ #
47
+ # @param target_url An URL to redirect the user to
48
+ # @param custom_code A custom HTML code to insert into the result document.
49
+ # Can be used to add OpenGraph tags to redirect page code.
50
+ def iframe_redirect_html_code(target_url, custom_code = nil)
51
+ %{
52
+ <html><head>
53
+ <script type="text/javascript">
54
+ window.top.location.href = #{ target_url.to_json };
55
+ </script>
56
+ <noscript>
57
+ <meta http-equiv="refresh" content="0;url=#{ target_url }" />
58
+ <meta http-equiv="window-target" content="_top" />
59
+ </noscript>
60
+ #{ custom_code }
61
+ </head></html>
62
+ }
63
+ end
64
+
65
+ # Generates JavaScript code to redirect user
66
+ #
67
+ # @param target_url An URL to redirect the user to
68
+ def iframe_redirect_js_code(target_url)
69
+ "window.top.location.href = #{ target_url.to_json };"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,35 @@
1
+ require 'facepalm/rails/helpers/url_helper'
2
+
3
+ module Facepalm
4
+ module Rails
5
+ module Controller
6
+ module UrlRewriting
7
+ include Facepalm::Rails::Helpers::UrlHelper
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ helper_method(:facebook_canvas_page_url, :facebook_callback_url)
12
+ end
13
+ end
14
+
15
+ protected
16
+
17
+ # A helper to generate an URL of the application canvas page URL
18
+ #
19
+ # @param protocol A request protocol, should be either 'http://' or 'https://'.
20
+ # Defaults to current protocol.
21
+ def facebook_canvas_page_url(protocol = nil)
22
+ facepalm.canvas_page_url(protocol || request.protocol)
23
+ end
24
+
25
+ # A helper to generate an application callback URL
26
+ #
27
+ # @param protocol A request protocol, should be either 'http://' or 'https://'.
28
+ # Defaults to current protocol.
29
+ def facebook_callback_url(protocol = nil)
30
+ facepalm.callback_url(protocol || request.protocol)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,67 @@
1
+ require 'facepalm/rails/controller/oauth_access'
2
+ require 'facepalm/rails/controller/url_rewriting'
3
+ require 'facepalm/rails/controller/redirects'
4
+
5
+ module Facepalm
6
+ module Rails
7
+
8
+ # Rails application controller extension
9
+ module Controller
10
+ def self.included(base)
11
+ base.class_eval do
12
+ include Facepalm::Rails::Controller::OauthAccess
13
+ include Facepalm::Rails::Controller::UrlRewriting
14
+ include Facepalm::Rails::Controller::Redirects
15
+
16
+ # Fix cookie permission issue in IE
17
+ before_filter :normal_cookies_for_ie_in_iframes!
18
+
19
+ helper_method(:facepalm, :fb_signed_request, :current_facebook_user, :params_without_facebook_data, :fb_canvas?)
20
+
21
+ helper Facepalm::Rails::Helpers
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ # Accessor to current application config. Override it in your controller
28
+ # if you need multi-application support or per-request configuration selection.
29
+ def facepalm
30
+ Facepalm::Config.default
31
+ end
32
+
33
+ # Accessor to current facebook user. Returns instance of Facepalm::User
34
+ def current_facebook_user
35
+ @current_facebook_user ||= fetch_current_facebook_user
36
+ end
37
+
38
+ # Accessor to secure cookie set by Facebook
39
+ def fb_cookie
40
+ cookies["fbsr_#{ facepalm.app_id }"]
41
+ end
42
+
43
+ # Accessor to signed request passed either in params or in flash
44
+ def fb_signed_request
45
+ request.env['HTTP_SIGNED_REQUEST'] || flash[:signed_request]
46
+ end
47
+
48
+ # A hash of params passed to this action, excluding secure information
49
+ # passed by Facebook
50
+ def params_without_facebook_data
51
+ params.except(:signed_request)
52
+ end
53
+
54
+ # Did the request come from canvas app
55
+ def fb_canvas?
56
+ request.env['HTTP_SIGNED_REQUEST'].present? || flash[:signed_request].present?
57
+ end
58
+
59
+ private
60
+
61
+ def fetch_current_facebook_user
62
+ Facepalm::User.from_signed_request(facepalm, fb_signed_request || fb_cookie)
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,107 @@
1
+ module Facepalm
2
+ module Rails
3
+ module Helpers
4
+ module JavascriptHelper
5
+
6
+ # A helper to integrate Facebook Connect to the current page. Generates a
7
+ # JavaScript code that initializes Facebook Javascript client for the
8
+ # current application.
9
+ #
10
+ # @param app_id Facebook App ID of the application. Defaults to value provided by the current config.
11
+ # @param options A hash of options for JavaScript generation. Available options are:
12
+ # :cookie - Enable cookie generation for the application. Default to true.
13
+ # :status - Enable login status check. Defaults to true.
14
+ # :xfbml - Enable XFBML tag parsing. Default to true.
15
+ # :frictionless - Enable frictionless app request delivery. Defaults to true
16
+ # :locale - Locale to use for JavaScript client. Defaults to 'en_US'.
17
+ # :weak_cache - Enable FB JS client cache expiration every minute. Defaults to false.
18
+ # :async - Enable asynchronous FB JS client code load and initialization. Defaults to false.
19
+ # :cache_url - An URL to load custom or cached version of the FB JS client code. Not used by default.
20
+ # @param &block A block of JS code to be inserted in addition to FB client initialization code.
21
+ def fb_connect_js(*args, &block)
22
+ options = args.extract_options!
23
+
24
+ app_id = args.shift || facepalm.app_id
25
+
26
+ options.reverse_merge!(
27
+ :cookie => true,
28
+ :status => true,
29
+ :xfbml => true,
30
+ :frictionless => true,
31
+ :locale => "en_US"
32
+ )
33
+
34
+ extra_js = capture(&block) if block_given?
35
+
36
+ init_js = <<-JAVASCRIPT
37
+ FB.init({
38
+ appId : '#{ app_id }',
39
+ version : 'v2.0',
40
+ status : #{ options[:status] },
41
+ cookie : #{ options[:cookie] },
42
+ xfbml : #{ options[:xfbml] },
43
+ frictionlessRequests : #{ options[:frictionless] },
44
+ channelUrl : '#{ options[:channel_url] || 'null' }'
45
+ });
46
+ JAVASCRIPT
47
+
48
+ init_js = "FB._https = true; #{ init_js }" if request.ssl?
49
+
50
+ js_url = "connect.facebook.net/#{options[:locale]}/sdk.js"
51
+ js_url << "?#{Time.now.change(:min => 0, :sec => 0, :usec => 0).to_i}" if options[:weak_cache]
52
+
53
+ if options[:async]
54
+ js = <<-JAVASCRIPT
55
+ window.fbAsyncInit = function() {
56
+ #{init_js}
57
+ #{extra_js}
58
+ };
59
+
60
+ (function() {
61
+ var e = document.createElement('script');
62
+ e.src = document.location.protocol + '//#{ js_url }';
63
+ e.async = true;
64
+ document.getElementById('fb-root').appendChild(e);
65
+ }());
66
+ JAVASCRIPT
67
+
68
+ js = <<-CODE
69
+ <div id="fb-root"></div>
70
+ <script type="text/javascript">#{ js }</script>
71
+ CODE
72
+ else
73
+ js = <<-CODE
74
+ <div id="fb-root"></div>
75
+ <script src="#{ request.protocol }#{ js_url }" type="text/javascript"></script>
76
+ CODE
77
+
78
+ if options[:cache_url]
79
+ js << <<-CODE
80
+ <script type="text/javascript">
81
+ window.FB || document.write(unescape(\"%3Cscript src='#{ options[:cache_url] }' type='text/javascript'%3E%3C/script%3E\"));
82
+ </script>
83
+ CODE
84
+ end
85
+
86
+ js << <<-CODE
87
+ <script type="text/javascript">
88
+ if(typeof FB !== 'undefined'){
89
+ #{init_js}
90
+ #{extra_js}
91
+ }
92
+ </script>
93
+ CODE
94
+ end
95
+
96
+ js = js.html_safe
97
+
98
+ if block_given? && ::Rails::VERSION::STRING.to_i < 3
99
+ concat(js)
100
+ else
101
+ js
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,27 @@
1
+ module Facepalm
2
+ module Rails
3
+ module Helpers
4
+ module UrlHelper
5
+ # Overrides UrlHelper#url_for to filter out secure Facebook params
6
+ # and add Facebook Canvas URL if necessary
7
+ def url_for(options = {})
8
+ if options.is_a?(Hash)
9
+ if options.delete(:canvas) && !options[:host]
10
+ options[:only_path] = true
11
+
12
+ canvas = true
13
+ else
14
+ canvas = false
15
+ end
16
+
17
+ url = super(options.except(:signed_request))
18
+
19
+ canvas ? facebook_canvas_page_url + url : url
20
+ else
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ require 'facepalm/rails/helpers/javascript_helper'
2
+ require 'facepalm/rails/helpers/url_helper'
3
+
4
+ module Facepalm
5
+ module Rails
6
+ module Helpers
7
+ include JavascriptHelper
8
+ include UrlHelper
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,78 @@
1
+ module Facepalm
2
+
3
+ # A class for Facebook user
4
+ class User
5
+ class UnsupportedAlgorithm < StandardError; end
6
+ class InvalidSignature < StandardError; end
7
+
8
+ class << self
9
+ # Creates an instance of Facepalm::User using application config and signed_request
10
+ def from_signed_request(config, input)
11
+ return if input.blank?
12
+
13
+ new(parse_signed_request(config, input))
14
+ end
15
+
16
+ # Originally provided directly by Facebook, however this has changed
17
+ # as their concept of crypto changed. For historic purposes, this is their proposal:
18
+ # https://developers.facebook.com/docs/authentication/canvas/encryption_proposal/
19
+ # Currently see https://github.com/facebook/php-sdk/blob/master/src/facebook.php#L758
20
+ # for a more accurate reference implementation strategy.
21
+ def parse_signed_request(config, input)
22
+ encoded_sig, encoded_envelope = input.split('.', 2)
23
+ signature = base64_url_decode(encoded_sig).unpack("H*").first
24
+
25
+ MultiJson.decode(base64_url_decode(encoded_envelope)).tap do |envelope|
26
+ raise UnsupportedAlgorithm.new("Unsupported encryption algorithm: #{ envelope['algorithm'] }") unless envelope['algorithm'] == 'HMAC-SHA256'
27
+
28
+ # now see if the signature is valid (digest, key, data)
29
+ hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, config.secret, encoded_envelope)
30
+
31
+ raise InvalidSignature.new('Invalid request signature') if (signature != hmac)
32
+ end
33
+ end
34
+
35
+ def base64_url_decode(str)
36
+ str += '=' * (4 - str.length.modulo(4))
37
+
38
+ Base64.decode64(str.tr('-_', '+/'))
39
+ end
40
+ end
41
+
42
+
43
+ def initialize(options = {})
44
+ @options = options
45
+ end
46
+
47
+ # Checks if user is authenticated in the application
48
+ def authenticated?
49
+ access_token && !access_token.empty?
50
+ end
51
+
52
+ # Facebook UID
53
+ def uid
54
+ @options['user_id']
55
+ end
56
+
57
+ # The code used for OAuth 2.0
58
+ def oauth_code
59
+ @options['code']
60
+ end
61
+
62
+ # OAuth 2.0 access token generated for this user
63
+ def access_token
64
+ @options['access_token'] || @options['oauth_token']
65
+ end
66
+
67
+ # Token expiration time
68
+ def access_token_expires_at
69
+ Time.at(@options['expires'])
70
+ end
71
+
72
+ # Koala Facebook API client instantiated with user's access token
73
+ def api_client
74
+ @api_client ||= Koala::Facebook::API.new(access_token)
75
+ end
76
+ end
77
+
78
+ end
data/lib/facepalm.rb ADDED
@@ -0,0 +1,17 @@
1
+ module Facepalm
2
+ end
3
+
4
+ # Dependencies
5
+ require 'ie_iframe_cookies'
6
+ require 'koala'
7
+
8
+ require 'facepalm/config'
9
+ require 'facepalm/user'
10
+
11
+ require 'facepalm/rack/post_canvas_middleware'
12
+
13
+ # Rails integration
14
+ require 'facepalm/rails/controller'
15
+ require 'facepalm/rails/helpers'
16
+
17
+ require 'facepalm/engine'
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: facepalm2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aleksey V. Dmitriev
8
+ - Volodymyr Stelmakh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-04-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ie_iframe_cookies
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 0.1.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 0.1.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: koala
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
42
+ description:
43
+ email: vk.stelmakh@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - LICENSE
48
+ - README.md
49
+ files:
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - VERSION
56
+ - app/.gitkeep
57
+ - app/controllers/facepalm/endpoint_controller.rb
58
+ - config/routes.rb
59
+ - facepalm.gemspec
60
+ - init.rb
61
+ - install
62
+ - lib/facepalm.rb
63
+ - lib/facepalm/config.rb
64
+ - lib/facepalm/engine.rb
65
+ - lib/facepalm/rack/post_canvas_middleware.rb
66
+ - lib/facepalm/rails/controller.rb
67
+ - lib/facepalm/rails/controller/oauth_access.rb
68
+ - lib/facepalm/rails/controller/redirects.rb
69
+ - lib/facepalm/rails/controller/url_rewriting.rb
70
+ - lib/facepalm/rails/helpers.rb
71
+ - lib/facepalm/rails/helpers/javascript_helper.rb
72
+ - lib/facepalm/rails/helpers/url_helper.rb
73
+ - lib/facepalm/user.rb
74
+ homepage: http://github.com/stelmakh/facepalm
75
+ licenses: []
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubyforge_project:
93
+ rubygems_version: 2.2.2
94
+ signing_key:
95
+ specification_version: 4
96
+ summary: Facebook v2 integration for Rack & Rails application
97
+ test_files: []