activeldap 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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