require 'spec_helper'

describe 'Rails integration' do
  API_KEY_STORE = { '1044' => 'l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==' }.freeze

  describe 'Rails controller integration' do
    class ApplicationController < ActionController::Base
      private

      def require_api_auth
        if (access_id = get_api_access_id_from_request) && api_authenticated?(API_KEY_STORE[access_id])
          return true
        end

        respond_to do |format|
          format.xml { render xml: 'You are unauthorized to perform this action.', status: 401 }
          format.json { render json: 'You are unauthorized to perform this action.', status: 401 }
          format.html { render plain: 'You are unauthorized to perform this action', status: 401 }
        end
      end
    end

    class TestController < ApplicationController
      before_action :require_api_auth, only: [:index]

      def self._routes
        ActionDispatch::Routing::RouteSet.new
      end

      def index
        render json: 'OK'
      end

      def public
        render json: 'OK'
      end

      def rescue_action(e)
        raise(e)
      end
    end

    def generated_response(request, action = :index)
      response = ActionDispatch::TestResponse.new
      controller = TestController.new
      controller.request = request
      controller.response = response
      controller.process(action)
      response
    end

    def generated_request
      request = if ActionController::TestRequest.respond_to?(:create)
                  if Gem.loaded_specs['actionpack'].version < Gem::Version.new('5.1.0')
                    ActionController::TestRequest.create
                  else
                    ActionController::TestRequest.create(TestController)
                  end
                else
                  ActionController::TestRequest.new
                end
      request.accept = ['application/json']
      request
    end

    it 'should permit a request with properly signed headers' do
      request = generated_request
      request.env['DATE'] = Time.now.utc.httpdate
      ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
      response = generated_response(request, :index)
      expect(response.code).to eq('200')
    end

    it 'should forbid a request with properly signed headers but timestamp > 15 minutes ago' do
      request = generated_request
      request.env['DATE'] = 'Mon, 23 Jan 1984 03:29:56 GMT'
      ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
      response = generated_response(request, :index)
      expect(response.code).to eq('401')
    end

    it 'should forbid a request with properly signed headers but timestamp > 15 minutes in the future' do
      request = generated_request
      request.env['DATE'] = 'Mon, 23 Jan 2100 03:29:56 GMT'
      ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
      response = generated_response(request, :index)
      expect(response.code).to eq('401')
    end

    it "should insert a DATE header in the request when one hasn't been specified" do
      request = generated_request
      ApiAuth.sign!(request, '1044', API_KEY_STORE['1044'])
      expect(request.headers['DATE']).not_to be_nil
    end

    it 'should forbid an unsigned request to a protected controller action' do
      request = generated_request
      response = generated_response(request, :index)
      expect(response.code).to eq('401')
    end

    it 'should forbid a request with a bogus signature' do
      request = generated_request
      request.env['Authorization'] = 'APIAuth bogus:bogus'
      response = generated_response(request, :index)
      expect(response.code).to eq('401')
    end

    it 'should allow non-protected controller actions to function as before' do
      request = generated_request
      response = generated_response(request, :public)
      expect(response.code).to eq('200')
    end
  end

  describe 'Rails ActiveResource integration' do
    class TestResource < ActiveResource::Base
      with_api_auth '1044', API_KEY_STORE['1044']
      self.site = 'https://localhost/'
      self.format = :xml
    end

    it 'should send signed requests automagically' do
      timestamp = Time.parse('Mon, 23 Jan 1984 03:29:56 GMT')
      allow(Time).to receive(:now).at_least(1).times.and_return(timestamp)
      ActiveResource::HttpMock.respond_to do |mock|
        mock.get '/test_resources/1.xml',
                 {
                   'Authorization' => 'APIAuth 1044:LZ1jujf3x1nnGR70/208WPXdUHw=',
                   'Accept' => 'application/xml',
                   'DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT'
                 },
                 { id: '1' }.to_xml(root: 'test_resource')
      end
      expect(ApiAuth).to receive(:sign!).with(anything, '1044', API_KEY_STORE['1044'], {}).and_call_original
      TestResource.find(1)
    end
  end
end
