cassette 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/README.md +106 -0
- data/lib/cassette/authentication/authorities.rb +37 -0
- data/lib/cassette/authentication/cache.rb +30 -0
- data/lib/cassette/authentication/filter.rb +41 -0
- data/lib/cassette/authentication/user.rb +27 -0
- data/lib/cassette/authentication.rb +72 -0
- data/lib/cassette/cache.rb +42 -0
- data/lib/cassette/client/cache.rb +43 -0
- data/lib/cassette/client.rb +68 -0
- data/lib/cassette/errors/not_a_customer.rb +14 -0
- data/lib/cassette/errors/not_an_employee.rb +14 -0
- data/lib/cassette/errors.rb +44 -0
- data/lib/cassette/rubycas/helper.rb +78 -0
- data/lib/cassette/rubycas/not_single_sign_out_constraint.rb +14 -0
- data/lib/cassette/rubycas/single_sign_out_constraint.rb +27 -0
- data/lib/cassette/rubycas.rb +11 -0
- data/lib/cassette/version.rb +15 -0
- data/lib/cassette.rb +75 -0
- data/spec/cas/authentication/authorities_spec.rb +82 -0
- data/spec/cas/authentication/cache_spec.rb +8 -0
- data/spec/cas/authentication/filter_spec.rb +172 -0
- data/spec/cas/authentication/user_spec.rb +70 -0
- data/spec/cas/authentication_spec.rb +84 -0
- data/spec/cas/cache_spec.rb +40 -0
- data/spec/cas/client/cache_spec.rb +7 -0
- data/spec/cas/errors_spec.rb +29 -0
- data/spec/cas_spec.rb +78 -0
- data/spec/config.yml +5 -0
- data/spec/fixtures/cas/fail.xml +6 -0
- data/spec/fixtures/cas/success.xml +12 -0
- data/spec/integration/cas/client_spec.rb +50 -0
- data/spec/spec_helper.rb +27 -0
- metadata +257 -0
    
        data/lib/cassette.rb
    ADDED
    
    | @@ -0,0 +1,75 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "cassette/errors"
         | 
| 4 | 
            +
            require "cassette/cache"
         | 
| 5 | 
            +
            require "cassette/client/cache"
         | 
| 6 | 
            +
            require "cassette/client"
         | 
| 7 | 
            +
            require "cassette/authentication"
         | 
| 8 | 
            +
            require "cassette/authentication/authorities"
         | 
| 9 | 
            +
            require "cassette/authentication/user"
         | 
| 10 | 
            +
            require "cassette/authentication/cache"
         | 
| 11 | 
            +
            require "cassette/authentication/filter"
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            require "faraday"
         | 
| 14 | 
            +
            require "logger"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            module Cassette
         | 
| 17 | 
            +
              extend self
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              DEFAULT_TIMEOUT = 10
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              def logger
         | 
| 22 | 
            +
                @@logger ||= begin
         | 
| 23 | 
            +
                  if defined?(Rails) && Rails.logger
         | 
| 24 | 
            +
                    Rails.logger
         | 
| 25 | 
            +
                  else
         | 
| 26 | 
            +
                    Logger.new("/dev/null")
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              def logger=(logger)
         | 
| 32 | 
            +
                @@logger = logger
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def config
         | 
| 36 | 
            +
                @@config
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def config=(config)
         | 
| 40 | 
            +
                @@config = config
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def new_request(uri, timeout)
         | 
| 44 | 
            +
                Faraday.new(url: uri, ssl: { verify: false, version: "TLSv1" }) do |builder|
         | 
| 45 | 
            +
                  builder.adapter :httpclient
         | 
| 46 | 
            +
                  builder.options.timeout = timeout
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              def get(uri, payload, timeout = DEFAULT_TIMEOUT)
         | 
| 51 | 
            +
                perform(:get, uri, payload, timeout) do |req|
         | 
| 52 | 
            +
                  req.params = payload
         | 
