data_store_with_fallbacks_spec.rb 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373
  1. # -*- coding:binary -*-
  2. require 'spec_helper'
  3. RSpec.shared_context 'datastore subjects', shared_context: :metadata do
  4. subject(:default_subject) do
  5. described_class.new
  6. end
  7. subject(:datastore_with_simple_options) do
  8. s = default_subject.copy
  9. options = Msf::OptionContainer.new(
  10. [
  11. Msf::OptString.new(
  12. 'foo',
  13. [true, 'foo option', 'default_foo_value']
  14. ),
  15. Msf::OptString.new(
  16. 'bar',
  17. [true, 'bar option', 'default_bar_value']
  18. ),
  19. Msf::OptString.new(
  20. 'baz',
  21. [false, 'baz option']
  22. )
  23. ]
  24. )
  25. s.import_options(options)
  26. s
  27. end
  28. subject(:datastore_with_aliases) do
  29. s = default_subject.copy
  30. options = Msf::OptionContainer.new(
  31. [
  32. Msf::OptString.new(
  33. 'NewOptionName',
  34. [true, 'An option with a new name. Aliases ensure the old and new names are synchronized', 'default_value'],
  35. aliases: ['OLD_OPTION_NAME']
  36. )
  37. ]
  38. )
  39. s.import_options(options)
  40. s
  41. end
  42. subject(:datastore_with_fallbacks) do
  43. s = default_subject.copy
  44. options = Msf::OptionContainer.new(
  45. [
  46. Msf::OptString.new(
  47. 'SMBUser',
  48. [true, 'The SMB username'],
  49. fallbacks: ['username']
  50. ),
  51. Msf::OptString.new(
  52. 'SMBDomain',
  53. [true, 'The SMB username', 'WORKGROUP'],
  54. fallbacks: ['domain']
  55. ),
  56. Msf::OptString.new(
  57. 'USER_ATTR',
  58. [true, 'The ldap username'],
  59. fallbacks: ['username']
  60. ),
  61. ]
  62. )
  63. s.import_options(options)
  64. s
  65. end
  66. subject(:complex_datastore) do
  67. datastore_with_simple_options
  68. .merge(datastore_with_aliases)
  69. .merge(datastore_with_fallbacks)
  70. end
  71. subject(:complex_datastore_with_imported_defaults) do
  72. s = complex_datastore.copy
  73. s.import_defaults_from_hash(
  74. {
  75. 'foo' => 'overridden_default_foo',
  76. 'NewOptionName' => 'overridden_default_new_option_name'
  77. },
  78. imported_by: 'datastore_spec'
  79. )
  80. s
  81. end
  82. end
  83. RSpec.shared_examples_for 'a datastore with lookup support' do |opts = {}|
  84. it { is_expected.to respond_to :[] }
  85. it { is_expected.to respond_to :[]= }
  86. it { is_expected.to respond_to :unset }
  87. it { is_expected.to respond_to :delete }
  88. describe '#[]' do
  89. it 'should have default keyed values' do
  90. expect(subject['foo']).to eq 'foo_value'
  91. expect(subject['bar']).to eq 'bar_value'
  92. end
  93. it 'should have case-insensitive lookups' do
  94. # Sorted by gray code, just for fun
  95. expect(subject['foo']).to eq 'foo_value'
  96. expect(subject['Foo']).to eq 'foo_value'
  97. expect(subject['FOo']).to eq 'foo_value'
  98. expect(subject['fOo']).to eq 'foo_value'
  99. expect(subject['fOO']).to eq 'foo_value'
  100. expect(subject['FOO']).to eq 'foo_value'
  101. expect(subject['FoO']).to eq 'foo_value'
  102. expect(subject['foO']).to eq 'foo_value'
  103. end
  104. end
  105. describe '#length' do
  106. it 'should return a number' do
  107. expect(subject.length).to be > 0
  108. end
  109. end
  110. describe '#count' do
  111. it 'should return a number' do
  112. expect(subject.length).to be > 0
  113. end
  114. end
  115. context '#to_h' do
  116. it 'should return a Hash with correct values' do
  117. expected_to_h = opts.fetch(:expected_to_h) do
  118. { 'foo' => 'foo_value', 'bar' => 'bar_value' }
  119. end
  120. expect(subject.to_h).to eq(expected_to_h)
  121. end
  122. end
  123. end
  124. RSpec.shared_examples_for 'a datastore' do
  125. describe '#import_options' do
  126. let(:foo_option) do
  127. Msf::OptString.new(
  128. 'foo',
  129. [true, 'foo option', 'default_foo_value']
  130. )
  131. end
  132. let(:bar_option) do
  133. Msf::OptString.new(
  134. 'bar',
  135. [true, 'bar option', 'default_bar_value']
  136. )
  137. end
  138. subject do
  139. s = default_subject
  140. options = Msf::OptionContainer.new(
  141. [
  142. foo_option,
  143. bar_option
  144. ]
  145. )
  146. s.import_options(options)
  147. s
  148. end
  149. it 'should import the given options' do
  150. expected_options = {
  151. 'foo' => foo_option,
  152. 'bar' => bar_option
  153. }
  154. expect(subject.options).to eq(expected_options)
  155. end
  156. end
  157. describe '#import_options_from_hash' do
  158. subject do
  159. hash = { 'foo' => 'foo_value', 'bar' => 'bar_value' }
  160. s = default_subject
  161. s.import_options_from_hash(hash)
  162. s
  163. end
  164. it_behaves_like 'a datastore with lookup support'
  165. end
  166. describe '#import_options_from_s' do
  167. subject do
  168. str = 'foo=foo_value bar=bar_value'
  169. s = default_subject
  170. s.import_options_from_s(str)
  171. s
  172. end
  173. it_behaves_like 'a datastore with lookup support'
  174. context "parsing corner cases" do
  175. it "should parse comma separated strings" do
  176. str = "foo=bar,fizz=buzz"
  177. subject.import_options_from_s(str)
  178. expect(subject).to have_key("foo")
  179. expect(subject["foo"]).to eql("bar")
  180. expect(subject).to have_key("fizz")
  181. expect(subject["fizz"]).to eql("buzz")
  182. end
  183. it "should parse options with nested equals" do
  184. str = "COMMAND=date --date=2023-01-01 --iso-8601=ns,SESSION=1"
  185. subject.import_options_from_s(str)
  186. expect(subject).to have_key("COMMAND")
  187. expect(subject["COMMAND"]).to eql("date --date=2023-01-01 --iso-8601=ns")
  188. expect(subject).to have_key("SESSION")
  189. expect(subject["SESSION"]).to eql("1")
  190. end
  191. end
  192. end
  193. describe '#from_file' do
  194. subject do
  195. ini_instance = double group?: true,
  196. :[] => {
  197. 'foo' => 'foo_value',
  198. 'bar' => 'bar_value'
  199. }
  200. ini_class = double from_file: ini_instance
  201. stub_const('Rex::Parser::Ini', ini_class)
  202. s = default_subject
  203. s.from_file('path')
  204. s
  205. end
  206. it_behaves_like 'a datastore with lookup support'
  207. end
  208. describe '#user_defined' do
  209. subject do
  210. complex_datastore
  211. end
  212. context 'when no options have been set' do
  213. it 'should return an empty hash' do
  214. expect(subject.user_defined).to eq({})
  215. end
  216. end
  217. context 'when a value has been unset' do
  218. before(:each) do
  219. subject.unset('foo')
  220. end
  221. it 'should should not include the unset values' do
  222. expect(subject.user_defined).to eq({})
  223. end
  224. end
  225. context 'when values have been explicitly set' do
  226. before(:each) do
  227. subject['foo'] = 'foo_value'
  228. subject['custom_key'] = 'custom_key_value'
  229. subject['OLD_OPTION_NAME'] = 'old_option_name_value'
  230. subject['SMBUser'] = 'smbuser_user'
  231. end
  232. it 'should return the set values' do
  233. expected_values = {
  234. 'NewOptionName' => 'old_option_name_value',
  235. 'custom_key' => 'custom_key_value',
  236. 'foo' => 'foo_value',
  237. 'SMBUser' => 'smbuser_user'
  238. }
  239. expect(subject.user_defined).to eq(expected_values)
  240. end
  241. end
  242. context 'when a fallback has been set' do
  243. before(:each) do
  244. subject.merge!(
  245. {
  246. 'username' => 'username'
  247. }
  248. )
  249. end
  250. it 'should not return SMBUser/USER_ATTR etc' do
  251. expected_values = {
  252. 'username' => 'username'
  253. }
  254. expect(subject.user_defined).to eq(expected_values)
  255. end
  256. end
  257. context 'when values have been merged with a hash' do
  258. before(:each) do
  259. subject.merge!(
  260. {
  261. 'NewOptionName' => 'old_option_name_value',
  262. 'custom_key' => 'custom_key_value',
  263. 'FOO' => 'foo_value'
  264. }
  265. )
  266. end
  267. it 'should return the set values' do
  268. expected_values = {
  269. 'NewOptionName' => 'old_option_name_value',
  270. 'custom_key' => 'custom_key_value',
  271. 'foo' => 'foo_value'
  272. }
  273. expect(subject.user_defined).to eq(expected_values)
  274. end
  275. end
  276. context 'when values have been merged with a datastore' do
  277. before(:each) do
  278. other_datastore = subject.copy
  279. subject.unset('foo')
  280. subject['bar'] = 'bar_value'
  281. subject['foo_bar'] = 'foo_bar_value'
  282. options = Msf::OptionContainer.new(
  283. Msf::Opt.stager_retry_options + Msf::Opt.http_proxy_options
  284. )
  285. other_datastore.import_options(options)
  286. other_datastore['BAR'] = 'new_bar_value'
  287. other_datastore['HttpProxyPass'] = 'http_proxy_pass_value'
  288. other_datastore['HttpProxyType'] = 'SOCKS'
  289. other_datastore.unset('FOO_BAR')
  290. other_datastore.import_defaults_from_hash({ 'PAYLOAD' => 'merged_default' }, imported_by: 'data_store_spec')
  291. subject.merge!(other_datastore)
  292. end
  293. it 'should return the set values' do
  294. expected_values = {
  295. 'HttpProxyPass' => 'http_proxy_pass_value',
  296. 'HttpProxyType' => 'SOCKS',
  297. 'foo_bar' => 'foo_bar_value',
  298. 'bar' => 'new_bar_value'
  299. }
  300. expect(subject.user_defined).to eq(expected_values)
  301. end
  302. it 'should still have defaults present' do
  303. expect(subject['payload']).to eq 'merged_default'
  304. end
  305. end
  306. end
  307. describe '#[]' do
  308. context 'when the datastore has no options registered' do
  309. subject do
  310. default_subject
  311. end
  312. it 'should reset the specified key' do
  313. expect(subject['foo']).to eq nil
  314. expect(subject['bar']).to eq nil
  315. end
  316. it 'should return imported defaults' do
  317. subject.import_defaults_from_hash({ 'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp' }, imported_by: 'data_store_spec')
  318. expect(subject.default?('payload')).to be true
  319. expect(subject.default?('PAYLOAD')).to be true
  320. expect(subject['PAYLOAD']).to eq 'linux/armle/meterpreter_reverse_tcp'
  321. expect(subject['payload']).to eq 'linux/armle/meterpreter_reverse_tcp'
  322. end
  323. end
  324. context 'when the datastore has aliases' do
  325. subject do
  326. datastore_with_aliases
  327. end
  328. it 'should have default keyed values' do
  329. expect(subject['NewOptionName']).to eq('default_value')
  330. expect(subject['OLD_OPTION_NAME']).to eq('default_value')
  331. end
  332. it 'should have case-insensitive lookups' do
  333. expect(subject['NEWOPTIONNAME']).to eq('default_value')
  334. expect(subject['Old_Option_Name']).to eq('default_value')
  335. end
  336. end
  337. context 'when the datastore has fallbacks' do
  338. subject do
  339. datastore_with_fallbacks
  340. end
  341. it 'should have default keyed values' do
  342. expect(subject['SMBUser']).to be(nil)
  343. expect(subject['SMBDomain']).to eq('WORKGROUP')
  344. expect(subject['USER_ATTR']).to be(nil)
  345. expect(subject['username']).to be(nil)
  346. end
  347. end
  348. end
  349. describe '#merge!' do
  350. context 'when merging with a hash' do
  351. subject do
  352. s = default_subject.copy
  353. options = Msf::OptionContainer.new(
  354. [
  355. Msf::OptFloat.new(
  356. 'FloatValue',
  357. [false, 'A FloatValue', 3.5]
  358. )
  359. ]
  360. )
  361. s.import_options(options)
  362. s
  363. end
  364. # Note: This aligns the first implementation of the DataStore class.
  365. # In certain scenarios it does not seem like desired behavior.
  366. it 'does not perform option validation' do
  367. subject.merge!({ 'FloatValue' => 'invalid_value' })
  368. expect(subject['FloatValue']).to eq('invalid_value')
  369. end
  370. end
  371. end
  372. describe '#[]=' do
  373. context 'when the datastore has aliases' do
  374. subject do
  375. datastore_with_aliases
  376. end
  377. [
  378. nil,
  379. false,
  380. '',
  381. 'new_value'
  382. ].each do |value|
  383. context "when the value is #{value.inspect}" do
  384. it 'should allow setting datastore values with the new option name' do
  385. subject['NewOptionName'] = value
  386. expect(subject['NewOptionName']).to eq(value)
  387. expect(subject['OLD_OPTION_NAME']).to eq(value)
  388. end
  389. it 'should allow setting datastore values with the old option name' do
  390. subject['OLD_OPTION_NAME'] = value
  391. expect(subject['NewOptionName']).to eq(value)
  392. expect(subject['OLD_OPTION_NAME']).to eq(value)
  393. end
  394. end
  395. end
  396. end
  397. context 'when the datastore has fallbacks' do
  398. subject do
  399. datastore_with_fallbacks
  400. end
  401. it 'should allow setting a key with fallbacks' do
  402. subject['SMBUser'] = 'username'
  403. expect(subject['SMBUser']).to eq('username')
  404. expect(subject['USER_ATTR']).to be(nil)
  405. expect(subject['username']).to be(nil)
  406. end
  407. it 'should allow setting a generic key' do
  408. subject['username'] = 'username'
  409. expect(subject['SMBUser']).to eq('username')
  410. expect(subject['USER_ATTR']).to eq('username')
  411. expect(subject['username']).to eq('username')
  412. end
  413. it 'should allow setting multiple keys with fallbacks' do
  414. subject['username'] = 'username_generic'
  415. subject['user_attr'] = 'username_attr'
  416. subject['smbuser'] = 'username_smb'
  417. expect(subject['SMBUser']).to eq('username_smb')
  418. expect(subject['USER_ATTR']).to eq('username_attr')
  419. expect(subject['username']).to eq('username_generic')
  420. end
  421. it 'should use the fallback in preference of the option default value' do
  422. subject['domain'] = 'example.local'
  423. expect(subject['SMBDomain']).to eq('example.local')
  424. end
  425. end
  426. end
  427. describe '#import_defaults_from_hash' do
  428. subject do
  429. complex_datastore.import_defaults_from_hash(
  430. {
  431. 'foo' => 'overridden_default_foo',
  432. 'NewOptionName' => 'overridden_default_new_option_name'
  433. # TODO: Add alias/old_option_name test as well
  434. # 'old_option_name' => 'overridden_default_old_option_name'
  435. },
  436. imported_by: 'self'
  437. )
  438. complex_datastore
  439. end
  440. it 'should have default keyed values' do
  441. expect(subject['foo']).to eq 'overridden_default_foo'
  442. expect(subject['bar']).to eq 'default_bar_value'
  443. expect(subject['NewOptionName']).to eq('overridden_default_new_option_name')
  444. expect(subject['OLD_OPTION_NAME']).to eq('overridden_default_new_option_name')
  445. end
  446. end
  447. describe '#unset' do
  448. context 'when the datastore has no options registered' do
  449. subject do
  450. default_subject
  451. end
  452. it 'should reset the value when it has been user defined' do
  453. subject['foo'] = 'new_value'
  454. expect(subject.unset('foo')).to eq 'new_value'
  455. expect(subject.unset('foo')).to eq nil
  456. end
  457. it 'should not change the value if not previously set' do
  458. expect(subject.unset('foo')).to eq nil
  459. expect(subject.unset('foo')).to eq nil
  460. end
  461. end
  462. context 'when the datastore has simple options' do
  463. subject do
  464. datastore_with_simple_options
  465. end
  466. it 'should reset the value when it has been user defined' do
  467. subject['foo'] = 'new_value'
  468. expect(subject.unset('foo')).to eq 'new_value'
  469. expect(subject.unset('foo')).to eq 'default_foo_value'
  470. end
  471. it 'should not change the value if not previously set' do
  472. expect(subject.unset('foo')).to eq 'default_foo_value'
  473. expect(subject.unset('foo')).to eq 'default_foo_value'
  474. end
  475. end
  476. context 'when the datastore has aliases' do
  477. subject do
  478. datastore_with_aliases
  479. end
  480. # Ensure that both the new name and old name can be used interchangeably
  481. [
  482. { set_key: 'NewOptionName', delete_key: 'NewOptionName' },
  483. { set_key: 'OLD_OPTION_NAME', delete_key: 'OLD_OPTION_NAME' },
  484. { set_key: 'NewOptionName', delete_key: 'OLD_OPTION_NAME' },
  485. { set_key: 'OLD_OPTION_NAME', delete_key: 'NewOptionName' },
  486. ].each do |test|
  487. context "when using #{test[:delete_key].inspect} to set the value and deleting with #{test[:delete_key].inspect}" do
  488. it 'should reset the value when it has been user defined' do
  489. subject[test[:set_key]] = 'new_value'
  490. expect(subject.unset(test[:delete_key])).to eq 'new_value'
  491. expect(subject.unset(test[:delete_key])).to eq 'default_value'
  492. end
  493. it 'should not change the value if not previously set' do
  494. expect(subject.unset(test[:delete_key])).to eq 'default_value'
  495. expect(subject.unset(test[:delete_key])).to eq 'default_value'
  496. end
  497. end
  498. end
  499. end
  500. context 'when the datastore has fallbacks' do
  501. subject do
  502. datastore_with_fallbacks
  503. end
  504. context 'when using the option name' do
  505. it 'should reset the value when it has been user defined' do
  506. subject['SMBDomain'] = 'new_value'
  507. expect(subject.unset('SMBDomain')).to eq 'new_value'
  508. expect(subject.unset('SMBDomain')).to eq 'WORKGROUP'
  509. end
  510. it 'should not change the value if not previously set' do
  511. expect(subject.unset('SMBDomain')).to eq 'WORKGROUP'
  512. expect(subject.unset('SMBDomain')).to eq 'WORKGROUP'
  513. end
  514. end
  515. context 'when using the fallback option name' do
  516. it 'should delete the value when it has been user defined' do
  517. subject['domain'] = 'new_value'
  518. # Explicitly unsetting SMBDomain shouldn't unset the domain
  519. expect(subject['SMBDomain']).to eq 'new_value'
  520. expect(subject.unset('SMBDomain')).to eq 'new_value'
  521. expect(subject.unset('SMBDomain')).to eq 'new_value'
  522. expect(subject['domain']).to eq 'new_value'
  523. expect(subject.unset('domain')).to eq 'new_value'
  524. expect(subject.unset('domain')).to eq nil
  525. end
  526. it 'should delete the value when it has not been user defined' do
  527. expect(subject.unset('domain')).to eq nil
  528. expect(subject.unset('SMBDomain')).to eq 'WORKGROUP'
  529. expect(subject['domain']).to eq nil
  530. end
  531. end
  532. end
  533. context 'when the datastore has imported defaults' do
  534. subject do
  535. complex_datastore_with_imported_defaults
  536. end
  537. it 'should reset the specified key' do
  538. subject['foo'] = 'new_value'
  539. subject.unset('foo')
  540. expect(subject['foo']).to eq 'overridden_default_foo'
  541. end
  542. end
  543. end
  544. context '#to_h' do
  545. context 'when the datastore has no options registered' do
  546. subject do
  547. default_subject
  548. end
  549. it 'should return a Hash with correct values' do
  550. expected_to_h = {
  551. }
  552. expect(subject.to_h).to eq(expected_to_h)
  553. end
  554. end
  555. context 'when the datastore has aliases' do
  556. subject do
  557. datastore_with_aliases
  558. end
  559. it 'should return a Hash with correct values' do
  560. expected_to_h = {
  561. 'NewOptionName' => 'default_value'
  562. }
  563. expect(subject.to_h).to eq(expected_to_h)
  564. end
  565. end
  566. context 'when the datastore has fallbacks' do
  567. subject do
  568. datastore_with_fallbacks
  569. end
  570. it 'should return a Hash with correct values' do
  571. expected_to_h = {
  572. 'SMBDomain' => 'WORKGROUP',
  573. 'SMBUser' => '',
  574. 'USER_ATTR' => ''
  575. }
  576. expect(subject.to_h).to eq(expected_to_h)
  577. end
  578. end
  579. context 'when the datastore has imported defaults' do
  580. subject do
  581. complex_datastore_with_imported_defaults
  582. end
  583. it 'should return a Hash with correct values' do
  584. expected_to_h = {
  585. 'NewOptionName' => 'overridden_default_new_option_name',
  586. 'SMBDomain' => 'WORKGROUP',
  587. 'SMBUser' => '',
  588. 'USER_ATTR' => '',
  589. 'foo' => 'overridden_default_foo',
  590. 'bar' => 'default_bar_value',
  591. 'baz' => ''
  592. }
  593. expect(subject.to_h).to eq(expected_to_h)
  594. end
  595. end
  596. end
  597. end
  598. RSpec.describe Msf::DataStoreWithFallbacks do
  599. include_context 'datastore subjects'
  600. subject(:default_subject) do
  601. described_class.new
  602. end
  603. subject { default_subject }
  604. it_behaves_like 'a datastore'
  605. end
  606. RSpec.describe Msf::ModuleDataStoreWithFallbacks do
  607. include_context 'datastore subjects'
  608. let(:framework_datastore) do
  609. Msf::DataStoreWithFallbacks.new
  610. end
  611. let(:mod) do
  612. framework = instance_double(Msf::Framework, datastore: framework_datastore)
  613. instance_double(
  614. Msf::Exploit,
  615. framework: framework
  616. )
  617. end
  618. subject(:default_subject) do
  619. described_class.new mod
  620. end
  621. subject { default_subject }
  622. # @param [DataStoreSearchResult] search_result
  623. # @return [Symbol] a human readable result for where the search result was found or not found
  624. def human_readable_result_for(search_result)
  625. "#{search_result.instance_variable_get(:@namespace)}__#{search_result.instance_variable_get(:@result)}".to_sym
  626. end
  627. context 'when the framework datastore is empty' do
  628. it_behaves_like 'a datastore'
  629. end
  630. context 'when the global framework datastore has values' do
  631. describe '#default?' do
  632. context 'when the datastore has no options registered' do
  633. subject do
  634. default_subject
  635. end
  636. it 'should return true when the value is not set' do
  637. expect(subject.default?('foo')).to be true
  638. end
  639. it 'should return false if the value is set' do
  640. subject['foo'] = 'bar'
  641. expect(subject.default?('foo')).to be false
  642. end
  643. it 'should return true if the value has been unset' do
  644. expect(subject.default?('foo')).to be true
  645. end
  646. it 'should return imported defaults' do
  647. subject.import_defaults_from_hash({ 'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp' }, imported_by: 'data_store_spec')
  648. expect(subject.default?('payload')).to be true
  649. expect(subject.default?('PAYLOAD')).to be true
  650. expect(subject['PAYLOAD']).to eq 'linux/armle/meterpreter_reverse_tcp'
  651. expect(subject['payload']).to eq 'linux/armle/meterpreter_reverse_tcp'
  652. end
  653. end
  654. context 'when the datastore has aliases' do
  655. subject do
  656. datastore_with_aliases
  657. end
  658. # Ensure that both the new name and old name can be used interchangeably
  659. [
  660. { set_key: 'NewOptionName', read_key: 'NewOptionName' },
  661. { set_key: 'OLD_OPTION_NAME', read_key: 'OLD_OPTION_NAME' },
  662. { set_key: 'NewOptionName', read_key: 'OLD_OPTION_NAME' },
  663. { set_key: 'OLD_OPTION_NAME', read_key: 'NewOptionName' },
  664. ].each do |test|
  665. context "when using #{test[:set_key].inspect} to set the value and reading with #{test[:read_key].inspect}" do
  666. it 'should return true when the value is not set' do
  667. expect(subject.default?(test[:read_key])).to be true
  668. end
  669. it 'should return false if the value is set' do
  670. subject[test[:set_key]] = 'bar'
  671. expect(subject.default?(test[:read_key])).to be false
  672. end
  673. it 'should return true if the value has been unset' do
  674. subject.unset(test[:set_key])
  675. expect(subject.default?(test[:read_key])).to be true
  676. end
  677. end
  678. end
  679. end
  680. context 'when the datastore has fallbacks' do
  681. subject do
  682. datastore_with_fallbacks
  683. end
  684. it 'should return true when the value is not set' do
  685. expect(subject.default?('SMBDomain')).to be true
  686. end
  687. it 'should return false if the value is set' do
  688. subject['SMBDomain'] = 'bar'
  689. expect(subject.default?('SMBDomain')).to be false
  690. end
  691. it 'should return true if the value has been unset' do
  692. subject.unset('SMBDomain')
  693. expect(subject.default?('SMBDomain')).to be true
  694. end
  695. it 'should return false if the fallback value has been set' do
  696. subject['domain'] = 'foo'
  697. expect(subject.default?('SMBDomain')).to be false
  698. end
  699. it 'should return true if the fallback value has been unset' do
  700. subject['domain'] = 'foo'
  701. subject.unset('domain')
  702. expect(subject.default?('SMBDomain')).to be true
  703. end
  704. end
  705. end
  706. describe '#[]' do
  707. context 'when the datastore has no options registered' do
  708. subject do
  709. default_subject
  710. end
  711. it 'should return nil by default' do
  712. expect(subject['foo']).to eq nil
  713. expect(subject['bar']).to eq nil
  714. end
  715. context 'when the key has been set in the framework datastore' do
  716. it 'should fall back to the framework datastore' do
  717. framework_datastore['foo'] = 'global_foo_value'
  718. expect(subject['foo']).to eq 'global_foo_value'
  719. expect(subject['bar']).to eq nil
  720. end
  721. end
  722. end
  723. context 'when the datastore has aliases' do
  724. subject do
  725. datastore_with_aliases
  726. end
  727. it 'should have default keyed values' do
  728. expect(subject['NewOptionName']).to eq('default_value')
  729. expect(subject['OLD_OPTION_NAME']).to eq('default_value')
  730. end
  731. it 'should have case-insensitive lookups' do
  732. expect(subject['NEWOPTIONNAME']).to eq('default_value')
  733. expect(subject['Old_Option_Name']).to eq('default_value')
  734. end
  735. context 'when the key has been set in the framework datastore' do
  736. # Ensure that both the new name and old name can be used interchangeably
  737. [
  738. { set_key: 'NewOptionName', read_key: 'NewOptionName' },
  739. { set_key: 'OLD_OPTION_NAME', read_key: 'OLD_OPTION_NAME' },
  740. # Not supported/implemented - the global datastore does not have aliases registered
  741. # { set_key: 'NewOptionName', read_key: 'OLD_OPTION_NAME' },
  742. # { set_key: 'OLD_OPTION_NAME', read_key: 'NewOptionName' },
  743. ].each do |test|
  744. context "when using #{test[:set_key].inspect} to set the value and reading with #{test[:read_key].inspect}" do
  745. it 'should fall back to the framework datastore if it is set' do
  746. framework_datastore[test[:set_key]] = 'global_foo_value'
  747. expect(subject[test[:read_key]]).to eq 'global_foo_value'
  748. end
  749. it 'should fallback to default value if the parent datastore is unset' do
  750. framework_datastore.unset(test[:set_key])
  751. expect(subject[test[:read_key]]).to eq 'default_value'
  752. end
  753. end
  754. end
  755. end
  756. end
  757. context 'when the datastore has fallbacks' do
  758. subject do
  759. datastore_with_fallbacks
  760. end
  761. it 'should allow setting a key with fallbacks' do
  762. subject['SMBUser'] = 'username'
  763. expect(subject['SMBUser']).to eq('username')
  764. expect(subject['USER_ATTR']).to be(nil)
  765. expect(subject['username']).to be(nil)
  766. end
  767. it 'should allow setting a generic key' do
  768. subject['username'] = 'username'
  769. expect(subject['SMBUser']).to eq('username')
  770. expect(subject['USER_ATTR']).to eq('username')
  771. expect(subject['username']).to eq('username')
  772. end
  773. it 'should allow setting multiple keys with fallbacks' do
  774. subject['username'] = 'username_generic'
  775. subject['user_attr'] = 'username_attr'
  776. subject['smbuser'] = 'username_smb'
  777. expect(subject['SMBUser']).to eq('username_smb')
  778. expect(subject['USER_ATTR']).to eq('username_attr')
  779. expect(subject['username']).to eq('username_generic')
  780. end
  781. it 'should use the fallback in preference of the option default value' do
  782. subject['domain'] = 'example.local'
  783. expect(subject['SMBDomain']).to eq('example.local')
  784. end
  785. context 'when the key has been set in the framework datastore' do
  786. it 'should use the framework datastore if it is set' do
  787. framework_datastore['SMBUser'] = 'global_username_value'
  788. framework_datastore['SMBDomain'] = 'global_domain_value'
  789. expect(subject['SMBUser']).to eq 'global_username_value'
  790. expect(subject['USER_ATTR']).to eq nil
  791. expect(subject['SMBDomain']).to eq 'global_domain_value'
  792. end
  793. it 'should use the framework fallback datastore value if it is set' do
  794. framework_datastore['username'] = 'global_username_value'
  795. framework_datastore['domain'] = 'global_domain_value'
  796. expect(subject['SMBUser']).to eq 'global_username_value'
  797. expect(subject['USER_ATTR']).to eq 'global_username_value'
  798. expect(subject['SMBDomain']).to eq 'global_domain_value'
  799. end
  800. it 'should fallback to option default value if the parent datastore is unset' do
  801. framework_datastore.unset('SMBUser')
  802. framework_datastore.unset('SMBDomain')
  803. # expect(subject['SMBUser']).to eq nil
  804. # expect(subject['USER_ATTR']).to eq nil
  805. expect(subject['SMBDomain']).to eq 'WORKGROUP'
  806. end
  807. end
  808. end
  809. end
  810. describe '#[]=' do
  811. context 'when the datastore has aliases' do
  812. subject do
  813. datastore_with_aliases
  814. end
  815. [
  816. nil,
  817. false,
  818. '',
  819. 'new_value'
  820. ].each do |value|
  821. context "when the value is #{value.inspect}" do
  822. it 'should allow setting datastore values with the new option name' do
  823. subject['NewOptionName'] = value
  824. expect(subject['NewOptionName']).to eq(value)
  825. expect(subject['OLD_OPTION_NAME']).to eq(value)
  826. end
  827. it 'should allow setting datastore values with the old option name' do
  828. subject['OLD_OPTION_NAME'] = value
  829. expect(subject['NewOptionName']).to eq(value)
  830. expect(subject['OLD_OPTION_NAME']).to eq(value)
  831. end
  832. end
  833. end
  834. end
  835. context 'when the datastore has fallbacks' do
  836. subject do
  837. datastore_with_fallbacks
  838. end
  839. it 'should allow setting a key with fallbacks' do
  840. subject['SMBUser'] = 'username'
  841. expect(subject['SMBUser']).to eq('username')
  842. expect(subject['USER_ATTR']).to be(nil)
  843. expect(subject['username']).to be(nil)
  844. end
  845. it 'should allow setting a generic key' do
  846. subject['username'] = 'username'
  847. expect(subject['SMBUser']).to eq('username')
  848. expect(subject['USER_ATTR']).to eq('username')
  849. expect(subject['username']).to eq('username')
  850. end
  851. it 'should allow setting multiple keys with fallbacks' do
  852. subject['username'] = 'username_generic'
  853. subject['user_attr'] = 'username_attr'
  854. subject['smbuser'] = 'username_smb'
  855. expect(subject['SMBUser']).to eq('username_smb')
  856. expect(subject['USER_ATTR']).to eq('username_attr')
  857. expect(subject['username']).to eq('username_generic')
  858. end
  859. it 'should use the fallback in preference of the option default value' do
  860. subject['domain'] = 'example.local'
  861. expect(subject['SMBDomain']).to eq('example.local')
  862. end
  863. end
  864. end
  865. end
  866. describe 'testing all the things' do
  867. context 'when the datastore has simple options' do
  868. subject do
  869. datastore_with_simple_options
  870. end
  871. [
  872. { option_default_value: nil, set_key: 'foo', set_value: nil },
  873. { option_default_value: '', set_key: 'foo', set_value: nil },
  874. { option_default_value: 'default_value', set_key: 'foo', set_value: nil },
  875. { option_default_value: nil, set_key: 'foo', set_value: '' },
  876. { option_default_value: '', set_key: 'foo', set_value: '' },
  877. { option_default_value: 'default_value', set_key: 'foo', set_value: '' },
  878. { option_default_value: nil, set_key: 'foo', set_value: 'set_value' },
  879. { option_default_value: '', set_key: 'foo', set_value: 'set_value' },
  880. { option_default_value: 'default_value', set_key: 'foo', set_value: 'set_value' },
  881. ].each do |test|
  882. context "when the option default value is #{test[:default_value]}" do
  883. option_default_value = test[:option_default_value]
  884. set_key = test[:set_key]
  885. set_value = test[:set_value]
  886. read_key = test[:set_key] || test[:read_key]
  887. before(:each) do
  888. subject.options['foo'].send(:default=, option_default_value)
  889. end
  890. # Test permutations, ints used for readability
  891. [
  892. # nothing changed on module
  893. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  894. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  895. { mod_set: 0, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  896. # module datastore unset
  897. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  898. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  899. { mod_set: 0, mod_unset: 1, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  900. # module datastore set
  901. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  902. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  903. { mod_set: 1, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  904. ].each do |opts|
  905. context "when #{opts.inspect}" do
  906. it 'returns the expected value' do
  907. subject[set_key] = set_value if opts[:mod_set] == 1
  908. subject.unset(set_key) if opts[:mod_unset] == 1
  909. framework_datastore[set_key] = set_value if opts[:framework_set] == 1
  910. framework_datastore.unset(set_key) if opts[:framework_unset] == 1
  911. # Assertions
  912. expected = opts[:expected]
  913. search_result = subject.search_for(read_key)
  914. expect(search_result.value).to eq expected[:value]
  915. expect(human_readable_result_for(search_result)).to eq expected[:reason]
  916. expect(subject[read_key]).to eq expected[:value]
  917. expect(search_result.default?).to eq(expected[:is_default])
  918. end
  919. end
  920. end
  921. end
  922. end
  923. end
  924. context 'when the datastore has aliases options' do
  925. subject do
  926. datastore_with_aliases
  927. end
  928. # Ensure that both the new name and old name can be used interchangeably
  929. [
  930. { set_key: 'NewOptionName', read_key: 'NewOptionName' },
  931. { set_key: 'OLD_OPTION_NAME', read_key: 'OLD_OPTION_NAME' },
  932. { set_key: 'NewOptionName', read_key: 'OLD_OPTION_NAME' },
  933. { set_key: 'OLD_OPTION_NAME', read_key: 'NewOptionName' },
  934. ].each do |keys|
  935. set_key = keys[:set_key]
  936. read_key = keys[:read_key]
  937. context "when using #{keys[:set_key].inspect} to set the value and reading with #{keys[:read_key].inspect}" do
  938. [
  939. { option_default_value: nil, set_key: set_key, set_value: nil, read_key: read_key },
  940. { option_default_value: '', set_key: set_key, set_value: nil, read_key: read_key },
  941. { option_default_value: 'default_value', set_key: set_key, set_value: nil, read_key: read_key },
  942. { option_default_value: nil, set_key: set_key, set_value: '', read_key: read_key },
  943. { option_default_value: '', set_key: set_key, set_value: '', read_key: read_key },
  944. { option_default_value: 'default_value', set_key: set_key, set_value: '', read_key: read_key },
  945. { option_default_value: nil, set_key: set_key, set_value: 'set_value', read_key: read_key },
  946. { option_default_value: '', set_key: set_key, set_value: 'set_value', read_key: read_key },
  947. { option_default_value: 'default_value', set_key: set_key, set_value: 'set_value', read_key: read_key },
  948. ].each do |test|
  949. context "when the default value is #{test[:default_value]}" do
  950. option_default_value = test[:option_default_value]
  951. set_key = test[:set_key]
  952. set_value = test[:set_value]
  953. read_key = test[:set_key] || test[:read_key]
  954. before(:each) do
  955. subject.options['NewOptionName'].send(:default=, option_default_value)
  956. end
  957. # Test permutations, ints used for readability
  958. [
  959. # nothing changed on module
  960. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  961. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  962. { mod_set: 0, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  963. # module datastore unset
  964. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  965. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  966. { mod_set: 0, mod_unset: 1, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  967. # module datastore set
  968. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  969. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  970. { mod_set: 1, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  971. ].each do |opts|
  972. context "when #{opts.inspect}" do
  973. it 'returns the expected value' do
  974. subject[set_key] = set_value if opts[:mod_set] == 1
  975. subject.unset(set_key) if opts[:mod_unset] == 1
  976. framework_datastore[set_key] = set_value if opts[:framework_set] == 1
  977. framework_datastore.unset(set_key) if opts[:framework_unset] == 1
  978. # Assertions
  979. expected = opts[:expected]
  980. search_result = subject.search_for(read_key)
  981. expect(search_result.value).to eq expected[:value]
  982. expect(human_readable_result_for(search_result)).to eq expected[:reason]
  983. expect(subject[read_key]).to eq expected[:value]
  984. expect(search_result.default?).to eq(expected[:is_default])
  985. end
  986. end
  987. end
  988. end
  989. end
  990. end
  991. end
  992. end
  993. context 'when the datastore has defaults imported' do
  994. subject do
  995. complex_datastore_with_imported_defaults
  996. end
  997. [
  998. { option_default_value: nil, set_key: 'foo', set_value: nil },
  999. { option_default_value: '', set_key: 'foo', set_value: nil },
  1000. { option_default_value: 'default_value', set_key: 'foo', set_value: nil },
  1001. { option_default_value: nil, set_key: 'foo', set_value: '' },
  1002. { option_default_value: '', set_key: 'foo', set_value: '' },
  1003. { option_default_value: 'default_value', set_key: 'foo', set_value: '' },
  1004. { option_default_value: nil, set_key: 'foo', set_value: 'set_value' },
  1005. { option_default_value: '', set_key: 'foo', set_value: 'set_value' },
  1006. { option_default_value: 'default_value', set_key: 'foo', set_value: 'set_value' },
  1007. ].each do |test|
  1008. context "when the option default value is #{test[:option_default_value]}" do
  1009. option_default_value = test[:option_default_value]
  1010. import_default_value = 'test'
  1011. set_key = test[:set_key]
  1012. set_value = test[:set_value]
  1013. read_key = test[:set_key] || test[:read_key]
  1014. before(:each) do
  1015. subject.options[set_key].send(:default=, option_default_value)
  1016. subject.import_defaults_from_hash({ set_key => import_default_value }, imported_by: 'data_store_spec')
  1017. end
  1018. # Test permutations, ints used for readability
  1019. [
  1020. # nothing changed on module
  1021. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: import_default_value, reason: :module_data_store__imported_default, is_default: true } },
  1022. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: import_default_value, reason: :module_data_store__imported_default, is_default: true } },
  1023. { mod_set: 0, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  1024. # module datastore unset
  1025. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 0, expected: { value: import_default_value, reason: :module_data_store__imported_default, is_default: true } },
  1026. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 1, expected: { value: import_default_value, reason: :module_data_store__imported_default, is_default: true } },
  1027. { mod_set: 0, mod_unset: 1, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  1028. # module datastore set
  1029. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1030. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1031. { mod_set: 1, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1032. ].each do |opts|
  1033. context "when #{opts.inspect}" do
  1034. it 'returns the expected value' do
  1035. subject[set_key] = set_value if opts[:mod_set] == 1
  1036. subject.unset(set_key) if opts[:mod_unset] == 1
  1037. framework_datastore[set_key] = set_value if opts[:framework_set] == 1
  1038. framework_datastore.unset(set_key) if opts[:framework_unset] == 1
  1039. # Assertions
  1040. expected = opts[:expected]
  1041. search_result = subject.search_for(read_key)
  1042. expect(search_result.value).to eq expected[:value]
  1043. expect(human_readable_result_for(search_result)).to eq expected[:reason]
  1044. expect(subject[read_key]).to eq expected[:value]
  1045. expect(search_result.default?).to eq(expected[:is_default])
  1046. end
  1047. end
  1048. end
  1049. end
  1050. end
  1051. end
  1052. context 'when the datastore has aliases and fallbacks' do
  1053. subject do
  1054. s = default_subject.copy
  1055. options = Msf::OptionContainer.new(
  1056. [
  1057. Msf::OptString.new(
  1058. 'SMBDomain',
  1059. [true, 'The SMB username', 'WORKGROUP'],
  1060. aliases: ['WindowsDomain'],
  1061. fallbacks: ['domain']
  1062. )
  1063. ]
  1064. )
  1065. s.import_options(options)
  1066. s
  1067. end
  1068. context 'when the fallback value is set' do
  1069. before(:each) do
  1070. subject['domain'] = 'domain_fallback'
  1071. end
  1072. it 'supports reading with the option name' do
  1073. expect(subject['SMBDomain']).to eq('domain_fallback')
  1074. end
  1075. it 'supports reading with the alias name' do
  1076. expect(subject['WindowsDomain']).to eq('domain_fallback')
  1077. end
  1078. end
  1079. context 'when the alias and fallback value are set' do
  1080. before(:each) do
  1081. subject['domain'] = 'domain_fallback'
  1082. subject['WindowsDomain'] = 'WindowsDomain'
  1083. end
  1084. it 'supports reading with the option name' do
  1085. expect(subject['SMBDomain']).to eq('WindowsDomain')
  1086. end
  1087. it 'supports reading with the alias name' do
  1088. expect(subject['SMBDomain']).to eq('WindowsDomain')
  1089. end
  1090. end
  1091. # Ensure that both the new name and old name can be used interchangeably, as well as fallbacks
  1092. [
  1093. { set_key: 'SMBDomain', read_key: 'SMBDomain' },
  1094. { set_key: 'WindowsDomain', read_key: 'WindowsDomain' },
  1095. { set_key: 'SMBDomain', read_key: 'WindowsDomain' },
  1096. { set_key: 'WindowsDomain', read_key: 'SMBDomain' },
  1097. ].each do |keys|
  1098. set_key = keys[:set_key]
  1099. read_key = keys[:read_key]
  1100. context "when using #{keys[:set_key].inspect} to set the value and reading with #{keys[:read_key].inspect}" do
  1101. [
  1102. { option_default_value: nil, set_key: set_key, set_value: nil, read_key: read_key },
  1103. { option_default_value: '', set_key: set_key, set_value: nil, read_key: read_key },
  1104. { option_default_value: 'default_value', set_key: set_key, set_value: nil, read_key: read_key },
  1105. { option_default_value: nil, set_key: set_key, set_value: '', read_key: read_key },
  1106. { option_default_value: '', set_key: set_key, set_value: '', read_key: read_key },
  1107. { option_default_value: 'default_value', set_key: set_key, set_value: '', read_key: read_key },
  1108. { option_default_value: nil, set_key: set_key, set_value: 'set_value', read_key: read_key },
  1109. { option_default_value: '', set_key: set_key, set_value: 'set_value', read_key: read_key },
  1110. { option_default_value: 'default_value', set_key: set_key, set_value: 'set_value', read_key: read_key },
  1111. ].each do |test|
  1112. context "when the default value is #{test[:default_value]}" do
  1113. option_default_value = test[:option_default_value]
  1114. set_key = test[:set_key]
  1115. set_value = test[:set_value]
  1116. read_key = test[:set_key] || test[:read_key]
  1117. before(:each) do
  1118. subject.options['SMBDomain'].send(:default=, option_default_value)
  1119. end
  1120. # Test permutations, ints used for readability
  1121. [
  1122. # nothing changed on module
  1123. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  1124. { mod_set: 0, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  1125. { mod_set: 0, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  1126. # module datastore unset
  1127. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 0, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  1128. { mod_set: 0, mod_unset: 1, framework_set: 0, framework_unset: 1, expected: { value: option_default_value, reason: :module_data_store__option_default, is_default: true } },
  1129. { mod_set: 0, mod_unset: 1, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :global_data_store__user_defined, is_default: false } },
  1130. # module datastore set
  1131. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1132. { mod_set: 1, mod_unset: 0, framework_set: 0, framework_unset: 1, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1133. { mod_set: 1, mod_unset: 0, framework_set: 1, framework_unset: 0, expected: { value: set_value, reason: :module_data_store__user_defined, is_default: false } },
  1134. ].each do |opts|
  1135. context "when #{opts.inspect}" do
  1136. it 'returns the expected value' do
  1137. subject[set_key] = set_value if opts[:mod_set] == 1
  1138. subject.unset(set_key) if opts[:mod_unset] == 1
  1139. framework_datastore[set_key] = set_value if opts[:framework_set] == 1
  1140. framework_datastore.unset(set_key) if opts[:framework_unset] == 1
  1141. # Assertions
  1142. expected = opts[:expected]
  1143. search_result = subject.search_for(read_key)
  1144. expect(search_result.value).to eq expected[:value]
  1145. expect(human_readable_result_for(search_result)).to eq expected[:reason]
  1146. expect(subject[read_key]).to eq expected[:value]
  1147. expect(search_result.default?).to eq(expected[:is_default])
  1148. end
  1149. end
  1150. end
  1151. end
  1152. end
  1153. end
  1154. end
  1155. end
  1156. end
  1157. end