ldap_spec.rb 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. # Copyright (C) 2012-2024 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. it 'falls back to default ldap port' do
  90. config = {
  91. host: 'localhost',
  92. }
  93. params = {
  94. host: 'localhost',
  95. port: 389,
  96. }
  97. mock_initialization(
  98. given: config,
  99. expected: params,
  100. )
  101. end
  102. it 'uses explicit ssl' do
  103. config = {
  104. host: 'localhost',
  105. port: 1337,
  106. ssl: 'ssl',
  107. }
  108. expected = {
  109. host: 'localhost',
  110. port: 1337,
  111. encryption: Hash,
  112. }
  113. mock_initialization(
  114. given: config,
  115. expected: expected,
  116. )
  117. end
  118. it 'uses ssl with default port' do
  119. config = {
  120. host: 'localhost',
  121. ssl: 'ssl',
  122. }
  123. expected = {
  124. host: 'localhost',
  125. port: 636,
  126. encryption: {
  127. method: :simple_tls,
  128. tls_options: {
  129. verify_mode: 0
  130. }
  131. }
  132. }
  133. mock_initialization(
  134. given: config,
  135. expected: expected,
  136. )
  137. end
  138. it 'uses starttls with default port' do
  139. config = {
  140. host: 'localhost',
  141. ssl: 'starttls',
  142. }
  143. expected = {
  144. host: 'localhost',
  145. port: 389,
  146. encryption: {
  147. method: :start_tls,
  148. tls_options: {
  149. verify_mode: 0
  150. }
  151. }
  152. }
  153. mock_initialization(
  154. given: config,
  155. expected: expected,
  156. )
  157. end
  158. end
  159. describe 'instance methods' do
  160. # required as 'let' to perform test based
  161. # expectations and reuse it in 'let' instance
  162. # as return param of Net::LDAP.new
  163. let(:mocked_ldap) { double(bind: true) }
  164. let(:instance) do
  165. allow(Net::LDAP).to receive(:new).and_return(mocked_ldap)
  166. described_class.new(
  167. host: 'localhost',
  168. port: 1337,
  169. )
  170. end
  171. describe '#preferences' do
  172. it 'responds to #preferences' do
  173. expect(instance).to respond_to(:preferences)
  174. end
  175. it 'returns preferences' do
  176. attributes = {
  177. namingcontexts: ['ou=dep1,ou=org', 'ou=dep2,ou=org']
  178. }
  179. allow(mocked_ldap).to receive(:search_root_dse).and_return(attributes)
  180. expect(instance.preferences).to eq(attributes)
  181. end
  182. end
  183. describe '#search' do
  184. let(:base) { 'DC=domain,DC=tld' }
  185. let(:filter) { '(objectClass=user)' }
  186. it 'responds to #search' do
  187. expect(instance).to respond_to(:search)
  188. end
  189. it 'performs search for a filter, base and scope and yields of returned entries' do
  190. scope = Net::LDAP::SearchScope_BaseObject
  191. additional = {
  192. base: base,
  193. scope: scope,
  194. }
  195. expected = {
  196. filter: filter,
  197. base: base,
  198. scope: scope,
  199. }
  200. yield_entry = build(:ldap_entry)
  201. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  202. check_entry = nil
  203. instance.search(filter, **additional) { |entry| check_entry = entry }
  204. expect(check_entry).to eq(yield_entry)
  205. end
  206. it 'falls back to whole subtree scope search' do
  207. additional = {
  208. base: base,
  209. }
  210. expected = {
  211. filter: filter,
  212. base: base,
  213. scope: Net::LDAP::SearchScope_WholeSubtree,
  214. }
  215. yield_entry = build(:ldap_entry)
  216. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  217. check_entry = nil
  218. instance.search(filter, **additional) { |entry| check_entry = entry }
  219. expect(check_entry).to eq(yield_entry)
  220. end
  221. it 'falls back to base_dn configuration parameter' do
  222. expected = {
  223. filter: filter,
  224. base: base,
  225. scope: Net::LDAP::SearchScope_WholeSubtree,
  226. }
  227. allow(Net::LDAP).to receive(:new).and_return(mocked_ldap)
  228. instance = described_class.new(
  229. host: 'localhost',
  230. port: 1337,
  231. base_dn: base,
  232. )
  233. yield_entry = build(:ldap_entry)
  234. allow(mocked_ldap).to receive(:search).with(include(expected)).and_yield(yield_entry).and_return(true)
  235. check_entry = nil
  236. instance.search(filter) { |entry| check_entry = entry }
  237. expect(check_entry).to eq(yield_entry)
  238. end
  239. end
  240. describe '#entries?' do
  241. let(:filter) { '(objectClass=user)' }
  242. it 'responds to #entries?' do
  243. expect(instance).to respond_to(:entries?)
  244. end
  245. it 'returns true if entries are present' do
  246. params = {
  247. filter: filter
  248. }
  249. allow(mocked_ldap).to receive(:search).with(include(params)).and_yield(build(:ldap_entry)).and_return(nil)
  250. expect(instance.entries?(filter)).to be true
  251. end
  252. it 'returns false if no entries are present' do
  253. params = {
  254. filter: filter
  255. }
  256. allow(mocked_ldap).to receive(:search).with(include(params)).and_return(true)
  257. expect(instance.entries?(filter)).to be false
  258. end
  259. end
  260. end
  261. end