| 53 | 
            +
                  logger.debug "Request: #{req.inspect}"
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def post(uri, payload, timeout = DEFAULT_TIMEOUT)
         | 
| 58 | 
            +
                perform(:post, uri, payload, timeout) do |req|
         | 
| 59 | 
            +
                  req.body = payload
         | 
| 60 | 
            +
                  logger.debug "Request: #{req.inspect}"
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              protected
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              def perform(op, uri, payload, timeout = DEFAULT_TIMEOUT, &block)
         | 
| 67 | 
            +
                request = new_request(uri, timeout)
         | 
| 68 | 
            +
                res = request.send(op, &block)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                res.tap do |response|
         | 
| 71 | 
            +
                  logger.debug "Got response: #{response.body.inspect} (#{response.status}), #{response.headers.inspect}"
         | 
| 72 | 
            +
                  Cassette::Errors.raise_by_code(response.status) unless response.success?
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Cassette::Authentication::Authorities do
         | 
| 4 | 
            +
              subject do
         | 
| 5 | 
            +
                Cassette::Authentication::Authorities
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe "#has_role?" do
         | 
| 9 | 
            +
                let(:input) { "[#{Cassette.config.base_authority}, SAPI, #{Cassette.config.base_authority}_CREATE-USER]" }
         | 
| 10 | 
            +
                let(:authorities) { subject.parse(input) }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                it "adds the application prefix to roles" do
         | 
| 13 | 
            +
                  expect(authorities.has_role?("CREATE-USER")).to eql(true)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it "ignores role case" do
         | 
| 17 | 
            +
                  expect(authorities.has_role?("create-user")).to eql(true)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                it "replaces underscores with dashes" do
         | 
| 21 | 
            +
                  expect(authorities.has_role?("create_user")).to eql(true)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              context "with a defined base authority" do
         | 
| 26 | 
            +
                let(:base_authority) { "SOMEAPI" }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                it "stores the base authority" do
         | 
| 29 | 
            +
                  input = "CUSTOMERAPI"
         | 
| 30 | 
            +
                  expect(subject.parse(input, base_authority).base).to eql(base_authority)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                describe "#has_role?" do
         | 
| 34 | 
            +
                  let(:input) { "[#{Cassette.config.base_authority}_TEST2, SOMEAPI_TEST]" }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  it "returns true for a role that is using the base authority" do
         | 
| 37 | 
            +
                    expect(subject.parse(input, base_authority)).to have_role(:test)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  it "returns false for a role that is not using the base authority" do
         | 
| 41 | 
            +
                    expect(subject.parse(input, base_authority)).not_to have_role(:test2)
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              context "CAS authorities parsing" do
         | 
| 47 | 
            +
                it "handles single authority" do
         | 
| 48 | 
            +
                  input = "CUSTOMERAPI"
         | 
| 49 | 
            +
                  expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI))
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                it "handles multiple authorities with surrounding []" do
         | 
| 53 | 
            +
                  input = "[CUSTOMERAPI, SAPI]"
         | 
| 54 | 
            +
                  expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                it "ignores whitespace in multiple authorities" do
         | 
| 58 | 
            +
                  input = "[CUSTOMERAPI,SAPI]"
         | 
| 59 | 
            +
                  expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                it "returns an empty array when input is nil" do
         | 
| 63 | 
            +
                  expect(subject.parse(nil).authorities).to eq([])
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
              end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
              context "with authentication disabled" do
         | 
| 68 | 
            +
                before { ENV["NOAUTH"] = "true" }
         | 
| 69 | 
            +
                after { ENV.delete("NOAUTH") }
         | 
| 70 | 
            +
                subject { Cassette::Authentication::Authorities.new("[]") }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                it "#has_role? returns true for every role" do
         | 
| 73 | 
            +
                  expect(subject.authorities).to be_empty
         | 
| 74 | 
            +
                  expect(subject.has_role?(:can_manage)).to eql(true)
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                it "#has_raw_role? returns true for every role" do
         | 
