1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
|
require_relative '../common'
require 'net/ssh/transport/session'
require 'net/ssh/proxy/http'
require 'logger'
# mocha adds #verify to Object, which throws off the host-key-verifier part of
# these tests.
# can't use .include? because ruby18 uses strings and ruby19 uses symbols :/
Object.send(:undef_method, :verify) if Object.instance_methods.any? { |v| v.to_sym == :verify }
module Transport
class TestSession < NetSSHTest
include Net::SSH::Transport::Constants
TEST_HOST = "net.ssh.test"
TEST_PORT = 22
def test_constructor_defaults
assert_equal TEST_HOST, session.host
assert_equal TEST_PORT, session.port
assert_instance_of(
Net::SSH::Verifiers::AcceptNewOrLocalTunnel,
session.host_key_verifier
)
end
def test_verify_host_key_true_uses_accept_new_or_local_tunnel_verifier
Kernel.expects(:warn).with(
'verify_host_key: true is deprecated, use :accept_new_or_local_tunnel'
)
assert_instance_of(
Net::SSH::Verifiers::AcceptNewOrLocalTunnel,
session(verify_host_key: true).host_key_verifier
)
end
def test_verify_host_key_accept_new_or_local_tunnel_uses_accept_new_or_local_tunnel_verifier
assert_instance_of(
Net::SSH::Verifiers::AcceptNewOrLocalTunnel,
session(verify_host_key: :accept_new_or_local_tunnel).host_key_verifier
)
end
def test_verify_host_key_nil_uses_accept_new_or_local_tunnel_verifier
assert_instance_of(
Net::SSH::Verifiers::AcceptNewOrLocalTunnel,
session(verify_host_key: nil).host_key_verifier
)
end
def test_verify_host_key_very_uses_accept_new_verifier
Kernel.expects(:warn).with('verify_host_key: :very is deprecated, use :accept_new')
assert_instance_of(
Net::SSH::Verifiers::AcceptNew,
session(verify_host_key: :very).host_key_verifier
)
end
def test_verify_host_key_accept_new_uses_accept_new_verifier
assert_instance_of(
Net::SSH::Verifiers::AcceptNew,
session(verify_host_key: :accept_new).host_key_verifier
)
end
def test_verify_host_key_secure_uses_always_verifier
Kernel.expects(:warn).with('verify_host_key: :secure is deprecated, use :always')
assert_instance_of(
Net::SSH::Verifiers::Always,
session(verify_host_key: :secure).host_key_verifier
)
end
def test_verify_host_key_false_uses_never_verifier
Kernel.expects(:warn).with('verify_host_key: false is deprecated, use :never')
assert_instance_of(
Net::SSH::Verifiers::Never,
session(verify_host_key: false).host_key_verifier
)
end
def test_verify_host_key_null_uses_never_verifier
assert_instance_of(
Net::SSH::Verifiers::Never,
session(verify_host_key: :never).host_key_verifier
)
end
def test_unknown_verify_host_key_value_raises_exception_if_value_does_not_respond_to_verify
assert_raises(ArgumentError) { session(verify_host_key: :bogus).host_key_verifier }
end
def test_verify_host_key_value_responding_to_verify_should_pass_muster
object = stub("thingy", verify: true, verify_signature: true)
assert_equal object, session(verify_host_key: object).host_key_verifier
end
def test_deprecated_host_key_verifier
Kernel.expects(:warn).with('Warning: verifier without :verify_signature is deprecated')
object = stub("thingy", verify: true)
assert_not_nil session(verify_host_key: object).host_key_verifier
end
def test_host_as_string_should_return_host_and_ip_when_port_is_default
session!
socket.stubs(:peer_ip).returns("1.2.3.4")
assert_equal "#{TEST_HOST},1.2.3.4", session.host_as_string
end
def test_host_as_string_should_return_host_and_ip_with_port_when_port_is_not_default
session(port: 1234) # force session to be instantiated
socket.stubs(:peer_ip).returns("1.2.3.4")
assert_equal "[#{TEST_HOST}]:1234,[1.2.3.4]:1234", session.host_as_string
end
def test_host_as_string_should_return_only_host_when_host_is_ip
session!(host: "1.2.3.4")
socket.stubs(:peer_ip).returns("1.2.3.4")
assert_equal "1.2.3.4", session.host_as_string
end
def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default
session!(host: "1.2.3.4", port: 1234)
socket.stubs(:peer_ip).returns("1.2.3.4")
assert_equal "[1.2.3.4]:1234", session.host_as_string
end
def test_host_as_string_should_return_only_host_when_proxy_command_is_set
session!(host: "1.2.3.4")
socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP)
assert_equal "1.2.3.4", session.host_as_string
end
def test_host_as_string_should_return_only_host_and_port_when_host_is_ip_and_port_is_not_default_and_proxy_command_is_set
session!(host: "1.2.3.4", port: 1234)
socket.stubs(:peer_ip).returns(Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP)
assert_equal "[1.2.3.4]:1234", session.host_as_string
end
def test_close_should_cleanup_and_close_socket
session!
socket.expects(:cleanup)
socket.expects(:close)
session.close
end
def test_service_request_should_return_buffer
assert_equal "\005\000\000\000\004sftp", session.service_request('sftp').to_s
end
def test_rekey_when_kex_is_pending_should_do_nothing
algorithms.stubs(pending?: true)
algorithms.expects(:rekey!).never
session.rekey!
end
def test_rekey_when_no_kex_is_pending_should_initiate_rekey_and_block_until_it_completes
algorithms.stubs(pending?: false)
algorithms.expects(:rekey!)
session.expects(:wait).yields
algorithms.expects(:initialized?).returns(true)
session.rekey!
end
def test_rekey_as_needed_when_kex_is_pending_should_do_nothing
session!
algorithms.stubs(pending?: true)
socket.expects(:if_needs_rekey?).never
session.rekey_as_needed
end
def test_rekey_as_needed_when_no_kex_is_pending_and_no_rekey_is_needed_should_do_nothing
session!
algorithms.stubs(pending?: false)
socket.stubs(if_needs_rekey?: false)
session.expects(:rekey!).never
session.rekey_as_needed
end
def test_rekey_as_needed_when_no_kex_is_pending_and_rekey_is_needed_should_initiate_rekey_and_block
session!
algorithms.stubs(pending?: false)
socket.expects(:if_needs_rekey?).yields
session.expects(:rekey!)
session.rekey_as_needed
end
def test_peer_should_return_hash_of_info_about_peer
session!
socket.stubs(peer_ip: "1.2.3.4")
assert_equal({ ip: "1.2.3.4", port: TEST_PORT, host: TEST_HOST, canonized: "net.ssh.test,1.2.3.4" }, session.peer)
end
def test_next_message_should_block_until_next_message_is_available
session.expects(:poll_message).with(:block)
session.next_message
end
def test_poll_message_should_query_next_packet_using_the_given_blocking_parameter
session!
socket.expects(:next_packet).with(:blocking_parameter, nil).returns(nil)
session.poll_message(:blocking_parameter)
end
def test_poll_message_should_query_next_packet_using_the_timeout_option
session!(timeout: 7)
socket.expects(:next_packet).with(:nonblock, 7).returns(nil)
session.poll_message
end
def test_poll_message_should_default_to_non_blocking
session!
socket.expects(:next_packet).with(:nonblock, nil).returns(nil)
session.poll_message
end
def test_poll_message_should_silently_handle_disconnect_packets
session!
socket.expects(:next_packet).returns(P(:byte, DISCONNECT, :long, 1, :string, "testing", :string, ""))
assert_raises(Net::SSH::Disconnect) { session.poll_message }
end
def test_poll_message_should_silently_handle_ignore_packets
session!
socket.expects(:next_packet).times(2).returns(P(:byte, IGNORE, :string, "test"), nil)
assert_nil session.poll_message
end
def test_poll_message_should_silently_handle_unimplemented_packets
session!
socket.expects(:next_packet).times(2).returns(P(:byte, UNIMPLEMENTED, :long, 15), nil)
assert_nil session.poll_message
end
def test_poll_message_should_silently_handle_debug_packets_with_always_display
session!
socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, true, :string, "testing", :string, ""), nil)
assert_nil session.poll_message
end
def test_poll_message_should_silently_handle_debug_packets_without_always_display
session!
socket.expects(:next_packet).times(2).returns(P(:byte, DEBUG, :bool, false, :string, "testing", :string, ""), nil)
assert_nil session.poll_message
end
def test_poll_message_should_silently_handle_kexinit_packets
session!
packet = P(:byte, KEXINIT, :raw, "lasdfalksdjfa;slkdfja;slkfjsdfaklsjdfa;df")
socket.expects(:next_packet).times(2).returns(packet, nil)
algorithms.expects(:accept_kexinit).with(packet)
assert_nil session.poll_message
end
def test_poll_message_should_return_other_packets
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
socket.expects(:next_packet).returns(packet)
assert_equal packet, session.poll_message
end
def test_poll_message_should_enqueue_packets_when_algorithm_disallows_packet
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
algorithms.stubs(:allow?).with(packet).returns(false)
socket.expects(:next_packet).times(2).returns(packet, nil)
assert_nil session.poll_message
assert_equal [packet], session.queue
end
def test_poll_message_should_read_from_queue_when_next_in_queue_is_allowed_and_consume_queue_is_true
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
session.push(packet)
socket.expects(:next_packet).never
assert_equal packet, session.poll_message
assert session.queue.empty?
end
def test_poll_message_should_not_read_from_queue_when_next_in_queue_is_not_allowed
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
algorithms.stubs(:allow?).with(packet).returns(false)
session.push(packet)
socket.expects(:next_packet).returns(nil)
assert_nil session.poll_message
assert_equal [packet], session.queue
end
def test_poll_message_should_not_read_from_queue_when_consume_queue_is_false
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
session.push(packet)
socket.expects(:next_packet).returns(nil)
assert_nil session.poll_message(:nonblock, false)
assert_equal [packet], session.queue
end
def test_wait_with_block_should_return_immediately_if_block_returns_truth
session.expects(:poll_message).never
session.wait { true }
end
def test_wait_should_not_consume_queue_on_reads
n = 0
session.expects(:poll_message).with(:nonblock, false).returns(nil)
session.wait { (n += 1) > 1 }
end
def test_wait_without_block_should_return_after_first_read
session.expects(:poll_message).returns(nil)
session.wait
end
def test_wait_should_enqueue_packets
session!
p1 = P(:byte, SERVICE_REQUEST, :string, "test")
p2 = P(:byte, SERVICE_ACCEPT, :string, "test")
socket.expects(:next_packet).times(2).returns(p1, p2)
n = 0
session.wait { (n += 1) > 2 }
assert_equal [p1, p2], session.queue
end
def test_push_should_enqueue_packet
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
session.push(packet)
assert_equal [packet], session.queue
end
def test_send_message_should_delegate_to_socket
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
socket.expects(:send_packet).with(packet)
session.send_message(packet)
end
def test_enqueue_message_should_delegate_to_socket
session!
packet = P(:byte, SERVICE_ACCEPT, :string, "test")
socket.expects(:enqueue_packet).with(packet)
session.enqueue_message(packet)
end
def test_configure_client_should_pass_options_to_socket_client_state
session.configure_client compression: :standard
assert_equal :standard, socket.client.compression
end
def test_configure_server_should_pass_options_to_socket_server_state
session.configure_server compression: :standard
assert_equal :standard, socket.server.compression
end
def test_hint_should_set_hint_on_socket
assert !socket.hints[:authenticated]
session.hint :authenticated
assert socket.hints[:authenticated]
end
class TestLogger < Logger
def initialize
@strio = StringIO.new
super(@strio)
end
def messages
@strio.string
end
end
def test_log_correct_debug_with_proxy
logger = TestLogger.new
proxy = Net::SSH::Proxy::HTTP.new("")
session!(logger: logger, proxy: proxy)
assert_match "establishing connection to #{TEST_HOST}:#{TEST_PORT} through proxy", logger.messages
end
def test_log_correct_debug_without_proxy
logger = TestLogger.new
session!(logger: logger)
assert_match "establishing connection to #{TEST_HOST}:#{TEST_PORT}", logger.messages
end
private
def socket
@socket ||= stub("socket", hints: {})
end
def server_version
@server_version ||= stub("server_version")
end
def algorithms
@algorithms ||= stub("algorithms", initialized?: true, allow?: true, start: true)
end
def session(options = {})
@session ||= begin
host = options.delete(:host) || TEST_HOST
if (proxy = options[:proxy])
proxy.stubs("open").returns(socket)
else
Socket.stubs(:tcp).with(host, options[:port] || TEST_PORT, nil, nil, { connect_timeout: options[:timeout] }).returns(socket)
end
Net::SSH::Transport::ServerVersion.stubs(:new).returns(server_version)
Net::SSH::Transport::Algorithms.stubs(:new).returns(algorithms)
Net::SSH::Transport::Session.new(host, options)
end
end
# a simple alias to make the tests more self-documenting. the bang
# version makes it look more like the session is being instantiated
alias session! session
end
end
|