require "pact_broker/api/middleware/basic_auth"
require "pact_broker/api/authorization/resource_access_policy"

module PactBroker
  module Api
    module Middleware
      describe "basic auth" do
        let(:protected_app) { ->(_) { [200, {}, []]} }

        let(:policy) { PactBroker::Api::Authorization::ResourceAccessPolicy.build(allow_public_read_access, allow_public_access_to_heartbeat, enable_public_badge_access) }
        let(:app) { BasicAuth.new(protected_app, [write_username, write_password], [read_username, read_password], policy) }
        let(:allow_public_read_access) { false }
        let(:allow_public_read_access) { false }
        let(:write_username) { "write_username" }
        let(:write_password) { "write_password" }
        let(:read_username) { "read_username" }
        let(:read_password) { "read_password" }
        let(:allow_public_access_to_heartbeat) { true }
        let(:enable_public_badge_access) { true }

        context "when requesting the heartbeat" do
          let(:path) { "/diagnostic/status/heartbeat" }

          context "when allow_public_access_to_heartbeat is true" do
            context "when no credentials are used" do
              it "allows GET" do
                get path
                expect(last_response.status).to eq 200
              end
            end
          end

          context "when allow_public_access_to_heartbeat is false" do
            let(:allow_public_access_to_heartbeat) { false }

            context "when no credentials are used" do
              it "does not allow GET" do
                get path
                expect(last_response.status).to eq 401
              end
            end

            context "when the correct credentials are used" do
              it "allows GET" do
                basic_authorize "read_username", "read_password"
                get path
                expect(last_response.status).to eq 200
              end
            end
          end
        end

        context "when requesting a pact badge" do
          context "when no credentials are used" do
            it "allows GET" do
              get "/pacts/provider/foo/consumer/bar/badge"
              expect(last_response.status).to eq 200
            end
          end

          context "when enable_public_badge_access=false" do
            let(:enable_public_badge_access) { false }

            it "does not allow GET" do
              get "/pacts/provider/foo/consumer/bar/badge"
              expect(last_response.status).to eq 401
            end
          end
        end

        context "when requesting a matrix badge" do
          context "when no credentials are used" do
            it "allows GET" do
              get "/matrix/provider/foo/latest/dev/consumer/bar/latest/dev/badge"
              expect(last_response.status).to eq 200
            end
          end
        end

        context "with the correct username and password for the write user" do
          it "allows GET" do
            basic_authorize "write_username", "write_password"
            get "/"
            expect(last_response.status).to eq 200
          end

          it "allows POST" do
            basic_authorize "write_username", "write_password"
            post "/"
            expect(last_response.status).to eq 200
          end

          it "allows HEAD" do
            basic_authorize "write_username", "write_password"
            head "/"
            expect(last_response.status).to eq 200
          end

          it "allows OPTIONS" do
            basic_authorize "write_username", "write_password"
            options "/"
            expect(last_response.status).to eq 200
          end

          it "allows PUT" do
            basic_authorize "write_username", "write_password"
            delete "/"
            expect(last_response.status).to eq 200
          end

          it "allows PATCH" do
            basic_authorize "write_username", "write_password"
            patch "/"
            expect(last_response.status).to eq 200
          end

          it "allows DELETE" do
            basic_authorize "write_username", "write_password"
            delete "/"
            expect(last_response.status).to eq 200
          end
        end

        context "with the incorrect username for the write user" do
          it "does not allow POST" do
            basic_authorize "wrong_username", "write_password"
            post "/"
            expect(last_response.status).to eq 401
          end
        end

        context "with the incorrect password for the write user" do
          it "does not allow POST" do
            basic_authorize "write_username", "wrong_password"
            post "/"
            expect(last_response.status).to eq 401
          end
        end

        context "with the correct username and password for the read user" do
          it "allows GET" do
            basic_authorize "read_username", "read_password"
            get "/"
            expect(last_response.status).to eq 200
          end

          it "allows OPTIONS" do
            basic_authorize "read_username", "read_password"
            options "/"
            expect(last_response.status).to eq 200
          end

          it "allows HEAD" do
            basic_authorize "read_username", "read_password"
            head "/"
            expect(last_response.status).to eq 200
          end

          it "does not allow POST" do
            basic_authorize "read_username", "read_password"
            post "/"
            expect(last_response.status).to eq 401
          end

          it "does not allow PUT" do
            basic_authorize "read_username", "read_password"
            put "/"
            expect(last_response.status).to eq 401
          end

          it "does not allow PATCH" do
            basic_authorize "read_username", "read_password"
            patch "/"
            expect(last_response.status).to eq 401
          end

          it "does not allow DELETE" do
            basic_authorize "read_username", "read_password"
            delete "/"
            expect(last_response.status).to eq 401
          end

          it "allows POST to the pacts for verification endpoint" do
            basic_authorize "read_username", "read_password"
            post "/pacts/provider/Foo/for-verification"
            expect(last_response.status).to eq 200
          end
        end

        context "with the incorrect username and password for the write user" do
          it "does not allow GET" do
            basic_authorize "write_username", "wrongpassword"
            get "/"
            expect(last_response.status).to eq 401
          end
        end

        context "with the incorrect username and password for the read user" do
          it "does not allow GET" do
            basic_authorize "read_username", "wrongpassword"
            get "/"
            expect(last_response.status).to eq 401
          end
        end

        context "with a request to the badge URL" do
          context "with no credentials" do
            it "allows GET" do
              get "/pacts/provider/foo/consumer/bar/badge"
              expect(last_response.status).to eq 200
            end
          end
        end

        context "when there is no read only user configured" do
          before do
            allow($stdout).to receive(:puts)
          end

          let(:read_username) { nil }
          let(:read_password) { nil }

          context "when allow_public_read_access is false" do
            context "with no credentials" do
              it "does not allow a GET" do
                get "/"
                expect(last_response.status).to eq 401
              end
            end

            context "with empty credentials" do
              it "does not allow a GET" do
                basic_authorize("", "")
                get "/"
                expect(last_response.status).to eq 401
              end
            end

            context "with incorrect credentials" do
              it "does not allow a GET" do
                basic_authorize("foo", "bar")
                get "/"
                expect(last_response.status).to eq 401
              end
            end
          end

          context "when allow_public_read_access is true" do
            let(:allow_public_read_access) { true }

            context "with no credentials" do
              it "allows a GET" do
                get "/"
                expect(last_response.status).to eq 200
              end

              it "does not allow POST" do
                post "/"
                expect(last_response.status).to eq 401
              end
            end

            context "with empty credentials" do
              before do
                basic_authorize("", "")
              end

              it "allows a GET" do
                get "/"
                expect(last_response.status).to eq 200
              end

              it "does not allow POST" do
                post "/"
                expect(last_response.status).to eq 401
              end
            end

            context "with incorrect credentials" do
              before do
                basic_authorize("foo", "bar")
              end

              it "allows a GET" do
                get "/"
                expect(last_response.status).to eq 200
              end

              it "does not allow POST" do
                post "/"
                expect(last_response.status).to eq 401
              end
            end
          end
        end

        context "when the credentials are configured with empty strings" do
          before do
            basic_authorize("", "")
          end

          let(:write_username) { "" }
          let(:write_password) { "" }

          subject { get("/") }

          its(:status) { is_expected.to eq 401 }
        end
      end
    end
  end
end