| 78 | 
            +
                  expect(subject.authorities).to be_empty
         | 
| 79 | 
            +
                  expect(subject.has_raw_role?("SAPI_CUSTOMER-CREATOR")).to eql(true)
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -0,0 +1,172 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
            require "active_support/core_ext/hash/indifferent_access"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            describe Cassette::Authentication::Filter do
         | 
| 7 | 
            +
              before do
         | 
| 8 | 
            +
                allow(Cassette::Authentication).to receive(:validate_ticket)
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              class ControllerMock
         | 
| 12 | 
            +
                attr_accessor :params, :request, :current_user
         | 
| 13 | 
            +
                def self.before_filter(*); end
         | 
| 14 | 
            +
                include Cassette::Authentication::Filter
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(params = {}, headers = {})
         | 
| 17 | 
            +
                  self.params = params.with_indifferent_access
         | 
| 18 | 
            +
                  self.request = OpenStruct.new(headers: headers.with_indifferent_access)
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              shared_context "with NOAUTH" do
         | 
| 23 | 
            +
                before do
         | 
| 24 | 
            +
                  ENV["NOAUTH"] = "yes"
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                after do
         | 
| 28 | 
            +
                  ENV.delete("NOAUTH")
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              describe "#validate_raw_role!" do
         | 
| 33 | 
            +
                let(:controller) { ControllerMock.new }
         | 
| 34 | 
            +
                let(:current_user) { instance_double(Cassette::Authentication::User) }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                before do
         | 
| 37 | 
            +
                  allow(controller).to receive(:current_user).and_return(current_user)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                it_behaves_like "with NOAUTH" do
         | 
| 41 | 
            +
                  it "never checks the role" do
         | 
| 42 | 
            +
                    expect(current_user).not_to receive(:has_raw_role?)
         | 
| 43 | 
            +
                    controller.validate_raw_role!(:something)
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  it "does not raise error" do
         | 
| 47 | 
            +
                    expect { controller.validate_raw_role!(:something) }.not_to raise_error
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                it "forwards to current_user" do
         | 
| 52 | 
            +
                  role = instance_double(String)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  expect(current_user).to receive(:has_raw_role?).with(role).and_return(true)
         | 
| 55 | 
            +
                  controller.validate_raw_role!(role)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
         | 
| 59 | 
            +
                  role = instance_double(String)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  expect(current_user).to receive(:has_raw_role?).with(role).and_return(false)
         | 
