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
|
require_relative '../common'
require 'net/ssh/authentication/session'
module Authentication
class TestSession < NetSSHTest
include Net::SSH::Transport::Constants
include Net::SSH::Authentication::Constants
def test_constructor_should_set_defaults
assert_equal %w[none publickey password keyboard-interactive], session.auth_methods
assert_equal session.auth_methods, session.allowed_auth_methods
end
def test_authenticate_should_continue_if_method_disallowed
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(SERVICE_ACCEPT)
end
Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").raises(Net::SSH::Authentication::DisallowedMethod)
Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(true)
Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
assert session.authenticate("next service", "username", "password")
end
def test_authenticate_should_raise_error_if_service_request_fails
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(255)
end
assert_raises(Net::SSH::Exception) { session.authenticate("next service", "username", "password") }
end
def test_authenticate_should_return_false_if_all_auth_methods_fail
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(SERVICE_ACCEPT)
end
Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
Net::SSH::Authentication::Methods::KeyboardInteractive.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
assert_equal false, session.authenticate("next service", "username", "password")
end
def test_next_message_should_silently_handle_USERAUTH_BANNER_packets
transport.return(USERAUTH_BANNER, :string, "Howdy, folks!")
transport.return(SERVICE_ACCEPT)
assert_equal SERVICE_ACCEPT, session.next_message.type
end
def test_next_message_should_understand_USERAUTH_FAILURE
transport.return(USERAUTH_FAILURE, :string, "a,b,c", :bool, false)
packet = session.next_message
assert_equal USERAUTH_FAILURE, packet.type
assert_equal %w[a b c], session.allowed_auth_methods
end
(60..79).each do |type|
define_method("test_next_message_should_return_packets_of_type_#{type}") do
transport.return(type)
assert_equal type, session.next_message.type
end
end
def test_next_message_should_understand_USERAUTH_SUCCESS
transport.return(USERAUTH_SUCCESS)
assert !transport.hints[:authenticated]
assert_equal USERAUTH_SUCCESS, session.next_message.type
assert transport.hints[:authenticated]
end
def test_next_message_should_raise_error_on_unrecognized_packet_types
transport.return(1)
assert_raises(Net::SSH::Exception) { session.next_message }
end
def test_expect_message_should_raise_exception_if_next_packet_is_not_expected_type
transport.return(SERVICE_ACCEPT)
assert_raises(Net::SSH::Exception) { session.expect_message(USERAUTH_BANNER) }
end
def test_expect_message_should_return_packet_if_next_packet_is_expected_type
transport.return(SERVICE_ACCEPT)
assert_equal SERVICE_ACCEPT, session.expect_message(SERVICE_ACCEPT).type
end
def test_uses_some_default_keys_if_none_are_provided
File.stubs(:file?).returns(false)
file_on_filesystem("~/.ssh/id_rsa", default_private_key)
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(SERVICE_ACCEPT)
end
transport.expect do |t, packet|
assert_none_request packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
transport.expect do |t, packet|
assert_public_key_request default_public_key, packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
session.authenticate("next service", "username")
end
def test_does_not_use_default_keys_if_keys_are_present_in_options
File.stubs(:file?).returns(false)
file_on_filesystem("~/.ssh/id_rsa", default_private_key)
file_on_filesystem("custom_rsa_id", custom_private_key)
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(SERVICE_ACCEPT)
end
transport.expect do |t, packet|
assert_none_request packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
transport.expect do |t, packet|
assert_public_key_request custom_public_key, packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
session(keys: "custom_rsa_id").authenticate("next service", "username")
end
def test_does_not_use_default_keys_if_key_data_are_present_in_options
File.stubs(:file?).returns(false)
file_on_filesystem("~/.ssh/id_rsa", default_private_key)
transport.expect do |t, packet|
assert_equal SERVICE_REQUEST, packet.type
assert_equal "ssh-userauth", packet.read_string
t.return(SERVICE_ACCEPT)
end
transport.expect do |t, packet|
assert_none_request packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
transport.expect do |t, packet|
assert_public_key_request custom_public_key, packet
t.return(USERAUTH_FAILURE, :string, "publickey")
end
session(key_data: custom_private_key).authenticate("next service", "username")
end
private
def session(options={})
@session ||= Net::SSH::Authentication::Session.new(transport(options), options)
end
def transport(options={})
@transport ||= MockTransport.new(options)
end
def assert_none_request(packet)
assert_equal "username", packet.read_string
assert_equal "next service", packet.read_string
assert_equal "none", packet.read_string
end
def assert_public_key_request(public_key, packet)
assert_equal "username", packet.read_string
assert_equal "next service", packet.read_string
assert_equal "publickey", packet.read_string
assert_equal false, packet.read_bool
assert_equal "ssh-rsa", packet.read_string
key_in_packet = Net::SSH::Buffer.new(packet.read_string).read_key
assert_equal public_key, key_in_packet.to_pem
end
def file_on_filesystem(name, contents)
path = File.expand_path(name)
File.stubs(:read).with(path).returns(contents)
File.stubs(:file?).with(path).returns(true)
File.stubs(:readable?).with(path).returns(true)
end
def custom_private_key
<<~EOF
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3id5gZ6bglJth
yli8JNaRxhsqKwwPlReEI/mplzz5IP6gWQ92LogXbdBXtHf9ZpA53BeLmtcNBEY0
Ygd7sPBhlHABS5D5///zltSSX2+L5GCEiC6dpfGsySjqymWF+SZ2PaqfZbkWLmCD
9u4ysueaHf7xbF6txGprNp69efttWxdy+vU5tno7HVxemMZQUalpShFrdAYKKXEo
cV7MtbkQjzubS14gaWGpWCXIl9uNKQeHpLKtre1Qn5Ft/zVpCHmhLQcYDuB1LAj9
7eoev4rIiOE2sfdkvKDlmFxvzq3myYH4o27WwAg9OZ5SBusn2zesKkRCBBEZ55rl
uVknOGHXAgMBAAECggEAZE0U2OxsNxkfXS6+lXswQ5PW7pF90towcsdSPgrniGIu
pKRnHbfKKbuaewOl+zZcpTIRL/rbgUKPtzrHSiJlC36aQyrvvJ/ZWV5ZJvC+vd19
nY/qob65NyrrkHwxRSjmiwGiR9/IaUXI+vUsMUqx5Ph1hawqhZ3sZlEAKR4LeDO8
M+OguG77jLaqj5/SNfi+GwyUDe85de4VfEG4S9HrMQk2Cp66rx0BqDnCLacyFQaI
R0VczMXTU52q0uETmgUr8G9A1SaRc5ZWKAfZwxJTvqdIImWC9E+CY7wm+mZD4FE6
iVzVC0ngcdEd596kTDdU2BPVMluWzLkfqIrTt/5CeQKBgQDzgRzCPNxFtai6RAIi
ekBSHqrDnrbeTaw32GVq5ACk1Zfk2I0svctz1iQ9qJ2SRINpygQhcyJKQ4r/LXi1
7Av9H/d6QV4T2AZzS4WcqBkxxRXFUfARtnKChzuCzNt9tNz4EZiv75RyQmztGZjV
i94+ZvCyqup5be4Svf4MBxin9QKBgQDA9P4nHzFWZakTMei78LGb/4Auc+r0rZp7
8xg8Z92tvrDeJjMdesdhiFrPP1qiSYHnQ81MSWpn6BycBsHZqitejQmYnYput/s4
qG+m7SrkN8WL6rijYsbB+U14VDjMlBlOgcEgjlSNU2oeS+68u+uVI/fgyXcXn4Jq
33TSWSgfGwKBgA2tRdE/G9wqfOShZ0FKfoxePpcoNfs8f5zPYbrkPYkEmjh3VU6b
Bm9mKrjv3JHXmU3608qRLe7f5lG42xvUu0OnZP4P59nTe2FEb6fB5VBfUn63wHUu
OzZLpDMPkJB59SNV0a6oFT1pr7aNhoEQDxaQL5rJcMwLOaEB3OAOEft1AoGASz7+
4Zi7b7rDPVYIMUpCqNfxT6wqovIUPWPmPqAuhXPIm0kAQ+2+VN2MtCc7m+/Ydawu
IiK7GPweNAY6kDxZH00WweolstmSYVzl9Y2lXUwWgGKvUB/T7I7g1Bzb7YOPftsA
ykZW2Kn/xwLLfdQ2oXleT82g4Jh2jmDHuMPF7qMCgYEA6QF45PvOgnrJessgmwO/
dEmkLl07PQYJPGZLaZteuWrvfMrn+AiW5aAdHzhzNaOtNy5B3T7zGUHtgxXegqgd
/QdCVCJgnZUO/zdAxkr22dDn+WEXkL4wgBVStQvvnQp9C2NJcoOExvex5PLzKWQg
WEKt5v3QsUEgVrzkM4K9UbI=
-----END PRIVATE KEY-----
EOF
end
def custom_public_key
<<~EOF
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt4neYGem4JSbYcpYvCTW
kcYbKisMD5UXhCP5qZc8+SD+oFkPdi6IF23QV7R3/WaQOdwXi5rXDQRGNGIHe7Dw
YZRwAUuQ+f//85bUkl9vi+RghIgunaXxrMko6splhfkmdj2qn2W5Fi5gg/buMrLn
mh3+8WxercRqazaevXn7bVsXcvr1ObZ6Ox1cXpjGUFGpaUoRa3QGCilxKHFezLW5
EI87m0teIGlhqVglyJfbjSkHh6Syra3tUJ+Rbf81aQh5oS0HGA7gdSwI/e3qHr+K
yIjhNrH3ZLyg5Zhcb86t5smB+KNu1sAIPTmeUgbrJ9s3rCpEQgQRGeea5blZJzhh
1wIDAQAB
-----END PUBLIC KEY-----
EOF
end
def default_private_key
<<~EOF
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxbz0rp+Z7MklMtSkfiRfceOeTOhOgkGqonCL1B0MRzSjA3yf
onvEobQNYv7uyQ+ZMGT9RL7AlUSUxeWF00A/O6kuwfs4JlPS/FMPy/B2V0UmoteT
p40LmclZHpKZs9yKmgkfa5j8Jjvd/VvV1r/DbkHjZetIe07pSnP3EOAG7sjyV7yr
HPvkgG5h/Vn2U19vTsvYIENcj5OCLF7eUSJZ/6m4qem+wZ4/9cau5E2t57oS8bTd
5k00Jn0E+qRVionLVLtHXKnr0nWlGPinL+UhKBMhLA6Olm5Y8W77sYcUSvlJMy4G
mpIvnWFKQE5vim4zKt3dBF256QPmRCWPTQ+sxwIDAQABAoIBAQCG+vrILVjEo3ZK
IY/8L9Ybh3arJzVYg3z4j/1TmVSlUtAodC0AnJ5Yh/FPb5kPFR/MQlQFVnVeL8ei
45Ab6dKAZnftoRDuUPBIoGa7H3WZEzJRnPlFOen+W80DKq3TcqwGhE23hGIzs1BR
QBxUEOlWXZHeI+OBkRd9ZHX2RgdVfhGK1eCRGkxVUx6lygK7RcLSDJgPGvTUL+Gz
xF4D03pDo2r6oghNk3Fbw4GMXMBLfrKfiee/QyBLEkq+nykVioxXO16ShJfxOzM4
Pt6/7XJW7uMBGSblS89svrsn7i+29wcgkX4rGWyswV6xicpLNBEmkYx119QKLGBk
a1QebsYxAoGBAOJL/KEGyO8z9l+b2xzWlAURCNJEqPgeAk6ck7woA+nnj2KH+/51
1vvbQlvdwN1eP753g3eACvro7XQmVpJzqwBqAGyxtgPoI4F+HR++3lIOA+zfZs3p
1R1/4AEN0E16sV54gVmvkoSm9UCUDM4RXDdC/YgjpVyXla7HFp2KSrTvAoGBAN+x
VzK/7hCFod5KZXq/Nfy4/Wg/1GTwzg8eQCUbRJ3jqk0UWvNnhwWTEHfa8ywDSJFi
bNMlTKtdlGKPHB9dGMt9izGoyeybz0RJz8aCLODN9PBr1GSAhWfqFNYgksScEOy9
c7eEn25Q91tanmni38Y0KZU9iOYAcKJR5Xulw3WpAoGAO/3lBVNlJXTjFcmdtvFz
4Dv52LR3Dv/1oJ2F1NXO482Nh5OBTJ401iP0XaJWJNl9kKLiaWW6g3YIrUgUn1Km
vL9dSXN7S2HZN9UVJ3tUOPCaPcuj12bsJpvl6KGe3UtvhhnwQLR45U3Vqr8U/fRA
PC44REUe64MMHX+OEUm+MGUCgYB/4UgyURruAxdIl0twYsOgWLk10dfAZRHH/sk4
7V/Ky45eRlbAc9zyyOJPQrJl5PKlepkwFFDCXtsnhRzUqUo1eu4KU64sP9678V6A
44Z4dgWjNGHVmsupXl7PEwwUrgvW62+t6HmkfVELvsB1VCgNjWCAWw9aPcImaZ9B
ksAtEQKBgQCs2ZwTMQOX0IyBMRxVDD8JLCYMNZPBisGTjtYQv6F1ITZOzAPwJjI8
3FzbcqCWtmbCe6rCd9p9NDU42cuizlSZfO+2emM5CnKdvb0IeHqODfBZm2vYYJ6p
Euy/YLiXxrwHUo1KecuH04+/s6OxEzMnrYxXqvcK9SwcNTwAkDaBUw==
-----END RSA PRIVATE KEY-----
EOF
end
def default_public_key
<<~EOF
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxbz0rp+Z7MklMtSkfiRf
ceOeTOhOgkGqonCL1B0MRzSjA3yfonvEobQNYv7uyQ+ZMGT9RL7AlUSUxeWF00A/
O6kuwfs4JlPS/FMPy/B2V0UmoteTp40LmclZHpKZs9yKmgkfa5j8Jjvd/VvV1r/D
bkHjZetIe07pSnP3EOAG7sjyV7yrHPvkgG5h/Vn2U19vTsvYIENcj5OCLF7eUSJZ
/6m4qem+wZ4/9cau5E2t57oS8bTd5k00Jn0E+qRVionLVLtHXKnr0nWlGPinL+Uh
KBMhLA6Olm5Y8W77sYcUSvlJMy4GmpIvnWFKQE5vim4zKt3dBF256QPmRCWPTQ+s
xwIDAQAB
-----END PUBLIC KEY-----
EOF
end
end
end
|