activeldap 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. data/CHANGES +454 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +58 -0
  4. data/README +101 -0
  5. data/Rakefile +175 -0
  6. data/TODO +25 -0
  7. data/benchmark/bench-al.rb +202 -0
  8. data/benchmark/config.yaml.sample +5 -0
  9. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  10. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  11. data/examples/al-admin/README +182 -0
  12. data/examples/al-admin/Rakefile +10 -0
  13. data/examples/al-admin/app/controllers/account_controller.rb +59 -0
  14. data/examples/al-admin/app/controllers/application.rb +34 -0
  15. data/examples/al-admin/app/controllers/attributes_controller.rb +17 -0
  16. data/examples/al-admin/app/controllers/directory_controller.rb +47 -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 +51 -0
  20. data/examples/al-admin/app/controllers/welcome_controller.rb +10 -0
  21. data/examples/al-admin/app/helpers/account_helper.rb +2 -0
  22. data/examples/al-admin/app/helpers/application_helper.rb +42 -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 +13 -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 +54 -0
  32. data/examples/al-admin/app/models/user.rb +91 -0
  33. data/examples/al-admin/app/views/_entry/_attributes_information.rhtml +23 -0
  34. data/examples/al-admin/app/views/_entry/_entry.rhtml +15 -0
  35. data/examples/al-admin/app/views/_schema/_aliases.rhtml +7 -0
  36. data/examples/al-admin/app/views/_switcher/_after.rhtml +2 -0
  37. data/examples/al-admin/app/views/_switcher/_before.rhtml +5 -0
  38. data/examples/al-admin/app/views/account/login.rhtml +26 -0
  39. data/examples/al-admin/app/views/account/sign_up.rhtml +28 -0
  40. data/examples/al-admin/app/views/attributes/_attributes.rhtml +19 -0
  41. data/examples/al-admin/app/views/attributes/_detail.rhtml +29 -0
  42. data/examples/al-admin/app/views/attributes/index.rhtml +3 -0
  43. data/examples/al-admin/app/views/attributes/show.rhtml +31 -0
  44. data/examples/al-admin/app/views/directory/_tree.rhtml +10 -0
  45. data/examples/al-admin/app/views/directory/_tree_view_js.rhtml +26 -0
  46. data/examples/al-admin/app/views/directory/index.rhtml +13 -0
  47. data/examples/al-admin/app/views/directory/populate.rhtml +2 -0
  48. data/examples/al-admin/app/views/layouts/_flash_box.rhtml +4 -0
  49. data/examples/al-admin/app/views/layouts/_footer.rhtml +9 -0
  50. data/examples/al-admin/app/views/layouts/_header_menu.rhtml +10 -0
  51. data/examples/al-admin/app/views/layouts/_main_menu.rhtml +18 -0
  52. data/examples/al-admin/app/views/layouts/application.rhtml +56 -0
  53. data/examples/al-admin/app/views/object_classes/_attributes.rhtml +28 -0
  54. data/examples/al-admin/app/views/object_classes/_object_classes.rhtml +19 -0
  55. data/examples/al-admin/app/views/object_classes/index.rhtml +3 -0
  56. data/examples/al-admin/app/views/object_classes/show.rhtml +39 -0
  57. data/examples/al-admin/app/views/syntaxes/_detail.rhtml +14 -0
  58. data/examples/al-admin/app/views/syntaxes/_syntaxes.rhtml +19 -0
  59. data/examples/al-admin/app/views/syntaxes/index.rhtml +3 -0
  60. data/examples/al-admin/app/views/syntaxes/show.rhtml +22 -0
  61. data/examples/al-admin/app/views/users/_attributes_update_form.rhtml +30 -0
  62. data/examples/al-admin/app/views/users/_form.rhtml +13 -0
  63. data/examples/al-admin/app/views/users/_object_classes_update_form.rhtml +40 -0
  64. data/examples/al-admin/app/views/users/_password_change_form.rhtml +20 -0
  65. data/examples/al-admin/app/views/users/edit.rhtml +15 -0
  66. data/examples/al-admin/app/views/users/index.rhtml +10 -0
  67. data/examples/al-admin/app/views/users/show.rhtml +11 -0
  68. data/examples/al-admin/app/views/welcome/index.rhtml +13 -0
  69. data/examples/al-admin/config/boot.rb +45 -0
  70. data/examples/al-admin/config/database.yml.example +19 -0
  71. data/examples/al-admin/config/environment.rb +70 -0
  72. data/examples/al-admin/config/environments/development.rb +21 -0
  73. data/examples/al-admin/config/environments/production.rb +18 -0
  74. data/examples/al-admin/config/environments/test.rb +19 -0
  75. data/examples/al-admin/config/ldap.yml.example +21 -0
  76. data/examples/al-admin/config/routes.rb +46 -0
  77. data/examples/al-admin/db/migrate/001_create_users.rb +16 -0
  78. data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +9 -0
  79. data/examples/al-admin/lib/authenticated_system.rb +124 -0
  80. data/examples/al-admin/lib/authenticated_test_helper.rb +113 -0
  81. data/examples/al-admin/lib/tasks/gettext.rake +35 -0
  82. data/examples/al-admin/po/en/al-admin.po +341 -0
  83. data/examples/al-admin/po/ja/al-admin.po +341 -0
  84. data/examples/al-admin/po/nl/al-admin.po +373 -0
  85. data/examples/al-admin/public/.htaccess +40 -0
  86. data/examples/al-admin/public/404.html +30 -0
  87. data/examples/al-admin/public/500.html +30 -0
  88. data/examples/al-admin/public/dispatch.cgi +10 -0
  89. data/examples/al-admin/public/dispatch.fcgi +24 -0
  90. data/examples/al-admin/public/dispatch.rb +10 -0
  91. data/examples/al-admin/public/favicon.ico +0 -0
  92. data/examples/al-admin/public/images/active-ldap.svg +6351 -0
  93. data/examples/al-admin/public/images/al-admin.png +0 -0
  94. data/examples/al-admin/public/images/al-admin.svg +6371 -0
  95. data/examples/al-admin/public/images/header-background.png +0 -0
  96. data/examples/al-admin/public/images/header-background.svg +99 -0
  97. data/examples/al-admin/public/images/rails.png +0 -0
  98. data/examples/al-admin/public/images/spinelz/accordion_tab_left_active.gif +0 -0
  99. data/examples/al-admin/public/images/spinelz/accordion_tab_left_inactive.gif +0 -0
  100. data/examples/al-admin/public/images/spinelz/accordion_tab_middle_active.gif +0 -0
  101. data/examples/al-admin/public/images/spinelz/accordion_tab_middle_inactive.gif +0 -0
  102. data/examples/al-admin/public/images/spinelz/accordion_tab_right_active.gif +0 -0
  103. data/examples/al-admin/public/images/spinelz/accordion_tab_right_inactive.gif +0 -0
  104. data/examples/al-admin/public/images/spinelz/balloon_back.gif +0 -0
  105. data/examples/al-admin/public/images/spinelz/balloon_bottom_left.gif +0 -0
  106. data/examples/al-admin/public/images/spinelz/balloon_bottom_middle.gif +0 -0
  107. data/examples/al-admin/public/images/spinelz/balloon_bottom_right.gif +0 -0
  108. data/examples/al-admin/public/images/spinelz/balloon_left_down_arrow.gif +0 -0
  109. data/examples/al-admin/public/images/spinelz/balloon_left_up_arrow.gif +0 -0
  110. data/examples/al-admin/public/images/spinelz/balloon_middle_left.gif +0 -0
  111. data/examples/al-admin/public/images/spinelz/balloon_middle_right.gif +0 -0
  112. data/examples/al-admin/public/images/spinelz/balloon_right_down_arrow.gif +0 -0
  113. data/examples/al-admin/public/images/spinelz/balloon_right_up_arrow.gif +0 -0
  114. data/examples/al-admin/public/images/spinelz/balloon_top_left.gif +0 -0
  115. data/examples/al-admin/public/images/spinelz/balloon_top_middle.gif +0 -0
  116. data/examples/al-admin/public/images/spinelz/balloon_top_right.gif +0 -0
  117. data/examples/al-admin/public/images/spinelz/barchart_h.gif +0 -0
  118. data/examples/al-admin/public/images/spinelz/barchart_v.gif +0 -0
  119. data/examples/al-admin/public/images/spinelz/button.gif +0 -0
  120. data/examples/al-admin/public/images/spinelz/calendar_default_handler.gif +0 -0
  121. data/examples/al-admin/public/images/spinelz/calendar_delete.gif +0 -0
  122. data/examples/al-admin/public/images/spinelz/calendar_next.gif +0 -0
  123. data/examples/al-admin/public/images/spinelz/calendar_next_second.gif +0 -0
  124. data/examples/al-admin/public/images/spinelz/calendar_pre.gif +0 -0
  125. data/examples/al-admin/public/images/spinelz/calendar_pre_second.gif +0 -0
  126. data/examples/al-admin/public/images/spinelz/calendar_private_icon.gif +0 -0
  127. data/examples/al-admin/public/images/spinelz/calendar_schedule.gif +0 -0
  128. data/examples/al-admin/public/images/spinelz/calender_back.gif +0 -0
  129. data/examples/al-admin/public/images/spinelz/calender_back_second.gif +0 -0
  130. data/examples/al-admin/public/images/spinelz/datepicker2_back.gif +0 -0
  131. data/examples/al-admin/public/images/spinelz/datepicker2_back_second.gif +0 -0
  132. data/examples/al-admin/public/images/spinelz/datepicker2_next.gif +0 -0
  133. data/examples/al-admin/public/images/spinelz/datepicker2_next_second.gif +0 -0
  134. data/examples/al-admin/public/images/spinelz/datepicker2_pre.gif +0 -0
  135. data/examples/al-admin/public/images/spinelz/datepicker2_pre_second.gif +0 -0
  136. data/examples/al-admin/public/images/spinelz/datepicker_back.gif +0 -0
  137. data/examples/al-admin/public/images/spinelz/datepicker_back_second.gif +0 -0
  138. data/examples/al-admin/public/images/spinelz/datepicker_next.gif +0 -0
  139. data/examples/al-admin/public/images/spinelz/datepicker_next_second.gif +0 -0
  140. data/examples/al-admin/public/images/spinelz/datepicker_pre.gif +0 -0
  141. data/examples/al-admin/public/images/spinelz/datepicker_pre_second.gif +0 -0
  142. data/examples/al-admin/public/images/spinelz/grid_down.gif +0 -0
  143. data/examples/al-admin/public/images/spinelz/grid_state.gif +0 -0
  144. data/examples/al-admin/public/images/spinelz/grid_up.gif +0 -0
  145. data/examples/al-admin/public/images/spinelz/icon_day.gif +0 -0
  146. data/examples/al-admin/public/images/spinelz/icon_month.gif +0 -0
  147. data/examples/al-admin/public/images/spinelz/icon_week.gif +0 -0
  148. data/examples/al-admin/public/images/spinelz/menubar_back.gif +0 -0
  149. data/examples/al-admin/public/images/spinelz/menubar_subcontents_back.gif +0 -0
  150. data/examples/al-admin/public/images/spinelz/navPanel_tab_left_active.gif +0 -0
  151. data/examples/al-admin/public/images/spinelz/navPanel_tab_left_inactive.gif +0 -0
  152. data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_active.gif +0 -0
  153. data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_inactive.gif +0 -0
  154. data/examples/al-admin/public/images/spinelz/navPanel_tab_right_active.gif +0 -0
  155. data/examples/al-admin/public/images/spinelz/navPanel_tab_right_inactive.gif +0 -0
  156. data/examples/al-admin/public/images/spinelz/select_date.gif +0 -0
  157. data/examples/al-admin/public/images/spinelz/selectabletable_selected.gif +0 -0
  158. data/examples/al-admin/public/images/spinelz/sideBarBox_about.gif +0 -0
  159. data/examples/al-admin/public/images/spinelz/sideBarBox_menu.gif +0 -0
  160. data/examples/al-admin/public/images/spinelz/sideBarBox_sample.gif +0 -0
  161. data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomActive.gif +0 -0
  162. data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomInactive.gif +0 -0
  163. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive.gif +0 -0
  164. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive2.gif +0 -0
  165. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive.gif +0 -0
  166. data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive2.gif +0 -0
  167. data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopActive.gif +0 -0
  168. data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopInactive.gif +0 -0
  169. data/examples/al-admin/public/images/spinelz/sideBarBox_tabbar.gif +0 -0
  170. data/examples/al-admin/public/images/spinelz/sortableTable_down.gif +0 -0
  171. data/examples/al-admin/public/images/spinelz/sortableTable_normal.gif +0 -0
  172. data/examples/al-admin/public/images/spinelz/sortableTable_up.gif +0 -0
  173. data/examples/al-admin/public/images/spinelz/switcher_close.gif +0 -0
  174. data/examples/al-admin/public/images/spinelz/switcher_open.gif +0 -0
  175. data/examples/al-admin/public/images/spinelz/tabBox_close.gif +0 -0
  176. data/examples/al-admin/public/images/spinelz/tabBox_tabLeftActive.gif +0 -0
  177. data/examples/al-admin/public/images/spinelz/tabBox_tabLeftInactive.gif +0 -0
  178. data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleActive.gif +0 -0
  179. data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleInactive.gif +0 -0
  180. data/examples/al-admin/public/images/spinelz/tabBox_tabRightActive.gif +0 -0
  181. data/examples/al-admin/public/images/spinelz/tabBox_tabRightInactive.gif +0 -0
  182. data/examples/al-admin/public/images/spinelz/tab_bar.gif +0 -0
  183. data/examples/al-admin/public/images/spinelz/table_back.gif +0 -0
  184. data/examples/al-admin/public/images/spinelz/timepicker_clock.gif +0 -0
  185. data/examples/al-admin/public/images/spinelz/toolbar_close.gif +0 -0
  186. data/examples/al-admin/public/images/spinelz/toolbar_left.gif +0 -0
  187. data/examples/al-admin/public/images/spinelz/toolbar_max.gif +0 -0
  188. data/examples/al-admin/public/images/spinelz/toolbar_middle.gif +0 -0
  189. data/examples/al-admin/public/images/spinelz/toolbar_min.gif +0 -0
  190. data/examples/al-admin/public/images/spinelz/toolbar_next.gif +0 -0
  191. data/examples/al-admin/public/images/spinelz/toolbar_right.gif +0 -0
  192. data/examples/al-admin/public/images/spinelz/treeview_dir.gif +0 -0
  193. data/examples/al-admin/public/images/spinelz/treeview_file.gif +0 -0
  194. data/examples/al-admin/public/images/spinelz/treeview_group.gif +0 -0
  195. data/examples/al-admin/public/images/spinelz/treeview_group_special.gif +0 -0
  196. data/examples/al-admin/public/images/spinelz/treeview_state.gif +0 -0
  197. data/examples/al-admin/public/images/spinelz/treeview_user.gif +0 -0
  198. data/examples/al-admin/public/images/spinelz/window_bottom_left.gif +0 -0
  199. data/examples/al-admin/public/images/spinelz/window_bottom_middle.gif +0 -0
  200. data/examples/al-admin/public/images/spinelz/window_bottom_right.gif +0 -0
  201. data/examples/al-admin/public/images/spinelz/window_close.gif +0 -0
  202. data/examples/al-admin/public/images/spinelz/window_max.gif +0 -0
  203. data/examples/al-admin/public/images/spinelz/window_middle_left.gif +0 -0
  204. data/examples/al-admin/public/images/spinelz/window_middle_right.gif +0 -0
  205. data/examples/al-admin/public/images/spinelz/window_min.gif +0 -0
  206. data/examples/al-admin/public/images/spinelz/window_top_left.gif +0 -0
  207. data/examples/al-admin/public/images/spinelz/window_top_middle.gif +0 -0
  208. data/examples/al-admin/public/images/spinelz/window_top_right.gif +0 -0
  209. data/examples/al-admin/public/javascripts/application.js +2 -0
  210. data/examples/al-admin/public/javascripts/controls.js +833 -0
  211. data/examples/al-admin/public/javascripts/dragdrop.js +942 -0
  212. data/examples/al-admin/public/javascripts/effects.js +1088 -0
  213. data/examples/al-admin/public/javascripts/prototype.js +2515 -0
  214. data/examples/al-admin/public/javascripts/spinelz/accordion.js +185 -0
  215. data/examples/al-admin/public/javascripts/spinelz/ajaxHistory.js +157 -0
  216. data/examples/al-admin/public/javascripts/spinelz/balloon.js +287 -0
  217. data/examples/al-admin/public/javascripts/spinelz/barchart.js +524 -0
  218. data/examples/al-admin/public/javascripts/spinelz/calendar.js +3012 -0
  219. data/examples/al-admin/public/javascripts/spinelz/colorpicker.js +128 -0
  220. data/examples/al-admin/public/javascripts/spinelz/datepicker.js +438 -0
  221. data/examples/al-admin/public/javascripts/spinelz/grid.js +1391 -0
  222. data/examples/al-admin/public/javascripts/spinelz/grid_resizeEx.js +100 -0
  223. data/examples/al-admin/public/javascripts/spinelz/grid_sortabletableEx.js +129 -0
  224. data/examples/al-admin/public/javascripts/spinelz/inplaceEditorEx.js +148 -0
  225. data/examples/al-admin/public/javascripts/spinelz/menubar.js +232 -0
  226. data/examples/al-admin/public/javascripts/spinelz/navPanel.js +170 -0
  227. data/examples/al-admin/public/javascripts/spinelz/selectableTable.js +433 -0
  228. data/examples/al-admin/public/javascripts/spinelz/sideBarBox.js +282 -0
  229. data/examples/al-admin/public/javascripts/spinelz/sideBarBox_effects.js +83 -0
  230. data/examples/al-admin/public/javascripts/spinelz/sortableTable.js +270 -0
  231. data/examples/al-admin/public/javascripts/spinelz/switcher.js +78 -0
  232. data/examples/al-admin/public/javascripts/spinelz/tabBox.js +469 -0
  233. data/examples/al-admin/public/javascripts/spinelz/timepicker.js +384 -0
  234. data/examples/al-admin/public/javascripts/spinelz/toolbar.js +152 -0
  235. data/examples/al-admin/public/javascripts/spinelz/treeview.js +703 -0
  236. data/examples/al-admin/public/javascripts/spinelz/window.js +641 -0
  237. data/examples/al-admin/public/javascripts/spinelz/window_resizeEx.js +130 -0
  238. data/examples/al-admin/public/javascripts/spinelz_lib/builder.js +131 -0
  239. data/examples/al-admin/public/javascripts/spinelz_lib/controls.js +835 -0
  240. data/examples/al-admin/public/javascripts/spinelz_lib/dragdrop.js +944 -0
  241. data/examples/al-admin/public/javascripts/spinelz_lib/effects.js +1090 -0
  242. data/examples/al-admin/public/javascripts/spinelz_lib/json.js +139 -0
  243. data/examples/al-admin/public/javascripts/spinelz_lib/prototype.js +2515 -0
  244. data/examples/al-admin/public/javascripts/spinelz_lib/resize.js +215 -0
  245. data/examples/al-admin/public/javascripts/spinelz_lib/scriptaculous.js +51 -0
  246. data/examples/al-admin/public/javascripts/spinelz_lib/slider.js +278 -0
  247. data/examples/al-admin/public/javascripts/spinelz_lib/spinelz_util.js +1266 -0
  248. data/examples/al-admin/public/javascripts/spinelz_lib/unittest.js +564 -0
  249. data/examples/al-admin/public/robots.txt +1 -0
  250. data/examples/al-admin/public/stylesheets/account.css +41 -0
  251. data/examples/al-admin/public/stylesheets/attributes.css +1 -0
  252. data/examples/al-admin/public/stylesheets/base.css +99 -0
  253. data/examples/al-admin/public/stylesheets/common.css +2 -0
  254. data/examples/al-admin/public/stylesheets/detail.css +36 -0
  255. data/examples/al-admin/public/stylesheets/directory.css +22 -0
  256. data/examples/al-admin/public/stylesheets/object-classes.css +6 -0
  257. data/examples/al-admin/public/stylesheets/rails.css +35 -0
  258. data/examples/al-admin/public/stylesheets/spinelz/accordion.css +59 -0
  259. data/examples/al-admin/public/stylesheets/spinelz/balloon.css +151 -0
  260. data/examples/al-admin/public/stylesheets/spinelz/calendar.css +564 -0
  261. data/examples/al-admin/public/stylesheets/spinelz/datepicker.css +175 -0
  262. data/examples/al-admin/public/stylesheets/spinelz/grid.css +137 -0
  263. data/examples/al-admin/public/stylesheets/spinelz/menubar.css +78 -0
  264. data/examples/al-admin/public/stylesheets/spinelz/modal.css +22 -0
  265. data/examples/al-admin/public/stylesheets/spinelz/navPanel.css +58 -0
  266. data/examples/al-admin/public/stylesheets/spinelz/selectableTable.css +28 -0
  267. data/examples/al-admin/public/stylesheets/spinelz/sideBarBox.css +82 -0
  268. data/examples/al-admin/public/stylesheets/spinelz/sortableTable.css +51 -0
  269. data/examples/al-admin/public/stylesheets/spinelz/switcher.css +23 -0
  270. data/examples/al-admin/public/stylesheets/spinelz/tabBox.css +94 -0
  271. data/examples/al-admin/public/stylesheets/spinelz/timepicker.css +508 -0
  272. data/examples/al-admin/public/stylesheets/spinelz/toolbar.css +82 -0
  273. data/examples/al-admin/public/stylesheets/spinelz/treeview.css +121 -0
  274. data/examples/al-admin/public/stylesheets/spinelz/window.css +140 -0
  275. data/examples/al-admin/public/stylesheets/structure.css +81 -0
  276. data/examples/al-admin/public/stylesheets/syntaxes.css +1 -0
  277. data/examples/al-admin/public/stylesheets/users.css +13 -0
  278. data/examples/al-admin/public/stylesheets/welcome.css +0 -0
  279. data/examples/al-admin/script/about +3 -0
  280. data/examples/al-admin/script/breakpointer +3 -0
  281. data/examples/al-admin/script/console +3 -0
  282. data/examples/al-admin/script/destroy +3 -0
  283. data/examples/al-admin/script/generate +3 -0
  284. data/examples/al-admin/script/performance/benchmarker +3 -0
  285. data/examples/al-admin/script/performance/profiler +3 -0
  286. data/examples/al-admin/script/plugin +3 -0
  287. data/examples/al-admin/script/process/inspector +3 -0
  288. data/examples/al-admin/script/process/reaper +3 -0
  289. data/examples/al-admin/script/process/spawner +3 -0
  290. data/examples/al-admin/script/runner +3 -0
  291. data/examples/al-admin/script/server +3 -0
  292. data/examples/al-admin/test/fixtures/users.yml +9 -0
  293. data/examples/al-admin/test/functional/account_controller_test.rb +24 -0
  294. data/examples/al-admin/test/functional/attributes_controller_test.rb +18 -0
  295. data/examples/al-admin/test/functional/directory_controller_test.rb +18 -0
  296. data/examples/al-admin/test/functional/object_classes_controller_test.rb +18 -0
  297. data/examples/al-admin/test/functional/syntaxes_controller_test.rb +18 -0
  298. data/examples/al-admin/test/functional/users_controller_test.rb +18 -0
  299. data/examples/al-admin/test/functional/welcome_controller_test.rb +18 -0
  300. data/examples/al-admin/test/run-test.sh +3 -0
  301. data/examples/al-admin/test/test_helper.rb +28 -0
  302. data/examples/al-admin/test/unit/user_test.rb +13 -0
  303. data/examples/al-admin/vendor/plugins/exception_notification/README +111 -0
  304. data/examples/al-admin/vendor/plugins/exception_notification/init.rb +1 -0
  305. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifiable.rb +99 -0
  306. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier.rb +67 -0
  307. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb +77 -0
  308. data/examples/al-admin/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb +61 -0
  309. data/examples/al-admin/vendor/plugins/exception_notification/test/test_helper.rb +7 -0
  310. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml +1 -0
  311. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml +7 -0
  312. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml +16 -0
  313. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml +3 -0
  314. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml +2 -0
  315. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml +3 -0
  316. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml +6 -0
  317. data/examples/config.yaml.example +5 -0
  318. data/examples/example.der +0 -0
  319. data/examples/example.jpg +0 -0
  320. data/examples/groupadd +41 -0
  321. data/examples/groupdel +35 -0
  322. data/examples/groupls +49 -0
  323. data/examples/groupmod +42 -0
  324. data/examples/lpasswd +55 -0
  325. data/examples/objects/group.rb +13 -0
  326. data/examples/objects/ou.rb +4 -0
  327. data/examples/objects/user.rb +20 -0
  328. data/examples/ouadd +38 -0
  329. data/examples/useradd +45 -0
  330. data/examples/useradd-binary +50 -0
  331. data/examples/userdel +34 -0
  332. data/examples/userls +50 -0
  333. data/examples/usermod +42 -0
  334. data/examples/usermod-binary-add +47 -0
  335. data/examples/usermod-binary-add-time +51 -0
  336. data/examples/usermod-binary-del +48 -0
  337. data/examples/usermod-lang-add +43 -0
  338. data/lib/active_ldap/acts/tree.rb +75 -0
  339. data/lib/active_ldap/adapter/base.rb +531 -0
  340. data/lib/active_ldap/adapter/ldap.rb +231 -0
  341. data/lib/active_ldap/adapter/ldap_ext.rb +69 -0
  342. data/lib/active_ldap/adapter/net_ldap.rb +292 -0
  343. data/lib/active_ldap/adapter/net_ldap_ext.rb +29 -0
  344. data/lib/active_ldap/association/belongs_to.rb +47 -0
  345. data/lib/active_ldap/association/belongs_to_many.rb +42 -0
  346. data/lib/active_ldap/association/children.rb +21 -0
  347. data/lib/active_ldap/association/collection.rb +83 -0
  348. data/lib/active_ldap/association/has_many.rb +31 -0
  349. data/lib/active_ldap/association/has_many_utils.rb +35 -0
  350. data/lib/active_ldap/association/has_many_wrap.rb +46 -0
  351. data/lib/active_ldap/association/proxy.rb +102 -0
  352. data/lib/active_ldap/associations.rb +172 -0
  353. data/lib/active_ldap/attributes.rb +211 -0
  354. data/lib/active_ldap/base.rb +1273 -0
  355. data/lib/active_ldap/callbacks.rb +19 -0
  356. data/lib/active_ldap/command.rb +49 -0
  357. data/lib/active_ldap/configuration.rb +147 -0
  358. data/lib/active_ldap/connection.rb +237 -0
  359. data/lib/active_ldap/distinguished_name.rb +251 -0
  360. data/lib/active_ldap/escape.rb +12 -0
  361. data/lib/active_ldap/get_text/parser.rb +159 -0
  362. data/lib/active_ldap/get_text.rb +8 -0
  363. data/lib/active_ldap/get_text_fallback.rb +53 -0
  364. data/lib/active_ldap/get_text_support.rb +26 -0
  365. data/lib/active_ldap/helper.rb +33 -0
  366. data/lib/active_ldap/human_readable.rb +112 -0
  367. data/lib/active_ldap/ldap_error.rb +74 -0
  368. data/lib/active_ldap/ldif.rb +52 -0
  369. data/lib/active_ldap/object_class.rb +93 -0
  370. data/lib/active_ldap/operations.rb +429 -0
  371. data/lib/active_ldap/populate.rb +44 -0
  372. data/lib/active_ldap/schema/syntaxes.rb +386 -0
  373. data/lib/active_ldap/schema.rb +530 -0
  374. data/lib/active_ldap/timeout.rb +75 -0
  375. data/lib/active_ldap/timeout_stub.rb +17 -0
  376. data/lib/active_ldap/user_password.rb +93 -0
  377. data/lib/active_ldap/validations.rb +171 -0
  378. data/lib/active_ldap.rb +982 -0
  379. data/po/en/active-ldap.po +3677 -0
  380. data/po/ja/active-ldap.po +3713 -0
  381. data/rails/plugin/active_ldap/README +54 -0
  382. data/rails/plugin/active_ldap/generators/model_active_ldap/USAGE +17 -0
  383. data/rails/plugin/active_ldap/generators/model_active_ldap/model_active_ldap_generator.rb +70 -0
  384. data/rails/plugin/active_ldap/generators/model_active_ldap/templates/fixtures.yml +11 -0
  385. data/rails/plugin/active_ldap/generators/model_active_ldap/templates/model_active_ldap.rb +3 -0
  386. data/rails/plugin/active_ldap/generators/model_active_ldap/templates/unit_test.rb +10 -0
  387. data/rails/plugin/active_ldap/generators/scaffold_active_ldap/scaffold_active_ldap_generator.rb +7 -0
  388. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +20 -0
  389. data/rails/plugin/active_ldap/init.rb +26 -0
  390. data/test/al-test-utils.rb +362 -0
  391. data/test/command.rb +62 -0
  392. data/test/config.yaml.sample +6 -0
  393. data/test/run-test.rb +31 -0
  394. data/test/test-unit-ext/always-show-result.rb +28 -0
  395. data/test/test-unit-ext/backtrace-filter.rb +17 -0
  396. data/test/test-unit-ext/long-display-for-emacs.rb +25 -0
  397. data/test/test-unit-ext/priority.rb +163 -0
  398. data/test/test-unit-ext.rb +4 -0
  399. data/test/test_acts_as_tree.rb +57 -0
  400. data/test/test_adapter.rb +98 -0
  401. data/test/test_associations.rb +353 -0
  402. data/test/test_attributes.rb +79 -0
  403. data/test/test_base.rb +547 -0
  404. data/test/test_base_per_instance.rb +55 -0
  405. data/test/test_bind.rb +62 -0
  406. data/test/test_callback.rb +35 -0
  407. data/test/test_configuration.rb +40 -0
  408. data/test/test_connection.rb +59 -0
  409. data/test/test_connection_per_class.rb +58 -0
  410. data/test/test_connection_per_dn.rb +78 -0
  411. data/test/test_dn.rb +160 -0
  412. data/test/test_find.rb +96 -0
  413. data/test/test_groupadd.rb +50 -0
  414. data/test/test_groupdel.rb +46 -0
  415. data/test/test_groupls.rb +107 -0
  416. data/test/test_groupmod.rb +51 -0
  417. data/test/test_ldif.rb +35 -0
  418. data/test/test_lpasswd.rb +75 -0
  419. data/test/test_object_class.rb +56 -0
  420. data/test/test_reflection.rb +179 -0
  421. data/test/test_schema.rb +433 -0
  422. data/test/test_syntax.rb +329 -0
  423. data/test/test_user.rb +227 -0
  424. data/test/test_user_password.rb +93 -0
  425. data/test/test_useradd-binary.rb +61 -0
  426. data/test/test_useradd.rb +57 -0
  427. data/test/test_userdel.rb +48 -0
  428. data/test/test_userls.rb +91 -0
  429. data/test/test_usermod-binary-add-time.rb +64 -0
  430. data/test/test_usermod-binary-add.rb +63 -0
  431. data/test/test_usermod-binary-del.rb +66 -0
  432. data/test/test_usermod-lang-add.rb +59 -0
  433. data/test/test_usermod.rb +58 -0
  434. data/test/test_validation.rb +110 -0
  435. metadata +536 -0