| 62 | 
            +
                  expect { controller.validate_raw_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              describe "#validate_role!" do
         | 
| 67 | 
            +
                let(:controller) { ControllerMock.new }
         | 
| 68 | 
            +
                let(:current_user) { instance_double(Cassette::Authentication::User) }
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                before do
         | 
| 71 | 
            +
                  allow(controller).to receive(:current_user).and_return(current_user)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                it_behaves_like "with NOAUTH" do
         | 
| 75 | 
            +
                  it "never checks the role" do
         | 
| 76 | 
            +
                    expect(current_user).not_to receive(:has_role?)
         | 
| 77 | 
            +
                    controller.validate_role!(:something)
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  it "does not raise error" do
         | 
| 81 | 
            +
                    expect { controller.validate_role!(:something) }.not_to raise_error
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                it "forwards to current_user" do
         | 
| 86 | 
            +
                  role = instance_double(String)
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  expect(current_user).to receive(:has_role?).with(role).and_return(true)
         | 
| 89 | 
            +
                  controller.validate_role!(role)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
         | 
| 93 | 
            +
                  role = instance_double(String)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  expect(current_user).to receive(:has_role?).with(role).and_return(false)
         | 
| 96 | 
            +
                  expect { controller.validate_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              describe "#validate_authentication_ticket" do
         | 
| 101 | 
            +
                it_behaves_like "with NOAUTH" do
         | 
| 102 | 
            +
                  context "and no ticket" do
         | 
| 103 | 
            +
                    let(:controller) { ControllerMock.new }
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    it "should not validate tickets" do
         | 
| 106 | 
            +
                      controller.validate_authentication_ticket
         | 
| 107 | 
            +
                      expect(Cassette::Authentication).not_to have_received(:validate_ticket)
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    it "should set current_user" do
         | 
| 111 | 
            +
                      controller.validate_authentication_ticket
         | 
| 112 | 
            +
                      expect(controller.current_user).to be_present
         | 
| 113 | 
            +
                    end
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                  context "and a ticket header" do
         | 
| 117 | 
            +
                    let(:controller) do
         | 
| 118 | 
            +
                      ControllerMock.new({}, "Service-Ticket" => "le ticket")
         | 
| 119 | 
            +
                    end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                    it "should validate tickets" do
         | 
| 122 | 
            +
                      controller.validate_authentication_ticket
         | 
| 123 | 
            +
                      expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                  context "and a ticket param" do
         | 
| 128 | 
            +
                    let(:controller) do
         | 
| 129 | 
            +
                      ControllerMock.new(ticket: "le ticket")
         | 
| 130 | 
            +
                    end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    it "should validate tickets" do
         | 
| 133 | 
            +
                      controller.validate_authentication_ticket
         | 
| 134 | 
            +
                      expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
                end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                context "with a ticket in the query string *AND* headers" do
         | 
| 140 | 
            +
                  let(:controller) do
         | 
| 141 | 
            +
                    ControllerMock.new({"ticket" => "le other ticket"}, "Service-Ticket" => "le ticket")
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  it "should send only the header ticket to validation" do
         | 
| 145 | 
            +
                    controller.validate_authentication_ticket
         | 
| 146 | 
            +
                    expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
         | 
| 147 | 
            +
                  end
         | 
| 148 | 
            +
                end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                context "with a ticket in the query string" do
         | 
| 151 | 
            +
                  let(:controller) do
         | 
| 152 | 
            +
                    ControllerMock.new("ticket" => "le ticket")
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  it "should send the ticket to validation" do
         | 
| 156 | 
            +
                    controller.validate_authentication_ticket
         | 
| 157 | 
            +
                    expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                context "with a ticket in the Service-Ticket header" do
         | 
| 162 | 
            +
                  let(:controller) do
         | 
| 163 | 
            +
                    ControllerMock.new({}, "Service-Ticket" => "le ticket")
         | 
| 164 | 
            +
                  end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                  it "should send the ticket to validation" do
         | 
| 167 | 
            +
                    controller.validate_authentication_ticket
         | 
| 168 | 
            +
                    expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                end
         | 
| 171 | 
            +
              end
         | 
| 172 | 
            +
            end
         | 
| @@ -0,0 +1,70 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Cassette::Authentication::User do
         | 
| 4 | 
            +
              let(:base_authority) do
         | 
| 5 | 
            +
                Cassette.config.base_authority
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe "#initialize" do
         | 
| 9 | 
            +
                context "without a config" do
         | 
| 10 | 
            +
                  it "forwards authorities parsing" do
         | 
| 11 | 
            +
                    expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", nil)
         | 
| 12 | 
            +
                    Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]")
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                context "with a config" do
         | 
| 17 | 
            +
                  it "forwards authorities parsing passing along the base authority" do
         | 
| 18 | 
            +
                    config = object_double(Cassette.config)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    expect(config).to receive(:base_authority).and_return("TESTAPI")
         | 
| 21 | 
            +
                    expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", "TESTAPI")
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]", config: config)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              describe "#has_role?" do
         | 
| 29 | 
            +
                let (:user) do
         | 
| 30 | 
            +
                  Cassette::Authentication::User.new(login: "john.doe", name: "John Doe",
         | 
| 31 | 
            +
                    authorities: "[#{base_authority}, SAPI, #{base_authority}_CREATE-USER]")
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                it "adds the application prefix to roles" do
         | 
