PageRenderTime 35ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/boomhammer/vendor/gems/ruby-openid-2.1.4/test/test_server.rb

http://strd6.googlecode.com/
Ruby | 2457 lines | 1972 code | 322 blank | 163 comment | 26 complexity | 1c5d2f9de227427638e28300cb536adc MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, GPL-3.0
  1. require 'openid/server'
  2. require 'openid/cryptutil'
  3. require 'openid/association'
  4. require 'openid/util'
  5. require 'openid/message'
  6. require 'openid/store/memory'
  7. require 'openid/dh'
  8. require 'openid/consumer/associationmanager'
  9. require 'util'
  10. require "testutil"
  11. require 'test/unit'
  12. require 'uri'
  13. # In general, if you edit or add tests here, try to move in the
  14. # direction of testing smaller units. For testing the external
  15. # interfaces, we'll be developing an implementation-agnostic testing
  16. # suite.
  17. # for more, see /etc/ssh/moduli
  18. module OpenID
  19. ALT_MODULUS = 0xCAADDDEC1667FC68B5FA15D53C4E1532DD24561A1A2D47A12C01ABEA1E00731F6921AAC40742311FDF9E634BB7131BEE1AF240261554389A910425E044E88C8359B010F5AD2B80E29CB1A5B027B19D9E01A6F63A6F45E5D7ED2FF6A2A0085050A7D0CF307C3DB51D2490355907B4427C23A98DF1EB8ABEF2BA209BB7AFFE86A7
  20. ALT_GEN = 5
  21. class CatchLogs
  22. def catchlogs_setup
  23. @old_logger = Util.logger
  24. Util.logger = self.method('got_log_message')
  25. @messages = []
  26. end
  27. def got_log_message(message)
  28. @messages << message
  29. end
  30. def teardown
  31. Util.logger = @old_logger
  32. end
  33. end
  34. class TestProtocolError < Test::Unit::TestCase
  35. def test_browserWithReturnTo
  36. return_to = "http://rp.unittest/consumer"
  37. # will be a ProtocolError raised by Decode or
  38. # CheckIDRequest.answer
  39. args = Message.from_post_args({
  40. 'openid.mode' => 'monkeydance',
  41. 'openid.identity' => 'http://wagu.unittest/',
  42. 'openid.return_to' => return_to,
  43. })
  44. e = Server::ProtocolError.new(args, "plucky")
  45. assert(e.has_return_to)
  46. expected_args = {
  47. 'openid.mode' => 'error',
  48. 'openid.error' => 'plucky',
  49. }
  50. rt_base, result_args = e.encode_to_url.split('?', 2)
  51. result_args = Util.parse_query(result_args)
  52. assert_equal(result_args, expected_args)
  53. end
  54. def test_browserWithReturnTo_OpenID2_GET
  55. return_to = "http://rp.unittest/consumer"
  56. # will be a ProtocolError raised by Decode or
  57. # CheckIDRequest.answer
  58. args = Message.from_post_args({
  59. 'openid.ns' => OPENID2_NS,
  60. 'openid.mode' => 'monkeydance',
  61. 'openid.identity' => 'http://wagu.unittest/',
  62. 'openid.claimed_id' => 'http://wagu.unittest/',
  63. 'openid.return_to' => return_to,
  64. })
  65. e = Server::ProtocolError.new(args, "plucky")
  66. assert(e.has_return_to)
  67. expected_args = {
  68. 'openid.ns' => OPENID2_NS,
  69. 'openid.mode' => 'error',
  70. 'openid.error' => 'plucky',
  71. }
  72. rt_base, result_args = e.encode_to_url.split('?', 2)
  73. result_args = Util.parse_query(result_args)
  74. assert_equal(result_args, expected_args)
  75. end
  76. def test_browserWithReturnTo_OpenID2_POST
  77. return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
  78. # will be a ProtocolError raised by Decode or
  79. # CheckIDRequest.answer
  80. args = Message.from_post_args({
  81. 'openid.ns' => OPENID2_NS,
  82. 'openid.mode' => 'monkeydance',
  83. 'openid.identity' => 'http://wagu.unittest/',
  84. 'openid.claimed_id' => 'http://wagu.unittest/',
  85. 'openid.return_to' => return_to,
  86. })
  87. e = Server::ProtocolError.new(args, "plucky")
  88. assert(e.has_return_to)
  89. expected_args = {
  90. 'openid.ns' => OPENID2_NS,
  91. 'openid.mode' => 'error',
  92. 'openid.error' => 'plucky',
  93. }
  94. assert(e.which_encoding == Server::ENCODE_HTML_FORM)
  95. assert(e.to_form_markup == e.to_message.to_form_markup(
  96. args.get_arg(OPENID_NS, 'return_to')))
  97. end
  98. def test_browserWithReturnTo_OpenID1_exceeds_limit
  99. return_to = "http://rp.unittest/consumer" + ('x' * OPENID1_URL_LIMIT)
  100. # will be a ProtocolError raised by Decode or
  101. # CheckIDRequest.answer
  102. args = Message.from_post_args({
  103. 'openid.mode' => 'monkeydance',
  104. 'openid.identity' => 'http://wagu.unittest/',
  105. 'openid.return_to' => return_to,
  106. })
  107. e = Server::ProtocolError.new(args, "plucky")
  108. assert(e.has_return_to)
  109. expected_args = {
  110. 'openid.mode' => 'error',
  111. 'openid.error' => 'plucky',
  112. }
  113. assert(e.which_encoding == Server::ENCODE_URL)
  114. rt_base, result_args = e.encode_to_url.split('?', 2)
  115. result_args = Util.parse_query(result_args)
  116. assert_equal(result_args, expected_args)
  117. end
  118. def test_noReturnTo
  119. # will be a ProtocolError raised by Decode or
  120. # CheckIDRequest.answer
  121. args = Message.from_post_args({
  122. 'openid.mode' => 'zebradance',
  123. 'openid.identity' => 'http://wagu.unittest/',
  124. })
  125. e = Server::ProtocolError.new(args, "waffles")
  126. assert(!e.has_return_to)
  127. expected = "error:waffles\nmode:error\n"
  128. assert_equal(e.encode_to_kvform, expected)
  129. end
  130. def test_no_message
  131. e = Server::ProtocolError.new(nil, "no message")
  132. assert(e.get_return_to.nil?)
  133. assert_equal(e.which_encoding, nil)
  134. end
  135. def test_which_encoding_no_message
  136. e = Server::ProtocolError.new(nil, "no message")
  137. assert(e.which_encoding.nil?)
  138. end
  139. end
  140. class TestDecode < Test::Unit::TestCase
  141. def setup
  142. @claimed_id = 'http://de.legating.de.coder.unittest/'
  143. @id_url = "http://decoder.am.unittest/"
  144. @rt_url = "http://rp.unittest/foobot/?qux=zam"
  145. @tr_url = "http://rp.unittest/"
  146. @assoc_handle = "{assoc}{handle}"
  147. @op_endpoint = 'http://endpoint.unittest/encode'
  148. @store = Store::Memory.new()
  149. @server = Server::Server.new(@store, @op_endpoint)
  150. @decode = Server::Decoder.new(@server).method('decode')
  151. end
  152. def test_none
  153. args = {}
  154. r = @decode.call(args)
  155. assert_equal(r, nil)
  156. end
  157. def test_irrelevant
  158. args = {
  159. 'pony' => 'spotted',
  160. 'sreg.mutant_power' => 'decaffinator',
  161. }
  162. assert_raise(Server::ProtocolError) {
  163. @decode.call(args)
  164. }
  165. end
  166. def test_bad
  167. args = {
  168. 'openid.mode' => 'twos-compliment',
  169. 'openid.pants' => 'zippered',
  170. }
  171. assert_raise(Server::ProtocolError) {
  172. @decode.call(args)
  173. }
  174. end
  175. def test_dictOfLists
  176. args = {
  177. 'openid.mode' => ['checkid_setup'],
  178. 'openid.identity' => @id_url,
  179. 'openid.assoc_handle' => @assoc_handle,
  180. 'openid.return_to' => @rt_url,
  181. 'openid.trust_root' => @tr_url,
  182. }
  183. begin
  184. result = @decode.call(args)
  185. rescue ArgumentError => err
  186. assert(!err.to_s.index('values').nil?, err)
  187. else
  188. flunk("Expected ArgumentError, but got result #{result}")
  189. end
  190. end
  191. def test_checkidImmediate
  192. args = {
  193. 'openid.mode' => 'checkid_immediate',
  194. 'openid.identity' => @id_url,
  195. 'openid.assoc_handle' => @assoc_handle,
  196. 'openid.return_to' => @rt_url,
  197. 'openid.trust_root' => @tr_url,
  198. # should be ignored
  199. 'openid.some.extension' => 'junk',
  200. }
  201. r = @decode.call(args)
  202. assert(r.is_a?(Server::CheckIDRequest))
  203. assert_equal(r.mode, "checkid_immediate")
  204. assert_equal(r.immediate, true)
  205. assert_equal(r.identity, @id_url)
  206. assert_equal(r.trust_root, @tr_url)
  207. assert_equal(r.return_to, @rt_url)
  208. assert_equal(r.assoc_handle, @assoc_handle)
  209. end
  210. def test_checkidImmediate_constructor
  211. r = Server::CheckIDRequest.new(@id_url, @rt_url, nil,
  212. @rt_url, true, @assoc_handle)
  213. assert(r.mode == 'checkid_immediate')
  214. assert(r.immediate)
  215. end
  216. def test_checkid_missing_return_to_and_trust_root
  217. args = {
  218. 'openid.ns' => OPENID2_NS,
  219. 'openid.mode' => 'checkid_setup',
  220. 'openid.identity' => @id_url,
  221. 'openid.claimed_id' => @id_url,
  222. 'openid.assoc_handle' => @assoc_handle,
  223. }
  224. assert_raise(Server::ProtocolError) {
  225. m = Message.from_post_args(args)
  226. Server::CheckIDRequest.from_message(m, @op_endpoint)
  227. }
  228. end
  229. def test_checkid_id_select
  230. args = {
  231. 'openid.ns' => OPENID2_NS,
  232. 'openid.mode' => 'checkid_setup',
  233. 'openid.identity' => IDENTIFIER_SELECT,
  234. 'openid.claimed_id' => IDENTIFIER_SELECT,
  235. 'openid.assoc_handle' => @assoc_handle,
  236. 'openid.return_to' => @rt_url,
  237. 'openid.realm' => @tr_url,
  238. }
  239. m = Message.from_post_args(args)
  240. req = Server::CheckIDRequest.from_message(m, @op_endpoint)
  241. assert(req.id_select)
  242. end
  243. def test_checkid_not_id_select
  244. args = {
  245. 'openid.ns' => OPENID2_NS,
  246. 'openid.mode' => 'checkid_setup',
  247. 'openid.assoc_handle' => @assoc_handle,
  248. 'openid.return_to' => @rt_url,
  249. 'openid.realm' => @tr_url,
  250. }
  251. id_args = [
  252. {'openid.claimed_id' => IDENTIFIER_SELECT,
  253. 'openid.identity' => 'http://bogus.com/'},
  254. {'openid.claimed_id' => 'http://bogus.com/',
  255. 'openid.identity' => 'http://bogus.com/'},
  256. ]
  257. id_args.each { |id|
  258. m = Message.from_post_args(args.merge(id))
  259. req = Server::CheckIDRequest.from_message(m, @op_endpoint)
  260. assert(!req.id_select)
  261. }
  262. end
  263. def test_checkidSetup
  264. args = {
  265. 'openid.mode' => 'checkid_setup',
  266. 'openid.identity' => @id_url,
  267. 'openid.assoc_handle' => @assoc_handle,
  268. 'openid.return_to' => @rt_url,
  269. 'openid.trust_root' => @tr_url,
  270. }
  271. r = @decode.call(args)
  272. assert(r.is_a?(Server::CheckIDRequest))
  273. assert_equal(r.mode, "checkid_setup")
  274. assert_equal(r.immediate, false)
  275. assert_equal(r.identity, @id_url)
  276. assert_equal(r.trust_root, @tr_url)
  277. assert_equal(r.return_to, @rt_url)
  278. end
  279. def test_checkidSetupOpenID2
  280. args = {
  281. 'openid.ns' => OPENID2_NS,
  282. 'openid.mode' => 'checkid_setup',
  283. 'openid.identity' => @id_url,
  284. 'openid.claimed_id' => @claimed_id,
  285. 'openid.assoc_handle' => @assoc_handle,
  286. 'openid.return_to' => @rt_url,
  287. 'openid.realm' => @tr_url,
  288. }
  289. r = @decode.call(args)
  290. assert(r.is_a?(Server::CheckIDRequest))
  291. assert_equal(r.mode, "checkid_setup")
  292. assert_equal(r.immediate, false)
  293. assert_equal(r.identity, @id_url)
  294. assert_equal(r.claimed_id, @claimed_id)
  295. assert_equal(r.trust_root, @tr_url)
  296. assert_equal(r.return_to, @rt_url)
  297. end
  298. def test_checkidSetupNoClaimedIDOpenID2
  299. args = {
  300. 'openid.ns' => OPENID2_NS,
  301. 'openid.mode' => 'checkid_setup',
  302. 'openid.identity' => @id_url,
  303. 'openid.assoc_handle' => @assoc_handle,
  304. 'openid.return_to' => @rt_url,
  305. 'openid.realm' => @tr_url,
  306. }
  307. assert_raise(Server::ProtocolError) {
  308. @decode.call(args)
  309. }
  310. end
  311. def test_checkidSetupNoIdentityOpenID2
  312. args = {
  313. 'openid.ns' => OPENID2_NS,
  314. 'openid.mode' => 'checkid_setup',
  315. 'openid.assoc_handle' => @assoc_handle,
  316. 'openid.return_to' => @rt_url,
  317. 'openid.realm' => @tr_url,
  318. }
  319. r = @decode.call(args)
  320. assert(r.is_a?(Server::CheckIDRequest))
  321. assert_equal(r.mode, "checkid_setup")
  322. assert_equal(r.immediate, false)
  323. assert_equal(r.identity, nil)
  324. assert_equal(r.trust_root, @tr_url)
  325. assert_equal(r.return_to, @rt_url)
  326. end
  327. def test_checkidSetupNoReturnOpenID1
  328. # Make sure an OpenID 1 request cannot be decoded if it lacks a
  329. # return_to.
  330. args = {
  331. 'openid.mode' => 'checkid_setup',
  332. 'openid.identity' => @id_url,
  333. 'openid.assoc_handle' => @assoc_handle,
  334. 'openid.trust_root' => @tr_url,
  335. }
  336. assert_raise(Server::ProtocolError) {
  337. @decode.call(args)
  338. }
  339. end
  340. def test_checkidSetupNoReturnOpenID2
  341. # Make sure an OpenID 2 request with no return_to can be decoded,
  342. # and make sure a response to such a request raises
  343. # NoReturnToError.
  344. args = {
  345. 'openid.ns' => OPENID2_NS,
  346. 'openid.mode' => 'checkid_setup',
  347. 'openid.identity' => @id_url,
  348. 'openid.claimed_id' => @id_url,
  349. 'openid.assoc_handle' => @assoc_handle,
  350. 'openid.realm' => @tr_url,
  351. }
  352. req = @decode.call(args)
  353. assert(req.is_a?(Server::CheckIDRequest))
  354. assert_raise(Server::NoReturnToError) {
  355. req.answer(false)
  356. }
  357. assert_raise(Server::NoReturnToError) {
  358. req.encode_to_url('bogus')
  359. }
  360. assert_raise(Server::NoReturnToError) {
  361. req.cancel_url
  362. }
  363. end
  364. def test_checkidSetupRealmRequiredOpenID2
  365. # Make sure that an OpenID 2 request which lacks return_to cannot
  366. # be decoded if it lacks a realm. Spec => This value
  367. # (openid.realm) MUST be sent if openid.return_to is omitted.
  368. args = {
  369. 'openid.ns' => OPENID2_NS,
  370. 'openid.mode' => 'checkid_setup',
  371. 'openid.identity' => @id_url,
  372. 'openid.assoc_handle' => @assoc_handle,
  373. }
  374. assert_raise(Server::ProtocolError) {
  375. @decode.call(args)
  376. }
  377. end
  378. def test_checkidSetupBadReturn
  379. args = {
  380. 'openid.mode' => 'checkid_setup',
  381. 'openid.identity' => @id_url,
  382. 'openid.assoc_handle' => @assoc_handle,
  383. 'openid.return_to' => 'not a url',
  384. }
  385. begin
  386. result = @decode.call(args)
  387. rescue Server::ProtocolError => err
  388. assert(err.openid_message)
  389. else
  390. flunk("Expected ProtocolError, instead returned with #{result}")
  391. end
  392. end
  393. def test_checkidSetupUntrustedReturn
  394. args = {
  395. 'openid.mode' => 'checkid_setup',
  396. 'openid.identity' => @id_url,
  397. 'openid.assoc_handle' => @assoc_handle,
  398. 'openid.return_to' => @rt_url,
  399. 'openid.trust_root' => 'http://not-the-return-place.unittest/',
  400. }
  401. begin
  402. result = @decode.call(args)
  403. rescue Server::UntrustedReturnURL => err
  404. assert(err.openid_message, err.to_s)
  405. else
  406. flunk("Expected UntrustedReturnURL, instead returned with #{result}")
  407. end
  408. end
  409. def test_checkidSetupUntrustedReturn_Constructor
  410. assert_raise(Server::UntrustedReturnURL) {
  411. Server::CheckIDRequest.new(@id_url, @rt_url, nil,
  412. 'http://not-the-return-place.unittest/',
  413. false, @assoc_handle)
  414. }
  415. end
  416. def test_checkidSetupMalformedReturnURL_Constructor
  417. assert_raise(Server::MalformedReturnURL) {
  418. Server::CheckIDRequest.new(@id_url, 'bogus://return.url', nil,
  419. 'http://trustroot.com/',
  420. false, @assoc_handle)
  421. }
  422. end
  423. def test_checkAuth
  424. args = {
  425. 'openid.mode' => 'check_authentication',
  426. 'openid.assoc_handle' => '{dumb}{handle}',
  427. 'openid.sig' => 'sigblob',
  428. 'openid.signed' => 'identity,return_to,response_nonce,mode',
  429. 'openid.identity' => 'signedval1',
  430. 'openid.return_to' => 'signedval2',
  431. 'openid.response_nonce' => 'signedval3',
  432. 'openid.baz' => 'unsigned',
  433. }
  434. r = @decode.call(args)
  435. assert(r.is_a?(Server::CheckAuthRequest))
  436. assert_equal(r.mode, 'check_authentication')
  437. assert_equal(r.sig, 'sigblob')
  438. end
  439. def test_checkAuthMissingSignature
  440. args = {
  441. 'openid.mode' => 'check_authentication',
  442. 'openid.assoc_handle' => '{dumb}{handle}',
  443. 'openid.signed' => 'foo,bar,mode',
  444. 'openid.foo' => 'signedval1',
  445. 'openid.bar' => 'signedval2',
  446. 'openid.baz' => 'unsigned',
  447. }
  448. assert_raise(Server::ProtocolError) {
  449. @decode.call(args)
  450. }
  451. end
  452. def test_checkAuthAndInvalidate
  453. args = {
  454. 'openid.mode' => 'check_authentication',
  455. 'openid.assoc_handle' => '{dumb}{handle}',
  456. 'openid.invalidate_handle' => '[[SMART_handle]]',
  457. 'openid.sig' => 'sigblob',
  458. 'openid.signed' => 'identity,return_to,response_nonce,mode',
  459. 'openid.identity' => 'signedval1',
  460. 'openid.return_to' => 'signedval2',
  461. 'openid.response_nonce' => 'signedval3',
  462. 'openid.baz' => 'unsigned',
  463. }
  464. r = @decode.call(args)
  465. assert(r.is_a?(Server::CheckAuthRequest))
  466. assert_equal(r.invalidate_handle, '[[SMART_handle]]')
  467. end
  468. def test_associateDH
  469. args = {
  470. 'openid.mode' => 'associate',
  471. 'openid.session_type' => 'DH-SHA1',
  472. 'openid.dh_consumer_public' => "Rzup9265tw==",
  473. }
  474. r = @decode.call(args)
  475. assert(r.is_a?(Server::AssociateRequest))
  476. assert_equal(r.mode, "associate")
  477. assert_equal(r.session.session_type, "DH-SHA1")
  478. assert_equal(r.assoc_type, "HMAC-SHA1")
  479. assert(r.session.consumer_pubkey)
  480. end
  481. def test_associateDHMissingKey
  482. # Trying DH assoc w/o public key
  483. args = {
  484. 'openid.mode' => 'associate',
  485. 'openid.session_type' => 'DH-SHA1',
  486. }
  487. # Using DH-SHA1 without supplying dh_consumer_public is an error.
  488. assert_raise(Server::ProtocolError) {
  489. @decode.call(args)
  490. }
  491. end
  492. def test_associateDHpubKeyNotB64
  493. args = {
  494. 'openid.mode' => 'associate',
  495. 'openid.session_type' => 'DH-SHA1',
  496. 'openid.dh_consumer_public' => "donkeydonkeydonkey",
  497. }
  498. assert_raise(Server::ProtocolError) {
  499. @decode.call(args)
  500. }
  501. end
  502. def test_associateDHModGen
  503. # test dh with non-default but valid values for dh_modulus and
  504. # dh_gen
  505. args = {
  506. 'openid.mode' => 'associate',
  507. 'openid.session_type' => 'DH-SHA1',
  508. 'openid.dh_consumer_public' => "Rzup9265tw==",
  509. 'openid.dh_modulus' => CryptUtil.num_to_base64(ALT_MODULUS),
  510. 'openid.dh_gen' => CryptUtil.num_to_base64(ALT_GEN) ,
  511. }
  512. r = @decode.call(args)
  513. assert(r.is_a?(Server::AssociateRequest))
  514. assert_equal(r.mode, "associate")
  515. assert_equal(r.session.session_type, "DH-SHA1")
  516. assert_equal(r.assoc_type, "HMAC-SHA1")
  517. assert_equal(r.session.dh.modulus, ALT_MODULUS)
  518. assert_equal(r.session.dh.generator, ALT_GEN)
  519. assert(r.session.consumer_pubkey)
  520. end
  521. def test_associateDHCorruptModGen
  522. # test dh with non-default but valid values for dh_modulus and
  523. # dh_gen
  524. args = {
  525. 'openid.mode' => 'associate',
  526. 'openid.session_type' => 'DH-SHA1',
  527. 'openid.dh_consumer_public' => "Rzup9265tw==",
  528. 'openid.dh_modulus' => 'pizza',
  529. 'openid.dh_gen' => 'gnocchi',
  530. }
  531. assert_raise(Server::ProtocolError) {
  532. @decode.call(args)
  533. }
  534. end
  535. def test_associateDHMissingGen
  536. args = {
  537. 'openid.mode' => 'associate',
  538. 'openid.session_type' => 'DH-SHA1',
  539. 'openid.dh_consumer_public' => "Rzup9265tw==",
  540. 'openid.dh_modulus' => 'pizza',
  541. }
  542. assert_raise(Server::ProtocolError) {
  543. @decode.call(args)
  544. }
  545. end
  546. def test_associateDHMissingMod
  547. args = {
  548. 'openid.mode' => 'associate',
  549. 'openid.session_type' => 'DH-SHA1',
  550. 'openid.dh_consumer_public' => "Rzup9265tw==",
  551. 'openid.dh_gen' => 'pizza',
  552. }
  553. assert_raise(Server::ProtocolError) {
  554. @decode.call(args)
  555. }
  556. end
  557. # def test_associateDHInvalidModGen(self):
  558. # # test dh with properly encoded values that are not a valid
  559. # # modulus/generator combination.
  560. # args = {
  561. # 'openid.mode': 'associate',
  562. # 'openid.session_type': 'DH-SHA1',
  563. # 'openid.dh_consumer_public': "Rzup9265tw==",
  564. # 'openid.dh_modulus': cryptutil.longToBase64(9),
  565. # 'openid.dh_gen': cryptutil.longToBase64(27) ,
  566. # }
  567. # self.failUnlessRaises(server.ProtocolError, self.decode, args)
  568. # test_associateDHInvalidModGen.todo = "low-priority feature"
  569. def test_associateWeirdSession
  570. args = {
  571. 'openid.mode' => 'associate',
  572. 'openid.session_type' => 'FLCL6',
  573. 'openid.dh_consumer_public' => "YQ==\n",
  574. }
  575. assert_raise(Server::ProtocolError) {
  576. @decode.call(args)
  577. }
  578. end
  579. def test_associatePlain
  580. args = {
  581. 'openid.mode' => 'associate',
  582. }
  583. r = @decode.call(args)
  584. assert(r.is_a?(Server::AssociateRequest))
  585. assert_equal(r.mode, "associate")
  586. assert_equal(r.session.session_type, "no-encryption")
  587. assert_equal(r.assoc_type, "HMAC-SHA1")
  588. end
  589. def test_nomode
  590. args = {
  591. 'openid.session_type' => 'DH-SHA1',
  592. 'openid.dh_consumer_public' => "my public keeey",
  593. }
  594. assert_raise(Server::ProtocolError) {
  595. @decode.call(args)
  596. }
  597. end
  598. def test_invalidns
  599. args = {'openid.ns' => 'Vegetables',
  600. 'openid.mode' => 'associate'}
  601. begin
  602. r = @decode.call(args)
  603. rescue Server::ProtocolError => err
  604. assert(err.openid_message)
  605. assert(err.to_s.index('Vegetables'))
  606. end
  607. end
  608. end
  609. class BogusEncoder < Server::Encoder
  610. def encode(response)
  611. return "BOGUS"
  612. end
  613. end
  614. class BogusDecoder < Server::Decoder
  615. def decode(query)
  616. return "BOGUS"
  617. end
  618. end
  619. class TestEncode < Test::Unit::TestCase
  620. def setup
  621. @encoder = Server::Encoder.new
  622. @encode = @encoder.method('encode')
  623. @op_endpoint = 'http://endpoint.unittest/encode'
  624. @store = Store::Memory.new
  625. @server = Server::Server.new(@store, @op_endpoint)
  626. end
  627. def test_id_res_OpenID2_GET
  628. # Check that when an OpenID 2 response does not exceed the OpenID
  629. # 1 message size, a GET response (i.e., redirect) is issued.
  630. request = Server::CheckIDRequest.new(
  631. 'http://bombom.unittest/',
  632. 'http://burr.unittest/999',
  633. @server.op_endpoint,
  634. 'http://burr.unittest/',
  635. false,
  636. nil)
  637. request.message = Message.new(OPENID2_NS)
  638. response = Server::OpenIDResponse.new(request)
  639. response.fields = Message.from_openid_args({
  640. 'ns' => OPENID2_NS,
  641. 'mode' => 'id_res',
  642. 'identity' => request.identity,
  643. 'claimed_id' => request.identity,
  644. 'return_to' => request.return_to,
  645. })
  646. assert(!response.render_as_form)
  647. assert(response.which_encoding == Server::ENCODE_URL)
  648. webresponse = @encode.call(response)
  649. assert(webresponse.headers.member?('location'))
  650. end
  651. def test_id_res_OpenID2_POST
  652. # Check that when an OpenID 2 response exceeds the OpenID 1
  653. # message size, a POST response (i.e., an HTML form) is returned.
  654. request = Server::CheckIDRequest.new(
  655. 'http://bombom.unittest/',
  656. 'http://burr.unittest/999',
  657. @server.op_endpoint,
  658. 'http://burr.unittest/',
  659. false,
  660. nil)
  661. request.message = Message.new(OPENID2_NS)
  662. response = Server::OpenIDResponse.new(request)
  663. response.fields = Message.from_openid_args({
  664. 'ns' => OPENID2_NS,
  665. 'mode' => 'id_res',
  666. 'identity' => request.identity,
  667. 'claimed_id' => request.identity,
  668. 'return_to' => 'x' * OPENID1_URL_LIMIT,
  669. })
  670. assert(response.render_as_form)
  671. assert(response.encode_to_url.length > OPENID1_URL_LIMIT)
  672. assert(response.which_encoding == Server::ENCODE_HTML_FORM)
  673. webresponse = @encode.call(response)
  674. assert_equal(webresponse.body, response.to_form_markup)
  675. end
  676. def test_to_form_markup
  677. request = Server::CheckIDRequest.new(
  678. 'http://bombom.unittest/',
  679. 'http://burr.unittest/999',
  680. @server.op_endpoint,
  681. 'http://burr.unittest/',
  682. false,
  683. nil)
  684. request.message = Message.new(OPENID2_NS)
  685. response = Server::OpenIDResponse.new(request)
  686. response.fields = Message.from_openid_args({
  687. 'ns' => OPENID2_NS,
  688. 'mode' => 'id_res',
  689. 'identity' => request.identity,
  690. 'claimed_id' => request.identity,
  691. 'return_to' => 'x' * OPENID1_URL_LIMIT,
  692. })
  693. form_markup = response.to_form_markup({'foo'=>'bar'})
  694. assert(/ foo="bar"/ =~ form_markup, form_markup)
  695. end
  696. def test_to_html
  697. request = Server::CheckIDRequest.new(
  698. 'http://bombom.unittest/',
  699. 'http://burr.unittest/999',
  700. @server.op_endpoint,
  701. 'http://burr.unittest/',
  702. false,
  703. nil)
  704. request.message = Message.new(OPENID2_NS)
  705. response = Server::OpenIDResponse.new(request)
  706. response.fields = Message.from_openid_args({
  707. 'ns' => OPENID2_NS,
  708. 'mode' => 'id_res',
  709. 'identity' => request.identity,
  710. 'claimed_id' => request.identity,
  711. 'return_to' => 'x' * OPENID1_URL_LIMIT,
  712. })
  713. html = response.to_html
  714. assert(html)
  715. end
  716. def test_id_res_OpenID1_exceeds_limit
  717. # Check that when an OpenID 1 response exceeds the OpenID 1
  718. # message size, a GET response is issued. Technically, this
  719. # shouldn't be permitted by the library, but this test is in place
  720. # to preserve the status quo for OpenID 1.
  721. request = Server::CheckIDRequest.new(
  722. 'http://bombom.unittest/',
  723. 'http://burr.unittest/999',
  724. @server.op_endpoint,
  725. 'http://burr.unittest/',
  726. false,
  727. nil)
  728. request.message = Message.new(OPENID1_NS)
  729. response = Server::OpenIDResponse.new(request)
  730. response.fields = Message.from_openid_args({
  731. 'mode' => 'id_res',
  732. 'identity' => request.identity,
  733. 'return_to' => 'x' * OPENID1_URL_LIMIT,
  734. })
  735. assert(!response.render_as_form)
  736. assert(response.encode_to_url.length > OPENID1_URL_LIMIT)
  737. assert(response.which_encoding == Server::ENCODE_URL)
  738. webresponse = @encode.call(response)
  739. assert_equal(webresponse.headers['location'], response.encode_to_url)
  740. end
  741. def test_id_res
  742. request = Server::CheckIDRequest.new(
  743. 'http://bombom.unittest/',
  744. 'http://burr.unittest/999',
  745. @server.op_endpoint,
  746. 'http://burr.unittest/',
  747. false, nil)
  748. request.message = Message.new(OPENID1_NS)
  749. response = Server::OpenIDResponse.new(request)
  750. response.fields = Message.from_openid_args({
  751. 'mode' => 'id_res',
  752. 'identity' => request.identity,
  753. 'return_to' => request.return_to,
  754. })
  755. webresponse = @encode.call(response)
  756. assert_equal(webresponse.code, Server::HTTP_REDIRECT)
  757. assert(webresponse.headers.member?('location'))
  758. location = webresponse.headers['location']
  759. assert(location.starts_with?(request.return_to),
  760. sprintf("%s does not start with %s",
  761. location, request.return_to))
  762. # argh.
  763. q2 = Util.parse_query(URI::parse(location).query)
  764. expected = response.fields.to_post_args
  765. assert_equal(q2, expected)
  766. end
  767. def test_cancel
  768. request = Server::CheckIDRequest.new(
  769. 'http://bombom.unittest/',
  770. 'http://burr.unittest/999',
  771. @server.op_endpoint,
  772. 'http://burr.unittest/',
  773. false, nil)
  774. request.message = Message.new(OPENID2_NS)
  775. response = Server::OpenIDResponse.new(request)
  776. response.fields = Message.from_openid_args({
  777. 'mode' => 'cancel',
  778. })
  779. webresponse = @encode.call(response)
  780. assert_equal(webresponse.code, Server::HTTP_REDIRECT)
  781. assert(webresponse.headers.member?('location'))
  782. end
  783. def test_cancel_to_form
  784. request = Server::CheckIDRequest.new(
  785. 'http://bombom.unittest/',
  786. 'http://burr.unittest/999',
  787. @server.op_endpoint,
  788. 'http://burr.unittest/',
  789. false, nil)
  790. request.message = Message.new(OPENID2_NS)
  791. response = Server::OpenIDResponse.new(request)
  792. response.fields = Message.from_openid_args({
  793. 'mode' => 'cancel',
  794. })
  795. form = response.to_form_markup
  796. assert(form.index(request.return_to))
  797. end
  798. def test_assocReply
  799. msg = Message.new(OPENID2_NS)
  800. msg.set_arg(OPENID2_NS, 'session_type', 'no-encryption')
  801. request = Server::AssociateRequest.from_message(msg)
  802. response = Server::OpenIDResponse.new(request)
  803. response.fields = Message.from_post_args(
  804. {'openid.assoc_handle' => "every-zig"})
  805. webresponse = @encode.call(response)
  806. body = "assoc_handle:every-zig\n"
  807. assert_equal(webresponse.code, Server::HTTP_OK)
  808. assert_equal(webresponse.headers, {})
  809. assert_equal(webresponse.body, body)
  810. end
  811. def test_checkauthReply
  812. request = Server::CheckAuthRequest.new('a_sock_monkey',
  813. 'siggggg',
  814. [])
  815. request.message = Message.new(OPENID2_NS)
  816. response = Server::OpenIDResponse.new(request)
  817. response.fields = Message.from_openid_args({
  818. 'is_valid' => 'true',
  819. 'invalidate_handle' => 'xXxX:xXXx'
  820. })
  821. body = "invalidate_handle:xXxX:xXXx\nis_valid:true\n"
  822. webresponse = @encode.call(response)
  823. assert_equal(webresponse.code, Server::HTTP_OK)
  824. assert_equal(webresponse.headers, {})
  825. assert_equal(webresponse.body, body)
  826. end
  827. def test_unencodableError
  828. args = Message.from_post_args({
  829. 'openid.identity' => 'http://limu.unittest/',
  830. })
  831. e = Server::ProtocolError.new(args, "wet paint")
  832. assert_raise(Server::EncodingError) {
  833. @encode.call(e)
  834. }
  835. end
  836. def test_encodableError
  837. args = Message.from_post_args({
  838. 'openid.mode' => 'associate',
  839. 'openid.identity' => 'http://limu.unittest/',
  840. })
  841. body="error:snoot\nmode:error\n"
  842. webresponse = @encode.call(Server::ProtocolError.new(args, "snoot"))
  843. assert_equal(webresponse.code, Server::HTTP_ERROR)
  844. assert_equal(webresponse.headers, {})
  845. assert_equal(webresponse.body, body)
  846. end
  847. end
  848. class TestSigningEncode < Test::Unit::TestCase
  849. def setup
  850. @_dumb_key = Server::Signatory._dumb_key
  851. @_normal_key = Server::Signatory._normal_key
  852. @store = Store::Memory.new()
  853. @server = Server::Server.new(@store, "http://signing.unittest/enc")
  854. @request = Server::CheckIDRequest.new(
  855. 'http://bombom.unittest/',
  856. 'http://burr.unittest/999',
  857. @server.op_endpoint,
  858. 'http://burr.unittest/',
  859. false, nil)
  860. @request.message = Message.new(OPENID2_NS)
  861. @response = Server::OpenIDResponse.new(@request)
  862. @response.fields = Message.from_openid_args({
  863. 'mode' => 'id_res',
  864. 'identity' => @request.identity,
  865. 'return_to' => @request.return_to,
  866. })
  867. @signatory = Server::Signatory.new(@store)
  868. @encoder = Server::SigningEncoder.new(@signatory)
  869. @encode = @encoder.method('encode')
  870. end
  871. def test_idres
  872. assoc_handle = '{bicycle}{shed}'
  873. @store.store_association(
  874. @_normal_key,
  875. Association.from_expires_in(60, assoc_handle,
  876. 'sekrit', 'HMAC-SHA1'))
  877. @request.assoc_handle = assoc_handle
  878. webresponse = @encode.call(@response)
  879. assert_equal(webresponse.code, Server::HTTP_REDIRECT)
  880. assert(webresponse.headers.member?('location'))
  881. location = webresponse.headers['location']
  882. query = Util.parse_query(URI::parse(location).query)
  883. assert(query.member?('openid.sig'))
  884. assert(query.member?('openid.assoc_handle'))
  885. assert(query.member?('openid.signed'))
  886. end
  887. def test_idresDumb
  888. webresponse = @encode.call(@response)
  889. assert_equal(webresponse.code, Server::HTTP_REDIRECT)
  890. assert(webresponse.headers.has_key?('location'))
  891. location = webresponse.headers['location']
  892. query = Util.parse_query(URI::parse(location).query)
  893. assert(query.member?('openid.sig'))
  894. assert(query.member?('openid.assoc_handle'))
  895. assert(query.member?('openid.signed'))
  896. end
  897. def test_forgotStore
  898. @encoder.signatory = nil
  899. assert_raise(ArgumentError) {
  900. @encode.call(@response)
  901. }
  902. end
  903. def test_cancel
  904. request = Server::CheckIDRequest.new(
  905. 'http://bombom.unittest/',
  906. 'http://burr.unittest/999',
  907. @server.op_endpoint,
  908. 'http://burr.unittest/',
  909. false, nil)
  910. request.message = Message.new(OPENID2_NS)
  911. response = Server::OpenIDResponse.new(request)
  912. response.fields.set_arg(OPENID_NS, 'mode', 'cancel')
  913. webresponse = @encode.call(response)
  914. assert_equal(webresponse.code, Server::HTTP_REDIRECT)
  915. assert(webresponse.headers.has_key?('location'))
  916. location = webresponse.headers['location']
  917. query = Util.parse_query(URI::parse(location).query)
  918. assert(!query.has_key?('openid.sig'), response.fields.to_post_args())
  919. end
  920. def test_assocReply
  921. msg = Message.new(OPENID2_NS)
  922. msg.set_arg(OPENID2_NS, 'session_type', 'no-encryption')
  923. request = Server::AssociateRequest.from_message(msg)
  924. response = Server::OpenIDResponse.new(request)
  925. response.fields = Message.from_openid_args({'assoc_handle' => "every-zig"})
  926. webresponse = @encode.call(response)
  927. body = "assoc_handle:every-zig\n"
  928. assert_equal(webresponse.code, Server::HTTP_OK)
  929. assert_equal(webresponse.headers, {})
  930. assert_equal(webresponse.body, body)
  931. end
  932. def test_alreadySigned
  933. @response.fields.set_arg(OPENID_NS, 'sig', 'priorSig==')
  934. assert_raise(Server::AlreadySigned) {
  935. @encode.call(@response)
  936. }
  937. end
  938. end
  939. class TestCheckID < Test::Unit::TestCase
  940. def setup
  941. @op_endpoint = 'http://endpoint.unittest/'
  942. @store = Store::Memory.new()
  943. @server = Server::Server.new(@store, @op_endpoint)
  944. @request = Server::CheckIDRequest.new(
  945. 'http://bambam.unittest/',
  946. 'http://bar.unittest/999',
  947. @server.op_endpoint,
  948. 'http://bar.unittest/',
  949. false)
  950. @request.message = Message.new(OPENID2_NS)
  951. end
  952. def test_trustRootInvalid
  953. @request.trust_root = "http://foo.unittest/17"
  954. @request.return_to = "http://foo.unittest/39"
  955. assert(!@request.trust_root_valid())
  956. end
  957. def test_trustRootInvalid_modified
  958. @request.trust_root = "does://not.parse/"
  959. @request.message = :sentinel
  960. begin
  961. result = @request.trust_root_valid
  962. rescue Server::MalformedTrustRoot => why
  963. assert_equal(:sentinel, why.openid_message)
  964. else
  965. flunk("Expected MalformedTrustRoot, got #{result.inspect}")
  966. end
  967. end
  968. def test_trustRootvalid_absent_trust_root
  969. @request.trust_root = nil
  970. assert(@request.trust_root_valid())
  971. end
  972. def test_trustRootValid
  973. @request.trust_root = "http://foo.unittest/"
  974. @request.return_to = "http://foo.unittest/39"
  975. assert(@request.trust_root_valid())
  976. end
  977. def test_trustRootValidNoReturnTo
  978. request = Server::CheckIDRequest.new(
  979. 'http://bambam.unittest/',
  980. nil,
  981. @server.op_endpoint,
  982. 'http://bar.unittest/',
  983. false)
  984. assert(request.trust_root_valid())
  985. end
  986. def test_returnToVerified_callsVerify
  987. # Make sure that verifyReturnTo is calling the trustroot
  988. # function verifyReturnTo
  989. # Ensure that exceptions are passed through
  990. sentinel = Exception.new()
  991. __req = @request
  992. tc = self
  993. vrfyExc = Proc.new { |trust_root, return_to|
  994. tc.assert_equal(__req.trust_root, trust_root)
  995. tc.assert_equal(__req.return_to, return_to)
  996. raise sentinel
  997. }
  998. TrustRoot.extend(OverrideMethodMixin)
  999. TrustRoot.with_method_overridden(:verify_return_to, vrfyExc) do
  1000. begin
  1001. @request.return_to_verified()
  1002. flunk("Expected sentinel to be raised, got success")
  1003. rescue Exception => e
  1004. assert(e.equal?(sentinel), [e, sentinel].inspect)
  1005. end
  1006. end
  1007. # Ensure that True and False are passed through unchanged
  1008. constVerify = Proc.new { |val|
  1009. verify = Proc.new { |trust_root, return_to|
  1010. tc.assert_equal(__req.trust_root, trust_root)
  1011. tc.assert_equal(__req.request.return_to, return_to)
  1012. return val
  1013. }
  1014. return verify
  1015. }
  1016. [true, false].each { |val|
  1017. verifier = constVerify.call(val)
  1018. TrustRoot.with_method_overridden(:verify_return_to, verifier) do
  1019. assert_equal(val, @request.return_to_verified())
  1020. end
  1021. }
  1022. end
  1023. def _expectAnswer(answer, identity=nil, claimed_id=nil)
  1024. expected_list = [
  1025. ['mode', 'id_res'],
  1026. ['return_to', @request.return_to],
  1027. ['op_endpoint', @op_endpoint],
  1028. ]
  1029. if identity
  1030. expected_list << ['identity', identity]
  1031. if claimed_id
  1032. expected_list << ['claimed_id', claimed_id]
  1033. else
  1034. expected_list << ['claimed_id', identity]
  1035. end
  1036. end
  1037. expected_list.each { |k, expected|
  1038. actual = answer.fields.get_arg(OPENID_NS, k)
  1039. assert_equal(expected, actual,
  1040. sprintf("%s: expected %s, got %s",
  1041. k, expected, actual))
  1042. }
  1043. assert(answer.fields.has_key?(OPENID_NS, 'response_nonce'))
  1044. assert(answer.fields.get_openid_namespace() == OPENID2_NS)
  1045. # One for nonce, one for ns
  1046. assert_equal(answer.fields.to_post_args.length,
  1047. expected_list.length + 2,
  1048. answer.fields.to_post_args.inspect)
  1049. end
  1050. def test_answerAllow
  1051. # Check the fields specified by "Positive Assertions"
  1052. #
  1053. # including mode=id_res, identity, claimed_id, op_endpoint,
  1054. # return_to
  1055. answer = @request.answer(true)
  1056. assert_equal(answer.request, @request)
  1057. _expectAnswer(answer, @request.identity)
  1058. end
  1059. def test_answerAllowDelegatedIdentity
  1060. @request.claimed_id = 'http://delegating.unittest/'
  1061. answer = @request.answer(true)
  1062. _expectAnswer(answer, @request.identity,
  1063. @request.claimed_id)
  1064. end
  1065. def test_answerAllowWithoutIdentityReally
  1066. @request.identity = nil
  1067. answer = @request.answer(true)
  1068. assert_equal(answer.request, @request)
  1069. _expectAnswer(answer)
  1070. end
  1071. def test_answerAllowAnonymousFail
  1072. @request.identity = nil
  1073. # XXX - Check on this, I think this behavior is legal in OpenID
  1074. # 2.0?
  1075. assert_raise(ArgumentError) {
  1076. @request.answer(true, nil, "=V")
  1077. }
  1078. end
  1079. def test_answerAllowWithIdentity
  1080. @request.identity = IDENTIFIER_SELECT
  1081. selected_id = 'http://anon.unittest/9861'
  1082. answer = @request.answer(true, nil, selected_id)
  1083. _expectAnswer(answer, selected_id)
  1084. end
  1085. def test_answerAllowWithNoIdentity
  1086. @request.identity = IDENTIFIER_SELECT
  1087. selected_id = 'http://anon.unittest/9861'
  1088. assert_raise(ArgumentError) {
  1089. answer = @request.answer(true, nil, nil)
  1090. }
  1091. end
  1092. def test_immediate_openid1_no_identity
  1093. @request.message = Message.new(OPENID1_NS)
  1094. @request.immediate = true
  1095. @request.mode = 'checkid_immediate'
  1096. resp = @request.answer(false)
  1097. assert(resp.fields.get_arg(OPENID_NS, 'mode') == 'id_res')
  1098. end
  1099. def test_checkid_setup_openid1_no_identity
  1100. @request.message = Message.new(OPENID1_NS)
  1101. @request.immediate = false
  1102. @request.mode = 'checkid_setup'
  1103. resp = @request.answer(false)
  1104. assert(resp.fields.get_arg(OPENID_NS, 'mode') == 'cancel')
  1105. end
  1106. def test_immediate_openid1_no_server_url
  1107. @request.message = Message.new(OPENID1_NS)
  1108. @request.immediate = true
  1109. @request.mode = 'checkid_immediate'
  1110. @request.op_endpoint = nil
  1111. assert_raise(ArgumentError) {
  1112. resp = @request.answer(false)
  1113. }
  1114. end
  1115. def test_immediate_encode_to_url
  1116. @request.message = Message.new(OPENID1_NS)
  1117. @request.immediate = true
  1118. @request.mode = 'checkid_immediate'
  1119. @request.trust_root = "BOGUS"
  1120. @request.assoc_handle = "ASSOC"
  1121. server_url = "http://server.com/server"
  1122. url = @request.encode_to_url(server_url)
  1123. assert(url.starts_with?(server_url))
  1124. unused, query = url.split("?", 2)
  1125. args = Util.parse_query(query)
  1126. m = Message.from_post_args(args)
  1127. assert(m.get_arg(OPENID_NS, 'trust_root') == "BOGUS")
  1128. assert(m.get_arg(OPENID_NS, 'assoc_handle') == "ASSOC")
  1129. assert(m.get_arg(OPENID_NS, 'mode'), "checkid_immediate")
  1130. assert(m.get_arg(OPENID_NS, 'identity') == @request.identity)
  1131. assert(m.get_arg(OPENID_NS, 'claimed_id') == @request.claimed_id)
  1132. assert(m.get_arg(OPENID_NS, 'return_to') == @request.return_to)
  1133. end
  1134. def test_answerAllowWithDelegatedIdentityOpenID2
  1135. # Answer an IDENTIFIER_SELECT case with a delegated identifier.
  1136. # claimed_id delegates to selected_id here.
  1137. @request.identity = IDENTIFIER_SELECT
  1138. selected_id = 'http://anon.unittest/9861'
  1139. claimed_id = 'http://monkeyhat.unittest/'
  1140. answer = @request.answer(true, nil, selected_id, claimed_id)
  1141. _expectAnswer(answer, selected_id, claimed_id)
  1142. end
  1143. def test_answerAllowWithDelegatedIdentityOpenID1
  1144. # claimed_id parameter doesn't exist in OpenID 1.
  1145. @request.message = Message.new(OPENID1_NS)
  1146. # claimed_id delegates to selected_id here.
  1147. @request.identity = IDENTIFIER_SELECT
  1148. selected_id = 'http://anon.unittest/9861'
  1149. claimed_id = 'http://monkeyhat.unittest/'
  1150. assert_raise(Server::VersionError) {
  1151. @request.answer(true, nil, selected_id, claimed_id)
  1152. }
  1153. end
  1154. def test_answerAllowWithAnotherIdentity
  1155. # XXX - Check on this, I think this behavior is legal in OpenID
  1156. # 2.0?
  1157. assert_raise(ArgumentError){
  1158. @request.answer(true, nil, "http://pebbles.unittest/")
  1159. }
  1160. end
  1161. def test_answerAllowNoIdentityOpenID1
  1162. @request.message = Message.new(OPENID1_NS)
  1163. @request.identity = nil
  1164. assert_raise(ArgumentError) {
  1165. @request.answer(true, nil, nil)
  1166. }
  1167. end
  1168. def test_answerAllowForgotEndpoint
  1169. @request.op_endpoint = nil
  1170. assert_raise(RuntimeError) {
  1171. @request.answer(true)
  1172. }
  1173. end
  1174. def test_checkIDWithNoIdentityOpenID1
  1175. msg = Message.new(OPENID1_NS)
  1176. msg.set_arg(OPENID_NS, 'return_to', 'bogus')
  1177. msg.set_arg(OPENID_NS, 'trust_root', 'bogus')
  1178. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1179. msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
  1180. assert_raise(Server::ProtocolError) {
  1181. Server::CheckIDRequest.from_message(msg, @server)
  1182. }
  1183. end
  1184. def test_fromMessageClaimedIDWithoutIdentityOpenID2
  1185. msg = Message.new(OPENID2_NS)
  1186. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1187. msg.set_arg(OPENID_NS, 'return_to', 'http://invalid:8000/rt')
  1188. msg.set_arg(OPENID_NS, 'claimed_id', 'https://example.myopenid.com')
  1189. assert_raise(Server::ProtocolError) {
  1190. Server::CheckIDRequest.from_message(msg, @server)
  1191. }
  1192. end
  1193. def test_fromMessageIdentityWithoutClaimedIDOpenID2
  1194. msg = Message.new(OPENID2_NS)
  1195. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1196. msg.set_arg(OPENID_NS, 'return_to', 'http://invalid:8000/rt')
  1197. msg.set_arg(OPENID_NS, 'identity', 'https://example.myopenid.com')
  1198. assert_raise(Server::ProtocolError) {
  1199. Server::CheckIDRequest.from_message(msg, @server)
  1200. }
  1201. end
  1202. def test_fromMessageWithEmptyTrustRoot
  1203. return_to = 'http://some.url/foo?bar=baz'
  1204. msg = Message.from_post_args({
  1205. 'openid.assoc_handle' => '{blah}{blah}{OZivdQ==}',
  1206. 'openid.claimed_id' => 'http://delegated.invalid/',
  1207. 'openid.identity' => 'http://op-local.example.com/',
  1208. 'openid.mode' => 'checkid_setup',
  1209. 'openid.ns' => 'http://openid.net/signon/1.0',
  1210. 'openid.return_to' => return_to,
  1211. 'openid.trust_root' => ''
  1212. });
  1213. result = Server::CheckIDRequest.from_message(msg, @server)
  1214. assert_equal(return_to, result.trust_root)
  1215. end
  1216. def test_trustRootOpenID1
  1217. # Ignore openid.realm in OpenID 1
  1218. msg = Message.new(OPENID1_NS)
  1219. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1220. msg.set_arg(OPENID_NS, 'trust_root', 'http://trustroot.com/')
  1221. msg.set_arg(OPENID_NS, 'realm', 'http://fake_trust_root/')
  1222. msg.set_arg(OPENID_NS, 'return_to', 'http://trustroot.com/foo')
  1223. msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
  1224. msg.set_arg(OPENID_NS, 'identity', 'george')
  1225. result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
  1226. assert(result.trust_root == 'http://trustroot.com/')
  1227. end
  1228. def test_trustRootOpenID2
  1229. # Ignore openid.trust_root in OpenID 2
  1230. msg = Message.new(OPENID2_NS)
  1231. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1232. msg.set_arg(OPENID_NS, 'realm', 'http://trustroot.com/')
  1233. msg.set_arg(OPENID_NS, 'trust_root', 'http://fake_trust_root/')
  1234. msg.set_arg(OPENID_NS, 'return_to', 'http://trustroot.com/foo')
  1235. msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
  1236. msg.set_arg(OPENID_NS, 'identity', 'george')
  1237. msg.set_arg(OPENID_NS, 'claimed_id', 'george')
  1238. result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
  1239. assert(result.trust_root == 'http://trustroot.com/')
  1240. end
  1241. def test_answerAllowNoTrustRoot
  1242. @request.trust_root = nil
  1243. answer = @request.answer(true)
  1244. assert_equal(answer.request, @request)
  1245. _expectAnswer(answer, @request.identity)
  1246. end
  1247. def test_answerImmediateDenyOpenID2
  1248. # Look for mode=setup_needed in checkid_immediate negative
  1249. # response in OpenID 2 case.
  1250. #
  1251. # See specification Responding to Authentication Requests /
  1252. # Negative Assertions / In Response to Immediate Requests.
  1253. @request.mode = 'checkid_immediate'
  1254. @request.immediate = true
  1255. server_url = "http://setup-url.unittest/"
  1256. # crappiting setup_url, you dirty my interface with your presence!
  1257. answer = @request.answer(false, server_url)
  1258. assert_equal(answer.request, @request)
  1259. assert_equal(answer.fields.to_post_args.length, 3, answer.fields)
  1260. assert_equal(answer.fields.get_openid_namespace, OPENID2_NS)
  1261. assert_equal(answer.fields.get_arg(OPENID_NS, 'mode'),
  1262. 'setup_needed')
  1263. # user_setup_url no longer required.
  1264. end
  1265. def test_answerImmediateDenyOpenID1
  1266. # Look for user_setup_url in checkid_immediate negative response
  1267. # in OpenID 1 case.
  1268. @request.message = Message.new(OPENID1_NS)
  1269. @request.mode = 'checkid_immediate'
  1270. @request.immediate = true
  1271. @request.claimed_id = 'http://claimed-id.test/'
  1272. server_url = "http://setup-url.unittest/"
  1273. # crappiting setup_url, you dirty my interface with your presence!
  1274. answer = @request.answer(false, server_url)
  1275. assert_equal(answer.request, @request)
  1276. assert_equal(2, answer.fields.to_post_args.length, answer.fields)
  1277. assert_equal(OPENID1_NS, answer.fields.get_openid_namespace)
  1278. assert_equal('id_res', answer.fields.get_arg(OPENID_NS, 'mode'))
  1279. usu = answer.fields.get_arg(OPENID_NS, 'user_setup_url', '')
  1280. assert(usu.starts_with?(server_url))
  1281. expected_substr = 'openid.claimed_id=http%3A%2F%2Fclaimed-id.test%2F'
  1282. assert(!usu.index(expected_substr).nil?, usu)
  1283. end
  1284. def test_answerSetupDeny
  1285. answer = @request.answer(false)
  1286. assert_equal(answer.fields.get_args(OPENID_NS), {
  1287. 'mode' => 'cancel',
  1288. })
  1289. end
  1290. def test_encodeToURL
  1291. server_url = 'http://openid-server.unittest/'
  1292. result = @request.encode_to_url(server_url)
  1293. # How to check? How about a round-trip test.
  1294. base, result_args = result.split('?', 2)
  1295. result_args = Util.parse_query(result_args)
  1296. message = Message.from_post_args(result_args)
  1297. rebuilt_request = Server::CheckIDRequest.from_message(message,
  1298. @server.op_endpoint)
  1299. @request.message = message
  1300. @request.instance_variables.each { |var|
  1301. assert_equal(@request.instance_variable_get(var),
  1302. rebuilt_request.instance_variable_get(var), var)
  1303. }
  1304. end
  1305. def test_getCancelURL
  1306. url = @request.cancel_url
  1307. rt, query_string = url.split('?', -1)
  1308. assert_equal(@request.return_to, rt)
  1309. query = Util.parse_query(query_string)
  1310. assert_equal(query, {'openid.mode' => 'cancel',
  1311. 'openid.ns' => OPENID2_NS})
  1312. end
  1313. def test_getCancelURLimmed
  1314. @request.mode = 'checkid_immediate'
  1315. @request.immediate = true
  1316. assert_raise(ArgumentError) {
  1317. @request.cancel_url
  1318. }
  1319. end
  1320. def test_fromMessageWithoutTrustRoot
  1321. msg = Message.new(OPENID2_NS)
  1322. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1323. msg.set_arg(OPENID_NS, 'return_to', 'http://real.trust.root/foo')
  1324. msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
  1325. msg.set_arg(OPENID_NS, 'identity', 'george')
  1326. msg.set_arg(OPENID_NS, 'claimed_id', 'george')
  1327. result = Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
  1328. assert_equal(result.trust_root, 'http://real.trust.root/foo')
  1329. end
  1330. def test_fromMessageWithoutTrustRootOrReturnTo
  1331. msg = Message.new(OPENID2_NS)
  1332. msg.set_arg(OPENID_NS, 'mode', 'checkid_setup')
  1333. msg.set_arg(OPENID_NS, 'assoc_handle', 'bogus')
  1334. msg.set_arg(OPENID_NS, 'identity', 'george')
  1335. msg.set_arg(OPENID_NS, 'claimed_id', 'george')
  1336. assert_raises(Server::ProtocolError) {
  1337. Server::CheckIDRequest.from_message(msg, @server.op_endpoint)
  1338. }
  1339. end
  1340. end
  1341. class TestCheckIDExtension < Test::Unit::TestCase
  1342. def setup
  1343. @op_endpoint = 'http://endpoint.unittest/ext'
  1344. @store = Store::Memory.new()
  1345. @server = Server::Server.new(@store, @op_endpoint)
  1346. @request = Server::CheckIDRequest.new(
  1347. 'http://bambam.unittest/',
  1348. 'http://bar.unittest/999',
  1349. @server.op_endpoint,
  1350. 'http://bar.unittest/',
  1351. false)
  1352. @request.message = Message.new(OPENID2_NS)
  1353. @response = Server::OpenIDResponse.new(@request)
  1354. @response.fields.set_arg(OPENID_NS, 'mode', 'id_res')
  1355. @response.fields.set_arg(OPENID_NS, 'blue', 'star')
  1356. end
  1357. def test_addField
  1358. namespace = 'something:'
  1359. @response.fields.set_arg(namespace, 'bright', 'potato')
  1360. assert_equal(@response.fields.get_args(OPENID_NS),
  1361. {'blue' => 'star',
  1362. 'mode' => 'id_res',
  1363. })
  1364. assert_equal(@response.fields.get_args(namespace),
  1365. {'bright' => 'potato'})
  1366. end
  1367. def test_addFields
  1368. namespace = 'mi5:'
  1369. args = {'tangy' => 'suspenders',
  1370. 'bravo' => 'inclusion'}
  1371. @response.fields.update_args(namespace, args)
  1372. assert_equal(@response.fields.get_args(OPENID_NS),
  1373. {'blue' => 'star',
  1374. 'mode' => 'id_res',
  1375. })
  1376. assert_equal(@response.fields.get_args(namespace), args)
  1377. end
  1378. end
  1379. class MockSignatory
  1380. attr_accessor :isValid, :assocs
  1381. def initialize(assoc)
  1382. @isValid = true
  1383. @assocs = [assoc]
  1384. end
  1385. def verify(assoc_handle, message)
  1386. Util.assert(message.has_key?(OPENID_NS, "sig"))
  1387. if self.assocs.member?([true, assoc_handle])
  1388. return @isValid
  1389. else
  1390. return false
  1391. end
  1392. end
  1393. def get_association(assoc_handle, dumb)
  1394. if self.assocs.member?([dumb, assoc_handle])
  1395. # This isn't a valid implementation for many uses of this
  1396. # function, mind you.
  1397. return true
  1398. else
  1399. return nil
  1400. end
  1401. end
  1402. def invalidate(assoc_handle, dumb)
  1403. if self.assocs.member?([dumb, assoc_handle])
  1404. @assocs.delete([dumb, assoc_handle])
  1405. end
  1406. end
  1407. end
  1408. class TestCheckAuth < Test::Unit::TestCase
  1409. def setup
  1410. @assoc_handle = 'mooooooooo'
  1411. @message = Message.from_post_args({
  1412. 'openid.sig' => 'signarture',
  1413. 'one' => 'alpha',
  1414. 'two' => 'beta',
  1415. })
  1416. @request = Server::CheckAuthRequest.new(
  1417. @assoc_handle, @message)
  1418. @request.message = Message.new(OPENID2_NS)
  1419. @signatory = MockSignatory.new([true, @assoc_handle])
  1420. end
  1421. def test_to_s
  1422. @request.to_s
  1423. end
  1424. def test_valid
  1425. r = @request.answer(@signatory)
  1426. assert_equal({'is_valid' => 'true'},
  1427. r.fields.get_args(OPENID_NS))
  1428. assert_equal(r.request, @request)
  1429. end
  1430. def test_invalid
  1431. @signatory.isValid = false
  1432. r = @request.answer(@signatory)
  1433. assert_equal({'is_valid' => 'false'},
  1434. r.fields.get_args(OPENID_NS))
  1435. end
  1436. def test_replay
  1437. # Don't validate the same response twice.
  1438. #
  1439. # From "Checking the Nonce"::
  1440. #
  1441. # When using "check_authentication", the OP MUST ensure that an
  1442. # assertion has not yet been accepted with the same value for
  1443. # "openid.response_nonce".
  1444. #
  1445. # In this implementation, the assoc_handle is only valid once.
  1446. # And nonces are a signed component of the message, so they can't
  1447. # be used with another handle without breaking the sig.
  1448. r = @request.answer(@signatory)
  1449. r = @request.answer(@signatory)
  1450. assert_equal({'is_valid' => 'false'},
  1451. r.fields.get_args(OPENID_NS))
  1452. end
  1453. def test_invalidatehandle
  1454. @request.invalidate_handle = "bogusHandle"
  1455. r = @request.answer(@signatory)
  1456. assert_equal(r.fields.get_args(OPENID_NS),
  1457. {'is_valid' => 'true',
  1458. 'invalidate_handle' => "bogusHandle"})
  1459. assert_equal(r.request, @request)
  1460. end
  1461. def test_invalidatehandleNo
  1462. assoc_handle = 'goodhandle'
  1463. @signatory.assocs << [false, 'goodhandle']
  1464. @request.invalidate_handle = assoc_handle
  1465. r = @request.answer(@signatory)
  1466. assert_equal(r.fields.get_args(OPENID_NS), {'is_valid' => 'true'})
  1467. end
  1468. end
  1469. class TestAssociate < Test::Unit::TestCase
  1470. # TODO: test DH with non-default values for modulus and gen.
  1471. # (important to do because we actually had it broken for a while.)
  1472. def setup
  1473. @request = Server::AssociateRequest.from_message(Message.from_post_args({}))
  1474. @store = Store::Memory.new()
  1475. @signatory = Server::Signatory.new(@store)
  1476. end
  1477. def test_dhSHA1
  1478. @assoc = @signatory.create_association(false, 'HMAC-SHA1')
  1479. consumer_dh = DiffieHellman.from_defaults()
  1480. cpub = consumer_dh.public
  1481. server_dh = DiffieHellman.from_defaults()
  1482. session = Server::DiffieHellmanSHA1ServerSession.new(server_dh, cpub)
  1483. @request = Server::AssociateRequest.new(session, 'HMAC-SHA1')
  1484. @request.message = Message.new(OPENID2_NS)
  1485. response = @request.answer(@assoc)
  1486. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1487. assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
  1488. assert_equal(rfg.call("assoc_handle"), @assoc.handle)
  1489. assert(!rfg.call("mac_key"))
  1490. assert_equal(rfg.call("session_type"), "DH-SHA1")
  1491. assert(rfg.call("enc_mac_key"))
  1492. assert(rfg.call("dh_server_public"))
  1493. enc_key = Util.from_base64(rfg.call("enc_mac_key"))
  1494. spub = CryptUtil.base64_to_num(rfg.call("dh_server_public"))
  1495. secret = consumer_dh.xor_secret(CryptUtil.method('sha1'),
  1496. spub, enc_key)
  1497. assert_equal(secret, @assoc.secret)
  1498. end
  1499. def test_dhSHA256
  1500. @assoc = @signatory.create_association(false, 'HMAC-SHA256')
  1501. consumer_dh = DiffieHellman.from_defaults()
  1502. cpub = consumer_dh.public
  1503. server_dh = DiffieHellman.from_defaults()
  1504. session = Server::DiffieHellmanSHA256ServerSession.new(server_dh, cpub)
  1505. @request = Server::AssociateRequest.new(session, 'HMAC-SHA256')
  1506. @request.message = Message.new(OPENID2_NS)
  1507. response = @request.answer(@assoc)
  1508. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1509. assert_equal(rfg.call("assoc_type"), "HMAC-SHA256")
  1510. assert_equal(rfg.call("assoc_handle"), @assoc.handle)
  1511. assert(!rfg.call("mac_key"))
  1512. assert_equal(rfg.call("session_type"), "DH-SHA256")
  1513. assert(rfg.call("enc_mac_key"))
  1514. assert(rfg.call("dh_server_public"))
  1515. enc_key = Util.from_base64(rfg.call("enc_mac_key"))
  1516. spub = CryptUtil.base64_to_num(rfg.call("dh_server_public"))
  1517. secret = consumer_dh.xor_secret(CryptUtil.method('sha256'),
  1518. spub, enc_key)
  1519. assert_equal(secret, @assoc.secret)
  1520. end
  1521. def test_protoError256
  1522. s256_session = Consumer::DiffieHellmanSHA256Session.new()
  1523. invalid_s256 = {'openid.assoc_type' => 'HMAC-SHA1',
  1524. 'openid.session_type' => 'DH-SHA256',}
  1525. invalid_s256.merge!(s256_session.get_request())
  1526. invalid_s256_2 = {'openid.assoc_type' => 'MONKEY-PIRATE',
  1527. 'openid.session_type' => 'DH-SHA256',}
  1528. invalid_s256_2.merge!(s256_session.get_request())
  1529. bad_request_argss = [
  1530. invalid_s256,
  1531. invalid_s256_2,
  1532. ]
  1533. bad_request_argss.each { |request_args|
  1534. message = Message.from_post_args(request_args)
  1535. assert_raise(Server::ProtocolError) {
  1536. Server::AssociateRequest.from_message(message)
  1537. }
  1538. }
  1539. end
  1540. def test_protoError
  1541. s1_session = Consumer::DiffieHellmanSHA1Session.new()
  1542. invalid_s1 = {'openid.assoc_type' => 'HMAC-SHA256',
  1543. 'openid.session_type' => 'DH-SHA1',}
  1544. invalid_s1.merge!(s1_session.get_request())
  1545. invalid_s1_2 = {'openid.assoc_type' => 'ROBOT-NINJA',
  1546. 'openid.session_type' => 'DH-SHA1',}
  1547. invalid_s1_2.merge!(s1_session.get_request())
  1548. bad_request_argss = [
  1549. {'openid.assoc_type' => 'Wha?'},
  1550. invalid_s1,
  1551. invalid_s1_2,
  1552. ]
  1553. bad_request_argss.each { |request_args|
  1554. message = Message.from_post_args(request_args)
  1555. assert_raise(Server::ProtocolError) {
  1556. Server::AssociateRequest.from_message(message)
  1557. }
  1558. }
  1559. end
  1560. def test_protoErrorFields
  1561. contact = 'user@example.invalid'
  1562. reference = 'Trac ticket number MAX_INT'
  1563. error = 'poltergeist'
  1564. openid1_args = {
  1565. 'openid.identitiy' => 'invalid',
  1566. 'openid.mode' => 'checkid_setup',
  1567. }
  1568. openid2_args = openid1_args.dup
  1569. openid2_args.merge!({'openid.ns' => OPENID2_NS})
  1570. # Check presence of optional fields in both protocol versions
  1571. openid1_msg = Message.from_post_args(openid1_args)
  1572. p = Server::ProtocolError.new(openid1_msg, error,
  1573. reference, contact)
  1574. reply = p.to_message()
  1575. assert_equal(reply.get_arg(OPENID_NS, 'reference'), reference)
  1576. assert_equal(reply.get_arg(OPENID_NS, 'contact'), contact)
  1577. openid2_msg = Message.from_post_args(openid2_args)
  1578. p = Server::ProtocolError.new(openid2_msg, error,
  1579. reference, contact)
  1580. reply = p.to_message()
  1581. assert_equal(reply.get_arg(OPENID_NS, 'reference'), reference)
  1582. assert_equal(reply.get_arg(OPENID_NS, 'contact'), contact)
  1583. end
  1584. def failUnlessExpiresInMatches(msg, expected_expires_in)
  1585. expires_in_str = msg.get_arg(OPENID_NS, 'expires_in', NO_DEFAULT)
  1586. expires_in = expires_in_str.to_i
  1587. # Slop is necessary because the tests can sometimes get run
  1588. # right on a second boundary
  1589. slop = 1 # second
  1590. difference = expected_expires_in - expires_in
  1591. error_message = sprintf('"expires_in" value not within %s of expected: ' +
  1592. 'expected=%s, actual=%s', slop, expected_expires_in,
  1593. expires_in)
  1594. assert((0 <= difference and difference <= slop), error_message)
  1595. end
  1596. def test_plaintext
  1597. @assoc = @signatory.create_association(false, 'HMAC-SHA1')
  1598. response = @request.answer(@assoc)
  1599. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1600. assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
  1601. assert_equal(rfg.call("assoc_handle"), @assoc.handle)
  1602. failUnlessExpiresInMatches(response.fields,
  1603. @signatory.secret_lifetime)
  1604. assert_equal(
  1605. rfg.call("mac_key"), Util.to_base64(@assoc.secret))
  1606. assert(!rfg.call("session_type"))
  1607. assert(!rfg.call("enc_mac_key"))
  1608. assert(!rfg.call("dh_server_public"))
  1609. end
  1610. def test_plaintext_v2
  1611. # The main difference between this and the v1 test is that
  1612. # session_type is always returned in v2.
  1613. args = {
  1614. 'openid.ns' => OPENID2_NS,
  1615. 'openid.mode' => 'associate',
  1616. 'openid.assoc_type' => 'HMAC-SHA1',
  1617. 'openid.session_type' => 'no-encryption',
  1618. }
  1619. @request = Server::AssociateRequest.from_message(
  1620. Message.from_post_args(args))
  1621. assert(!@request.message.is_openid1())
  1622. @assoc = @signatory.create_association(false, 'HMAC-SHA1')
  1623. response = @request.answer(@assoc)
  1624. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1625. assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
  1626. assert_equal(rfg.call("assoc_handle"), @assoc.handle)
  1627. failUnlessExpiresInMatches(
  1628. response.fields, @signatory.secret_lifetime)
  1629. assert_equal(
  1630. rfg.call("mac_key"), Util.to_base64(@assoc.secret))
  1631. assert_equal(rfg.call("session_type"), "no-encryption")
  1632. assert(!rfg.call("enc_mac_key"))
  1633. assert(!rfg.call("dh_server_public"))
  1634. end
  1635. def test_plaintext256
  1636. @assoc = @signatory.create_association(false, 'HMAC-SHA256')
  1637. response = @request.answer(@assoc)
  1638. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1639. assert_equal(rfg.call("assoc_type"), "HMAC-SHA1")
  1640. assert_equal(rfg.call("assoc_handle"), @assoc.handle)
  1641. failUnlessExpiresInMatches(
  1642. response.fields, @signatory.secret_lifetime)
  1643. assert_equal(
  1644. rfg.call("mac_key"), Util.to_base64(@assoc.secret))
  1645. assert(!rfg.call("session_type"))
  1646. assert(!rfg.call("enc_mac_key"))
  1647. assert(!rfg.call("dh_server_public"))
  1648. end
  1649. def test_unsupportedPrefer
  1650. allowed_assoc = 'COLD-PET-RAT'
  1651. allowed_sess = 'FROG-BONES'
  1652. message = 'This is a unit test'
  1653. # Set an OpenID 2 message so answerUnsupported doesn't raise
  1654. # ProtocolError.
  1655. @request.message = Message.new(OPENID2_NS)
  1656. response = @request.answer_unsupported(message,
  1657. allowed_assoc,
  1658. allowed_sess)
  1659. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1660. assert_equal(rfg.call('error_code'), 'unsupported-type')
  1661. assert_equal(rfg.call('assoc_type'), allowed_assoc)
  1662. assert_equal(rfg.call('error'), message)
  1663. assert_equal(rfg.call('session_type'), allowed_sess)
  1664. end
  1665. def test_unsupported
  1666. message = 'This is a unit test'
  1667. # Set an OpenID 2 message so answerUnsupported doesn't raise
  1668. # ProtocolError.
  1669. @request.message = Message.new(OPENID2_NS)
  1670. response = @request.answer_unsupported(message)
  1671. rfg = lambda { |f| response.fields.get_arg(OPENID_NS, f) }
  1672. assert_equal(rfg.call('error_code'), 'unsupported-type')
  1673. assert_equal(rfg.call('assoc_type'), nil)
  1674. assert_equal(rfg.call('error'), message)
  1675. assert_equal(rfg.call('session_type'), nil)
  1676. end
  1677. def test_openid1_unsupported_explode
  1678. # answer_unsupported on an associate request should explode if
  1679. # the request was an OpenID 1 request.
  1680. m = Message.new(OPENID1_NS)
  1681. assert_raise(Server::ProtocolError) {
  1682. @request.answer_unsupported(m)
  1683. }
  1684. end
  1685. end
  1686. class Counter
  1687. def initialize
  1688. @count = 0
  1689. end
  1690. def inc
  1691. @count += 1
  1692. end
  1693. end
  1694. class UnhandledError < Exception
  1695. end
  1696. class TestServer < Test::Unit::TestCase
  1697. include TestUtil
  1698. def setup
  1699. @store = Store::Memory.new()
  1700. @server = Server::Server.new(@store, "http://server.unittest/endpt")
  1701. # catchlogs_setup()
  1702. end
  1703. def test_failed_dispatch
  1704. request = Server::OpenIDRequest.new()
  1705. request.mode = "monkeymode"
  1706. request.message = Message.new(OPENID1_NS)
  1707. assert_raise(RuntimeError) {
  1708. webresult = @server.handle_request(request)
  1709. }
  1710. end
  1711. def test_decode_request
  1712. @server.decoder = BogusDecoder.new(@server)
  1713. assert(@server.decode_request({}) == "BOGUS")
  1714. end
  1715. def test_encode_response
  1716. @server.encoder = BogusEncoder.new
  1717. assert(@server.encode_response(nil) == "BOGUS")
  1718. end
  1719. def test_dispatch
  1720. monkeycalled = Counter.new()
  1721. @server.extend(InstanceDefExtension)
  1722. @server.instance_def(:openid_monkeymode) do |request|
  1723. raise UnhandledError
  1724. end
  1725. request = Server::OpenIDRequest.new()
  1726. request.mode = "monkeymode"
  1727. request.message = Message.new(OPENID1_NS)
  1728. assert_raise(UnhandledError) {
  1729. webresult = @server.handle_request(request)
  1730. }
  1731. end
  1732. def test_associate
  1733. request = Server::AssociateRequest.from_message(Message.from_post_args({}))
  1734. response = @server.openid_associate(request)
  1735. assert(response.fields.has_key?(OPENID_NS, "assoc_handle"),
  1736. sprintf("No assoc_handle here: %s", response.fields.inspect))
  1737. end
  1738. def test_associate2
  1739. # Associate when the server has no allowed association types
  1740. #
  1741. # Gives back an error with error_code and no fallback session or
  1742. # assoc types.
  1743. @server.negotiator.allowed_types = []
  1744. # Set an OpenID 2 message so answerUnsupported doesn't raise
  1745. # ProtocolError.
  1746. msg = Message.from_post_args({
  1747. 'openid.ns' => OPENID2_NS,
  1748. 'openid.session_type' => 'no-encryption',
  1749. })
  1750. request = Server::AssociateRequest.from_message(msg)
  1751. response = @server.openid_associate(request)
  1752. assert(response.fields.has_key?(OPENID_NS, "error"))
  1753. assert(response.fields.has_key?(OPENID_NS, "error_code"))
  1754. assert(!response.fields.has_key?(OPENID_NS, "assoc_handle"))
  1755. assert(!response.fields.has_key?(OPENID_NS, "assoc_type"))
  1756. assert(!response.fields.has_key?(OPENID_NS, "session_type"))
  1757. end
  1758. def test_associate3
  1759. # Request an assoc type that is not supported when there are
  1760. # supported types.
  1761. #
  1762. # Should give back an error message with a fallback type.
  1763. @server.negotiator.allowed_types = [['HMAC-SHA256', 'DH-SHA256']]
  1764. msg = Message.from_post_args({
  1765. 'openid.ns' => OPENID2_NS,
  1766. 'openid.session_type' => 'no-encryption',
  1767. })
  1768. request = Server::AssociateRequest.from_message(msg)
  1769. response = @server.openid_associate(request)
  1770. assert(response.fields.has_key?(OPENID_NS, "error"))
  1771. assert(response.fields.has_key?(OPENID_NS, "error_code"))
  1772. assert(!response.fields.has_key?(OPENID_NS, "assoc_handle"))
  1773. assert_equal(response.fields.get_arg(OPENID_NS, "assoc_type"),
  1774. 'HMAC-SHA256')
  1775. assert_equal(response.fields.get_arg(OPENID_NS, "session_type"),
  1776. 'DH-SHA256')
  1777. end
  1778. def test_associate4
  1779. # DH-SHA256 association session
  1780. @server.negotiator.allowed_types = [['HMAC-SHA256', 'DH-SHA256']]
  1781. query = {
  1782. 'openid.dh_consumer_public' =>
  1783. 'ALZgnx8N5Lgd7pCj8K86T/DDMFjJXSss1SKoLmxE72kJTzOtG6I2PaYrHX' +
  1784. 'xku4jMQWSsGfLJxwCZ6280uYjUST/9NWmuAfcrBfmDHIBc3H8xh6RBnlXJ' +
  1785. '1WxJY3jHd5k1/ZReyRZOxZTKdF/dnIqwF8ZXUwI6peV0TyS/K1fOfF/s',
  1786. 'openid.assoc_type' => 'HMAC-SHA256',
  1787. 'openid.session_type' => 'DH-SHA256',
  1788. }
  1789. message = Message.from_post_args(query)
  1790. request = Server::AssociateRequest.from_message(message)
  1791. response = @server.openid_associate(request)
  1792. assert(response.fields.has_key?(OPENID_NS, "assoc_handle"))
  1793. end
  1794. def test_no_encryption_openid1
  1795. # Make sure no-encryption associate requests for OpenID 1 are
  1796. # logged.
  1797. assert_log_matches(/Continuing anyway./) {
  1798. m = Message.from_openid_args({
  1799. 'session_type' => 'no-encryption',
  1800. })
  1801. req = Server::AssociateRequest.from_message(m)
  1802. }
  1803. end
  1804. def test_missingSessionTypeOpenID2
  1805. # Make sure session_type is required in OpenID 2
  1806. msg = Message.from_post_args({
  1807. 'openid.ns' => OPENID2_NS,
  1808. })
  1809. assert_raises(Server::ProtocolError) {
  1810. Server::AssociateRequest.from_message(msg)
  1811. }
  1812. end
  1813. def test_checkAuth
  1814. request = Server::CheckAuthRequest.new('arrrrrf', '0x3999', [])
  1815. request.message = Message.new(OPENID2_NS)
  1816. response = nil
  1817. silence_logging {
  1818. response = @server.openid_check_authentication(request)
  1819. }
  1820. assert(response.fields.has_key?(OPENID_NS, "is_valid"))
  1821. end
  1822. end
  1823. class TestingRequest < Server::OpenIDRequest
  1824. attr_accessor :assoc_handle, :namespace
  1825. end
  1826. class TestSignatory < Test::Unit::TestCase
  1827. include TestUtil
  1828. def setup
  1829. @store = Store::Memory.new()
  1830. @signatory = Server::Signatory.new(@store)
  1831. @_dumb_key = @signatory.class._dumb_key
  1832. @_normal_key = @signatory.class._normal_key
  1833. # CatchLogs.setUp(self)
  1834. end
  1835. def test_get_association_nil
  1836. assert_raises(ArgumentError) {
  1837. @signatory.get_association(nil, false)
  1838. }
  1839. end
  1840. def test_sign
  1841. request = TestingRequest.new()
  1842. assoc_handle = '{assoc}{lookatme}'
  1843. @store.store_association(
  1844. @_normal_key,
  1845. Association.from_expires_in(60, assoc_handle,
  1846. 'sekrit', 'HMAC-SHA1'))
  1847. request.assoc_handle = assoc_handle
  1848. request.namespace = OPENID1_NS
  1849. response = Server::OpenIDResponse.new(request)
  1850. response.fields = Message.from_openid_args({
  1851. 'foo' => 'amsigned',
  1852. 'bar' => 'notsigned',
  1853. 'azu' => 'alsosigned',
  1854. })
  1855. sresponse = @signatory.sign(response)
  1856. assert_equal(
  1857. sresponse.fields.get_arg(OPENID_NS, 'assoc_handle'),
  1858. assoc_handle)
  1859. assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
  1860. 'assoc_handle,azu,bar,foo,signed')
  1861. assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
  1862. # assert(!@messages, @messages)
  1863. end
  1864. def test_signDumb
  1865. request = TestingRequest.new()
  1866. request.assoc_handle = nil
  1867. request.namespace = OPENID2_NS
  1868. response = Server::OpenIDResponse.new(request)
  1869. response.fields = Message.from_openid_args({
  1870. 'foo' => 'amsigned',
  1871. 'bar' => 'notsigned',
  1872. 'azu' => 'alsosigned',
  1873. 'ns' => OPENID2_NS,
  1874. })
  1875. sresponse = @signatory.sign(response)
  1876. assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
  1877. assert(assoc_handle)
  1878. assoc = @signatory.get_association(assoc_handle, true)
  1879. assert(assoc)
  1880. assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
  1881. 'assoc_handle,azu,bar,foo,ns,signed')
  1882. assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
  1883. # assert(!@messages, @messages)
  1884. end
  1885. def test_signExpired
  1886. # Sign a response to a message with an expired handle (using
  1887. # invalidate_handle).
  1888. #
  1889. # From "Verifying with an Association":
  1890. #
  1891. # If an authentication request included an association handle
  1892. # for an association between the OP and the Relying party, and
  1893. # the OP no longer wishes to use that handle (because it has
  1894. # expired or the secret has been compromised, for instance),
  1895. # the OP will send a response that must be verified directly
  1896. # with the OP, as specified in Section 11.3.2. In that
  1897. # instance, the OP will include the field
  1898. # "openid.invalidate_handle" set to the association handle
  1899. # that the Relying Party included with the original request.
  1900. request = TestingRequest.new()
  1901. request.namespace = OPENID2_NS
  1902. assoc_handle = '{assoc}{lookatme}'
  1903. @store.store_association(
  1904. @_normal_key,
  1905. Association.from_expires_in(-10, assoc_handle,
  1906. 'sekrit', 'HMAC-SHA1'))
  1907. assert(@store.get_association(@_normal_key, assoc_handle))
  1908. request.assoc_handle = assoc_handle
  1909. response = Server::OpenIDResponse.new(request)
  1910. response.fields = Message.from_openid_args({
  1911. 'foo' => 'amsigned',
  1912. 'bar' => 'notsigned',
  1913. 'azu' => 'alsosigned',
  1914. })
  1915. sresponse = nil
  1916. silence_logging {
  1917. sresponse = @signatory.sign(response)
  1918. }
  1919. new_assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
  1920. assert(new_assoc_handle)
  1921. assert(new_assoc_handle != assoc_handle)
  1922. assert_equal(
  1923. sresponse.fields.get_arg(OPENID_NS, 'invalidate_handle'),
  1924. assoc_handle)
  1925. assert_equal(sresponse.fields.get_arg(OPENID_NS, 'signed'),
  1926. 'assoc_handle,azu,bar,foo,invalidate_handle,signed')
  1927. assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
  1928. # make sure the expired association is gone
  1929. assert(!@store.get_association(@_normal_key, assoc_handle),
  1930. "expired association is still retrievable.")
  1931. # make sure the new key is a dumb mode association
  1932. assert(@store.get_association(@_dumb_key, new_assoc_handle))
  1933. assert(!@store.get_association(@_normal_key, new_assoc_handle))
  1934. # assert(@messages)
  1935. end
  1936. def test_signInvalidHandle
  1937. request = TestingRequest.new()
  1938. request.namespace = OPENID2_NS
  1939. assoc_handle = '{bogus-assoc}{notvalid}'
  1940. request.assoc_handle = assoc_handle
  1941. response = Server::OpenIDResponse.new(request)
  1942. response.fields = Message.from_openid_args({
  1943. 'foo' => 'amsigned',
  1944. 'bar' => 'notsigned',
  1945. 'azu' => 'alsosigned',
  1946. })
  1947. sresponse = @signatory.sign(response)
  1948. new_assoc_handle = sresponse.fields.get_arg(OPENID_NS, 'assoc_handle')
  1949. assert(new_assoc_handle)
  1950. assert(new_assoc_handle != assoc_handle)
  1951. assert_equal(
  1952. sresponse.fields.get_arg(OPENID_NS, 'invalidate_handle'),
  1953. assoc_handle)
  1954. assert_equal(
  1955. sresponse.fields.get_arg(OPENID_NS, 'signed'),
  1956. 'assoc_handle,azu,bar,foo,invalidate_handle,signed')
  1957. assert(sresponse.fields.get_arg(OPENID_NS, 'sig'))
  1958. # make sure the new key is a dumb mode association
  1959. assert(@store.get_association(@_dumb_key, new_assoc_handle))
  1960. assert(!@store.get_association(@_normal_key, new_assoc_handle))
  1961. # @failIf(@messages, @messages)
  1962. end
  1963. def test_verify
  1964. assoc_handle = '{vroom}{zoom}'
  1965. assoc = Association.from_expires_in(
  1966. 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
  1967. @store.store_association(@_dumb_key, assoc)
  1968. signed = Message.from_post_args({
  1969. 'openid.foo' => 'bar',
  1970. 'openid.apple' => 'orange',
  1971. 'openid.assoc_handle' => assoc_handle,
  1972. 'openid.signed' => 'apple,assoc_handle,foo,signed',
  1973. 'openid.sig' => 'uXoT1qm62/BB09Xbj98TQ8mlBco=',
  1974. })
  1975. verified = @signatory.verify(assoc_handle, signed)
  1976. assert(verified)
  1977. # assert(!@messages, @messages)
  1978. end
  1979. def test_verifyBadSig
  1980. assoc_handle = '{vroom}{zoom}'
  1981. assoc = Association.from_expires_in(
  1982. 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
  1983. @store.store_association(@_dumb_key, assoc)
  1984. signed = Message.from_post_args({
  1985. 'openid.foo' => 'bar',
  1986. 'openid.apple' => 'orange',
  1987. 'openid.assoc_handle' => assoc_handle,
  1988. 'openid.signed' => 'apple,assoc_handle,foo,signed',
  1989. 'openid.sig' => 'uXoT1qm62/BB09Xbj98TQ8mlBco=BOGUS'
  1990. })
  1991. verified = @signatory.verify(assoc_handle, signed)
  1992. # @failIf(@messages, @messages)
  1993. assert(!verified)
  1994. end
  1995. def test_verifyBadHandle
  1996. assoc_handle = '{vroom}{zoom}'
  1997. signed = Message.from_post_args({
  1998. 'foo' => 'bar',
  1999. 'apple' => 'orange',
  2000. 'openid.sig' => "Ylu0KcIR7PvNegB/K41KpnRgJl0=",
  2001. })
  2002. verified = nil
  2003. silence_logging {
  2004. verified = @signatory.verify(assoc_handle, signed)
  2005. }
  2006. assert(!verified)
  2007. #assert(@messages)
  2008. end
  2009. def test_verifyAssocMismatch
  2010. # Attempt to validate sign-all message with a signed-list assoc.
  2011. assoc_handle = '{vroom}{zoom}'
  2012. assoc = Association.from_expires_in(
  2013. 60, assoc_handle, 'sekrit', 'HMAC-SHA1')
  2014. @store.store_association(@_dumb_key, assoc)
  2015. signed = Message.from_post_args({
  2016. 'foo' => 'bar',
  2017. 'apple' => 'orange',
  2018. 'openid.sig' => "d71xlHtqnq98DonoSgoK/nD+QRM=",
  2019. })
  2020. verified = nil
  2021. silence_logging {
  2022. verified = @signatory.verify(assoc_handle, signed)
  2023. }
  2024. assert(!verified)
  2025. #assert(@messages)
  2026. end
  2027. def test_getAssoc
  2028. assoc_handle = makeAssoc(true)
  2029. assoc = @signatory.get_association(assoc_handle, true)
  2030. assert(assoc)
  2031. assert_equal(assoc.handle, assoc_handle)
  2032. # @failIf(@messages, @messages)
  2033. end
  2034. def test_getAssocExpired
  2035. assoc_handle = makeAssoc(true, -10)
  2036. assoc = nil
  2037. silence_logging {
  2038. assoc = @signatory.get_association(assoc_handle, true)
  2039. }
  2040. assert(!assoc, assoc)
  2041. # assert(@messages)
  2042. end
  2043. def test_getAssocInvalid
  2044. ah = 'no-such-handle'
  2045. silence_logging {
  2046. assert_equal(
  2047. @signatory.get_association(ah, false), nil)
  2048. }
  2049. # assert(!@messages, @messages)
  2050. end
  2051. def test_getAssocDumbVsNormal
  2052. # getAssociation(dumb=False) cannot get a dumb assoc
  2053. assoc_handle = makeAssoc(true)
  2054. silence_logging {
  2055. assert_equal(
  2056. @signatory.get_association(assoc_handle, false), nil)
  2057. }
  2058. # @failIf(@messages, @messages)
  2059. end
  2060. def test_getAssocNormalVsDumb
  2061. # getAssociation(dumb=True) cannot get a shared assoc
  2062. #
  2063. # From "Verifying Directly with the OpenID Provider"::
  2064. #
  2065. # An OP MUST NOT verify signatures for associations that have shared
  2066. # MAC keys.
  2067. assoc_handle = makeAssoc(false)
  2068. silence_logging {
  2069. assert_equal(
  2070. @signatory.get_association(assoc_handle, true), nil)
  2071. }
  2072. # @failIf(@messages, @messages)
  2073. end
  2074. def test_createAssociation
  2075. assoc = @signatory.create_association(false)
  2076. silence_logging {
  2077. assert(@signatory.get_association(assoc.handle, false))
  2078. }
  2079. # @failIf(@messages, @messages)
  2080. end
  2081. def makeAssoc(dumb, lifetime=60)
  2082. assoc_handle = '{bling}'
  2083. assoc = Association.from_expires_in(lifetime, assoc_handle,
  2084. 'sekrit', 'HMAC-SHA1')
  2085. silence_logging {
  2086. @store.store_association(((dumb and @_dumb_key) or @_normal_key), assoc)
  2087. }
  2088. return assoc_handle
  2089. end
  2090. def test_invalidate
  2091. assoc_handle = '-squash-'
  2092. assoc = Association.from_expires_in(60, assoc_handle,
  2093. 'sekrit', 'HMAC-SHA1')
  2094. silence_logging {
  2095. @store.store_association(@_dumb_key, assoc)
  2096. assoc = @signatory.get_association(assoc_handle, true)
  2097. assert(assoc)
  2098. assoc = @signatory.get_association(assoc_handle, true)
  2099. assert(assoc)
  2100. @signatory.invalidate(assoc_handle, true)
  2101. assoc = @signatory.get_association(assoc_handle, true)
  2102. assert(!assoc)
  2103. }
  2104. # @failIf(@messages, @messages)
  2105. end
  2106. end
  2107. class RunthroughTestCase < Test::Unit::TestCase
  2108. def setup
  2109. @store = Store::Memory.new
  2110. @server = Server::Server.new(@store, "http://example.com/openid/server")
  2111. end
  2112. def test_openid1_assoc_checkid
  2113. assoc_args = {'openid.mode' => 'associate',
  2114. 'openid.assoc_type' => 'HMAC-SHA1'}
  2115. areq = @server.decode_request(assoc_args)
  2116. aresp = @server.handle_request(areq)
  2117. amess = aresp.fields
  2118. assert(amess.is_openid1)
  2119. ahandle = amess.get_arg(OPENID_NS, 'assoc_handle')
  2120. assert(ahandle)
  2121. assoc = @store.get_association('http://localhost/|normal', ahandle)
  2122. assert(assoc.is_a?(Association))
  2123. checkid_args = {'openid.mode' => 'checkid_setup',
  2124. 'openid.return_to' => 'http://example.com/openid/consumer',
  2125. 'openid.assoc_handle' => ahandle,
  2126. 'openid.identity' => 'http://foo.com/'}
  2127. cireq = @server.decode_request(checkid_args)
  2128. ciresp = cireq.answer(true)
  2129. signed_resp = @server.signatory.sign(ciresp)
  2130. assert_equal(assoc.get_message_signature(signed_resp.fields),
  2131. signed_resp.fields.get_arg(OPENID_NS, 'sig'))
  2132. assert(assoc.check_message_signature(signed_resp.fields))
  2133. end
  2134. end
  2135. end