activeldap3 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (443) hide show
  1. data/.gitignore +6 -0
  2. data/CHANGES +691 -0
  3. data/COPYING +340 -0
  4. data/LICENSE +58 -0
  5. data/README +154 -0
  6. data/Rakefile +121 -0
  7. data/TODO +32 -0
  8. data/activeldap3.gemspec +18 -0
  9. data/benchmark/bench-al.rb +263 -0
  10. data/benchmark/config.yaml.sample +5 -0
  11. data/examples/al-admin/README +182 -0
  12. data/examples/al-admin/Rakefile +16 -0
  13. data/examples/al-admin/app/controllers/account_controller.rb +60 -0
  14. data/examples/al-admin/app/controllers/application_controller.rb +46 -0
  15. data/examples/al-admin/app/controllers/attributes_controller.rb +17 -0
  16. data/examples/al-admin/app/controllers/directory_controller.rb +49 -0
  17. data/examples/al-admin/app/controllers/object_classes_controller.rb +17 -0
  18. data/examples/al-admin/app/controllers/syntaxes_controller.rb +17 -0
  19. data/examples/al-admin/app/controllers/users_controller.rb +66 -0
  20. data/examples/al-admin/app/controllers/welcome_controller.rb +12 -0
  21. data/examples/al-admin/app/helpers/account_helper.rb +2 -0
  22. data/examples/al-admin/app/helpers/application_helper.rb +46 -0
  23. data/examples/al-admin/app/helpers/attributes_helper.rb +15 -0
  24. data/examples/al-admin/app/helpers/directory_helper.rb +7 -0
  25. data/examples/al-admin/app/helpers/object_classes_helper.rb +10 -0
  26. data/examples/al-admin/app/helpers/syntaxes_helper.rb +10 -0
  27. data/examples/al-admin/app/helpers/url_helper.rb +17 -0
  28. data/examples/al-admin/app/helpers/users_helper.rb +17 -0
  29. data/examples/al-admin/app/helpers/welcome_helper.rb +2 -0
  30. data/examples/al-admin/app/models/entry.rb +23 -0
  31. data/examples/al-admin/app/models/ldap_user.rb +58 -0
  32. data/examples/al-admin/app/models/user.rb +91 -0
  33. data/examples/al-admin/app/views/_entry/_attributes_information.html.erb +29 -0
  34. data/examples/al-admin/app/views/_entry/_entry.html.erb +15 -0
  35. data/examples/al-admin/app/views/_schema/_aliases.html.erb +7 -0
  36. data/examples/al-admin/app/views/_switcher/_after.html.erb +2 -0
  37. data/examples/al-admin/app/views/_switcher/_before.html.erb +5 -0
  38. data/examples/al-admin/app/views/account/login.html.erb +26 -0
  39. data/examples/al-admin/app/views/account/sign_up.html.erb +29 -0
  40. data/examples/al-admin/app/views/attributes/_attributes.html.erb +19 -0
  41. data/examples/al-admin/app/views/attributes/_detail.html.erb +29 -0
  42. data/examples/al-admin/app/views/attributes/index.html.erb +3 -0
  43. data/examples/al-admin/app/views/attributes/show.html.erb +31 -0
  44. data/examples/al-admin/app/views/directory/_tree.html.erb +10 -0
  45. data/examples/al-admin/app/views/directory/_tree_view_js.html.erb +25 -0
  46. data/examples/al-admin/app/views/directory/index.html.erb +13 -0
  47. data/examples/al-admin/app/views/directory/populate.html.erb +2 -0
  48. data/examples/al-admin/app/views/layouts/_footer.html.erb +11 -0
  49. data/examples/al-admin/app/views/layouts/_header_menu.html.erb +10 -0
  50. data/examples/al-admin/app/views/layouts/_main_menu.html.erb +18 -0
  51. data/examples/al-admin/app/views/layouts/application.html.erb +57 -0
  52. data/examples/al-admin/app/views/object_classes/_attributes.html.erb +28 -0
  53. data/examples/al-admin/app/views/object_classes/_object_classes.html.erb +19 -0
  54. data/examples/al-admin/app/views/object_classes/index.html.erb +3 -0
  55. data/examples/al-admin/app/views/object_classes/show.html.erb +39 -0
  56. data/examples/al-admin/app/views/syntaxes/_detail.html.erb +14 -0
  57. data/examples/al-admin/app/views/syntaxes/_syntaxes.html.erb +19 -0
  58. data/examples/al-admin/app/views/syntaxes/index.html.erb +3 -0
  59. data/examples/al-admin/app/views/syntaxes/show.html.erb +22 -0
  60. data/examples/al-admin/app/views/users/_attributes_update_form.html.erb +42 -0
  61. data/examples/al-admin/app/views/users/_form.html.erb +13 -0
  62. data/examples/al-admin/app/views/users/_object_classes_update_form.html.erb +46 -0
  63. data/examples/al-admin/app/views/users/_password_change_form.html.erb +20 -0
  64. data/examples/al-admin/app/views/users/edit.html.erb +15 -0
  65. data/examples/al-admin/app/views/users/index.html.erb +10 -0
  66. data/examples/al-admin/app/views/users/show.html.erb +11 -0
  67. data/examples/al-admin/app/views/welcome/index.html.erb +13 -0
  68. data/examples/al-admin/config.ru +7 -0
  69. data/examples/al-admin/config/boot.rb +110 -0
  70. data/examples/al-admin/config/database.yml.example +19 -0
  71. data/examples/al-admin/config/environment.rb +73 -0
  72. data/examples/al-admin/config/environments/development.rb +17 -0
  73. data/examples/al-admin/config/environments/production.rb +24 -0
  74. data/examples/al-admin/config/environments/test.rb +22 -0
  75. data/examples/al-admin/config/initializers/exception_notifier.rb +2 -0
  76. data/examples/al-admin/config/initializers/fast_gettext.rb +3 -0
  77. data/examples/al-admin/config/initializers/inflections.rb +10 -0
  78. data/examples/al-admin/config/initializers/mime_types.rb +5 -0
  79. data/examples/al-admin/config/initializers/session_store.rb +23 -0
  80. data/examples/al-admin/config/ldap.yml.example +21 -0
  81. data/examples/al-admin/config/routes.rb +58 -0
  82. data/examples/al-admin/db/migrate/001_create_users.rb +16 -0
  83. data/examples/al-admin/doc/README_FOR_APP +2 -0
  84. data/examples/al-admin/lib/authenticated_system.rb +124 -0
  85. data/examples/al-admin/lib/authenticated_test_helper.rb +113 -0
  86. data/examples/al-admin/lib/ldap_test_helper.rb +38 -0
  87. data/examples/al-admin/lib/tasks/testing.rake +10 -0
  88. data/examples/al-admin/po/en/al-admin.po +343 -0
  89. data/examples/al-admin/po/ja/al-admin.po +343 -0
  90. data/examples/al-admin/po/nl/al-admin.po +380 -0
  91. data/examples/al-admin/public/.htaccess +40 -0
  92. data/examples/al-admin/public/404.html +30 -0
  93. data/examples/al-admin/public/500.html +30 -0
  94. data/examples/al-admin/public/dispatch.cgi +10 -0
  95. data/examples/al-admin/public/dispatch.fcgi +24 -0
  96. data/examples/al-admin/public/dispatch.rb +10 -0
  97. data/examples/al-admin/public/favicon.ico +0 -0
  98. data/examples/al-admin/public/images/active-ldap.svg +6351 -0
  99. data/examples/al-admin/public/images/al-admin.png +0 -0
  100. data/examples/al-admin/public/images/al-admin.svg +6371 -0
  101. data/examples/al-admin/public/images/header-background.png +0 -0
  102. data/examples/al-admin/public/images/header-background.svg +99 -0
  103. data/examples/al-admin/public/images/rails.png +0 -0
  104. data/examples/al-admin/public/images/spinelz/accordion_tab_left_active.gif +0 -0
  105. data/examples/al-admin/public/images/spinelz/accordion_tab_left_inactive.gif +0 -0
  106. data/examples/al-admin/public/images/spinelz/accordion_tab_middle_active.gif +0 -0
  107. data/examples/al-admin/public/images/spinelz/accordion_tab_middle_inactive.gif +0 -0
  108. data/examples/al-admin/public/images/spinelz/accordion_tab_right_active.gif +0 -0
  109. data/examples/al-admin/public/images/spinelz/accordion_tab_right_inactive.gif +0 -0
  110. data/examples/al-admin/public/images/spinelz/balloon_back.gif +0 -0
  111. data/examples/al-admin/public/images/spinelz/balloon_bottom_left.gif +0 -0
  112. data/examples/al-admin/public/images/spinelz/balloon_bottom_middle.gif +0 -0
  113. data/examples/al-admin/public/images/spinelz/balloon_bottom_right.gif +0 -0
  114. data/examples/al-admin/public/images/spinelz/balloon_left_down_arrow.gif +0 -0
  115. data/examples/al-admin/public/images/spinelz/balloon_left_up_arrow.gif +0 -0
  116. data/examples/al-admin/public/images/spinelz/balloon_middle_left.gif +0 -0
  117. data/examples/al-admin/public/images/spinelz/balloon_middle_right.gif +0 -0
  118. data/examples/al-admin/public/images/spinelz/balloon_right_down_arrow.gif +0 -0
  119. data/examples/al-admin/public/images/spinelz/balloon_right_up_arrow.gif +0 -0
  120. data/examples/al-admin/public/images/spinelz/balloon_top_left.gif +0 -0
  121. data/examples/al-admin/public/images/spinelz/balloon_top_middle.gif +0 -0
  122. data/examples/al-admin/public/images/spinelz/balloon_top_right.gif +0 -0
  123. data/examples/al-admin/public/images/spinelz/barchart_h.gif +0 -0
  124. data/examples/al-admin/public/images/spinelz/barchart_v.gif +0 -0
  125. data/examples/al-admin/public/images/spinelz/button.gif +0 -0
  126. data/examples/al-admin/public/images/spinelz/calendar_default_handler.gif +0 -0
  127. data/examples/al-admin/public/images/spinelz/calendar_delete.gif +0 -0
  128. data/examples/al-admin/public/images/spinelz/calendar_next.gif +0 -0
  129. data/examples/al-admin/public/images/spinelz/calendar_next_second.gif +0 -0
  130. data/examples/al-admin/public/images/spinelz/calendar_pre.gif +0 -0
  131. data/examples/al-admin/public/images/spinelz/calendar_pre_second.gif +0 -0
  132. data/examples/al-admin/public/images/spinelz/calendar_private_icon.gif +0 -0
  133. data/examples/al-admin/public/images/spinelz/calendar_schedule.gif +0 -0
  134. data/examples/al-admin/public/images/spinelz/calender_back.gif +0 -0
  135. data/examples/al-admin/public/images/spinelz/calender_back_second.gif +0 -0
  136. data/examples/al-admin/public/images/spinelz/datepicker2_back.gif +0 -0
  137. data/examples/al-admin/public/images/spinelz/datepicker2_back_second.gif +0 -0
  138. data/examples/al-admin/public/images/spinelz/datepicker2_next.gif +0 -0
  139. data/examples/al-admin/public/images/spinelz/datepicker2_next_second.gif +0 -0
  140. data/examples/al-admin/public/images/spinelz/datepicker2_pre.gif +0 -0
  141. data/examples/al-admin/public/images/spinelz/datepicker2_pre_second.gif +0 -0
  142. data/examples/al-admin/public/images/spinelz/datepicker_back.gif +0 -0
  143. data/examples/al-admin/public/images/spinelz/datepicker_back_second.gif +0 -0
  144. data/examples/al-admin/public/images/spinelz/datepicker_next.gif +0 -0
  145. data/examples/al-admin/public/images/spinelz/datepicker_next_second.gif +0 -0
  146. data/examples/al-admin/public/images/spinelz/datepicker_pre.gif +0 -0
  147. data/examples/al-admin/public/images/spinelz/datepicker_pre_second.gif +0 -0
  148. data/examples/al-admin/public/images/spinelz/grid_down.gif +0 -0
  149. data/examples/al-admin/public/images/spinelz/grid_state.gif +0 -0
  150. data/examples/al-admin/public/images/spinelz/grid_up.gif +0 -0
  151. data/examples/al-admin/public/images/spinelz/icon_day.gif +0 -0
  152. data/examples/al-admin/public/images/spinelz/icon_month.gif +0 -0
  153. data/examples/al-admin/public/images/spinelz/icon_week.gif +0 -0
  154. data/examples/al-admin/public/images/spinelz/menubar_back.gif +0 -0
  155. data/examples/al-admin/public/images/spinelz/menubar_subcontents_back.gif +0 -0
  156. data/examples/al-admin/public/images/spinelz/navPanel_tab_left_active.gif +0 -0
  157. data/examples/al-admin/public/images/spinelz/navPanel_tab_left_inactive.gif +0 -0
  158. data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_active.gif +0 -0
  159. data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_inactive.gif +0 -0
  160. data/examples/al-admin/public/images/spinelz/navPanel_tab_right_active.gif +0 -0
  161. data/examples/al-admin/public/images/spinelz/navPanel_tab_right_inactive.gif +0 -0
  162. data/examples/al-admin/public/images/spinelz/select_date.gif +0 -0
  163. data/examples/al-admin/public/images/spinelz/selectabletable_selected.gif +0 -0
  164. data/examples/al-admin/public/images/spinelz/sideBarBox_about.gif +0 -0
  165. data/examples/al-admin/public/images/spinelz/sideBarBox_menu.gif +0 -0
  166. data/examples/al-admin/public/images/spinelz/sideBarBox_sample.gif +0 -0
  167. data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomActive.gif +0 -0
  168. data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomInactive.gif +0 -0
  169. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive.gif +0 -0
  170. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive2.gif +0 -0
  171. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive.gif +0 -0
  172. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive2.gif +0 -0
  173. data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopActive.gif +0 -0
  174. data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopInactive.gif +0 -0
  175. data/examples/al-admin/public/images/spinelz/sideBarBox_tabbar.gif +0 -0
  176. data/examples/al-admin/public/images/spinelz/sortableTable_down.gif +0 -0
  177. data/examples/al-admin/public/images/spinelz/sortableTable_normal.gif +0 -0
  178. data/examples/al-admin/public/images/spinelz/sortableTable_up.gif +0 -0
  179. data/examples/al-admin/public/images/spinelz/switcher_close.gif +0 -0
  180. data/examples/al-admin/public/images/spinelz/switcher_open.gif +0 -0
  181. data/examples/al-admin/public/images/spinelz/tabBox_close.gif +0 -0
  182. data/examples/al-admin/public/images/spinelz/tabBox_tabLeftActive.gif +0 -0
  183. data/examples/al-admin/public/images/spinelz/tabBox_tabLeftInactive.gif +0 -0
  184. data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleActive.gif +0 -0
  185. data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleInactive.gif +0 -0
  186. data/examples/al-admin/public/images/spinelz/tabBox_tabRightActive.gif +0 -0
  187. data/examples/al-admin/public/images/spinelz/tabBox_tabRightInactive.gif +0 -0
  188. data/examples/al-admin/public/images/spinelz/tab_bar.gif +0 -0
  189. data/examples/al-admin/public/images/spinelz/table_back.gif +0 -0
  190. data/examples/al-admin/public/images/spinelz/timepicker_clock.gif +0 -0
  191. data/examples/al-admin/public/images/spinelz/toolbar_close.gif +0 -0
  192. data/examples/al-admin/public/images/spinelz/toolbar_left.gif +0 -0
  193. data/examples/al-admin/public/images/spinelz/toolbar_max.gif +0 -0
  194. data/examples/al-admin/public/images/spinelz/toolbar_middle.gif +0 -0
  195. data/examples/al-admin/public/images/spinelz/toolbar_min.gif +0 -0
  196. data/examples/al-admin/public/images/spinelz/toolbar_next.gif +0 -0
  197. data/examples/al-admin/public/images/spinelz/toolbar_right.gif +0 -0
  198. data/examples/al-admin/public/images/spinelz/treeview_dir.gif +0 -0
  199. data/examples/al-admin/public/images/spinelz/treeview_file.gif +0 -0
  200. data/examples/al-admin/public/images/spinelz/treeview_group.gif +0 -0
  201. data/examples/al-admin/public/images/spinelz/treeview_group_special.gif +0 -0
  202. data/examples/al-admin/public/images/spinelz/treeview_state.gif +0 -0
  203. data/examples/al-admin/public/images/spinelz/treeview_user.gif +0 -0
  204. data/examples/al-admin/public/images/spinelz/window_bottom_left.gif +0 -0
  205. data/examples/al-admin/public/images/spinelz/window_bottom_middle.gif +0 -0
  206. data/examples/al-admin/public/images/spinelz/window_bottom_right.gif +0 -0
  207. data/examples/al-admin/public/images/spinelz/window_close.gif +0 -0
  208. data/examples/al-admin/public/images/spinelz/window_max.gif +0 -0
  209. data/examples/al-admin/public/images/spinelz/window_middle_left.gif +0 -0
  210. data/examples/al-admin/public/images/spinelz/window_middle_right.gif +0 -0
  211. data/examples/al-admin/public/images/spinelz/window_min.gif +0 -0
  212. data/examples/al-admin/public/images/spinelz/window_top_left.gif +0 -0
  213. data/examples/al-admin/public/images/spinelz/window_top_middle.gif +0 -0
  214. data/examples/al-admin/public/images/spinelz/window_top_right.gif +0 -0
  215. data/examples/al-admin/public/javascripts/application.js +2 -0
  216. data/examples/al-admin/public/javascripts/controls.js +963 -0
  217. data/examples/al-admin/public/javascripts/dragdrop.js +973 -0
  218. data/examples/al-admin/public/javascripts/effects.js +1128 -0
  219. data/examples/al-admin/public/javascripts/prototype.js +4320 -0
  220. data/examples/al-admin/public/javascripts/spinelz/accordion.js +185 -0
  221. data/examples/al-admin/public/javascripts/spinelz/ajaxHistory.js +157 -0
  222. data/examples/al-admin/public/javascripts/spinelz/balloon.js +287 -0
  223. data/examples/al-admin/public/javascripts/spinelz/barchart.js +524 -0
  224. data/examples/al-admin/public/javascripts/spinelz/calendar.js +3012 -0
  225. data/examples/al-admin/public/javascripts/spinelz/colorpicker.js +128 -0
  226. data/examples/al-admin/public/javascripts/spinelz/datepicker.js +438 -0
  227. data/examples/al-admin/public/javascripts/spinelz/grid.js +1391 -0
  228. data/examples/al-admin/public/javascripts/spinelz/grid_resizeEx.js +100 -0
  229. data/examples/al-admin/public/javascripts/spinelz/grid_sortabletableEx.js +129 -0
  230. data/examples/al-admin/public/javascripts/spinelz/inplaceEditorEx.js +148 -0
  231. data/examples/al-admin/public/javascripts/spinelz/menubar.js +232 -0
  232. data/examples/al-admin/public/javascripts/spinelz/navPanel.js +170 -0
  233. data/examples/al-admin/public/javascripts/spinelz/selectableTable.js +433 -0
  234. data/examples/al-admin/public/javascripts/spinelz/sideBarBox.js +282 -0
  235. data/examples/al-admin/public/javascripts/spinelz/sideBarBox_effects.js +83 -0
  236. data/examples/al-admin/public/javascripts/spinelz/sortableTable.js +270 -0
  237. data/examples/al-admin/public/javascripts/spinelz/switcher.js +78 -0
  238. data/examples/al-admin/public/javascripts/spinelz/tabBox.js +469 -0
  239. data/examples/al-admin/public/javascripts/spinelz/timepicker.js +384 -0
  240. data/examples/al-admin/public/javascripts/spinelz/toolbar.js +152 -0
  241. data/examples/al-admin/public/javascripts/spinelz/treeview.js +703 -0
  242. data/examples/al-admin/public/javascripts/spinelz/window.js +641 -0
  243. data/examples/al-admin/public/javascripts/spinelz/window_resizeEx.js +130 -0
  244. data/examples/al-admin/public/javascripts/spinelz_lib/builder.js +131 -0
  245. data/examples/al-admin/public/javascripts/spinelz_lib/controls.js +835 -0
  246. data/examples/al-admin/public/javascripts/spinelz_lib/dragdrop.js +944 -0
  247. data/examples/al-admin/public/javascripts/spinelz_lib/effects.js +1090 -0
  248. data/examples/al-admin/public/javascripts/spinelz_lib/json.js +139 -0
  249. data/examples/al-admin/public/javascripts/spinelz_lib/prototype.js +2515 -0
  250. data/examples/al-admin/public/javascripts/spinelz_lib/resize.js +215 -0
  251. data/examples/al-admin/public/javascripts/spinelz_lib/scriptaculous.js +51 -0
  252. data/examples/al-admin/public/javascripts/spinelz_lib/slider.js +278 -0
  253. data/examples/al-admin/public/javascripts/spinelz_lib/spinelz_util.js +1266 -0
  254. data/examples/al-admin/public/javascripts/spinelz_lib/unittest.js +564 -0
  255. data/examples/al-admin/public/robots.txt +1 -0
  256. data/examples/al-admin/public/stylesheets/account.css +41 -0
  257. data/examples/al-admin/public/stylesheets/attributes.css +1 -0
  258. data/examples/al-admin/public/stylesheets/base.css +104 -0
  259. data/examples/al-admin/public/stylesheets/detail.css +36 -0
  260. data/examples/al-admin/public/stylesheets/directory.css +22 -0
  261. data/examples/al-admin/public/stylesheets/object-classes.css +6 -0
  262. data/examples/al-admin/public/stylesheets/rails.css +35 -0
  263. data/examples/al-admin/public/stylesheets/spinelz/accordion.css +59 -0
  264. data/examples/al-admin/public/stylesheets/spinelz/balloon.css +151 -0
  265. data/examples/al-admin/public/stylesheets/spinelz/calendar.css +564 -0
  266. data/examples/al-admin/public/stylesheets/spinelz/datepicker.css +175 -0
  267. data/examples/al-admin/public/stylesheets/spinelz/grid.css +137 -0
  268. data/examples/al-admin/public/stylesheets/spinelz/menubar.css +78 -0
  269. data/examples/al-admin/public/stylesheets/spinelz/modal.css +22 -0
  270. data/examples/al-admin/public/stylesheets/spinelz/navPanel.css +58 -0
  271. data/examples/al-admin/public/stylesheets/spinelz/selectableTable.css +28 -0
  272. data/examples/al-admin/public/stylesheets/spinelz/sideBarBox.css +82 -0
  273. data/examples/al-admin/public/stylesheets/spinelz/sortableTable.css +51 -0
  274. data/examples/al-admin/public/stylesheets/spinelz/switcher.css +23 -0
  275. data/examples/al-admin/public/stylesheets/spinelz/tabBox.css +94 -0
  276. data/examples/al-admin/public/stylesheets/spinelz/timepicker.css +508 -0
  277. data/examples/al-admin/public/stylesheets/spinelz/toolbar.css +82 -0
  278. data/examples/al-admin/public/stylesheets/spinelz/treeview.css +121 -0
  279. data/examples/al-admin/public/stylesheets/spinelz/window.css +140 -0
  280. data/examples/al-admin/public/stylesheets/structure.css +81 -0
  281. data/examples/al-admin/public/stylesheets/syntaxes.css +1 -0
  282. data/examples/al-admin/public/stylesheets/users.css +13 -0
  283. data/examples/al-admin/public/stylesheets/welcome.css +0 -0
  284. data/examples/al-admin/script/about +3 -0
  285. data/examples/al-admin/script/console +3 -0
  286. data/examples/al-admin/script/dbconsole +3 -0
  287. data/examples/al-admin/script/destroy +3 -0
  288. data/examples/al-admin/script/generate +3 -0
  289. data/examples/al-admin/script/performance/benchmarker +3 -0
  290. data/examples/al-admin/script/performance/profiler +3 -0
  291. data/examples/al-admin/script/performance/request +3 -0
  292. data/examples/al-admin/script/plugin +3 -0
  293. data/examples/al-admin/script/process/inspector +3 -0
  294. data/examples/al-admin/script/process/reaper +3 -0
  295. data/examples/al-admin/script/process/spawner +3 -0
  296. data/examples/al-admin/script/runner +3 -0
  297. data/examples/al-admin/script/server +3 -0
  298. data/examples/al-admin/test/fixtures/users.yml +9 -0
  299. data/examples/al-admin/test/functional/account_controller_test.rb +12 -0
  300. data/examples/al-admin/test/functional/attributes_controller_test.rb +8 -0
  301. data/examples/al-admin/test/functional/directory_controller_test.rb +8 -0
  302. data/examples/al-admin/test/functional/object_classes_controller_test.rb +8 -0
  303. data/examples/al-admin/test/functional/syntaxes_controller_test.rb +8 -0
  304. data/examples/al-admin/test/functional/users_controller_test.rb +8 -0
  305. data/examples/al-admin/test/functional/welcome_controller_test.rb +8 -0
  306. data/examples/al-admin/test/integration/sign_up_test.rb +44 -0
  307. data/examples/al-admin/test/run-test.sh +3 -0
  308. data/examples/al-admin/test/test_helper.rb +52 -0
  309. data/examples/al-admin/test/unit/user_test.rb +12 -0
  310. data/examples/al-admin/vendor/plugins/exception_notification/README +111 -0
  311. data/examples/al-admin/vendor/plugins/exception_notification/init.rb +1 -0
  312. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifiable.rb +99 -0
  313. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier.rb +66 -0
  314. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb +78 -0
  315. data/examples/al-admin/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb +61 -0
  316. data/examples/al-admin/vendor/plugins/exception_notification/test/test_helper.rb +7 -0
  317. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml +1 -0
  318. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml +7 -0
  319. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml +16 -0
  320. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml +4 -0
  321. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml +2 -0
  322. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml +3 -0
  323. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml +6 -0
  324. data/examples/config.yaml.example +5 -0
  325. data/examples/example.der +0 -0
  326. data/examples/example.jpg +0 -0
  327. data/examples/groupadd +41 -0
  328. data/examples/groupdel +35 -0
  329. data/examples/groupls +49 -0
  330. data/examples/groupmod +42 -0
  331. data/examples/lpasswd +55 -0
  332. data/examples/objects/group.rb +13 -0
  333. data/examples/objects/ou.rb +4 -0
  334. data/examples/objects/user.rb +20 -0
  335. data/examples/ouadd +38 -0
  336. data/examples/useradd +45 -0
  337. data/examples/useradd-binary +53 -0
  338. data/examples/userdel +34 -0
  339. data/examples/userls +50 -0
  340. data/examples/usermod +42 -0
  341. data/examples/usermod-binary-add +50 -0
  342. data/examples/usermod-binary-add-time +54 -0
  343. data/examples/usermod-binary-del +48 -0
  344. data/examples/usermod-lang-add +43 -0
  345. data/lib/active_ldap.rb +105 -0
  346. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
  347. data/lib/active_ldap/acts/tree.rb +75 -0
  348. data/lib/active_ldap/adapter/base.rb +705 -0
  349. data/lib/active_ldap/adapter/jndi.rb +184 -0
  350. data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
  351. data/lib/active_ldap/adapter/ldap.rb +290 -0
  352. data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
  353. data/lib/active_ldap/adapter/net_ldap.rb +309 -0
  354. data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
  355. data/lib/active_ldap/association/belongs_to.rb +47 -0
  356. data/lib/active_ldap/association/belongs_to_many.rb +58 -0
  357. data/lib/active_ldap/association/children.rb +21 -0
  358. data/lib/active_ldap/association/collection.rb +105 -0
  359. data/lib/active_ldap/association/has_many.rb +31 -0
  360. data/lib/active_ldap/association/has_many_utils.rb +44 -0
  361. data/lib/active_ldap/association/has_many_wrap.rb +62 -0
  362. data/lib/active_ldap/association/proxy.rb +107 -0
  363. data/lib/active_ldap/associations.rb +202 -0
  364. data/lib/active_ldap/attributes.rb +184 -0
  365. data/lib/active_ldap/base.rb +1594 -0
  366. data/lib/active_ldap/callbacks.rb +52 -0
  367. data/lib/active_ldap/command.rb +49 -0
  368. data/lib/active_ldap/compatible.rb +44 -0
  369. data/lib/active_ldap/configuration.rb +147 -0
  370. data/lib/active_ldap/connection.rb +294 -0
  371. data/lib/active_ldap/distinguished_name.rb +291 -0
  372. data/lib/active_ldap/entry_attribute.rb +78 -0
  373. data/lib/active_ldap/escape.rb +12 -0
  374. data/lib/active_ldap/get_text.rb +9 -0
  375. data/lib/active_ldap/get_text/parser.rb +161 -0
  376. data/lib/active_ldap/get_text_fallback.rb +60 -0
  377. data/lib/active_ldap/get_text_support.rb +20 -0
  378. data/lib/active_ldap/helper.rb +92 -0
  379. data/lib/active_ldap/human_readable.rb +132 -0
  380. data/lib/active_ldap/ldap_error.rb +74 -0
  381. data/lib/active_ldap/ldif.rb +930 -0
  382. data/lib/active_ldap/object_class.rb +95 -0
  383. data/lib/active_ldap/operations.rb +610 -0
  384. data/lib/active_ldap/populate.rb +53 -0
  385. data/lib/active_ldap/railtie.rb +31 -0
  386. data/lib/active_ldap/schema.rb +699 -0
  387. data/lib/active_ldap/schema/syntaxes.rb +417 -0
  388. data/lib/active_ldap/timeout.rb +75 -0
  389. data/lib/active_ldap/timeout_stub.rb +17 -0
  390. data/lib/active_ldap/user_password.rb +92 -0
  391. data/lib/active_ldap/validations.rb +229 -0
  392. data/lib/active_ldap/xml.rb +122 -0
  393. data/po/en/active-ldap.po +4029 -0
  394. data/po/ja/active-ldap.po +4060 -0
  395. data/rails_generators/model_active_ldap/USAGE +17 -0
  396. data/rails_generators/model_active_ldap/model_active_ldap_generator.rb +69 -0
  397. data/rails_generators/model_active_ldap/templates/model_active_ldap.rb +3 -0
  398. data/rails_generators/model_active_ldap/templates/unit_test.rb +8 -0
  399. data/rails_generators/scaffold_active_ldap/scaffold_active_ldap_generator.rb +7 -0
  400. data/rails_generators/scaffold_active_ldap/templates/ldap.yml +18 -0
  401. data/test/al-test-utils.rb +439 -0
  402. data/test/command.rb +112 -0
  403. data/test/config.yaml.sample +6 -0
  404. data/test/fixtures/lower_case_object_class_schema.rb +802 -0
  405. data/test/run-test.rb +44 -0
  406. data/test/test_acts_as_tree.rb +60 -0
  407. data/test/test_adapter.rb +121 -0
  408. data/test/test_associations.rb +664 -0
  409. data/test/test_attributes.rb +117 -0
  410. data/test/test_base.rb +1177 -0
  411. data/test/test_base_per_instance.rb +61 -0
  412. data/test/test_bind.rb +62 -0
  413. data/test/test_callback.rb +37 -0
  414. data/test/test_configuration.rb +40 -0
  415. data/test/test_connection.rb +82 -0
  416. data/test/test_connection_per_class.rb +112 -0
  417. data/test/test_connection_per_dn.rb +112 -0
  418. data/test/test_dn.rb +172 -0
  419. data/test/test_find.rb +176 -0
  420. data/test/test_groupadd.rb +50 -0
  421. data/test/test_groupdel.rb +46 -0
  422. data/test/test_groupls.rb +107 -0
  423. data/test/test_groupmod.rb +51 -0
  424. data/test/test_ldif.rb +1891 -0
  425. data/test/test_load.rb +133 -0
  426. data/test/test_lpasswd.rb +75 -0
  427. data/test/test_object_class.rb +74 -0
  428. data/test/test_reflection.rb +182 -0
  429. data/test/test_schema.rb +559 -0
  430. data/test/test_syntax.rb +383 -0
  431. data/test/test_user.rb +217 -0
  432. data/test/test_user_password.rb +101 -0
  433. data/test/test_useradd-binary.rb +62 -0
  434. data/test/test_useradd.rb +57 -0
  435. data/test/test_userdel.rb +48 -0
  436. data/test/test_userls.rb +91 -0
  437. data/test/test_usermod-binary-add-time.rb +65 -0
  438. data/test/test_usermod-binary-add.rb +64 -0
  439. data/test/test_usermod-binary-del.rb +66 -0
  440. data/test/test_usermod-lang-add.rb +60 -0
  441. data/test/test_usermod.rb +58 -0
  442. data/test/test_validation.rb +274 -0
  443. metadata +502 -0