| 35 | 
            +
                  expect(user.has_role?("CREATE-USER")).to eql(true)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                it "ignores role case" do
         | 
| 39 | 
            +
                  expect(user.has_role?("create-user")).to eql(true)
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                it "replaces underscores with dashes" do
         | 
| 43 | 
            +
                  expect(user.has_role?("create_user")).to eql(true)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              context "user types" do
         | 
| 48 | 
            +
                context "#employee?" do
         | 
| 49 | 
            +
                  it "returns true when user is an employee" do
         | 
| 50 | 
            +
                    expect(Cassette::Authentication::User.new(type: "employee")).to be_employee
         | 
| 51 | 
            +
                    expect(Cassette::Authentication::User.new(type: "Employee")).to be_employee
         | 
| 52 | 
            +
                    expect(Cassette::Authentication::User.new(type: :employee)).to be_employee
         | 
| 53 | 
            +
                    expect(Cassette::Authentication::User.new(type: "customer")).not_to be_employee
         | 
| 54 | 
            +
                    expect(Cassette::Authentication::User.new(type: nil)).not_to be_employee
         | 
| 55 | 
            +
                    expect(Cassette::Authentication::User.new(type: "")).not_to be_employee
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                context "#customer?" do
         | 
| 60 | 
            +
                  it "returns true when the user is a customer" do
         | 
| 61 | 
            +
                    expect(Cassette::Authentication::User.new(type: "customer")).to be_customer
         | 
| 62 | 
            +
                    expect(Cassette::Authentication::User.new(type: "Customer")).to be_customer
         | 
| 63 | 
            +
                    expect(Cassette::Authentication::User.new(type: :customer)).to be_customer
         | 
| 64 | 
            +
                    expect(Cassette::Authentication::User.new(type: "employee")).not_to be_customer
         | 
| 65 | 
            +
                    expect(Cassette::Authentication::User.new(type: nil)).not_to be_customer
         | 
| 66 | 
            +
                    expect(Cassette::Authentication::User.new(type: "")).not_to be_customer
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe Cassette::Authentication do
         | 
| 6 | 
            +
              let(:cache) { instance_double(Cassette::Authentication::Cache) }
         | 
| 7 | 
            +
              let(:http)  { class_double(Cassette) }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              subject do
         | 
| 10 | 
            +
                Cassette::Authentication.new(cache: cache, http_client: http)
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              describe "#ticket_user" do
         | 
| 14 | 
            +
                context "when cached" do
         | 
| 15 | 
            +
                  it "returns the cached value when cached" do
         | 
| 16 | 
            +
                    cached = double('cached')
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    expect(cache).to receive(:fetch_authentication) do |ticket, &block|
         | 
| 19 | 
            +
                      expect(ticket).to eql("ticket")
         | 
| 20 | 
            +
                      expect(block).to be_present
         | 
| 21 | 
            +
                      cached
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    expect(subject.ticket_user("ticket")).to eql(cached)
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                context "when not cached" do
         | 
| 29 | 
            +
                  before do
         | 
| 30 | 
            +
                    expect(cache).to receive(:fetch_authentication) do |ticket, &block|
         | 
| 31 | 
            +
                      block.call
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  it "raises a Forbidden exception on any exceptions" do
         | 
| 36 | 
            +
                    allow(http).to receive(:post).with(anything, anything).and_raise(Cassette::Errors::BadRequest)
         | 
| 37 | 
            +
                    expect { subject.ticket_user("ticket") }.to raise_error(Cassette::Errors::Forbidden)
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  context "with a failed CAS response" do
         | 
| 41 | 
            +
                    before do
         | 
| 42 | 
            +
                      allow(http).to receive(:post).with(anything, anything)
         | 
| 43 | 
            +
                        .and_return(OpenStruct.new(body: fixture("cas/fail.xml")))
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    it "returns nil" do
         | 
| 47 | 
            +
                      expect(subject.ticket_user("ticket")).to be_nil
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  context "with a successful CAS response" do
         | 
