require "spec_helper"

class FakeController < ActionController::Base
  include Facebooker2::Rails::Controller
end

describe Facebooker2::Rails::Controller do


  def compare_cookie_values(actual,expected)
    pairify_cookie(actual).should == pairify_cookie(expected)
  end

  def pairify_cookie(value)   
    value.gsub('"',"").split("&").sort
  end

  # This spec has side effects. The configuration
  # needs to be rebuilt for the following tests to pass.
  after(:all) do
    Facebooker2.app_id = "12345"
    Facebooker2.secret = "42ca6de519d53f6e0420247a4d108d90"
  end

  context "Without oauth2" do
    before(:each) do
      Facebooker2.app_id = "12345"
      Facebooker2.secret = "42ca6de519d53f6e0420247a4d108d90"
      Facebooker2.oauth2 = false
      Facebooker2.stub(:cookie_prefix).and_return("fbs_")
    end

    let :controller do
      controller = FakeController.new
      controller.stub!(:params).and_return({})
      controller.stub!(:cookies).and_return("fbs_12345"=>"\"access_token=114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.&expires=0&secret=1e3375dcc4527e7ead0f82c095421690&session_key=57f0206b01ad48bf84ac86f1-12451752&sig=4337fcdee4cc68bb70ec495c0eebf89c&uid=12451752\"")
      controller
    end



    describe "Cookie handling" do

      it "knows if a cookie exists for this app" do
        controller.fb_cookie.should be_true
      end

      it "knows when there isn't a cookie" do
        controller.stub!(:cookies).and_return("")
        controller.fb_cookie?.should be_false
      end

      it "knows when there is a nil cookie value" do
        controller.stub!(:cookies).and_return({
          controller.fb_cookie_name => nil
        })
        controller.fb_cookie?.should be_false
      end

      it "knows when there is a blank cookie value" do
        controller.stub!(:cookies).and_return({
          controller.fb_cookie_name => ''
        })
        controller.fb_cookie?.should be_false
      end


      it "doesn't create a user if there is no app cookie" do
        Facebooker2.app_id="other_app"
        controller.current_facebook_user.should be_nil
      end



      it "does not set a cookie if it is not required" do
        controller.stub!(:cookies).and_return("")
        controller.set_fb_cookie(nil,
                                Time.now,
                                "5436785463785",
                                "4337fcdee4cc68bb70ec495c0eebf89c").should be_nil
        controller.fb_cookie.should be_nil
      end

      it "sets a valid cookie if the access token is not nil" do
        tn = Time.now
        controller.set_fb_cookie("114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.",
                                 tn,
                                 "5436785463785",
                                 "4337fcdee4cc68bb70ec495c0eebf89c").should be_true
       compare_cookie_values(controller.fb_cookie[:value], "\"session_key=57f0206b01ad48bf84ac86f1-12451752&expires=#{tn.to_i}&uid=5436785463785&sig=4337fcdee4cc68bb70ec495c0eebf89c&secret=1e3375dcc4527e7ead0f82c095421690&access_token=114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.\"")
      end

      it "sets a cookie with an expires value of 0 if the access token expires value is far in the future" do
        controller.set_fb_cookie("114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.",
                                 Time.now+2.year,
                                 "5436785463785",
                                 "4337fcdee4cc68bb70ec495c0eebf89c").should be_true
       compare_cookie_values(controller.fb_cookie[:value], "\"session_key=57f0206b01ad48bf84ac86f1-12451752&expires=0&uid=5436785463785&sig=4337fcdee4cc68bb70ec495c0eebf89c&secret=1e3375dcc4527e7ead0f82c095421690&access_token=114355055262088|57f0206b01ad48bf84ac86f1-12451752|63WyZjRQbzowpN8ibdIfrsg80OA.\"")
      end

     it "sets a cookie with the value deleted if the access token is nil" do
       controller.set_fb_cookie(nil,
                                Time.now,
                                "5436785463785",
                                "4337fcdee4cc68bb70ec495c0eebf89c").should be_true
       controller.fb_cookie[:value].should == "deleted"
     end
    end

    describe "Signed Request handling" do
      it "should correctly parse JSON that is poorly encoded" do
        controller.fb_signed_request_json("eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk4MTQ0MDAsIm9hdXRoX3Rva2VuIjoiMTAzMTg4NzE2Mzk2NzI1fDIuTjBrQnE1RDBjYndqVEdtOUo0eFJnQV9fLjM2MDAuMTI3OTgxNDQwMC01ODU2MTI2NTd8VHh3eThTN3NXQklKbnlBWGViRWdTeDZudGdZLiIsInVzZXJfaWQiOiI1ODU2MTI2NTcifQ").
          should == "{\"algorithm\":\"HMAC-SHA256\",\"expires\":1279814400,\"oauth_token\":\"103188716396725|2.N0kBq5D0cbwjTGm9J4xRgA__.3600.1279814400-585612657|Txwy8S7sWBIJnyAXebEgSx6ntgY.\",\"user_id\":\"585612657\"}"
      end

      it "should properly decode as url" do
        controller.fb_signed_request_json("eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImFwcF9kYXRhIjp7InJlZGlyZWN0IjoiXC9hcHBcL2Nhc2luby1iYWRlbi1mb3RvY29udGVzdFwvcGFnZXNcLzM_c3VibWlzc2lvbj0xdEJJMnowaUlIIn0sImlzc3VlZF9hdCI6MTM0Njc5MjM0MSwicGFnZSI6eyJpZCI6IjEwMjYwODMxOTgxODU4NiIsImxpa2VkIjpmYWxzZSwiYWRtaW4iOmZhbHNlfSwidXNlciI6eyJjb3VudHJ5IjoiYXQiLCJsb2NhbGUiOiJkZV9ERSIsImFnZSI6eyJtaW4iOjIxfX19").should ==
          '{"algorithm":"HMAC-SHA256","app_data":{"redirect":"\/app\/casino-baden-fotocontest\/pages\/3?submission=1tBI2z0iIH"},"issued_at":1346792341,"page":{"id":"102608319818586","liked":false,"admin":false},"user":{"country":"at","locale":"de_DE","age":{"min":21}}}'
      end

      it "provides facebook_params if the sig is valid" do
        Facebooker2.secret = "mysecretkey"
        controller.stub!(:params).and_return(:signed_request=>"N1JJFILX63MufS1zpHZwN109VK1ggzEsD0N4pH-yPtc.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk4MjE2MDAsIm9hdXRoX3Rva2VuIjoiMTAzMTg4NzE2Mzk2NzI1fDIucnJRSktyRzFRYXpGYTFoa2Z6MWpMZ19fLjM2MDAuMTI3OTgyMTYwMC01MzI4Mjg4Njh8TWF4QVdxTWtVS3lKbEFwOVgwZldGWEF0M004LiIsInVzZXJfaWQiOiI1MzI4Mjg4NjgifQ")
        controller.facebook_params[:user_id].should == "532828868"
      end

      it "doesn't provide facebook params if the sig is invalid" do
        Facebooker2.secret = "mysecretkey"
        controller.stub!(:params).and_return(:signed_request=>"invalid.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk4MjE2MDAsIm9hdXRoX3Rva2VuIjoiMTAzMTg4NzE2Mzk2NzI1fDIucnJRSktyRzFRYXpGYTFoa2Z6MWpMZ19fLjM2MDAuMTI3OTgyMTYwMC01MzI4Mjg4Njh8TWF4QVdxTWtVS3lKbEFwOVgwZldGWEF0M004LiIsInVzZXJfaWQiOiI1MzI4Mjg4NjgifQ")
        controller.facebook_params.should be_blank
      end

      it "writes a cookie to the client if the sig is valid" do
        controller.stub!(:params).and_return(:signed_request=>"N1JJFILX63MufS1zpHZwN109VK1ggzEsD0N4pH-yPtc.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImV4cGlyZXMiOjEyNzk4MjE2MDAsIm9hdXRoX3Rva2VuIjoiMTAzMTg4NzE2Mzk2NzI1fDIucnJRSktyRzFRYXpGYTFoa2Z6MWpMZ19fLjM2MDAuMTI3OTgyMTYwMC01MzI4Mjg4Njh8TWF4QVdxTWtVS3lKbEFwOVgwZldGWEF0M004LiIsInVzZXJfaWQiOiI1MzI4Mjg4NjgifQ")
        Facebooker2.secret = "mysecretkey"
        controller.fetch_client_and_user.should be_true
        controller.fb_cookie.should_not be_nil
        sig = controller.generate_signature({"uid"=>controller.facebook_params[:user_id],
                                            "access_token"=>controller.facebook_params[:oauth_token],
                                            "expires"=>controller.facebook_params[:expires]},
                                           Facebooker2.secret)

        compare_cookie_values(controller.fb_cookie[:value], "\"session_key=57f0206b01ad48bf84ac86f1-12451752&"+
                                               "expires=1279821600&"+
                                               "uid=532828868&"+
                                               "sig=0d82b1bb944e5bf8e753a71ee72e9e23&"+
                                               "secret=1e3375dcc4527e7ead0f82c095421690&"+
                                               "access_token=103188716396725|2.rrQJKrG1QazFa1hkfz1jLg__.3600.1279821600-532828868|MaxAWqMkUKyJlAp9X0fWFXAt3M8.\"")

      end

      it "uses the cookie if no signed_request is provided" do
        controller.fetch_client_and_user.should be_true
        controller.current_facebook_user.id.should == "12451752"
      end

      it "deletes the cookie if the signed_request does not contain a oauth token (logged out or unauthorized user)" do
        controller.stub!(:facebook_params).and_return({"algorithm"=>"HMAC-SHA256", "issued_at"=>1300976861, "user"=>{"country"=>"ca", "locale"=>"fr_CA", "age"=>{"min"=>21}}})
        controller.fetch_client_and_user.should be_true
        controller.fb_cookie[:value].should == "deleted"
        controller.current_facebook_user.should be_nil
      end

    end

    describe "Methods" do

      it "allows you to sign in a user" do
        controller.fb_sign_in_user_and_client(Mogli::User.new,Mogli::Client.new)
      end
      it "has a current_facebook_user" do
        user = mock("user",:client= => nil)
        controller.fb_sign_in_user_and_client(user,Mogli::Client.new)
        controller.current_facebook_user.should == user
      end

      it "has a current_facebook_client" do
        client = mock("client")
        controller.fb_sign_in_user_and_client(Mogli::User.new,client)
        controller.current_facebook_client.should == client
      end

    end
  end

  describe "Using oauth2" do
    let :controller do
      controller = FakeController.new
    end

    it "decodes valid base64url data" do
      # The Base64 representation of '>>>>??????' is 'Pj4+Pj8/Pz8/Pw=='
      # The FB doc http://developers.facebook.com/docs/authentication/signed_request/ references
      # http://developers.facebook.com/docs/authentication/signed_request/. The test input is a valid
      # Base64URL encoded string.
      controller.base64_url_decode('Pj4-Pj8_Pz8_Pw').should == ">>>>??????"
    end

    it "correctly decodes valid JSON payload" do
      JSON.parse(controller.base64_url_decode('eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0')).should == {"algorithm"=>"HMAC-SHA256", "0" => "payload"}
    end

    context "Signed request present" do
      before do
        Facebooker2.secret='secret'
      end
      # Example from the FB Signed Request doc : http://developers.facebook.com/docs/authentication/signed_request/
      #vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0
      it "recognizes a valid signature" do
        controller.fb_signed_request_sig_valid?('vlXgu64BQGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso','eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0').should be_true
      end
      it "rejects an invalid signature" do
        controller.fb_signed_request_sig_valid?('QGFSQrY0ZcJBZASMvYvTHu9GQ0YM9rjPSso','eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsIjAiOiJwYXlsb2FkIn0').should be_false
      end

    end
  end
  
end
