[rubygems/rubygems] Add WebauthnListener response classes
https://github.com/rubygems/rubygems/commit/0e9a26acb1
This commit is contained in:
parent
ea95ec5443
commit
332c4b6726
75
lib/rubygems/webauthn_listener/response.rb
Normal file
75
lib/rubygems/webauthn_listener/response.rb
Normal file
@ -0,0 +1,75 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
##
|
||||
# The WebauthnListener Response class is used by the WebauthnListener to print
|
||||
# the specified response to the Gem host using the provided socket. It also closes
|
||||
# the socket after printing the response.
|
||||
#
|
||||
# Types of response classes:
|
||||
# - ResponseOk
|
||||
# - ResponseNoContent
|
||||
# - ResponseBadRequest
|
||||
# - ResponseNotFound
|
||||
# - ResponseMethodNotAllowed
|
||||
#
|
||||
# Example:
|
||||
# socket = TCPSocket.new(host, port)
|
||||
# Gem::WebauthnListener::ResponseOk.send(socket, host)
|
||||
#
|
||||
|
||||
class Gem::WebauthnListener
|
||||
class Response
|
||||
attr_reader :host
|
||||
|
||||
def initialize(host)
|
||||
@host = host
|
||||
end
|
||||
|
||||
def self.send(socket, host)
|
||||
socket.print new(host).payload
|
||||
socket.close
|
||||
end
|
||||
|
||||
def payload
|
||||
status_line_and_connection + access_control_headers + content
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def status_line_and_connection
|
||||
<<~RESPONSE
|
||||
HTTP/1.1 #{status}
|
||||
Connection: close
|
||||
RESPONSE
|
||||
end
|
||||
|
||||
def access_control_headers
|
||||
return "" unless add_access_control_headers?
|
||||
<<~RESPONSE
|
||||
Access-Control-Allow-Origin: #{host}
|
||||
Access-Control-Allow-Methods: POST
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization, x-csrf-token
|
||||
RESPONSE
|
||||
end
|
||||
|
||||
def content
|
||||
return "" unless body
|
||||
<<~RESPONSE
|
||||
Content-Type: text/plain
|
||||
Content-Length: #{body.bytesize}
|
||||
|
||||
#{body}
|
||||
RESPONSE
|
||||
end
|
||||
|
||||
def status
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def add_access_control_headers?
|
||||
false
|
||||
end
|
||||
|
||||
def body; end
|
||||
end
|
||||
end
|
@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative "../response"
|
||||
|
||||
class Gem::WebauthnListener::ResponseBadRequest < Gem::WebauthnListener::Response
|
||||
private
|
||||
|
||||
def status
|
||||
"400 Bad Request"
|
||||
end
|
||||
|
||||
def body
|
||||
"missing code parameter"
|
||||
end
|
||||
end
|
@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative "../response"
|
||||
|
||||
class Gem::WebauthnListener::ResponseMethodNotAllowed < Gem::WebauthnListener::Response
|
||||
private
|
||||
|
||||
def status
|
||||
"405 Method Not Allowed"
|
||||
end
|
||||
|
||||
def content
|
||||
<<~RESPONSE
|
||||
Allow: GET, OPTIONS
|
||||
RESPONSE
|
||||
end
|
||||
end
|
@ -0,0 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative "../response"
|
||||
|
||||
class Gem::WebauthnListener::ResponseNoContent < Gem::WebauthnListener::Response
|
||||
private
|
||||
|
||||
def status
|
||||
"204 No Content"
|
||||
end
|
||||
|
||||
def add_access_control_headers?
|
||||
true
|
||||
end
|
||||
end
|
@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative "../response"
|
||||
|
||||
class Gem::WebauthnListener::ResponseNotFound < Gem::WebauthnListener::Response
|
||||
private
|
||||
|
||||
def status
|
||||
"404 Not Found"
|
||||
end
|
||||
end
|
18
lib/rubygems/webauthn_listener/response/response_ok.rb
Normal file
18
lib/rubygems/webauthn_listener/response/response_ok.rb
Normal file
@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
require_relative "../response"
|
||||
|
||||
class Gem::WebauthnListener::ResponseOk < Gem::WebauthnListener::Response
|
||||
private
|
||||
|
||||
def status
|
||||
"200 OK"
|
||||
end
|
||||
|
||||
def add_access_control_headers?
|
||||
true
|
||||
end
|
||||
|
||||
def body
|
||||
"success"
|
||||
end
|
||||
end
|
107
test/rubygems/test_webauthn_listener_response.rb
Normal file
107
test/rubygems/test_webauthn_listener_response.rb
Normal file
@ -0,0 +1,107 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "helper"
|
||||
require "rubygems/webauthn_listener/response/response_ok"
|
||||
require "rubygems/webauthn_listener/response/response_no_content"
|
||||
require "rubygems/webauthn_listener/response/response_bad_request"
|
||||
require "rubygems/webauthn_listener/response/response_not_found"
|
||||
require "rubygems/webauthn_listener/response/response_method_not_allowed"
|
||||
|
||||
class WebauthnListenerResponseTest < Gem::TestCase
|
||||
class MockResponse < Gem::WebauthnListener::Response
|
||||
def payload
|
||||
"hello world"
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@host = "rubygems.example"
|
||||
end
|
||||
|
||||
def test_ok_response_payload
|
||||
payload = Gem::WebauthnListener::ResponseOk.new(@host).payload
|
||||
|
||||
expected_payload = <<~RESPONSE
|
||||
HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Access-Control-Allow-Origin: rubygems.example
|
||||
Access-Control-Allow-Methods: POST
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization, x-csrf-token
|
||||
Content-Type: text/plain
|
||||
Content-Length: 7
|
||||
|
||||
success
|
||||
RESPONSE
|
||||
|
||||
assert_equal expected_payload, payload
|
||||
end
|
||||
|
||||
def test_no_payload_response_payload
|
||||
payload = Gem::WebauthnListener::ResponseNoContent.new(@host).payload
|
||||
|
||||
expected_payload = <<~RESPONSE
|
||||
HTTP/1.1 204 No Content
|
||||
Connection: close
|
||||
Access-Control-Allow-Origin: rubygems.example
|
||||
Access-Control-Allow-Methods: POST
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization, x-csrf-token
|
||||
RESPONSE
|
||||
|
||||
assert_equal expected_payload, payload
|
||||
end
|
||||
|
||||
def test_method_not_allowed_response_payload
|
||||
payload = Gem::WebauthnListener::ResponseMethodNotAllowed.new(@host).payload
|
||||
|
||||
expected_payload = <<~RESPONSE
|
||||
HTTP/1.1 405 Method Not Allowed
|
||||
Connection: close
|
||||
Allow: GET, OPTIONS
|
||||
RESPONSE
|
||||
|
||||
assert_equal expected_payload, payload
|
||||
end
|
||||
|
||||
def test_method_not_found_response_payload
|
||||
payload = Gem::WebauthnListener::ResponseNotFound.new(@host).payload
|
||||
|
||||
expected_payload = <<~RESPONSE
|
||||
HTTP/1.1 404 Not Found
|
||||
Connection: close
|
||||
RESPONSE
|
||||
|
||||
assert_equal expected_payload, payload
|
||||
end
|
||||
|
||||
def test_bad_request_response_payload
|
||||
payload = Gem::WebauthnListener::ResponseBadRequest.new(@host).payload
|
||||
|
||||
expected_payload = <<~RESPONSE
|
||||
HTTP/1.1 400 Bad Request
|
||||
Connection: close
|
||||
Content-Type: text/plain
|
||||
Content-Length: 22
|
||||
|
||||
missing code parameter
|
||||
RESPONSE
|
||||
|
||||
assert_equal expected_payload, payload
|
||||
end
|
||||
|
||||
def test_send_response
|
||||
server = TCPServer.new "localhost", 5678
|
||||
thread = Thread.new do
|
||||
receive_socket = server.accept
|
||||
Thread.current[:payload] = receive_socket.read
|
||||
receive_socket.close
|
||||
end
|
||||
|
||||
send_socket = TCPSocket.new "localhost", 5678
|
||||
MockResponse.send(send_socket, @host)
|
||||
|
||||
thread.join
|
||||
assert_equal "hello world", thread[:payload]
|
||||
assert_predicate send_socket, :closed?
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user