| 52 | 
            +
                    before do
         | 
| 53 | 
            +
                      allow(http).to receive(:post).with(anything, anything)
         | 
| 54 | 
            +
                        .and_return(OpenStruct.new(body: fixture("cas/success.xml")))
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    it "returns an User" do
         | 
| 58 | 
            +
                      expect(subject.ticket_user("ticket")).to be_instance_of(Cassette::Authentication::User)
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              describe "#validate_ticket" do
         | 
| 65 | 
            +
                it "raises a authorization required error when no ticket is provided" do
         | 
| 66 | 
            +
                  expect { subject.validate_ticket(nil) }.to raise_error(Cassette::Errors::AuthorizationRequired)
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                it "raises a authorization required error when ticket is blank" do
         | 
| 70 | 
            +
                  expect { subject.validate_ticket("") }.to raise_error(Cassette::Errors::AuthorizationRequired)
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                it "raises a forbidden error when the associated user is not found" do
         | 
| 74 | 
            +
                  expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(nil)
         | 
| 75 | 
            +
                  expect { subject.validate_ticket("ticket") }.to raise_error(Cassette::Errors::Forbidden)
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                it "returns the associated user" do
         | 
| 79 | 
            +
                  user = double('User')
         | 
| 80 | 
            +
                  expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(user)
         | 
| 81 | 
            +
                  expect(subject.validate_ticket("ticket")).to eql(user)
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'spec_helper'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe Cassette::Cache do
         | 
| 6 | 
            +
              subject do
         | 
| 7 | 
            +
                c = Class.new
         | 
| 8 | 
            +
                c.send(:include, Cassette::Cache)
         | 
| 9 | 
            +
                c.new
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              describe "backend" do
         | 
| 13 | 
            +
                before { subject.backend = nil }
         | 
| 14 | 
            +
                after { subject.backend = nil }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it "sets the backend" do
         | 
| 17 | 
            +
                  backend = double('Backend')
         | 
| 18 | 
            +
                  subject.backend = backend
         | 
| 19 | 
            +
                  expect(subject.backend).to eql(backend)
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                it "defaults to rails backend" do
         | 
| 23 | 
            +
                  rails = double("Rails")
         | 
| 24 | 
            +
                  allow(rails).to receive(:cache).and_return(rails)
         | 
| 25 | 
            +
                  stub_const("Rails", rails)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  expect(subject.backend).to eql(rails)
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              it "invalidates the cache after the configured number of uses" do
         | 
| 32 | 
            +
                generator = double('Generator')
         | 
| 33 | 
            +
                expect(generator).to receive(:generate).twice
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                6.times do
         | 
| 36 | 
            +
                  subject.fetch("Generator", max_uses: 5) { generator.generate }
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| 40 | 
            +
             | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            # encoding: UTF-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe Cassette::Errors do
         | 
| 6 | 
            +
              describe Cassette::Errors::Base do
         | 
| 7 | 
            +
                describe "#code" do
         | 
| 8 | 
            +
                  it "returns the HTTP status code accordlingly" do
         | 
| 9 | 
            +
                    expect(Cassette::Errors::Forbidden.new.code).to eql(403)
         | 
| 10 | 
            +
                    expect(Cassette::Errors::NotFound.new.code).to eql(404)
         | 
| 11 | 
            +
                    expect(Cassette::Errors::InternalServerError.new.code).to eql(500)
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              describe ".raise_by_code" do
         | 
| 17 | 
            +
                it "raises the correct exception for the status code" do
         | 
| 18 | 
            +
                  expect { Cassette::Errors.raise_by_code(404) }.to raise_error(Cassette::Errors::NotFound)
         | 
| 19 | 
            +
                  expect { Cassette::Errors.raise_by_code(403) }.to raise_error(Cassette::Errors::Forbidden)
         | 
| 20 | 
            +
                  expect { Cassette::Errors.raise_by_code(412) }.to raise_error(Cassette::Errors::PreconditionFailed)
         | 
