ldap_spec.rb 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. # Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Ldap do
  4. describe 'initialization config parameters' do
  5. # required as 'let' to perform test based
  6. # expectations and reuse it in mock_initialization
  7. # as return param of Net::LDAP.new
  8. let(:mocked_ldap) { double(bind: true) }
  9. def mock_initialization(given:, expected:)
  10. allow(Net::LDAP).to receive(:new).with(expected).and_return(mocked_ldap)
  11. described_class.new(given)
  12. end
  13. it 'uses explicit host and port' do
  14. config = {
  15. host: 'localhost',
  16. port: 1337,
  17. }
  18. mock_initialization(
  19. given: config,
  20. expected: config,
  21. )
  22. end
  23. describe 'bind credentials' do
  24. it 'uses given credentials' do
  25. config = {
  26. host: 'localhost',
  27. port: 1337,
  28. bind_user: 'JohnDoe',
  29. bind_pw: 'zammad',
  30. }
  31. params = {
  32. host: 'localhost',
  33. port: 1337,
  34. }
  35. allow(mocked_ldap).to receive(:auth).with(config[:bind_user], config[:bind_pw])
  36. mock_initialization(
  37. given: config,
  38. expected: params,
  39. )
  40. end
  41. it 'requires bind_user' do
  42. config = {
  43. host: 'localhost',
  44. port: 1337,
  45. bind_pw: 'zammad',
  46. }
  47. params = {
  48. host: 'localhost',
  49. port: 1337,
  50. }
  51. allow(mocked_ldap).to receive(:auth)
  52. mock_initialization(
  53. given: config,
  54. expected: params,
  55. )
  56. expect(mocked_ldap).not_to have_received(:auth).with(config[:bind_user], config[:bind_pw])
  57. end
  58. it 'requires bind_pw' do
  59. config = {
  60. host: 'localhost',
  61. port: 1337,
  62. bind_user: 'JohnDoe',
  63. }
  64. params = {
  65. host: 'localhost',
  66. port: 1337,
  67. }
  68. allow(mocked_ldap).to receive(:auth)
  69. mock_initialization(
  70. given: config,
  71. expected: params,
  72. )
  73. expect(mocked_ldap).not_to have_received(:auth).with(config[:bind_user], config[:bind_pw])
  74. end
  75. end
  76. it 'extracts port from host' do
  77. config = {
  78. host: 'localhost:1337'
  79. }
  80. params = {
  81. host: 'localhost',
  82. port: 1337,
  83. }
  84. mock_initialization(
  85. given: config,
  86. expected: params,
  87. )
  88. end
  89. describe 'host_url' do
  90. it 'parses protocol and host' do
  91. config = {
  92. host_url: 'ldaps://localhost'
  93. }
  94. params = {
  95. host: 'localhost',
  96. port: 636,
  97. encryption: Hash
  98. }
  99. mock_initialization(
  100. given: config,
  101. expected: params,
  102. )
  103. end
  104. it 'prefers parsing over explicit parameters' do
  105. config = {
  106. host: 'anotherhost',
  107. port: 7777,
  108. host_url: 'ldap://localhost:389'
  109. }
  110. params = {
  111. host: 'localhost',
  112. port: 389,
  113. }
  114. mock_initialization(
  115. given: config,
  116. expected: params,
  117. )
  118. end
  119. end
  120. it 'falls back to default ldap port' do
  121. config = {
  122. host: 'localhost',
  123. }
  124. params = {
  125. host: 'localhost',
  126. port: 389,
  127. }
  128. mock_initialization(
  129. given: config,
  130. expected: params,
  131. )
  132. end
  133. it 'uses explicit ssl' do
  134. config = {
  135. host: 'localhost',
  136. port: 1337,
  137. ssl: true,
  138. }
  139. expected = {
  140. host: 'localhost',
  141. port: 1337,
  142. encryption: Hash,
  143. }
  144. mock_initialization(
  145. given: config,
  146. expected: expected,
  147. )
  148. end
  149. end
  150. describe 'instance methods' do
  151. # required as 'let' to perform test based
  152. # expectations and reuse it in 'let' instance
  153. # as return param of Net::LDAP.new
  154. let(:mocked_ldap) { double(bind: true) }
  155. let(:instance) do
  156. allow(Net::LDAP).to receive(:new).and_return(mocked_ldap)
  157. described_class.new(
  158. host: 'localhost',
  159. port: 1337,
  160. )
  161. end
  162. describe '#preferences' do
  163. it 'responds to #preferences' do
  164. expect(instance).to respond_to(:preferences)
  165. end
  166. it 'returns preferences' do
  167. attributes = {
  168. namingcontexts: ['ou=dep1,ou=org', 'ou=dep2,ou=org']
  169. }
  170. allow(mocked_ldap).to receive(:search_root_dse).and_return(attributes)
  171. expect(instance.preferences).to eq(attributes)
  172. end
  173. end
  174. describe '#search' do
  175. let(:base) { 'DC=domain,DC=tld' }
  176. let(:filter) { '(objectClass=user)' }
  177. it 'responds to #search' do
  178. expect(instance).to respond_to(:search)
  179. end
  180. it 'performs search for a filter, base and scope and yields of returned entries' do
  181. scope = Net::LDAP::SearchScope_BaseObject
  182. additional = {
  183. base: base,
  184. scope: scope,
  185. }
  186. expected = {
  187. filter: filter,
  188. base: base,
  189. scope: scope,
  190. }
  191. yield_entry = build(:ldap_entry)
  192. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  193. check_entry = nil
  194. instance.search(filter, **additional) { |entry| check_entry = entry }
  195. expect(check_entry).to eq(yield_entry)
  196. end
  197. it 'falls back to whole subtree scope search' do
  198. additional = {
  199. base: base,
  200. }
  201. expected = {
  202. filter: filter,
  203. base: base,
  204. scope: Net::LDAP::SearchScope_WholeSubtree,
  205. }
  206. yield_entry = build(:ldap_entry)
  207. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  208. check_entry = nil
  209. instance.search(filter, **additional) { |entry| check_entry = entry }
  210. expect(check_entry).to eq(yield_entry)
  211. end
  212. it 'falls back to base_dn configuration parameter' do
  213. expected = {
  214. filter: filter,
  215. base: base,
  216. scope: Net::LDAP::SearchScope_WholeSubtree,
  217. }
  218. allow(Net::LDAP).to receive(:new).and_return(mocked_ldap)
  219. instance = described_class.new(
  220. host: 'localhost',
  221. port: 1337,
  222. base_dn: base,
  223. )
  224. yield_entry = build(:ldap_entry)
  225. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  226. check_entry = nil
  227. instance.search(filter) { |entry| check_entry = entry }
  228. expect(check_entry).to eq(yield_entry)
  229. end
  230. end
  231. describe '#entries?' do
  232. let(:filter) { '(objectClass=user)' }
  233. it 'responds to #entries?' do
  234. expect(instance).to respond_to(:entries?)
  235. end
  236. it 'returns true if entries are present' do
  237. params = {
  238. filter: filter
  239. }
  240. allow(mocked_ldap).to receive(:search).with(include(params)).and_yield(build(:ldap_entry)).and_return(nil)
  241. expect(instance.entries?(filter)).to be true
  242. end
  243. it 'returns false if no entries are present' do
  244. params = {
  245. filter: filter
  246. }
  247. allow(mocked_ldap).to receive(:search).with(include(params)).and_return(true)
  248. expect(instance.entries?(filter)).to be false
  249. end
  250. end
  251. end
  252. end