@@ -0,0 +1,184 @@
1
+ module ActiveLdap
2
+ module Attributes
3
+ def self.included(base)
4
+ base.class_eval do
5
+ extend(ClassMethods)
6
+ extend(Normalizable)
7
+ include(Normalizable)
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def attr_protected(*attributes)
13
+ targets = attributes.collect {|attr| attr.to_s} - protected_attributes
14
+ instance_variable_set("@attr_protected", targets)
15
+ end
16
+
17
+ def protected_attributes
18
+ ancestors[0..(ancestors.index(Base))].inject([]) do |result, ancestor|
19
+ result + ancestor.instance_eval {@attr_protected ||= []}
20
+ end
21
+ end
22
+
23
+ def blank_value?(value)
24
+ case value
25
+ when Hash
26
+ value.values.all? {|val| blank_value?(val)}
27
+ when Array
28
+ value.all? {|val| blank_value?(val)}
29
+ when String
30
+ /\A\s*\z/ =~ value
31
+ when nil
32
+ true
33
+ else
34
+ value.blank?
35
+ end
36
+ end
37
+
38
+ def remove_blank_value(value)
39
+ case value
40
+ when Hash
41
+ result = {}
42
+ value.each do |k, v|
43
+ v = remove_blank_value(v)
44
+ next if v.nil?
45
+ result[k] = v
46
+ end
47
+ result = nil if result.blank?
48
+ result
49
+ when Array
50
+ result = []
51
+ value.each do |v|
52
+ v = remove_blank_value(v)
53
+ next if v.nil?
54
+ result << v
55
+ end
56
+ result = nil if result.blank?
57
+ result
58
+ when String
59
+ if /\A\s*\z/ =~ value
60
+ nil
61
+ else
62
+ value
63
+ end
64
+ else
65
+ value
66
+ end
67
+ end
68
+ end
69
+
70
+ module Normalizable
71
+ def normalize_attribute_name(name)
72
+ name.to_s.downcase
73
+ end
74
+
75
+ # Enforce typing:
76
+ # Hashes are for subtypes
77
+ # Arrays are for multiple entries
78
+ def normalize_attribute(name, value)
79
+ if name.nil?
80
+ raise RuntimeError, _('The first argument, name, must not be nil. ' \
81
+ 'Please report this as a bug!')
82
+ end
83
+
84
+ name = normalize_attribute_name(name)
85
+ [name, schema.attribute(name).normalize_value(value)]
86
+ end
87
+
88
+ def unnormalize_attributes(attributes)
89
+ result = {}
90
+ attributes.each do |name, values|
91
+ unnormalize_attribute(name, values, result)
92
+ end
93
+ result
94
+ end
95
+
96
+ def unnormalize_attribute(name, values, result={})
97
+ if values.empty?
98
+ result[name] = []
99
+ else
100
+ values.each do |value|
101
+ if value.is_a?(Hash)
102
+ suffix, real_value = unnormalize_attribute_options(value)
103
+ new_name = name + suffix
104
+ unnormalize_attribute(new_name, real_value, result)
105
+ else
106
+ result[name] ||= []
107
+ if value.is_a?(DN)
108
+ result[name] << value.to_s
109
+ else
110
+ result[name] << value.dup
111
+ end
112
+ end
113
+ end
114
+ end
115
+ result
116
+ end
117
+
118
+ # normalize_attribute_options
119
+ #
120
+ # Makes the Hashized value from the full attribute name
121
+ # e.g. userCertificate;binary => "some_bin"
122
+ # becomes userCertificate => {"binary" => "some_bin"}
123
+ def normalize_attribute_options(attr, value)
124
+ return [attr, value] unless attr.match(/;/)
125
+
126
+ ret_attr, *options = attr.split(/;/)
127
+ [ret_attr,
128
+ [options.reverse.inject(value) {|result, option| {option => result}}]]
129
+ end
130
+
131
+ # unnormalize_attribute_options
132
+ #
133
+ # Unnormalizes all of the subtypes from a given set of nested hashes
134
+ # and returns the attribute suffix and the final true value
135
+ def unnormalize_attribute_options(value)
136
+ options = ''
137
+ ret_val = value
138
+ if value.class == Hash
139
+ options = ';' + value.keys[0]
140
+ ret_val = value[value.keys[0]]
141
+ if ret_val.class == Hash
142
+ sub_options, ret_val = unnormalize_attribute_options(ret_val)
143
+ options += sub_options
144
+ end
145
+ end
146
+ ret_val = [ret_val] unless ret_val.class == Array
147
+ [options, ret_val]
148
+ end
149
+ end
150
+
151
+ private
152
+ def remove_attributes_protected_from_mass_assignment(targets)
153
+ needless_attributes = {}
154
+ (attributes_protected_by_default +
155
+ (self.class.protected_attributes || [])).each do |name|
156
+ needless_attributes[to_real_attribute_name(name)] = true
157
+ end
158
+
159
+ _dn_attribute = nil
160
+ begin
161
+ _dn_attribute = dn_attribute_with_fallback
162
+ rescue DistinguishedNameInvalid
163
+ end
164
+ targets.collect do |key, value|
165
+ key = _dn_attribute if ["id", "dn"].include?(key.to_s)
166
+ [to_real_attribute_name(key) || key, value]
167
+ end.reject do |key, value|
168
+ needless_attributes[key]
169
+ end
170
+ end
171
+
172
+ def attributes_protected_by_default
173
+ begin
174
+ _dn_attribute = dn_attribute_with_fallback
175
+ rescue DistinguishedNameInvalid
176
+ end
177
+ [_dn_attribute, 'objectClass'].compact
178
+ end
179
+
180
+ def normalize_attribute_name(name)
181
+ self.class.normalize_attribute_name(name)
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,1594 @@
1
+ # === activeldap - an OO-interface to LDAP objects inspired by ActiveRecord
2
+ # Author: Will Drewry <will@alum.bu.edu>
3
+ # License: See LICENSE and COPYING.txt
4
+ # Copyright 2004-2006 Will Drewry <will@alum.bu.edu>
5
+ # Some portions Copyright 2006 Google Inc
6
+ #
7
+ # == Summary
8
+ # ActiveLdap lets you read and update LDAP entries in a completely object
9
+ # oriented fashion, even handling attributes with multiple names seamlessly.
10
+ # It was inspired by ActiveRecord so extending it to deal with custom
11
+ # LDAP schemas is as effortless as knowing the 'ou' of the objects, and the
12
+ # primary key. (fix this up some)
13
+ #
14
+ # == Example
15
+ # irb> require 'active_ldap'
16
+ # > true
17
+ # irb> user = ActiveLdap::User.new("drewry")
18
+ # > #<ActiveLdap::User:0x402e...
19
+ # irb> user.cn
20
+ # > "foo"
21
+ # irb> user.common_name
22
+ # > "foo"
23
+ # irb> user.cn = "Will Drewry"
24
+ # > "Will Drewry"
25
+ # irb> user.cn
26
+ # > "Will Drewry"
27
+ # irb> user.save
28
+ #
29
+ #
30
+
31
+ require 'English'
32
+ require 'thread'
33
+ require 'erb'
34
+
35
+ module ActiveLdap
36
+ # OO-interface to LDAP assuming pam/nss_ldap-style organization with
37
+ # Active specifics
38
+ # Each subclass does a ldapsearch for the matching entry.
39
+ # If no exact match, raise an error.
40
+ # If match, change all LDAP attributes in accessor attributes on the object.
41
+ # -- these are ACTUALLY populated from schema - see active_ldap/schema.rb
42
+ # example
43
+ # -- extract objectClasses from match and populate
44
+ # Multiple entries become lists.
45
+ # If this isn't read-only then lists become multiple entries, etc.
46
+
47
+ class << self
48
+ include GetTextSupport
49
+ include ActiveRecord::Persistence
50
+ def const_missing(id)
51
+ case id
52
+ when :ConnectionNotEstablished
53
+ message =
54
+ _("ActiveLdap::ConnectionNotEstablished has been deprecated " \
55
+ "since 1.1.0. " \
56
+ "Please use ActiveLdap::ConnectionNotSetup instead.")
57
+ ActiveSupport::Deprecation.warn(message)
58
+ const_set("ConnectionNotEstablished", ConnectionNotSetup)
59
+ ConnectionNotEstablished
60
+ else
61
+ super
62
+ end
63
+ end
64
+ end
65
+
66
+ class Error < StandardError
67
+ include GetTextSupport
68
+ end
69
+
70
+ # ConfigurationError
71
+ #
72
+ # An exception raised when there is a problem with Base.connect arguments
73
+ class ConfigurationError < Error
74
+ end
75
+
76
+ # DeleteError
77
+ #
78
+ # An exception raised when an ActiveLdap delete action fails
79
+ class DeleteError < Error
80
+ end
81
+
82
+ # SaveError
83
+ #
84
+ # An exception raised when an ActiveLdap save action failes
85
+ class SaveError < Error
86
+ end
87
+
88
+ # AuthenticationError
89
+ #
90
+ # An exception raised when user authentication fails
91
+ class AuthenticationError < Error
92
+ end
93
+
94
+ # ConnectionError
95
+ #
96
+ # An exception raised when the LDAP conenction fails
97
+ class ConnectionError < Error
98
+ end
99
+
100
+ # ObjectClassError
101
+ #
102
+ # An exception raised when an objectClass is not defined in the schema
103
+ class ObjectClassError < Error
104
+ end
105
+
106
+ # AttributeAssignmentError
107
+ #
108
+ # An exception raised when there is an issue assigning a value to
109
+ # an attribute
110
+ class AttributeAssignmentError < Error
111
+ end
112
+
113
+ # TimeoutError
114
+ #
115
+ # An exception raised when a connection action fails due to a timeout
116
+ class TimeoutError < Error
117
+ end
118
+
119
+ class EntryNotFound < Error
120
+ end
121
+
122
+ class EntryAlreadyExist < Error
123
+ end
124
+
125
+ class StrongAuthenticationRequired < Error
126
+ end
127
+
128
+ class DistinguishedNameInputInvalid < Error
129
+ attr_reader :input
130
+ def initialize(input=nil)
131
+ @input = input
132
+ super(_("invalid distinguished name (DN) to parse: %s") % @input.inspect)
133
+ end
134
+ end
135
+
136
+ class DistinguishedNameInvalid < Error
137
+ attr_reader :dn, :reason
138
+ def initialize(dn, reason=nil)
139
+ @dn = dn
140
+ @reason = reason
141
+ if @reason
142
+ message = _("%s is invalid distinguished name (DN): %s") % [@dn, @reason]
143
+ else
144
+ message = _("%s is invalid distinguished name (DN)") % @dn
145
+ end
146
+ super(message)
147
+ end
148
+ end
149
+
150
+ class DistinguishedNameNotSetError < Error
151
+ end
152
+
153
+ class LdifInvalid < Error
154
+ attr_reader :ldif, :reason, :line, :column, :nearest
155
+ def initialize(ldif, reason=nil, line=nil, column=nil)
156
+ @ldif = ldif
157
+ @reason = reason
158
+ @line = line
159
+ @column = column
160
+ @nearest = nil
161
+ if @reason
162
+ message = _("invalid LDIF: %s:") % @reason
163
+ else
164
+ message = _("invalid LDIF:")
165
+ end
166
+ if @line and @column
167
+ @nearest = detect_nearest(@line, @column)
168
+ snippet = generate_snippet
169
+ message << "\n#{snippet}\n"
170
+ end
171
+ super("#{message}\n#{numbered_ldif}")
172
+ end
173
+
174
+ NEAREST_MARK = "|@|"
175
+ private
176
+ def detect_nearest(line, column)
177
+ lines = Compatible.string_to_lines(@ldif).to_a
178
+ nearest = lines[line - 1] || ""
179
+ if column - 1 == nearest.size # for JRuby 1.0.2 :<
180
+ nearest << NEAREST_MARK
181
+ else
182
+ nearest[column - 1, 0] = NEAREST_MARK
183
+ end
184
+ if nearest == NEAREST_MARK
185
+ nearest = "#{lines[line - 2]}#{nearest}"
186
+ end
187
+ nearest
188
+ end
189
+
190
+ def generate_snippet
191
+ nearest = @nearest.chomp
192
+ column_column = ":#{@column}"
193
+ target_position_info = "#{@line}#{column_column}: "
194
+ if /\n/ =~ nearest
195
+ snippet = "%#{Math.log10(@line).truncate}d" % (@line - 1)
196
+ snippet << " " * column_column.size
197
+ snippet << ": "
198
+ snippet << nearest.gsub(/\n/, "\n#{target_position_info}")
199
+ else
200
+ snippet = "#{target_position_info}#{nearest}"
201
+ end
202
+ snippet
203
+ end
204
+
205
+ def numbered_ldif
206
+ return @ldif if @ldif.blank?
207
+ lines = Compatible.string_to_lines(@ldif)
208
+ format = "%#{Math.log10(lines.size).truncate + 1}d: %s"
209
+ i = 0
210
+ lines.collect do |line|
211
+ i += 1
212
+ format % [i, line]
213
+ end.join
214
+ end
215
+ end
216
+
217
+ class EntryNotSaved < Error
218
+ end
219
+
220
+ class RequiredObjectClassMissed < Error
221
+ end
222
+
223
+ class RequiredAttributeMissed < Error
224
+ end
225
+
226
+ class EntryInvalid < Error
227
+ end
228
+
229
+ class OperationNotPermitted < Error
230
+ end
231
+
232
+ class ConnectionNotSetup < Error
233
+ end
234
+
235
+ class AdapterNotSpecified < Error
236
+ end
237
+
238
+ class AdapterNotFound < Error
239
+ attr_reader :adapter
240
+ def initialize(adapter)
241
+ @adapter = adapter
242
+ super(_("LDAP configuration specifies nonexistent %s adapter") % adapter)
243
+ end
244
+ end
245
+
246
+ class UnknownAttribute < Error
247
+ attr_reader :name
248
+ def initialize(name)
249
+ @name = name
250
+ super(_("%s is unknown attribute") % @name)
251
+ end
252
+ end
253
+
254
+ class AttributeValueInvalid < Error
255
+ attr_reader :attribute, :value
256
+ def initialize(attribute, value, message)
257
+ @attribute = attribute
258
+ @value = value
259
+ super(message)
260
+ end
261
+ end
262
+
263
+ class NotImplemented < Error
264
+ attr_reader :target
265
+ def initialize(target)
266
+ @target = target
267
+ super(_("not implemented: %s") % @target)
268
+ end
269
+ end
270
+
271
+ # Base
272
+ #
273
+ # Base is the primary class which contains all of the core
274
+ # ActiveLdap functionality. It is meant to only ever be subclassed
275
+ # by extension classes.
276
+ class Base
277
+ include GetTextSupport
278
+ public :_
279
+
280
+ if Object.const_defined?(:Reloadable)
281
+ if Reloadable.const_defined?(:Deprecated)
282
+ include Reloadable::Deprecated
283
+ else
284
+ include Reloadable::Subclasses
285
+ end
286
+ end
287
+
288
+ cattr_accessor :colorize_logging, :instance_writer => false
289
+ @@colorize_logging = true
290
+
291
+ VALID_LDAP_MAPPING_OPTIONS = [:dn_attribute, :prefix, :scope,
292
+ :classes, :recommended_classes,
293
+ :excluded_classes, :sort_by, :order]
294
+
295
+ cattr_accessor :logger
296
+ cattr_accessor :configurations
297
+ @@configurations = {}
298
+
299
+ def self.class_local_attr_accessor(search_ancestors, *syms)
300
+ syms.flatten.each do |sym|
301
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
302
+ def self.#{sym}(search_superclasses=#{search_ancestors})
303
+ @#{sym} ||= nil
304
+ return @#{sym} if @#{sym}
305
+ if search_superclasses
306
+ target = superclass
307
+ value = nil
308
+ loop do
309
+ break nil unless target.respond_to?(:#{sym})
310
+ value = target.#{sym}
311
+ break if value
312
+ target = target.superclass
313
+ end
314
+ value
315
+ else
316
+ nil
317
+ end
318
+ end
319
+ def #{sym}; self.class.#{sym}; end
320
+ def self.#{sym}=(value); @#{sym} = value; end
321
+ EOS
322
+ end
323
+ end
324
+
325
+ class_local_attr_accessor false, :inheritable_prefix, :inheritable_base
326
+ class_local_attr_accessor true, :dn_attribute, :scope, :sort_by, :order
327
+ class_local_attr_accessor true, :required_classes, :recommended_classes
328
+ class_local_attr_accessor true, :excluded_classes
329
+
330
+ class << self
331
+ # Hide new in Base
332
+ private :new
333
+
334
+ def inherited(sub_class)
335
+ super
336
+ sub_class.module_eval do
337
+ include GetTextSupport
338
+ end
339
+ end
340
+
341
+ # Connect and bind to LDAP creating a class variable for use by
342
+ # all ActiveLdap objects.
343
+ #
344
+ # == +config+
345
+ # +config+ must be a hash that may contain any of the following fields:
346
+ # :password_block, :logger, :host, :port, :base, :bind_dn,
347
+ # :try_sasl, :allow_anonymous
348
+ # :bind_dn specifies the DN to bind with.
349
+ # :password_block specifies a Proc object that will yield a String to
350
+ # be used as the password when called.
351
+ # :logger specifies a logger object (Logger, Log4r::Logger and s on)
352
+ # :host sets the LDAP server hostname
353
+ # :port sets the LDAP server port
354
+ # :base overwrites Base.base - this affects EVERYTHING
355
+ # :try_sasl indicates that a SASL bind should be attempted when binding
356
+ # to the server (default: false)
357
+ # :sasl_mechanisms is an array of SASL mechanism to try
358
+ # (default: ["GSSAPI", "CRAM-MD5", "EXTERNAL"])
359
+ # :allow_anonymous indicates that a true anonymous bind is allowed when
360
+ # trying to bind to the server (default: true)
361
+ # :retries - indicates the number of attempts to reconnect that will be
362
+ # undertaken when a stale connection occurs. -1 means infinite.
363
+ # :sasl_quiet - if true, sets @sasl_quiet on the Ruby/LDAP connection
364
+ # :method - whether to use :ssl, :tls, or :plain (unencrypted)
365
+ # :retry_wait - seconds to wait before retrying a connection
366
+ # :scope - dictates how to find objects. ONELEVEL by default to
367
+ # avoid dn_attr collisions across OUs. Think before changing.
368
+ # :timeout - time in seconds - defaults to disabled. This CAN interrupt
369
+ # search() requests. Be warned.
370
+ # :retry_on_timeout - whether to reconnect when timeouts occur. Defaults
371
+ # to true
372
+ # See lib/active_ldap/configuration.rb for defaults for each option
373
+ def setup_connection(config=nil)
374
+ super
375
+ ensure_logger
376
+ nil
377
+ end
378
+
379
+ # establish_connection is deprecated since 1.1.0. Please use
380
+ # setup_connection() instead.
381
+ def establish_connection(config=nil)
382
+ message =
383
+ _("ActiveLdap::Base.establish_connection has been deprecated " \
384
+ "since 1.1.0. " \
385
+ "Please use ActiveLdap::Base.setup_connection instead.")
386
+ ActiveSupport::Deprecation.warn(message)
387
+ setup_connection(config)
388
+ end
389
+
390
+ def create(attributes=nil, &block)
391
+ if attributes.is_a?(Array)
392
+ attributes.collect {|attrs| create(attrs, &block)}
393
+ else
394
+ object = new(attributes, &block)
395
+ object.save
396
+ object
397
+ end
398
+ end
399
+
400
+ # This class function is used to setup all mappings between the subclass
401
+ # and ldap for use in activeldap
402
+ #
403
+ # Example:
404
+ # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People',
405
+ # :classes => ['top', 'posixAccount'],
406
+ # :scope => :sub
407
+ def ldap_mapping(options={})
408
+ options = options.symbolize_keys
409
+ validate_ldap_mapping_options(options)
410
+
411
+ self.dn_attribute = options[:dn_attribute] || default_dn_attribute
412
+ self.dn_attribute = dn_attribute.to_s if dn_attribute.is_a?(Symbol)
413
+ self.prefix = options[:prefix] || default_prefix
414
+ self.scope = options[:scope]
415
+ self.required_classes = options[:classes]
416
+ self.recommended_classes = options[:recommended_classes]
417
+ self.excluded_classes = options[:excluded_classes]
418
+ self.sort_by = options[:sort_by]
419
+ self.order = options[:order]
420
+
421
+ public_class_method :new
422
+ end
423
+
424
+ # Base.base
425
+ #
426
+ # This method when included into Base provides
427
+ # an inheritable, overwritable configuration setting
428
+ #
429
+ # This should be a string with the base of the
430
+ # ldap server such as 'dc=example,dc=com', and
431
+ # it should be overwritten by including
432
+ # configuration.rb into this class.
433
+ # When subclassing, the specified prefix will be concatenated.
434
+ def base
435
+ @base ||= compute_base
436
+ end
437
+ alias_method :parsed_base, :base # for backward compatibility
438
+
439
+ def base=(value)
440
+ self.inheritable_base = value
441
+ @base = nil
442
+ end
443
+
444
+ def prefix
445
+ @prefix ||= inheritable_prefix and DN.parse(inheritable_prefix)
446
+ end
447
+
448
+ def prefix=(value)
449
+ self.inheritable_prefix = value
450
+ @prefix = nil
451
+ @base = nil
452
+ end
453
+
454
+ alias_method :scope_without_validation=, :scope=
455
+ def scope=(scope)
456
+ validate_scope(scope)
457
+ self.scope_without_validation = scope
458
+ end
459
+
460
+ def validate_scope(scope)
461
+ scope = scope.to_sym if scope.is_a?(String)
462
+ return if scope.nil? or scope.is_a?(Symbol)
463
+ raise ConfigurationError,
464
+ _("scope '%s' must be a Symbol") % scope.inspect
465
+ end
466
+
467
+ def base_class
468
+ if self == Base or superclass == Base
469
+ self
470
+ else
471
+ superclass.base_class
472
+ end
473
+ end
474
+
475
+ def default_search_attribute
476
+ dn_attribute
477
+ end
478
+
479
+ def inspect
480
+ if self == Base
481
+ super
482
+ elsif abstract_class?
483
+ "#{super}(abstract)"
484
+ else
485
+ detail = nil
486
+ begin
487
+ must = []
488
+ may = []
489
+ class_names = classes.collect do |object_class|
490
+ must.concat(object_class.must)
491
+ may.concat(object_class.may)
492
+ object_class.name
493
+ end
494
+ detail = ["objectClass:<#{class_names.join(', ')}>",
495
+ "must:<#{inspect_attributes(must)}>",
496
+ "may:<#{inspect_attributes(may)}>"].join(", ")
497
+ rescue ActiveLdap::ConnectionNotSetup
498
+ detail = "not-connected"
499
+ rescue ActiveLdap::Error
500
+ detail = "connection-failure"
501
+ end
502
+ "#{super}(#{detail})"
503
+ end
504
+ end
505
+
506
+ attr_accessor :abstract_class
507
+ def abstract_class?
508
+ defined?(@abstract_class) && @abstract_class
509
+ end
510
+
511
+ def class_of_active_ldap_descendant(klass)
512
+ if klass.superclass == Base or klass.superclass.abstract_class?
513
+ klass
514
+ elsif klass.superclass.nil?
515
+ raise Error, _("%s doesn't belong in a hierarchy descending " \
516
+ "from ActiveLdap") % (name || to_s)
517
+ else
518
+ class_of_active_ldap_descendant(klass.superclass)
519
+ end
520
+ end
521
+
522
+ def self_and_descendants_from_active_ldap
523
+ klass = self
524
+ classes = [klass]
525
+ while klass != klass.base_class
526
+ classes << klass = klass.superclass
527
+ end
528
+ classes
529
+ rescue
530
+ [self]
531
+ end
532
+ if ActiveRecord::Base.respond_to?(:self_and_descendents_from_active_record)
533
+ # ActiveRecord 2.2.2 has a typo. :<
534
+ alias_method(:self_and_descendents_from_active_record,
535
+ :self_and_descendants_from_active_ldap)
536
+ else
537
+ alias_method(:self_and_descendants_from_active_record,
538
+ :self_and_descendants_from_active_ldap)
539
+ end
540
+
541
+ def human_name(options={})
542
+ defaults = self_and_descendants_from_active_ldap.collect do |klass|
543
+ if klass.name.blank?
544
+ nil
545
+ else
546
+ :"#{klass.name.underscore}"
547
+ end
548
+ end
549
+ defaults << name.humanize
550
+ defaults = defaults.compact
551
+ defaults.first || name || to_s
552
+ end
553
+
554
+ private
555
+ def inspect_attributes(attributes)
556
+ inspected_attribute_names = {}
557
+ attributes.collect do |attribute|
558
+ if inspected_attribute_names.has_key?(attribute.name)
559
+ nil
560
+ else
561
+ inspected_attribute_names[attribute.name] = true
562
+ inspect_attribute(attribute)
563
+ end
564
+ end.compact.join(', ')
565
+ end
566
+
567
+ def inspect_attribute(attribute)
568
+ syntax = attribute.syntax
569
+ result = "#{attribute.name}"
570
+ if syntax and !syntax.description.blank?
571
+ result << ": #{syntax.description}"
572
+ end
573
+ properties = []
574
+ properties << "read-only" if attribute.read_only?
575
+ properties << "binary" if attribute.binary?
576
+ properties << "binary-required" if attribute.binary_required?
577
+ result << "(#{properties.join(', ')})" unless properties.empty?
578
+ result
579
+ end
580
+
581
+ def validate_ldap_mapping_options(options)
582
+ options.assert_valid_keys(VALID_LDAP_MAPPING_OPTIONS)
583
+ end
584
+
585
+ def ensure_logger
586
+ @@logger ||= configuration[:logger]
587
+ # Setup default logger to console
588
+ if @@logger.nil?
589
+ require 'logger'
590
+ @@logger = Logger.new(STDERR)
591
+ @@logger.progname = 'ActiveLdap'
592
+ @@logger.level = Logger::ERROR
593
+ end
594
+ configuration[:logger] ||= @@logger
595
+ end
596
+
597
+ def instantiate(args)
598
+ dn, attributes, options = args
599
+ options ||= {}
600
+ if self.class == Class
601
+ klass = self.ancestors[0].to_s.split(':').last
602
+ real_klass = self.ancestors[0]
603
+ else
604
+ klass = self.class.to_s.split(':').last
605
+ real_klass = self.class
606
+ end
607
+
608
+ obj = real_klass.allocate
609
+ conn = options[:connection] || connection
610
+ obj.connection = conn if conn != connection
611
+ obj.instance_eval do
612
+ initialize_by_ldap_data(dn, attributes)
613
+ end
614
+ obj
615
+ end
616
+
617
+ def default_dn_attribute
618
+ dn_attribute = nil
619
+ parent_class = ancestors[1]
620
+ if parent_class.respond_to?(:dn_attribute)
621
+ dn_attribute = parent_class.dn_attribute
622
+ end
623
+ dn_attribute || "cn"
624
+ end
625
+
626
+ def default_prefix
627
+ if name.blank?
628
+ nil
629
+ else
630
+ "ou=#{name.demodulize.pluralize}"
631
+ end
632
+ end
633
+
634
+ def compute_base
635
+ _base = inheritable_base
636
+ _base = configuration[:base] if _base.nil? and configuration
637
+ if _base.nil?
638
+ target = superclass
639
+ loop do
640
+ break unless target.respond_to?(:base)
641
+ _base = target.base
642
+ break if _base
643
+ target = target.superclass
644
+ end
645
+ end
646
+ _prefix = prefix
647
+
648
+ _base ||= connection.naming_contexts.first
649
+ return _prefix if _base.blank?
650
+
651
+ _base = DN.parse(_base)
652
+ _base = _prefix + _base if _prefix
653
+ _base
654
+ end
655
+ end
656
+
657
+ self.scope = :sub
658
+ self.required_classes = ['top']
659
+ self.recommended_classes = []
660
+ self.excluded_classes = []
661
+
662
+ include Enumerable
663
+
664
+ ### All instance methods, etc
665
+
666
+ # new
667
+ #
668
+ # Creates a new instance of Base initializing all class and all
669
+ # initialization. Defines local defaults. See examples If multiple values
670
+ # exist for dn_attribute, the first one put here will be authoritative
671
+ def initialize(attributes=nil)
672
+ init_base
673
+ @new_entry = true
674
+ initial_classes = required_classes | recommended_classes
675
+ case attributes
676
+ when nil
677
+ self.classes = initial_classes
678
+ when String, Array, DN
679
+ self.classes = initial_classes
680
+ self.dn = attributes
681
+ when Hash
682
+ classes, attributes = extract_object_class(attributes)
683
+ self.classes = classes | initial_classes
684
+ normalized_attributes = {}
685
+ attributes.each do |key, value|
686
+ real_key = to_real_attribute_name(key) || key
687
+ normalized_attributes[real_key] = value
688
+ end
689
+ self.dn = normalized_attributes.delete(dn_attribute)
690
+ self.attributes = normalized_attributes
691
+ else
692
+ format = _("'%s' must be either nil, DN value as ActiveLdap::DN, " \
693
+ "String or Array or attributes as Hash")
694
+ raise ArgumentError, format % attributes.inspect
695
+ end
696
+ yield self if block_given?
697
+ end
698
+
699
+ # Returns true if the +comparison_object+ is the same object, or is of
700
+ # the same type and has the same dn.
701
+ def ==(comparison_object)
702
+ comparison_object.equal?(self) or
703
+ (comparison_object.instance_of?(self.class) and
704
+ comparison_object.dn == dn and
705
+ !comparison_object.new_entry?)
706
+ end
707
+
708
+ # Delegates to ==
709
+ def eql?(comparison_object)
710
+ self == (comparison_object)
711
+ end
712
+
713
+ # Delegates to id in order to allow two records of the same type and id
714
+ # to work with something like:
715
+ # [ User.find("a"), User.find("b"), User.find("c") ] &
716
+ # [ User.find("a"), User.find("d") ] # => [ User.find("a") ]
717
+ def hash
718
+ return super if @_hashing # workaround for GetText :<
719
+ _dn = nil
720
+ begin
721
+ @_hashing = true
722
+ _dn = dn
723
+ rescue DistinguishedNameInvalid, DistinguishedNameNotSetError
724
+ return super
725
+ ensure
726
+ @_hashing = false
727
+ end
728
+ _dn.hash
729
+ end
730
+
731
+ def may
732
+ entry_attribute.may
733
+ end
734
+
735
+ def must
736
+ entry_attribute.must
737
+ end
738
+
739
+ # attributes
740
+ #
741
+ # Return attribute methods so that a program can determine available
742
+ # attributes dynamically without schema awareness
743
+ def attribute_names(normalize=false)
744
+ entry_attribute.names(normalize)
745
+ end
746
+
747
+ def attribute_present?(name)
748
+ values = get_attribute(name, true)
749
+ !values.empty? or values.any? {|x| !(x and x.empty?)}
750
+ end
751
+
752
+ # exist?
753
+ #
754
+ # Return whether the entry exists in LDAP or not
755
+ def exist?
756
+ self.class.exists?(dn)
757
+ end
758
+ alias_method(:exists?, :exist?)
759
+
760
+ # new_entry?
761
+ #
762
+ # Return whether the entry is new entry in LDAP or not
763
+ def new_entry?
764
+ @new_entry
765
+ end
766
+
767
+ # dn
768
+ #
769
+ # Return the authoritative dn
770
+ def dn
771
+ @dn ||= compute_dn
772
+ end
773
+
774
+ def id
775
+ get_attribute(dn_attribute_with_fallback)
776
+ end
777
+
778
+ def to_param
779
+ id
780
+ end
781
+
782
+ def dn=(value)
783
+ set_attribute(dn_attribute_with_fallback, value)
784
+ end
785
+ alias_method(:id=, :dn=)
786
+
787
+ alias_method(:dn_attribute_of_class, :dn_attribute)
788
+ def dn_attribute
789
+ ensure_update_dn
790
+ _dn_attribute = @dn_attribute || dn_attribute_of_class
791
+ to_real_attribute_name(_dn_attribute) || _dn_attribute
792
+ end
793
+
794
+ def default_search_attribute
795
+ self.class.default_search_attribute
796
+ end
797
+
798
+ # destroy
799
+ #
800
+ # Delete this entry from LDAP
801
+ def destroy
802
+ self.class.delete(dn)
803
+ @new_entry = true
804
+ end
805
+
806
+ def delete(options={})
807
+ super(dn, options)
808
+ end
809
+
810
+ # save
811
+ #
812
+ # Save and validate this object into LDAP
813
+ # either adding or replacing attributes
814
+ # TODO: Relative DN support
815
+ def save
816
+ create_or_update
817
+ end
818
+
819
+ def save!
820
+ unless create_or_update
821
+ raise EntryNotSaved, _("entry %s can't be saved") % dn
822
+ end
823
+ end
824
+
825
+ # method_missing
826
+ #
827
+ # If a given method matches an attribute or an attribute alias
828
+ # then call the appropriate method.
829
+ # TODO: Determine if it would be better to define each allowed method
830
+ # using class_eval instead of using method_missing. This would
831
+ # give tab completion in irb.
832
+ def method_missing(name, *args, &block)
833
+ key = name.to_s
834
+ case key
835
+ when /=$/
836
+ real_key = $PREMATCH
837
+ if have_attribute?(real_key, ['objectClass'])
838
+ if args.size != 1
839
+ raise ArgumentError,
840
+ _("wrong number of arguments (%d for 1)") % args.size
841
+ end
842
+ return set_attribute(real_key, *args, &block)
843
+ end
844
+ when /(?:(_before_type_cast)|(\?))?$/
845
+ real_key = $PREMATCH
846
+ before_type_cast = !$1.nil?
847
+ query = !$2.nil?
848
+ if have_attribute?(real_key, ['objectClass'])
849
+ if args.size > 1
850
+ raise ArgumentError,
851
+ _("wrong number of arguments (%d for 1)") % args.size
852
+ end
853
+ if before_type_cast
854
+ return get_attribute_before_type_cast(real_key, *args)[1]
855
+ elsif query
856
+ return get_attribute_as_query(real_key, *args)
857
+ else
858
+ return get_attribute(real_key, *args)
859
+ end
860
+ end
861
+ end
862
+ super
863
+ end
864
+
865
+ # Add available attributes to the methods
866
+ def methods(inherited_too=true)
867
+ target_names = entry_attribute.all_names
868
+ target_names -= ['objectClass', 'objectClass'.underscore]
869
+ super + target_names.uniq.collect do |x|
870
+ [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
871
+ end.flatten
872
+ end
873
+
874
+ alias_method :respond_to_without_attributes?, :respond_to?
875
+ def respond_to?(name, include_priv=false)
876
+ return true if super
877
+
878
+ name = name.to_s
879
+ return true if have_attribute?(name, ["objectClass"])
880
+ return false if /(?:=|\?|_before_type_cast)$/ !~ name
881
+ have_attribute?($PREMATCH, ["objectClass"])
882
+ end
883
+
884
+ # Updates a given attribute and saves immediately
885
+ def update_attribute(name, value)
886
+ send("#{name}=", value)
887
+ save
888
+ end
889
+
890
+ # This performs a bulk update of attributes and immediately
891
+ # calls #save.
892
+ def update_attributes(attrs)
893
+ self.attributes = attrs
894
+ save
895
+ end
896
+
897
+ def update_attributes!(attrs)
898
+ self.attributes = attrs
899
+ save!
900
+ end
901
+
902
+ # This returns the key value pairs in @data with all values
903
+ # cloned
904
+ def attributes
905
+ @simplified_data ||= simplify_data(@data)
906
+ @simplified_data.clone
907
+ end
908
+
909
+ # This allows a bulk update to the attributes of a record
910
+ # without forcing an immediate save or validation.
911
+ #
912
+ # It is unwise to attempt objectClass updates this way.
913
+ # Also be sure to only pass in key-value pairs of your choosing.
914
+ # Do not let URL/form hackers supply the keys.
915
+ def attributes=(new_attributes)
916
+ return if new_attributes.blank?
917
+ _schema = _local_entry_attribute = nil
918
+ targets = remove_attributes_protected_from_mass_assignment(new_attributes)
919
+ targets.each do |key, value|
920
+ setter = "#{key}="
921
+ unless respond_to?(setter)
922
+ _schema ||= schema
923
+ attribute = _schema.attribute(key)
924
+ next if attribute.id.nil?
925
+ _local_entry_attribute ||= local_entry_attribute
926
+ _local_entry_attribute.register(attribute)
927
+ end
928
+ send(setter, value)
929
+ end
930
+ end
931
+
932
+ def to_ldif_record
933
+ super(dn, normalize_data(@data))
934
+ end
935
+
936
+ def to_ldif
937
+ Ldif.new([to_ldif_record]).to_s
938
+ end
939
+
940
+ def to_xml(options={})
941
+ options = options.dup
942
+ options[:root] ||= (self.class.name || '').underscore
943
+ options[:root] = 'anonymous' if options[:root].blank?
944
+ except = options[:except]
945
+ if except
946
+ options[:except] = except.collect do |name|
947
+ if name.to_s.downcase == "dn"
948
+ "dn"
949
+ else
950
+ to_real_attribute_name(name)
951
+ end
952
+ end.compact
953
+ end
954
+ XML.new(dn, normalize_data(@data), schema).to_s(options)
955
+ end
956
+
957
+ def to_s
958
+ to_ldif
959
+ end
960
+
961
+ def have_attribute?(name, except=[])
962
+ real_name = to_real_attribute_name(name)
963
+ !real_name.nil? and !except.include?(real_name)
964
+ end
965
+ alias_method :has_attribute?, :have_attribute?
966
+
967
+ def reload
968
+ clear_association_cache
969
+ _, attributes = search(:value => id).find do |_dn, _attributes|
970
+ dn == _dn
971
+ end
972
+ if attributes.nil?
973
+ raise EntryNotFound, _("Can't find DN '%s' to reload") % dn
974
+ end
975
+
976
+ @ldap_data.update(attributes)
977
+ classes, attributes = extract_object_class(attributes)
978
+ self.classes = classes
979
+ self.attributes = attributes
980
+ @new_entry = false
981
+ self
982
+ end
983
+
984
+ def [](name, force_array=false)
985
+ if name == "dn"
986
+ array_of(dn, force_array)
987
+ else
988
+ get_attribute(name, force_array)
989
+ end
990
+ end
991
+
992
+ def []=(name, value)
993
+ set_attribute(name, value)
994
+ end
995
+
996
+ def each
997
+ @data.each do |key, values|
998
+ yield(key.dup, values.dup)
999
+ end
1000
+ end
1001
+
1002
+ def bind(config_or_password={}, config_or_ignore=nil, &block)
1003
+ if config_or_password.is_a?(String)
1004
+ config = (config_or_ignore || {}).merge(:password => config_or_password)
1005
+ else
1006
+ config = config_or_password
1007
+ end
1008
+ config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
1009
+ config[:password_block] ||= block if block_given?
1010
+ setup_connection(config)
1011
+
1012
+ before_connection = @connection
1013
+ begin
1014
+ @connection = nil
1015
+ connection.connect
1016
+ @connection = connection
1017
+ clear_connection_based_cache
1018
+ clear_association_cache
1019
+ rescue ActiveLdap::Error
1020
+ remove_connection
1021
+ @connection = before_connection
1022
+ raise
1023
+ end
1024
+ true
1025
+ end
1026
+
1027
+ def clear_connection_based_cache
1028
+ @schema = nil
1029
+ @local_entry_attribute = nil
1030
+ clear_object_class_based_cache
1031
+ end
1032
+
1033
+ def clear_object_class_based_cache
1034
+ @entry_attribute = nil
1035
+ @real_names = {}
1036
+ end
1037
+
1038
+ def schema
1039
+ @schema ||= super
1040
+ end
1041
+
1042
+ def base
1043
+ @base ||= compute_base
1044
+ end
1045
+
1046
+ def base=(object_local_base)
1047
+ ensure_update_dn
1048
+ @dn = nil
1049
+ @base = nil
1050
+ @base_value = object_local_base
1051
+ end
1052
+
1053
+ alias_method :scope_of_class, :scope
1054
+ def scope
1055
+ @scope || scope_of_class
1056
+ end
1057
+
1058
+ def scope=(scope)
1059
+ self.class.validate_scope(scope)
1060
+ @scope = scope
1061
+ end
1062
+
1063
+ def delete_all(options={})
1064
+ super({:base => dn}.merge(options || {}))
1065
+ end
1066
+
1067
+ def destroy_all(options={})
1068
+ super({:base => dn}.merge(options || {}))
1069
+ end
1070
+
1071
+ def inspect
1072
+ object_classes = entry_attribute.object_classes
1073
+ inspected_object_classes = object_classes.collect do |object_class|
1074
+ object_class.name
1075
+ end.join(', ')
1076
+ must_attributes = must.collect(&:name).sort.join(', ')
1077
+ may_attributes = may.collect(&:name).sort.join(', ')
1078
+ inspected_attributes = attribute_names.sort.collect do |name|
1079
+ inspect_attribute(name)
1080
+ end.join(', ')
1081
+ result = "\#<#{self.class} objectClass:<#{inspected_object_classes}>, "
1082
+ result << "must:<#{must_attributes}>, may:<#{may_attributes}>, "
1083
+ result << "#{inspected_attributes}>"
1084
+ result
1085
+ end
1086
+
1087
+ private
1088
+ def dn_attribute_with_fallback
1089
+ begin
1090
+ dn_attribute
1091
+ rescue DistinguishedNameInvalid
1092
+ _dn_attribute = @dn_attribute || dn_attribute_of_class
1093
+ _dn_attribute = to_real_attribute_name(_dn_attribute) || _dn_attribute
1094
+ raise if _dn_attribute.nil?
1095
+ _dn_attribute
1096
+ end
1097
+ end
1098
+
1099
+ def inspect_attribute(name)
1100
+ values = get_attribute(name, true)
1101
+ values.collect do |value|
1102
+ if value.is_a?(String) and value.length > 50
1103
+ "#{value[0, 50]}...".inspect
1104
+ elsif value.is_a?(Date) || value.is_a?(Time)
1105
+ "#{value.to_s(:db)}"
1106
+ else
1107
+ value.inspect
1108
+ end
1109
+ end
1110
+ "#{name}: #{values.inspect}"
1111
+ end
1112
+
1113
+ def find_object_class_values(data)
1114
+ data["objectClass"] || data["objectclass"]
1115
+ end
1116
+
1117
+ def attribute_name_resolvable_without_connection?
1118
+ @entry_attribute and @local_entry_attribute
1119
+ end
1120
+
1121
+ def entry_attribute
1122
+ @entry_attribute ||=
1123
+ connection.entry_attribute(find_object_class_values(@data) || [])
1124
+ end
1125
+
1126
+ def local_entry_attribute
1127
+ @local_entry_attribute ||= connection.entry_attribute([])
1128
+ end
1129
+
1130
+ def extract_object_class(attributes)
1131
+ classes = []
1132
+ attrs = {}
1133
+ attributes.each do |key, value|
1134
+ key = key.to_s
1135
+ if /\Aobject_?class\z/i =~ key
1136
+ classes.concat(value.to_a)
1137
+ else
1138
+ attrs[key] = value
1139
+ end
1140
+ end
1141
+ [classes, attributes]
1142
+ end
1143
+
1144
+ def init_base
1145
+ init_instance_variables
1146
+ end
1147
+
1148
+ def initialize_by_ldap_data(dn, attributes)
1149
+ init_base
1150
+ dn = Compatible.convert_to_utf8_encoded_object(dn)
1151
+ attributes = Compatible.convert_to_utf8_encoded_object(attributes)
1152
+ @original_dn = dn.clone
1153
+ @dn = dn
1154
+ @base = nil
1155
+ @base_value = nil
1156
+ @new_entry = false
1157
+ @dn_is_base = false
1158
+ @ldap_data = attributes
1159
+ classes, attributes = extract_object_class(attributes)
1160
+ self.classes = classes
1161
+ self.dn = dn
1162
+ self.attributes = attributes
1163
+ yield self if block_given?
1164
+ end
1165
+
1166
+ def instantiate(args)
1167
+ dn, attributes, options = args
1168
+ options ||= {}
1169
+
1170
+ obj = self.class.allocate
1171
+ obj.connection = options[:connection] || @connection
1172
+ obj.instance_eval do
1173
+ initialize_by_ldap_data(dn, attributes)
1174
+ end
1175
+ obj
1176
+ end
1177
+
1178
+ def to_real_attribute_name(name, allow_normalized_name=true)
1179
+ return name if name.nil?
1180
+ if allow_normalized_name
1181
+ entry_attribute.normalize(name, allow_normalized_name) ||
1182
+ local_entry_attribute.normalize(name, allow_normalized_name)
1183
+ else
1184
+ @real_names[name] ||=
1185
+ entry_attribute.normalize(name, false) ||
1186
+ local_entry_attribute.normalize(name, false)
1187
+ end
1188
+ end
1189
+
1190
+ # enforce_type
1191
+ #
1192
+ # enforce_type applies your changes without attempting to write to LDAP.
1193
+ # This means that if you set userCertificate to somebinary value, it will
1194
+ # wrap it up correctly.
1195
+ def enforce_type(key, value)
1196
+ # Enforce attribute value formatting
1197
+ normalize_attribute(key, value)[1]
1198
+ end
1199
+
1200
+ def init_instance_variables
1201
+ @mutex = Mutex.new
1202
+ @data = {} # where the r/w entry data is stored
1203
+ @ldap_data = {} # original ldap entry data
1204
+ @dn_attribute = nil
1205
+ @base = nil
1206
+ @scope = nil
1207
+ @dn = nil
1208
+ @dn_is_base = false
1209
+ @dn_split_value = nil
1210
+ @connection ||= nil
1211
+ @_hashing = false
1212
+ clear_connection_based_cache
1213
+ end
1214
+
1215
+ # get_attribute
1216
+ #
1217
+ # Return the value of the attribute called by method_missing?
1218
+ def get_attribute(name, force_array=false)
1219
+ name, value = get_attribute_before_type_cast(name, force_array)
1220
+ return value if name.nil?
1221
+ attribute = schema.attribute(name)
1222
+ type_cast(attribute, value)
1223
+ end
1224
+
1225
+ def type_cast(attribute, value)
1226
+ case value
1227
+ when Hash
1228
+ result = {}
1229
+ value.each do |option, val|
1230
+ result[option] = type_cast(attribute, val)
1231
+ end
1232
+ if result.size == 1 and result.has_key?("binary")
1233
+ result["binary"]
1234
+ else
1235
+ result
1236
+ end
1237
+ when Array
1238
+ value.collect do |val|
1239
+ type_cast(attribute, val)
1240
+ end
1241
+ else
1242
+ attribute.type_cast(value)
1243
+ end
1244
+ end
1245
+
1246
+ def get_attribute_before_type_cast(name, force_array=false)
1247
+ name = to_real_attribute_name(name)
1248
+
1249
+ value = @data[name]
1250
+ value = [] if value.nil?
1251
+ [name, array_of(value, force_array)]
1252
+ end
1253
+
1254
+ def get_attribute_as_query(name, force_array=false)
1255
+ name, value = get_attribute_before_type_cast(name, force_array)
1256
+ if force_array
1257
+ value.collect {|x| !false_value?(x)}
1258
+ else
1259
+ !false_value?(value)
1260
+ end
1261
+ end
1262
+
1263
+ def false_value?(value)
1264
+ value.nil? or value == false or value == [] or
1265
+ value == "false" or value == "FALSE" or value == ""
1266
+ end
1267
+
1268
+ # set_attribute
1269
+ #
1270
+ # Set the value of the attribute called by method_missing?
1271
+ def set_attribute(name, value)
1272
+ real_name = to_real_attribute_name(name)
1273
+ _dn_attribute = nil
1274
+ valid_dn_attribute = true
1275
+ begin
1276
+ _dn_attribute = dn_attribute
1277
+ rescue DistinguishedNameInvalid
1278
+ valid_dn_attribute = false
1279
+ end
1280
+ if valid_dn_attribute and real_name == _dn_attribute
1281
+ real_name, value = register_new_dn_attribute(real_name, value)
1282
+ end
1283
+ raise UnknownAttribute.new(name) if real_name.nil?
1284
+
1285
+ @data[real_name] = value
1286
+ @simplified_data = nil
1287
+ end
1288
+
1289
+ def register_new_dn_attribute(name, value)
1290
+ @dn = nil
1291
+ @dn_is_base = false
1292
+ if value.blank?
1293
+ @dn_split_value = nil
1294
+ [name, nil]
1295
+ else
1296
+ new_name, new_value, raw_new_value, new_bases = split_dn_value(value)
1297
+ @dn_split_value = [new_name, new_value, new_bases]
1298
+ if new_name.nil? and new_value.nil?
1299
+ new_name, raw_new_value = new_bases[0].to_a[0]
1300
+ end
1301
+ [to_real_attribute_name(new_name) || name,
1302
+ raw_new_value || value]
1303
+ end
1304
+ end
1305
+
1306
+ def update_dn(new_name, new_value, bases)
1307
+ if new_name.nil? and new_value.nil?
1308
+ @dn_is_base = true
1309
+ @base = nil
1310
+ @base_value = nil
1311
+ attr, value = bases[0].to_a[0]
1312
+ @dn_attribute = attr
1313
+ else
1314
+ new_name ||= @dn_attribute || dn_attribute_of_class
1315
+ new_name = to_real_attribute_name(new_name)
1316
+ if new_name.nil?
1317
+ new_name = @dn_attribute || dn_attribute_of_class
1318
+ new_name = to_real_attribute_name(new_name)
1319
+ end
1320
+ new_bases = bases.empty? ? nil : DN.new(*bases).to_s
1321
+ dn_components = ["#{new_name}=#{new_value}",
1322
+ new_bases,
1323
+ self.class.base.to_s]
1324
+ dn_components = dn_components.find_all {|component| !component.blank?}
1325
+ DN.parse(dn_components.join(','))
1326
+ @base = nil
1327
+ @base_value = new_bases
1328
+ @dn_attribute = new_name
1329
+ end
1330
+ end
1331
+
1332
+ def split_dn_value(value)
1333
+ dn_value = relative_dn_value = nil
1334
+ begin
1335
+ dn_value = value if value.is_a?(DN)
1336
+ dn_value ||= DN.parse(value)
1337
+ rescue DistinguishedNameInvalid
1338
+ begin
1339
+ dn_value = DN.parse("#{dn_attribute}=#{value}")
1340
+ rescue DistinguishedNameInvalid
1341
+ return [nil, value, value, []]
1342
+ end
1343
+ end
1344
+
1345
+ val = bases = nil
1346
+ begin
1347
+ relative_dn_value = dn_value
1348
+ base_of_class = self.class.base
1349
+ relative_dn_value -= base_of_class if base_of_class
1350
+ if relative_dn_value.rdns.empty?
1351
+ val = []
1352
+ bases = dn_value.rdns
1353
+ else
1354
+ val, *bases = relative_dn_value.rdns
1355
+ end
1356
+ rescue ArgumentError
1357
+ val, *bases = dn_value.rdns
1358
+ end
1359
+
1360
+ dn_attribute_name, dn_attribute_value = val.to_a[0]
1361
+ escaped_dn_attribute_value = nil
1362
+ unless dn_attribute_value.nil?
1363
+ escaped_dn_attribute_value = DN.escape_value(dn_attribute_value)
1364
+ end
1365
+ [dn_attribute_name, escaped_dn_attribute_value,
1366
+ dn_attribute_value, bases]
1367
+ end
1368
+
1369
+ def need_update_dn?
1370
+ not @dn_split_value.nil?
1371
+ end
1372
+
1373
+ def ensure_update_dn
1374
+ return unless need_update_dn?
1375
+ @mutex.synchronize do
1376
+ if @dn_split_value
1377
+ update_dn(*@dn_split_value)
1378
+ @dn_split_value = nil
1379
+ end
1380
+ end
1381
+ end
1382
+
1383
+ def compute_dn
1384
+ return base if @dn_is_base
1385
+
1386
+ ensure_update_dn
1387
+ dn_value = id
1388
+ if dn_value.nil?
1389
+ format = _("%s's DN attribute (%s) isn't set")
1390
+ message = format % [self.inspect, dn_attribute]
1391
+ raise DistinguishedNameNotSetError.new, message
1392
+ end
1393
+ dn_value = DN.escape_value(dn_value.to_s)
1394
+ _base = base
1395
+ _base = nil if _base.blank?
1396
+ DN.parse(["#{dn_attribute}=#{dn_value}", _base].compact.join(","))
1397
+ end
1398
+
1399
+ def compute_base
1400
+ base_of_class = self.class.base
1401
+ if @base_value.nil?
1402
+ base_of_class
1403
+ else
1404
+ base_of_object = DN.parse(@base_value)
1405
+ base_of_object += base_of_class if base_of_class
1406
+ base_of_object
1407
+ end
1408
+ end
1409
+
1410
+ # array_of
1411
+ #
1412
+ # Returns the array form of a value, or not an array if
1413
+ # false is passed in.
1414
+ def array_of(value, to_a=true)
1415
+ case value
1416
+ when Array
1417
+ if to_a or value.size > 1
1418
+ value.collect {|v| array_of(v, false)}.compact
1419
+ else
1420
+ if value.empty?
1421
+ nil
1422
+ else
1423
+ array_of(value.first, to_a)
1424
+ end
1425
+ end
1426
+ when Hash
1427
+ if to_a
1428
+ [value]
1429
+ else
1430
+ result = {}
1431
+ value.each {|k, v| result[k] = array_of(v, to_a)}
1432
+ result
1433
+ end
1434
+ else
1435
+ to_a ? [value] : value
1436
+ end
1437
+ end
1438
+
1439
+ def normalize_data(data, except=[])
1440
+ _schema = schema
1441
+ result = {}
1442
+ data.each do |key, values|
1443
+ next if except.include?(key)
1444
+ real_name = to_real_attribute_name(key)
1445
+ next if real_name and except.include?(real_name)
1446
+ real_name ||= key
1447
+ next if _schema.attribute(real_name).id.nil?
1448
+ result[real_name] ||= []
1449
+ result[real_name].concat(enforce_type(real_name, values))
1450
+ end
1451
+ result
1452
+ end
1453
+
1454
+ def simplify_data(data)
1455
+ _schema = schema
1456
+ result = {}
1457
+ data.each do |key, values|
1458
+ attribute = _schema.attribute(key)
1459
+ if attribute.single_value? and values.is_a?(Array) and values.size == 1
1460
+ values = values[0]
1461
+ end
1462
+ result[key] = type_cast(attribute, values)
1463
+ end
1464
+ result
1465
+ end
1466
+
1467
+ def collect_modified_attributes(ldap_data, data)
1468
+ klass = self.class
1469
+ _dn_attribute = dn_attribute
1470
+ new_dn_value = nil
1471
+ attributes = []
1472
+
1473
+ # Now that all the options will be treated as unique attributes
1474
+ # we can see what's changed and add anything that is brand-spankin'
1475
+ # new.
1476
+ ldap_data.each do |k, v|
1477
+ value = data[k] || []
1478
+
1479
+ next if v == value
1480
+
1481
+ value = klass.remove_blank_value(value) || []
1482
+ next if v == value
1483
+
1484
+ if klass.blank_value?(value) and
1485
+ schema.attribute(k).binary_required?
1486
+ value = [{'binary' => []}]
1487
+ end
1488
+ if k == _dn_attribute
1489
+ new_dn_value = value[0]
1490
+ else
1491
+ attributes.push([:replace, k, value])
1492
+ end
1493
+ end
1494
+
1495
+ data.each do |k, v|
1496
+ value = v || []
1497
+ next if ldap_data.has_key?(k)
1498
+
1499
+ value = klass.remove_blank_value(value) || []
1500
+ next if klass.blank_value?(value)
1501
+
1502
+ # Detect subtypes and account for them
1503
+ # REPLACE will function like ADD, but doesn't hit EQUALITY problems
1504
+ # TODO: Added equality(attr) to Schema
1505
+ attributes.push([:replace, k, value])
1506
+ end
1507
+
1508
+ [new_dn_value, attributes]
1509
+ end
1510
+
1511
+ def collect_all_attributes(data)
1512
+ dn_attr = dn_attribute
1513
+ dn_value = data[dn_attr]
1514
+
1515
+ attributes = []
1516
+ attributes.push([dn_attr, dn_value])
1517
+
1518
+ oc_value = data['objectClass']
1519
+ attributes.push(['objectClass', oc_value])
1520
+ except_keys = ['objectClass', dn_attr].collect(&:downcase)
1521
+ data.each do |key, value|
1522
+ next if except_keys.include?(key.downcase)
1523
+ value = self.class.remove_blank_value(value)
1524
+ next if self.class.blank_value?(value)
1525
+
1526
+ attributes.push([key, value])
1527
+ end
1528
+
1529
+ attributes
1530
+ end
1531
+
1532
+ def create_or_update
1533
+ new_entry? ? create : update
1534
+ end
1535
+
1536
+ def prepare_data_for_saving
1537
+ # Expand subtypes to real ldap_data attributes
1538
+ # We can't reuse @ldap_data because an exception would leave
1539
+ # an object in an unknown state
1540
+ ldap_data = normalize_data(@ldap_data)
1541
+
1542
+ # Expand subtypes to real data attributes, but leave @data alone
1543
+ object_classes = find_object_class_values(@ldap_data) || []
1544
+ original_attributes =
1545
+ connection.entry_attribute(object_classes).names
1546
+ bad_attrs = original_attributes - entry_attribute.names
1547
+ data = normalize_data(@data, bad_attrs)
1548
+
1549
+ success = yield(data, ldap_data)
1550
+
1551
+ if success
1552
+ @ldap_data = data.clone
1553
+ # Delete items disallowed by objectclasses.
1554
+ # They should have been removed from ldap.
1555
+ bad_attrs.each do |remove_me|
1556
+ @ldap_data.delete(remove_me)
1557
+ end
1558
+ @original_dn = dn.clone
1559
+ end
1560
+
1561
+ success
1562
+ end
1563
+
1564
+ def create
1565
+ prepare_data_for_saving do |data, ldap_data|
1566
+ attributes = collect_all_attributes(data)
1567
+ add_entry(dn, attributes)
1568
+ @new_entry = false
1569
+ true
1570
+ end
1571
+ end
1572
+
1573
+ def update
1574
+ prepare_data_for_saving do |data, ldap_data|
1575
+ new_dn_value, attributes = collect_modified_attributes(ldap_data, data)
1576
+ modify_entry(@original_dn, attributes)
1577
+ if new_dn_value
1578
+ old_dn_base = DN.parse(@original_dn).parent
1579
+ new_dn_base = dn.clone.parent
1580
+ if old_dn_base == new_dn_base
1581
+ new_superior = nil
1582
+ else
1583
+ new_superior = new_dn_base
1584
+ end
1585
+ modify_rdn_entry(@original_dn,
1586
+ "#{dn_attribute}=#{DN.escape_value(new_dn_value)}",
1587
+ true,
1588
+ new_superior)
1589
+ end
1590
+ true
1591
+ end
1592
+ end
1593
+ end # Base
1594
+ end # ActiveLdap