| 21 | 
            +
                  expect { Cassette::Errors.raise_by_code(500) }.to raise_error(Cassette::Errors::InternalServerError)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                it "raises internal server error for unmapped errors" do
         | 
| 25 | 
            +
                  expect { Cassette::Errors.raise_by_code(406) }.to raise_error(Cassette::Errors::InternalServerError)
         | 
| 26 | 
            +
                  expect { Cassette::Errors.raise_by_code(200) }.to raise_error(Cassette::Errors::InternalServerError)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
    
        data/spec/cas_spec.rb
    ADDED
    
    | @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            require "spec_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Cassette do
         | 
| 4 | 
            +
              let(:uri) { "http://example.org/" }
         | 
| 5 | 
            +
              let(:response) do
         | 
| 6 | 
            +
                Faraday.new do |builder|
         | 
| 7 | 
            +
                  builder.adapter :test do |stub|
         | 
| 8 | 
            +
                    stub.post(uri, 'data') do |env|
         | 
| 9 | 
            +
                      headers = env.request_headers
         | 
| 10 | 
            +
                      [200, {}, "{ok: true}"]
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              let(:failed_response) do
         | 
| 17 | 
            +
                Faraday.new do |builder|
         | 
| 18 | 
            +
                  builder.adapter :test do |stub|
         | 
| 19 | 
            +
                    stub.post(uri, 'data') do |env|
         | 
| 20 | 
            +
                      headers = env.request_headers
         | 
| 21 | 
            +
                      [500, {}, "{ok: false}"]
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              describe ".new_request" do
         | 
| 28 | 
            +
                it "returns an instance" do
         | 
| 29 | 
            +
                  # damn coverage
         | 
| 30 | 
            +
                  expect(Cassette.new_request(uri, 5)).to be_instance_of(Faraday::Connection)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              describe ".post" do
         | 
| 35 | 
            +
                it "forwards requests" do
         | 
| 36 | 
            +
                  allow(Cassette).to receive(:new_request).with(uri, 5).and_return(response)
         | 
| 37 | 
            +
                  Cassette.post(uri, "data", 5)
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                it "raises an exception when failed" do
         | 
| 41 | 
            +
                  allow(Cassette).to receive(:new_request).with(uri, 5).and_return(failed_response)
         | 
| 42 | 
            +
                  expect { Cassette.post(uri, "data", 5) }.to raise_error(Cassette::Errors::InternalServerError)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def keeping_logger(&block)
         | 
| 47 | 
            +
                original_logger = Cassette.logger
         | 
| 48 | 
            +
                block.call
         | 
| 49 | 
            +
                Cassette.logger = original_logger
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              describe ".logger" do
         | 
| 53 | 
            +
                it "returns a default instance" do
         | 
| 54 | 
            +
                  expect(Cassette.logger).not_to be_nil
         | 
| 55 | 
            +
                  expect(Cassette.logger.kind_of?(Logger)).to eql(true)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                it "returns rails logger when Rails is available" do
         | 
| 59 | 
            +
                  keeping_logger do
         | 
| 60 | 
            +
                    Cassette.logger = nil
         | 
| 61 | 
            +
                    rails = double("Rails")
         | 
| 62 | 
            +
                    expect(rails).to receive(:logger).and_return(rails).at_least(:once)
         | 
| 63 | 
            +
                    stub_const("Rails", rails)
         | 
| 64 | 
            +
                    expect(Cassette.logger).to eql(rails)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              describe ".logger=" do
         | 
| 70 | 
            +
                let(:logger) { Logger.new(STDOUT) }
         | 
| 71 | 
            +
                it "defines the logger instance" do
         | 
| 72 | 
            +
                  keeping_logger do
         | 
| 73 | 
            +
                    Cassette.logger = logger
         | 
| 74 | 
            +
                    expect(Cassette.logger).to eq(logger)
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
            end
         | 
    
        data/spec/config.yml
    ADDED