@@ -0,0 +1,1273 @@
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
+
34
+ module ActiveLdap
35
+ # OO-interface to LDAP assuming pam/nss_ldap-style organization with
36
+ # Active specifics
37
+ # Each subclass does a ldapsearch for the matching entry.
38
+ # If no exact match, raise an error.
39
+ # If match, change all LDAP attributes in accessor attributes on the object.
40
+ # -- these are ACTUALLY populated from schema - see active_ldap/schema.rb
41
+ # example
42
+ # -- extract objectClasses from match and populate
43
+ # Multiple entries become lists.
44
+ # If this isn't read-only then lists become multiple entries, etc.
45
+
46
+ class Error < StandardError
47
+ include GetTextSupport
48
+ end
49
+
50
+ # ConfigurationError
51
+ #
52
+ # An exception raised when there is a problem with Base.connect arguments
53
+ class ConfigurationError < Error
54
+ end
55
+
56
+ # DeleteError
57
+ #
58
+ # An exception raised when an ActiveLdap delete action fails
59
+ class DeleteError < Error
60
+ end
61
+
62
+ # SaveError
63
+ #
64
+ # An exception raised when an ActiveLdap save action failes
65
+ class SaveError < Error
66
+ end
67
+
68
+ # AuthenticationError
69
+ #
70
+ # An exception raised when user authentication fails
71
+ class AuthenticationError < Error
72
+ end
73
+
74
+ # ConnectionError
75
+ #
76
+ # An exception raised when the LDAP conenction fails
77
+ class ConnectionError < Error
78
+ end
79
+
80
+ # ObjectClassError
81
+ #
82
+ # An exception raised when an objectClass is not defined in the schema
83
+ class ObjectClassError < Error
84
+ end
85
+
86
+ # AttributeAssignmentError
87
+ #
88
+ # An exception raised when there is an issue assigning a value to
89
+ # an attribute
90
+ class AttributeAssignmentError < Error
91
+ end
92
+
93
+ # TimeoutError
94
+ #
95
+ # An exception raised when a connection action fails due to a timeout
96
+ class TimeoutError < Error
97
+ end
98
+
99
+ class EntryNotFound < Error
100
+ end
101
+
102
+ class EntryAlreadyExist < Error
103
+ end
104
+
105
+ class StrongAuthenticationRequired < Error
106
+ end
107
+
108
+ class DistinguishedNameInvalid < Error
109
+ attr_reader :dn, :reason
110
+ def initialize(dn, reason=nil)
111
+ @dn = dn
112
+ @reason = reason
113
+ if @reason
114
+ message = _("%s is invalid distinguished name (DN): %s") % [@dn, @reason]
115
+ else
116
+ message = _("%s is invalid distinguished name (DN)") % @dn
117
+ end
118
+ super(message)
119
+ end
120
+ end
121
+
122
+ class DistinguishedNameNotSetError < Error
123
+ end
124
+
125
+ class LdifInvalid < Error
126
+ attr_reader :ldif, :reason
127
+ def initialize(ldif, reason=nil)
128
+ @ldif = ldif
129
+ @reason = reason
130
+ if @reason
131
+ message = _("%s is invalid LDIF: %s") % [@ldif, @reason]
132
+ else
133
+ message = _("%s is invalid LDIF") % @ldif
134
+ end
135
+ super(message)
136
+ end
137
+ end
138
+
139
+ class EntryNotSaved < Error
140
+ end
141
+
142
+ class RequiredObjectClassMissed < Error
143
+ end
144
+
145
+ class RequiredAttributeMissed < Error
146
+ end
147
+
148
+ class EntryInvalid < Error
149
+ end
150
+
151
+ class OperationNotPermitted < Error
152
+ end
153
+
154
+ class ConnectionNotEstablished < Error
155
+ end
156
+
157
+ class AdapterNotSpecified < Error
158
+ end
159
+
160
+ class AdapterNotFound < Error
161
+ attr_reader :adapter
162
+ def initialize(adapter)
163
+ @adapter = adapter
164
+ super(_("LDAP configuration specifies nonexistent %s adapter") % adapter)
165
+ end
166
+ end
167
+
168
+ class UnknownAttribute < Error
169
+ attr_reader :name
170
+ def initialize(name)
171
+ @name = name
172
+ super(_("%s is unknown attribute") % @name)
173
+ end
174
+ end
175
+
176
+ # Base
177
+ #
178
+ # Base is the primary class which contains all of the core
179
+ # ActiveLdap functionality. It is meant to only ever be subclassed
180
+ # by extension classes.
181
+ class Base
182
+ include GetTextSupport
183
+ public :gettext
184
+
185
+ if Object.const_defined?(:Reloadable)
186
+ if Reloadable.const_defined?(:Deprecated)
187
+ include Reloadable::Deprecated
188
+ else
189
+ include Reloadable::Subclasses
190
+ end
191
+ end
192
+
193
+ VALID_LDAP_MAPPING_OPTIONS = [:dn_attribute, :prefix, :scope,
194
+ :classes, :recommended_classes,
195
+ :sort_by, :order]
196
+
197
+ cattr_accessor :logger
198
+ cattr_accessor :configurations
199
+ @@configurations = {}
200
+
201
+ def self.class_local_attr_accessor(search_ancestors, *syms)
202
+ syms.flatten.each do |sym|
203
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
204
+ def self.#{sym}(search_superclasses=#{search_ancestors})
205
+ @#{sym} ||= nil
206
+ return @#{sym} if @#{sym}
207
+ if search_superclasses
208
+ target = superclass
209
+ value = nil
210
+ loop do
211
+ break nil unless target.respond_to?(:#{sym})
212
+ value = target.#{sym}
213
+ break if value
214
+ target = target.superclass
215
+ end
216
+ value
217
+ else
218
+ nil
219
+ end
220
+ end
221
+ def #{sym}; self.class.#{sym}; end
222
+ def self.#{sym}=(value); @#{sym} = value; end
223
+ def #{sym}=(value); self.class.#{sym} = value; end
224
+ EOS
225
+ end
226
+ end
227
+
228
+ class_local_attr_accessor false, :prefix, :base
229
+ class_local_attr_accessor true, :dn_attribute, :scope, :sort_by, :order
230
+ class_local_attr_accessor true, :required_classes, :recommended_classes
231
+
232
+ class << self
233
+ # Hide new in Base
234
+ private :new
235
+
236
+ # Connect and bind to LDAP creating a class variable for use by
237
+ # all ActiveLdap objects.
238
+ #
239
+ # == +config+
240
+ # +config+ must be a hash that may contain any of the following fields:
241
+ # :password_block, :logger, :host, :port, :base, :bind_dn,
242
+ # :try_sasl, :allow_anonymous
243
+ # :bind_dn specifies the DN to bind with.
244
+ # :password_block specifies a Proc object that will yield a String to
245
+ # be used as the password when called.
246
+ # :logger specifies a logger object (Logger, Log4r::Logger and s on)
247
+ # :host sets the LDAP server hostname
248
+ # :port sets the LDAP server port
249
+ # :base overwrites Base.base - this affects EVERYTHING
250
+ # :try_sasl indicates that a SASL bind should be attempted when binding
251
+ # to the server (default: false)
252
+ # :sasl_mechanisms is an array of SASL mechanism to try
253
+ # (default: ["GSSAPI", "CRAM-MD5", "EXTERNAL"])
254
+ # :allow_anonymous indicates that a true anonymous bind is allowed when
255
+ # trying to bind to the server (default: true)
256
+ # :retries - indicates the number of attempts to reconnect that will be
257
+ # undertaken when a stale connection occurs. -1 means infinite.
258
+ # :sasl_quiet - if true, sets @sasl_quiet on the Ruby/LDAP connection
259
+ # :method - whether to use :ssl, :tls, or :plain (unencrypted)
260
+ # :retry_wait - seconds to wait before retrying a connection
261
+ # :scope - dictates how to find objects. ONELEVEL by default to
262
+ # avoid dn_attr collisions across OUs. Think before changing.
263
+ # :timeout - time in seconds - defaults to disabled. This CAN interrupt
264
+ # search() requests. Be warned.
265
+ # :retry_on_timeout - whether to reconnect when timeouts occur. Defaults
266
+ # to true
267
+ # See lib/configuration.rb for defaults for each option
268
+ def establish_connection(config=nil)
269
+ super
270
+ ensure_logger
271
+ nil
272
+ end
273
+
274
+ def create(attributes=nil, &block)
275
+ if attributes.is_a?(Array)
276
+ attributes.collect {|attrs| create(attrs, &block)}
277
+ else
278
+ object = new(attributes, &block)
279
+ object.save
280
+ object
281
+ end
282
+ end
283
+
284
+ # This class function is used to setup all mappings between the subclass
285
+ # and ldap for use in activeldap
286
+ #
287
+ # Example:
288
+ # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People',
289
+ # :classes => ['top', 'posixAccount'],
290
+ # :scope => :sub
291
+ def ldap_mapping(options={})
292
+ options = options.symbolize_keys
293
+ validate_ldap_mapping_options(options)
294
+
295
+ self.dn_attribute = options[:dn_attribute] || default_dn_attribute
296
+ self.prefix = options[:prefix] || default_prefix
297
+ self.scope = options[:scope]
298
+ self.required_classes = options[:classes]
299
+ self.recommended_classes = options[:recommended_classes]
300
+ self.sort_by = options[:sort_by]
301
+ self.order = options[:order]
302
+
303
+ public_class_method :new
304
+ end
305
+
306
+ alias_method :base_inheritable, :base
307
+ # Base.base
308
+ #
309
+ # This method when included into Base provides
310
+ # an inheritable, overwritable configuration setting
311
+ #
312
+ # This should be a string with the base of the
313
+ # ldap server such as 'dc=example,dc=com', and
314
+ # it should be overwritten by including
315
+ # configuration.rb into this class.
316
+ # When subclassing, the specified prefix will be concatenated.
317
+ def base
318
+ _base = base_inheritable
319
+ _base = configuration[:base] if _base.nil? and configuration
320
+ _base ||= base_inheritable(true)
321
+ [prefix, _base].find_all do |component|
322
+ !component.blank?
323
+ end.join(",")
324
+ end
325
+
326
+ alias_method :scope_without_validation=, :scope=
327
+ def scope=(scope)
328
+ validate_scope(scope)
329
+ self.scope_without_validation = scope
330
+ end
331
+
332
+ def validate_scope(scope)
333
+ scope = scope.to_sym if scope.is_a?(String)
334
+ return if scope.nil? or scope.is_a?(Symbol)
335
+ raise ConfigurationError,
336
+ _("scope '%s' must be a Symbol") % scope.inspect
337
+ end
338
+
339
+ def base_class
340
+ if self == Base or superclass == Base
341
+ self
342
+ else
343
+ superclass.base_class
344
+ end
345
+ end
346
+
347
+ def default_search_attribute
348
+ dn_attribute
349
+ end
350
+
351
+ private
352
+ def validate_ldap_mapping_options(options)
353
+ options.assert_valid_keys(VALID_LDAP_MAPPING_OPTIONS)
354
+ end
355
+
356
+ def ensure_logger
357
+ @@logger ||= configuration[:logger]
358
+ # Setup default logger to console
359
+ if @@logger.nil?
360
+ require 'logger'
361
+ @@logger = Logger.new(STDERR)
362
+ @@logger.progname = 'ActiveLdap'
363
+ @@logger.level = Logger::UNKNOWN
364
+ end
365
+ configuration[:logger] ||= @@logger
366
+ end
367
+
368
+ def instantiate(args)
369
+ dn, attributes, options = args
370
+ options ||= {}
371
+ if self.class == Class
372
+ klass = self.ancestors[0].to_s.split(':').last
373
+ real_klass = self.ancestors[0]
374
+ else
375
+ klass = self.class.to_s.split(':').last
376
+ real_klass = self.class
377
+ end
378
+
379
+ obj = real_klass.allocate
380
+ conn = options[:connection] || connection
381
+ obj.connection = conn if conn != connection
382
+ obj.instance_eval do
383
+ initialize_by_ldap_data(dn, attributes)
384
+ end
385
+ obj
386
+ end
387
+
388
+ def default_dn_attribute
389
+ if name.empty?
390
+ dn_attribute = nil
391
+ parent_class = ancestors[1]
392
+ if parent_class.respond_to?(:dn_attribute)
393
+ dn_attribute = parent_class.dn_attribute
394
+ end
395
+ dn_attribute || "cn"
396
+ else
397
+ Inflector.underscore(Inflector.demodulize(name))
398
+ end
399
+ end
400
+
401
+ def default_prefix
402
+ if name.empty?
403
+ nil
404
+ else
405
+ "ou=#{Inflector.pluralize(Inflector.demodulize(name))}"
406
+ end
407
+ end
408
+ end
409
+
410
+ self.scope = :sub
411
+ self.required_classes = ['top']
412
+ self.recommended_classes = []
413
+
414
+ include Enumerable
415
+
416
+ ### All instance methods, etc
417
+
418
+ # new
419
+ #
420
+ # Creates a new instance of Base initializing all class and all
421
+ # initialization. Defines local defaults. See examples If multiple values
422
+ # exist for dn_attribute, the first one put here will be authoritative
423
+ def initialize(attributes=nil)
424
+ init_base
425
+ @new_entry = true
426
+ initial_classes = required_classes | recommended_classes
427
+ if attributes.nil?
428
+ apply_object_class(initial_classes)
429
+ elsif attributes.is_a?(String) or attributes.is_a?(Array)
430
+ apply_object_class(initial_classes)
431
+ self.dn = attributes
432
+ elsif attributes.is_a?(Hash)
433
+ classes, attributes = extract_object_class(attributes)
434
+ apply_object_class(classes | initial_classes)
435
+ normalized_attributes = {}
436
+ attributes.each do |key, value|
437
+ real_key = to_real_attribute_name(key) || key
438
+ normalized_attributes[real_key] = value
439
+ end
440
+ self.dn = normalized_attributes[dn_attribute]
441
+ self.attributes = normalized_attributes
442
+ else
443
+ message = _("'%s' must be either nil, DN value as String or Array " \
444
+ "or attributes as Hash") % attributes.inspect
445
+ raise ArgumentError, message
446
+ end
447
+ yield self if block_given?
448
+ assert_dn_attribute
449
+ end
450
+
451
+ # Returns true if the +comparison_object+ is the same object, or is of
452
+ # the same type and has the same dn.
453
+ def ==(comparison_object)
454
+ comparison_object.equal?(self) or
455
+ (comparison_object.instance_of?(self.class) and
456
+ comparison_object.dn == dn and
457
+ !comparison_object.new_entry?)
458
+ end
459
+
460
+ # Delegates to ==
461
+ def eql?(comparison_object)
462
+ self == (comparison_object)
463
+ end
464
+
465
+ # Delegates to id in order to allow two records of the same type and id
466
+ # to work with something like:
467
+ # [ User.find("a"), User.find("b"), User.find("c") ] &
468
+ # [ User.find("a"), User.find("d") ] # => [ User.find("a") ]
469
+ def hash
470
+ dn.hash
471
+ end
472
+
473
+ def may
474
+ ensure_apply_object_class
475
+ @may
476
+ end
477
+
478
+ def must
479
+ ensure_apply_object_class
480
+ @must
481
+ end
482
+
483
+ # attributes
484
+ #
485
+ # Return attribute methods so that a program can determine available
486
+ # attributes dynamically without schema awareness
487
+ def attribute_names(normalize=false)
488
+ ensure_apply_object_class
489
+ names = @attribute_names.keys
490
+ if normalize
491
+ names.collect do |name|
492
+ to_real_attribute_name(name)
493
+ end.uniq
494
+ else
495
+ names
496
+ end
497
+ end
498
+
499
+ def attribute_present?(name)
500
+ values = get_attribute(name, true)
501
+ !values.empty? or values.any? {|x| not (x and x.empty?)}
502
+ end
503
+
504
+ # exist?
505
+ #
506
+ # Return whether the entry exists in LDAP or not
507
+ def exist?
508
+ self.class.exists?(dn)
509
+ end
510
+ alias_method(:exists?, :exist?)
511
+
512
+ # new_entry?
513
+ #
514
+ # Return whether the entry is new entry in LDAP or not
515
+ def new_entry?
516
+ @new_entry
517
+ end
518
+
519
+ # dn
520
+ #
521
+ # Return the authoritative dn
522
+ def dn
523
+ return base if @dn_is_base
524
+
525
+ dn_value = id
526
+ if dn_value.nil?
527
+ raise DistinguishedNameNotSetError.new,
528
+ _("%s's DN attribute (%s) isn't set") % [self, dn_attribute]
529
+ end
530
+ _base = base
531
+ _base = nil if _base.empty?
532
+ ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
533
+ end
534
+
535
+ def id
536
+ get_attribute(dn_attribute)
537
+ end
538
+
539
+ def to_param
540
+ id
541
+ end
542
+
543
+ def dn=(value)
544
+ set_attribute(dn_attribute, value)
545
+ end
546
+ alias_method(:id=, :dn=)
547
+
548
+ alias_method(:dn_attribute_of_class, :dn_attribute)
549
+ def dn_attribute
550
+ @dn_attribute || dn_attribute_of_class
551
+ end
552
+
553
+ def default_search_attribute
554
+ self.class.default_search_attribute
555
+ end
556
+
557
+ # destroy
558
+ #
559
+ # Delete this entry from LDAP
560
+ def destroy
561
+ begin
562
+ self.class.delete(dn)
563
+ @new_entry = true
564
+ rescue Error
565
+ raise DeleteError.new(_("Failed to delete LDAP entry: %s") % dn)
566
+ end
567
+ end
568
+
569
+ def delete(options={})
570
+ super(dn, options)
571
+ end
572
+
573
+ # save
574
+ #
575
+ # Save and validate this object into LDAP
576
+ # either adding or replacing attributes
577
+ # TODO: Relative DN support
578
+ def save
579
+ create_or_update
580
+ end
581
+
582
+ def save!
583
+ unless create_or_update
584
+ raise EntryNotSaved, _("entry %s can't be saved") % dn
585
+ end
586
+ end
587
+
588
+ # method_missing
589
+ #
590
+ # If a given method matches an attribute or an attribute alias
591
+ # then call the appropriate method.
592
+ # TODO: Determine if it would be better to define each allowed method
593
+ # using class_eval instead of using method_missing. This would
594
+ # give tab completion in irb.
595
+ def method_missing(name, *args, &block)
596
+ ensure_apply_object_class
597
+
598
+ key = name.to_s
599
+ case key
600
+ when /=$/
601
+ real_key = $PREMATCH
602
+ if have_attribute?(real_key, ['objectClass'])
603
+ if args.size != 1
604
+ raise ArgumentError,
605
+ _("wrong number of arguments (%d for 1)") % args.size
606
+ end
607
+ return set_attribute(real_key, *args, &block)
608
+ end
609
+ when /(?:(_before_type_cast)|(\?))?$/
610
+ real_key = $PREMATCH
611
+ before_type_cast = !$1.nil?
612
+ query = !$2.nil?
613
+ if have_attribute?(real_key, ['objectClass'])
614
+ if args.size > 1
615
+ raise ArgumentError,
616
+ _("wrong number of arguments (%d for 1)") % args.size
617
+ end
618
+ if before_type_cast
619
+ return get_attribute_before_type_cast(real_key, *args)[1]
620
+ elsif query
621
+ return get_attribute_as_query(real_key, *args)
622
+ else
623
+ return get_attribute(real_key, *args)
624
+ end
625
+ end
626
+ end
627
+ super
628
+ end
629
+
630
+ # Add available attributes to the methods
631
+ def methods(inherited_too=true)
632
+ ensure_apply_object_class
633
+ target_names = @attribute_names.keys + @attribute_aliases.keys
634
+ target_names -= ['objectClass', Inflector.underscore('objectClass')]
635
+ super + target_names.uniq.collect do |x|
636
+ [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
637
+ end.flatten
638
+ end
639
+
640
+ alias_method :respond_to_without_attributes?, :respond_to?
641
+ def respond_to?(name, include_priv=false)
642
+ have_attribute?(name.to_s) or
643
+ (/(?:=|\?|_before_type_cast)$/ =~ name.to_s and
644
+ have_attribute?($PREMATCH)) or
645
+ super
646
+ end
647
+
648
+ # Updates a given attribute and saves immediately
649
+ def update_attribute(name, value)
650
+ send("#{name}=", value)
651
+ save
652
+ end
653
+
654
+ # This performs a bulk update of attributes and immediately
655
+ # calls #save.
656
+ def update_attributes(attrs)
657
+ self.attributes = attrs
658
+ save
659
+ end
660
+
661
+ def update_attributes!(attrs)
662
+ self.attributes = attrs
663
+ save!
664
+ end
665
+
666
+ # This returns the key value pairs in @data with all values
667
+ # cloned
668
+ def attributes
669
+ Marshal.load(Marshal.dump(@data))
670
+ end
671
+
672
+ # This allows a bulk update to the attributes of a record
673
+ # without forcing an immediate save or validation.
674
+ #
675
+ # It is unwise to attempt objectClass updates this way.
676
+ # Also be sure to only pass in key-value pairs of your choosing.
677
+ # Do not let URL/form hackers supply the keys.
678
+ def attributes=(new_attributes)
679
+ return if new_attributes.nil?
680
+ _schema = nil
681
+ targets = remove_attributes_protected_from_mass_assignment(new_attributes)
682
+ targets.each do |key, value|
683
+ setter = "#{key}="
684
+ unless respond_to?(setter)
685
+ _schema ||= schema
686
+ attribute = _schema.attribute(key)
687
+ next if attribute.id.nil?
688
+ define_attribute_methods(attribute)
689
+ end
690
+ send(setter, value)
691
+ end
692
+ end
693
+
694
+ def to_ldif
695
+ super(dn, normalize_data(@data))
696
+ end
697
+
698
+ def to_xml(options={})
699
+ root = options[:root] || Inflector.underscore(self.class.name)
700
+ result = "<#{root}>\n"
701
+ result << " <dn>#{dn}</dn>\n"
702
+ normalize_data(@data).sort_by {|key, values| key}.each do |key, values|
703
+ targets = []
704
+ values.each do |value|
705
+ if value.is_a?(Hash)
706
+ value.each do |option, real_value|
707
+ targets << [real_value, " #{option}=\"true\""]
708
+ end
709
+ else
710
+ targets << [value]
711
+ end
712
+ end
713
+ targets.sort_by {|value, attr| value}.each do |value, attr|
714
+ result << " <#{key}#{attr}>#{value}</#{key}>\n"
715
+ end
716
+ end
717
+ result << "</#{root}>\n"
718
+ result
719
+ end
720
+
721
+ def have_attribute?(name, except=[])
722
+ real_name = to_real_attribute_name(name)
723
+ real_name and !except.include?(real_name)
724
+ end
725
+ alias_method :has_attribute?, :have_attribute?
726
+
727
+ def reload
728
+ clear_association_cache
729
+ _, attributes = search(:value => id).find do |_dn, _attributes|
730
+ dn == _dn
731
+ end
732
+ if attributes.nil?
733
+ raise EntryNotFound, _("Can't find DN '%s' to reload") % dn
734
+ end
735
+
736
+ @ldap_data.update(attributes)
737
+ classes, attributes = extract_object_class(attributes)
738
+ apply_object_class(classes)
739
+ self.attributes = attributes
740
+ @new_entry = false
741
+ self
742
+ end
743
+
744
+ def [](name, force_array=false)
745
+ if name == "dn"
746
+ array_of(dn, force_array)
747
+ else
748
+ get_attribute(name, force_array)
749
+ end
750
+ end
751
+
752
+ def []=(name, value)
753
+ set_attribute(name, value)
754
+ end
755
+
756
+ def each
757
+ @data.each do |key, values|
758
+ yield(key.dup, values.dup)
759
+ end
760
+ end
761
+
762
+ def bind(config_or_password={}, &block)
763
+ if config_or_password.is_a?(String)
764
+ config = {:password => config_or_password}
765
+ elsif config_or_password.respond_to?(:call)
766
+ config = {:password_block => config_or_password}
767
+ else
768
+ config = config_or_password
769
+ end
770
+ config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
771
+ config[:password_block] ||= block if block_given?
772
+ establish_connection(config)
773
+
774
+ before_connection = @connection
775
+ begin
776
+ @connection = nil
777
+ connection.connect
778
+ @connection = connection
779
+ @schema = nil
780
+ clear_association_cache
781
+ rescue ActiveLdap::Error
782
+ remove_connection
783
+ @connection = before_connection
784
+ raise
785
+ end
786
+ true
787
+ end
788
+
789
+ def schema
790
+ @schema ||= super
791
+ end
792
+
793
+ alias_method :base_of_class, :base
794
+ def base
795
+ [@base, base_of_class].compact.join(",")
796
+ end
797
+
798
+ undef_method :base=
799
+ def base=(object_local_base)
800
+ @base = object_local_base
801
+ end
802
+
803
+ alias_method :scope_of_class, :scope
804
+ def scope
805
+ @scope || scope_of_class
806
+ end
807
+
808
+ undef_method :scope=
809
+ def scope=(scope)
810
+ self.class.validate_scope(scope)
811
+ @scope = scope
812
+ end
813
+
814
+ def inspect
815
+ abbreviate_instance_variables do
816
+ super
817
+ end
818
+ end
819
+
820
+ def pretty_print(q)
821
+ abbreviate_instance_variables do
822
+ q.pp_object(self)
823
+ end
824
+ end
825
+
826
+ private
827
+ def abbreviate_instance_variables
828
+ @abbreviating ||= nil
829
+ connection, @connection = @connection, nil
830
+ schema, @schema = @schema, nil
831
+ attribute_schemata, @attribute_schemata = @attribute_schemata, nil
832
+ must, may = @must, @may
833
+ object_classes = @object_classes
834
+ unless @abbreviating
835
+ @abbreviating = true
836
+ @must, @may = @must.collect(&:name), @may.collect(&:name)
837
+ @object_classes = @object_classes.collect(&:name)
838
+ end
839
+ yield
840
+ ensure
841
+ @connection = connection
842
+ @schema = schema
843
+ @attribute_schemata = attribute_schemata
844
+ @must = must
845
+ @may = may
846
+ @object_classes = object_classes
847
+ @abbreviating = false
848
+ end
849
+
850
+ def extract_object_class(attributes)
851
+ classes = []
852
+ attrs = attributes.stringify_keys.reject do |key, value|
853
+ if key == 'objectClass' or
854
+ key.underscore == 'object_class' or
855
+ key.downcase == 'objectclass'
856
+ classes |= [value].flatten
857
+ true
858
+ else
859
+ false
860
+ end
861
+ end
862
+ [classes, attrs]
863
+ end
864
+
865
+ def init_base
866
+ init_instance_variables
867
+ end
868
+
869
+ def initialize_by_ldap_data(dn, attributes)
870
+ init_base
871
+ @new_entry = false
872
+ @dn_is_base = false
873
+ @ldap_data = attributes
874
+ classes, attributes = extract_object_class(attributes)
875
+ apply_object_class(classes)
876
+ self.dn = dn
877
+ self.attributes = attributes
878
+ yield self if block_given?
879
+ assert_dn_attribute
880
+ end
881
+
882
+ def instantiate(args)
883
+ dn, attributes, options = args
884
+ options ||= {}
885
+
886
+ obj = self.class.allocate
887
+ obj.connection = options[:connection] || @connection
888
+ obj.instance_eval do
889
+ initialize_by_ldap_data(dn, attributes)
890
+ end
891
+ obj
892
+ end
893
+
894
+ def to_real_attribute_name(name, allow_normalized_name=false)
895
+ return name if name.nil?
896
+ ensure_apply_object_class
897
+ name = name.to_s
898
+ real_name = @attribute_names[name]
899
+ real_name ||= @attribute_aliases[Inflector.underscore(name)]
900
+ if real_name
901
+ real_name
902
+ elsif allow_normalized_name
903
+ @normalized_attribute_names[normalize_attribute_name(name)]
904
+ else
905
+ nil
906
+ end
907
+ end
908
+
909
+ def ensure_apply_object_class
910
+ current_object_class = @data['objectClass']
911
+ return if current_object_class.nil? or current_object_class == @last_oc
912
+ apply_object_class(current_object_class)
913
+ end
914
+
915
+ # enforce_type
916
+ #
917
+ # enforce_type applies your changes without attempting to write to LDAP.
918
+ # This means that if you set userCertificate to somebinary value, it will
919
+ # wrap it up correctly.
920
+ def enforce_type(key, value)
921
+ ensure_apply_object_class
922
+ # Enforce attribute value formatting
923
+ normalize_attribute(key, value)[1]
924
+ end
925
+
926
+ def init_instance_variables
927
+ @mutex = Mutex.new
928
+ @data = {} # where the r/w entry data is stored
929
+ @ldap_data = {} # original ldap entry data
930
+ @attribute_schemata = {}
931
+ @attribute_names = {} # list of valid method calls for attributes used
932
+ # for dereferencing
933
+ @normalized_attribute_names = {} # list of normalized attribute name
934
+ @attribute_aliases = {} # aliases of @attribute_names
935
+ @last_oc = false # for use in other methods for "caching"
936
+ @dn_attribute = nil
937
+ @base = nil
938
+ @scope = nil
939
+ @connection ||= nil
940
+ end
941
+
942
+ # apply_object_class
943
+ #
944
+ # objectClass= special case for updating appropriately
945
+ # This updates the objectClass entry in @data. It also
946
+ # updating all required and allowed attributes while
947
+ # removing defined attributes that are no longer valid
948
+ # given the new objectclasses.
949
+ def apply_object_class(val)
950
+ new_oc = val
951
+ new_oc = [val] if new_oc.class != Array
952
+ new_oc = new_oc.uniq
953
+ return new_oc if @last_oc == new_oc
954
+
955
+ # Store for caching purposes
956
+ @last_oc = new_oc.dup
957
+
958
+ # Set the actual objectClass data
959
+ define_attribute_methods(schema.attribute('objectClass'))
960
+ replace_class(*new_oc)
961
+
962
+ # Build |data| from schema
963
+ # clear attribute name mapping first
964
+ @attribute_schemata = {}
965
+ @attribute_names = {}
966
+ @normalized_attribute_names = {}
967
+ @attribute_aliases = {}
968
+ @must = []
969
+ @may = []
970
+ @object_classes = []
971
+ new_oc.each do |objc|
972
+ # get all attributes for the class
973
+ object_class = schema.object_class(objc)
974
+ @object_classes << object_class
975
+ @must.concat(object_class.must)
976
+ @may.concat(object_class.may)
977
+ end
978
+ @must.uniq!
979
+ @may.uniq!
980
+ (@must + @may).each do |attr|
981
+ # Update attr_method with appropriate
982
+ define_attribute_methods(attr)
983
+ end
984
+ end
985
+
986
+ # get_attribute
987
+ #
988
+ # Return the value of the attribute called by method_missing?
989
+ def get_attribute(name, force_array=false)
990
+ name, value = get_attribute_before_type_cast(name, force_array)
991
+ attribute = schema.attribute(name)
992
+ type_cast(attribute, value)
993
+ end
994
+
995
+ def type_cast(attribute, value)
996
+ case value
997
+ when Hash
998
+ result = {}
999
+ value.each do |option, val|
1000
+ result[option] = type_cast(attribute, val)
1001
+ end
1002
+ result
1003
+ when Array
1004
+ value.collect do |val|
1005
+ type_cast(attribute, val)
1006
+ end
1007
+ else
1008
+ attribute.type_cast(value)
1009
+ end
1010
+ end
1011
+
1012
+ def get_attribute_before_type_cast(name, force_array=false)
1013
+ name = to_real_attribute_name(name)
1014
+
1015
+ value = @data[name] || []
1016
+ if force_array
1017
+ [name, value.dup]
1018
+ else
1019
+ [name, array_of(value.dup, false)]
1020
+ end
1021
+ end
1022
+
1023
+ def get_attribute_as_query(name, force_array=false)
1024
+ name, value = get_attribute_before_type_cast(name, force_array)
1025
+ if force_array
1026
+ value.collect {|x| !false_value?(x)}
1027
+ else
1028
+ !false_value?(value)
1029
+ end
1030
+ end
1031
+
1032
+ def false_value?(value)
1033
+ value.nil? or value == false or value == [] or
1034
+ value == "false" or value == "FALSE" or value == ""
1035
+ end
1036
+
1037
+ # set_attribute
1038
+ #
1039
+ # Set the value of the attribute called by method_missing?
1040
+ def set_attribute(name, value)
1041
+ attr = to_real_attribute_name(name)
1042
+ attr, value = update_dn(attr, value) if attr == dn_attribute
1043
+ raise UnknownAttribute.new(name) if attr.nil?
1044
+
1045
+ case value
1046
+ when nil, ""
1047
+ value = []
1048
+ when Array
1049
+ value = value.collect {|c| c.blank? ? [] : c}.flatten
1050
+ when String
1051
+ value = [value]
1052
+ when Numeric
1053
+ value = [value.to_s]
1054
+ end
1055
+
1056
+ @data[attr] = enforce_type(attr, value)
1057
+ end
1058
+
1059
+ def update_dn(attr, value)
1060
+ @dn_is_base = false
1061
+ return [attr, value] if value.blank?
1062
+
1063
+ new_dn_attribute, new_value, base = split_dn_value(value)
1064
+ if new_dn_attribute.nil? and new_value.nil?
1065
+ @dn_is_base = true
1066
+ @base = nil
1067
+ attr, value = DN.parse(base).rdns[0].to_a[0]
1068
+ @dn_attribute = attr
1069
+ else
1070
+ new_dn_attribute = to_real_attribute_name(new_dn_attribute)
1071
+ if new_dn_attribute
1072
+ value = new_value
1073
+ @base = base
1074
+ if dn_attribute != new_dn_attribute
1075
+ @dn_attribute = attr = new_dn_attribute
1076
+ end
1077
+ end
1078
+ end
1079
+ [attr, value]
1080
+ end
1081
+
1082
+ def split_dn_value(value)
1083
+ dn_value = relative_dn_value = nil
1084
+ begin
1085
+ dn_value = DN.parse(value)
1086
+ rescue DistinguishedNameInvalid
1087
+ dn_value = DN.parse("#{dn_attribute}=#{value}")
1088
+ end
1089
+
1090
+ begin
1091
+ relative_dn_value = dn_value - DN.parse(base_of_class)
1092
+ if relative_dn_value.rdns.empty?
1093
+ val = []
1094
+ bases = dn_value.rdns
1095
+ else
1096
+ val, *bases = relative_dn_value.rdns
1097
+ end
1098
+ rescue ArgumentError
1099
+ val, *bases = dn_value.rdns
1100
+ end
1101
+
1102
+ dn_attribute_name, dn_attribute_value = val.to_a[0]
1103
+ [dn_attribute_name, dn_attribute_value,
1104
+ bases.empty? ? nil : DN.new(*bases).to_s]
1105
+ end
1106
+
1107
+ # define_attribute_methods
1108
+ #
1109
+ # Make a method entry for _every_ alias of a valid attribute and map it
1110
+ # onto the first attribute passed in.
1111
+ def define_attribute_methods(attribute)
1112
+ real_name = attribute.name
1113
+ return if @attribute_schemata.has_key?(real_name)
1114
+ @attribute_schemata[real_name] = attribute
1115
+ ([real_name] + attribute.aliases).each do |name|
1116
+ @attribute_names[name] = real_name
1117
+ @attribute_aliases[Inflector.underscore(name)] = real_name
1118
+ @normalized_attribute_names[normalize_attribute_name(name)] = real_name
1119
+ end
1120
+ end
1121
+
1122
+ # array_of
1123
+ #
1124
+ # Returns the array form of a value, or not an array if
1125
+ # false is passed in.
1126
+ def array_of(value, to_a=true)
1127
+ case value
1128
+ when Array
1129
+ if to_a or value.size > 1
1130
+ value.collect {|v| array_of(v, to_a)}
1131
+ else
1132
+ if value.empty?
1133
+ nil
1134
+ else
1135
+ array_of(value.first, to_a)
1136
+ end
1137
+ end
1138
+ when Hash
1139
+ if to_a
1140
+ [value]
1141
+ else
1142
+ result = {}
1143
+ value.each {|k, v| result[k] = array_of(v, to_a)}
1144
+ result
1145
+ end
1146
+ else
1147
+ to_a ? [value.to_s] : value.to_s
1148
+ end
1149
+ end
1150
+
1151
+ def normalize_data(data, except=[])
1152
+ _schema = schema
1153
+ result = {}
1154
+ data.each do |key, values|
1155
+ next if except.include?(key)
1156
+ real_name = to_real_attribute_name(key)
1157
+ next if real_name and except.include?(real_name)
1158
+ real_name ||= key
1159
+ next if _schema.attribute(real_name).id.nil?
1160
+ result[real_name] ||= []
1161
+ result[real_name].concat(values)
1162
+ end
1163
+ result
1164
+ end
1165
+
1166
+ def collect_modified_attributes(ldap_data, data)
1167
+ attributes = []
1168
+ # Now that all the options will be treated as unique attributes
1169
+ # we can see what's changed and add anything that is brand-spankin'
1170
+ # new.
1171
+ ldap_data.each do |k, v|
1172
+ value = data[k] || []
1173
+
1174
+ next if v == value
1175
+
1176
+ # Create mod entries
1177
+ if value.empty?
1178
+ # Since some types do not have equality matching rules,
1179
+ # delete doesn't work
1180
+ # Replacing with nothing is equivalent.
1181
+ if !data.has_key?(k) and schema.attribute(k).binary_required?
1182
+ value = [{'binary' => []}]
1183
+ end
1184
+ else
1185
+ # Ditched delete then replace because attribs with no equality
1186
+ # match rules will fails
1187
+ end
1188
+ attributes.push([:replace, k, value])
1189
+ end
1190
+ data.each do |k, v|
1191
+ value = v || []
1192
+ next if ldap_data.has_key?(k) or value.empty?
1193
+
1194
+ # Detect subtypes and account for them
1195
+ # REPLACE will function like ADD, but doesn't hit EQUALITY problems
1196
+ # TODO: Added equality(attr) to Schema
1197
+ attributes.push([:replace, k, value])
1198
+ end
1199
+
1200
+ attributes
1201
+ end
1202
+
1203
+ def collect_all_attributes(data)
1204
+ dn_attr = to_real_attribute_name(dn_attribute)
1205
+ dn_value = data[dn_attr]
1206
+
1207
+ attributes = []
1208
+ attributes.push([:add, dn_attr, dn_value])
1209
+
1210
+ oc_value = data['objectClass']
1211
+ attributes.push([:add, 'objectClass', oc_value])
1212
+ data.each do |key, value|
1213
+ next if value.empty? or key == 'objectClass' or key == dn_attr
1214
+
1215
+ attributes.push([:add, key, value])
1216
+ end
1217
+
1218
+ attributes
1219
+ end
1220
+
1221
+ def assert_dn_attribute
1222
+ unless dn_attribute
1223
+ raise ConfigurationError,
1224
+ _("dn_attribute isn't set for this class: %s") % self.class
1225
+ end
1226
+ end
1227
+
1228
+ def create_or_update
1229
+ new_entry? ? create : update
1230
+ end
1231
+
1232
+ def prepare_data_for_saving
1233
+ # Expand subtypes to real ldap_data attributes
1234
+ # We can't reuse @ldap_data because an exception would leave
1235
+ # an object in an unknown state
1236
+ ldap_data = normalize_data(@ldap_data)
1237
+
1238
+ # Expand subtypes to real data attributes, but leave @data alone
1239
+ bad_attrs = @data.keys - attribute_names
1240
+ data = normalize_data(@data, bad_attrs)
1241
+
1242
+ success = yield(data, ldap_data)
1243
+
1244
+ if success
1245
+ @ldap_data = Marshal.load(Marshal.dump(data))
1246
+ # Delete items disallowed by objectclasses.
1247
+ # They should have been removed from ldap.
1248
+ bad_attrs.each do |remove_me|
1249
+ @ldap_data.delete(remove_me)
1250
+ end
1251
+ end
1252
+
1253
+ success
1254
+ end
1255
+
1256
+ def create
1257
+ prepare_data_for_saving do |data, ldap_data|
1258
+ attributes = collect_all_attributes(data)
1259
+ add_entry(dn, attributes)
1260
+ @new_entry = false
1261
+ true
1262
+ end
1263
+ end
1264
+
1265
+ def update
1266
+ prepare_data_for_saving do |data, ldap_data|
1267
+ attributes = collect_modified_attributes(ldap_data, data)
1268
+ modify_entry(dn, attributes)
1269
+ true
1270
+ end
1271
+ end
1272
+ end # Base
1273
+ end # ActiveLdap