# frozen_string_literal: true require "spec_helper" module ControllerActions def index render plain: "index" end def show render plain: "show" end def doorkeeper_unauthorized_render_options(*); end def doorkeeper_forbidden_render_options(*); end end describe "doorkeeper authorize filter" do context "accepts token code specified as" do controller do before_action :doorkeeper_authorize! def index render plain: "index" end end let(:token_string) { "1A2BC3" } let(:token) do double( Doorkeeper::AccessToken, acceptable?: true, previous_refresh_token: "", revoke_previous_refresh_token!: true, ) end it "access_token param" do expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token) get :index, params: { access_token: token_string } end it "bearer_token param" do expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token) get :index, params: { bearer_token: token_string } end it "Authorization header" do expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token) request.env["HTTP_AUTHORIZATION"] = "Bearer #{token_string}" get :index end it "different kind of Authorization header" do expect(Doorkeeper::AccessToken).not_to receive(:by_token) request.env["HTTP_AUTHORIZATION"] = "MAC #{token_string}" get :index end it "does not change Authorization header value" do expect(Doorkeeper::AccessToken).to receive(:by_token).exactly(2).times.and_return(token) request.env["HTTP_AUTHORIZATION"] = "Bearer #{token_string}" get :index controller.send(:remove_instance_variable, :@doorkeeper_token) get :index end end context "defined for all actions" do controller do before_action :doorkeeper_authorize! include ControllerActions end context "with valid token", token: :valid do it "allows into index action" do get :index, params: { access_token: token_string } expect(response).to be_successful end it "allows into show action" do get :show, params: { id: "4", access_token: token_string } expect(response).to be_successful end end context "with invalid token", token: :invalid do it "does not allow into index action" do get :index, params: { access_token: token_string } expect(response.status).to eq 401 expect(response.header["WWW-Authenticate"]).to match(/^Bearer/) end it "does not allow into show action" do get :show, params: { id: "4", access_token: token_string } expect(response.status).to eq 401 expect(response.header["WWW-Authenticate"]).to match(/^Bearer/) end end end context "defined with scopes" do controller do before_action -> { doorkeeper_authorize! :write } include ControllerActions end let(:token_string) { "1A2DUWE" } it "allows if the token has particular scopes" do token = double( Doorkeeper::AccessToken, accessible?: true, scopes: %w[write public], previous_refresh_token: "", revoke_previous_refresh_token!: true, ) expect(token).to receive(:acceptable?).with([:write]).and_return(true) expect( Doorkeeper::AccessToken, ).to receive(:by_token).with(token_string).and_return(token) get :index, params: { access_token: token_string } expect(response).to be_successful end it "does not allow if the token does not include given scope" do token = double( Doorkeeper::AccessToken, accessible?: true, scopes: ["public"], revoked?: false, expired?: false, previous_refresh_token: "", revoke_previous_refresh_token!: true, ) expect( Doorkeeper::AccessToken, ).to receive(:by_token).with(token_string).and_return(token) expect(token).to receive(:acceptable?).with([:write]).and_return(false) get :index, params: { access_token: token_string } expect(response.status).to eq 403 expect(response.header).to_not include("WWW-Authenticate") end end context "when custom unauthorized render options are configured" do controller do before_action :doorkeeper_authorize! include ControllerActions end context "with a JSON custom render", token: :invalid do before do module ControllerActions remove_method :doorkeeper_unauthorized_render_options def doorkeeper_unauthorized_render_options(error: nil) { json: ActiveSupport::JSON.encode(error_message: error.description) } end end end after do module ControllerActions remove_method :doorkeeper_unauthorized_render_options def doorkeeper_unauthorized_render_options(error: nil); end end end it "it renders a custom JSON response", token: :invalid do get :index, params: { access_token: token_string } expect(response.status).to eq 401 expect(response.content_type).to include("application/json") expect(response.header["WWW-Authenticate"]).to match(/^Bearer/) expect(json_response).not_to be_nil expect(json_response["error_message"]).to match("token is invalid") end end context "with a text custom render", token: :invalid do before do module ControllerActions remove_method :doorkeeper_unauthorized_render_options def doorkeeper_unauthorized_render_options(**) { plain: "Unauthorized" } end end end after do module ControllerActions remove_method :doorkeeper_unauthorized_render_options def doorkeeper_unauthorized_render_options(error: nil); end end end it "it renders a custom text response", token: :invalid do get :index, params: { access_token: token_string } expect(response.status).to eq 401 expect(response.content_type).to include("text/plain") expect(response.header["WWW-Authenticate"]).to match(/^Bearer/) expect(response.body).to eq("Unauthorized") end end end context "when custom forbidden render options are configured" do before do expect(Doorkeeper::AccessToken).to receive(:by_token).with(token_string).and_return(token) expect(token).to receive(:acceptable?).with([:write]).and_return(false) end after do module ControllerActions remove_method :doorkeeper_forbidden_render_options def doorkeeper_forbidden_render_options(*); end end end controller do before_action -> { doorkeeper_authorize! :write } include ControllerActions end let(:token) do double( Doorkeeper::AccessToken, accessible?: true, scopes: ["public"], revoked?: false, expired?: false, previous_refresh_token: "", revoke_previous_refresh_token!: true, ) end let(:token_string) { "1A2DUWE" } context "with a JSON custom render" do before do module ControllerActions remove_method :doorkeeper_forbidden_render_options def doorkeeper_forbidden_render_options(*) { json: { error_message: "Forbidden" } } end end end it "renders a custom JSON response" do get :index, params: { access_token: token_string } expect(response.header).to_not include("WWW-Authenticate") expect(response.content_type).to include("application/json") expect(response.status).to eq 403 expect(json_response).not_to be_nil expect(json_response["error_message"]).to match("Forbidden") end end context "with a status and JSON custom render" do before do module ControllerActions remove_method :doorkeeper_forbidden_render_options def doorkeeper_forbidden_render_options(*) { json: { error_message: "Not Found" }, respond_not_found_when_forbidden: true, } end end end it "overrides the default status code" do get :index, params: { access_token: token_string } expect(response.status).to eq 404 end end context "with a text custom render" do before do module ControllerActions remove_method :doorkeeper_forbidden_render_options def doorkeeper_forbidden_render_options(*) { plain: "Forbidden" } end end end it "renders a custom status code and text response" do get :index, params: { access_token: token_string } expect(response.header).to_not include("WWW-Authenticate") expect(response.status).to eq 403 expect(response.body).to eq("Forbidden") end end context "with a status and text custom render" do before do module ControllerActions remove_method :doorkeeper_forbidden_render_options def doorkeeper_forbidden_render_options(*) { respond_not_found_when_forbidden: true, plain: "Not Found" } end end end it "overrides the default status code" do get :index, params: { access_token: token_string } expect(response.status).to eq 404 end end end context "when handle_auth_errors option is set to :raise" do subject { get :index, params: { access_token: token_string } } before do config_is_set(:handle_auth_errors, :raise) end controller do before_action :doorkeeper_authorize! include ControllerActions end context "when token is unknown" do it "raises Doorkeeper::Errors::TokenUnknown exception", token: :invalid do expect { subject }.to raise_error(Doorkeeper::Errors::TokenUnknown) end end context "when token is expired" do it "raises Doorkeeper::Errors::TokenExpired exception", token: :expired do expect { subject }.to raise_error(Doorkeeper::Errors::TokenExpired) end end context "when token is revoked" do it "raises Doorkeeper::Errors::TokenRevoked exception", token: :revoked do expect { subject }.to raise_error(Doorkeeper::Errors::TokenRevoked) end end context "when token is forbidden" do it "raises Doorkeeper::Errors::TokenForbidden exception", token: :forbidden do expect { subject }.to raise_error(Doorkeeper::Errors::TokenForbidden) end end context "when token is valid" do it "allows into index action", token: :valid do expect(response).to be_successful end end end end