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.
- data/CHANGES +454 -0
- data/COPYING +340 -0
- data/LICENSE +58 -0
- data/README +101 -0
- data/Rakefile +175 -0
- data/TODO +25 -0
- data/benchmark/bench-al.rb +202 -0
- data/benchmark/config.yaml.sample +5 -0
- data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
- data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
- data/examples/al-admin/README +182 -0
- data/examples/al-admin/Rakefile +10 -0
- data/examples/al-admin/app/controllers/account_controller.rb +59 -0
- data/examples/al-admin/app/controllers/application.rb +34 -0
- data/examples/al-admin/app/controllers/attributes_controller.rb +17 -0
- data/examples/al-admin/app/controllers/directory_controller.rb +47 -0
- data/examples/al-admin/app/controllers/object_classes_controller.rb +17 -0
- data/examples/al-admin/app/controllers/syntaxes_controller.rb +17 -0
- data/examples/al-admin/app/controllers/users_controller.rb +51 -0
- data/examples/al-admin/app/controllers/welcome_controller.rb +10 -0
- data/examples/al-admin/app/helpers/account_helper.rb +2 -0
- data/examples/al-admin/app/helpers/application_helper.rb +42 -0
- data/examples/al-admin/app/helpers/attributes_helper.rb +15 -0
- data/examples/al-admin/app/helpers/directory_helper.rb +7 -0
- data/examples/al-admin/app/helpers/object_classes_helper.rb +10 -0
- data/examples/al-admin/app/helpers/syntaxes_helper.rb +10 -0
- data/examples/al-admin/app/helpers/url_helper.rb +13 -0
- data/examples/al-admin/app/helpers/users_helper.rb +17 -0
- data/examples/al-admin/app/helpers/welcome_helper.rb +2 -0
- data/examples/al-admin/app/models/entry.rb +23 -0
- data/examples/al-admin/app/models/ldap_user.rb +54 -0
- data/examples/al-admin/app/models/user.rb +91 -0
- data/examples/al-admin/app/views/_entry/_attributes_information.rhtml +23 -0
- data/examples/al-admin/app/views/_entry/_entry.rhtml +15 -0
- data/examples/al-admin/app/views/_schema/_aliases.rhtml +7 -0
- data/examples/al-admin/app/views/_switcher/_after.rhtml +2 -0
- data/examples/al-admin/app/views/_switcher/_before.rhtml +5 -0
- data/examples/al-admin/app/views/account/login.rhtml +26 -0
- data/examples/al-admin/app/views/account/sign_up.rhtml +28 -0
- data/examples/al-admin/app/views/attributes/_attributes.rhtml +19 -0
- data/examples/al-admin/app/views/attributes/_detail.rhtml +29 -0
- data/examples/al-admin/app/views/attributes/index.rhtml +3 -0
- data/examples/al-admin/app/views/attributes/show.rhtml +31 -0
- data/examples/al-admin/app/views/directory/_tree.rhtml +10 -0
- data/examples/al-admin/app/views/directory/_tree_view_js.rhtml +26 -0
- data/examples/al-admin/app/views/directory/index.rhtml +13 -0
- data/examples/al-admin/app/views/directory/populate.rhtml +2 -0
- data/examples/al-admin/app/views/layouts/_flash_box.rhtml +4 -0
- data/examples/al-admin/app/views/layouts/_footer.rhtml +9 -0
- data/examples/al-admin/app/views/layouts/_header_menu.rhtml +10 -0
- data/examples/al-admin/app/views/layouts/_main_menu.rhtml +18 -0
- data/examples/al-admin/app/views/layouts/application.rhtml +56 -0
- data/examples/al-admin/app/views/object_classes/_attributes.rhtml +28 -0
- data/examples/al-admin/app/views/object_classes/_object_classes.rhtml +19 -0
- data/examples/al-admin/app/views/object_classes/index.rhtml +3 -0
- data/examples/al-admin/app/views/object_classes/show.rhtml +39 -0
- data/examples/al-admin/app/views/syntaxes/_detail.rhtml +14 -0
- data/examples/al-admin/app/views/syntaxes/_syntaxes.rhtml +19 -0
- data/examples/al-admin/app/views/syntaxes/index.rhtml +3 -0
- data/examples/al-admin/app/views/syntaxes/show.rhtml +22 -0
- data/examples/al-admin/app/views/users/_attributes_update_form.rhtml +30 -0
- data/examples/al-admin/app/views/users/_form.rhtml +13 -0
- data/examples/al-admin/app/views/users/_object_classes_update_form.rhtml +40 -0
- data/examples/al-admin/app/views/users/_password_change_form.rhtml +20 -0
- data/examples/al-admin/app/views/users/edit.rhtml +15 -0
- data/examples/al-admin/app/views/users/index.rhtml +10 -0
- data/examples/al-admin/app/views/users/show.rhtml +11 -0
- data/examples/al-admin/app/views/welcome/index.rhtml +13 -0
- data/examples/al-admin/config/boot.rb +45 -0
- data/examples/al-admin/config/database.yml.example +19 -0
- data/examples/al-admin/config/environment.rb +70 -0
- data/examples/al-admin/config/environments/development.rb +21 -0
- data/examples/al-admin/config/environments/production.rb +18 -0
- data/examples/al-admin/config/environments/test.rb +19 -0
- data/examples/al-admin/config/ldap.yml.example +21 -0
- data/examples/al-admin/config/routes.rb +46 -0
- data/examples/al-admin/db/migrate/001_create_users.rb +16 -0
- data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +9 -0
- data/examples/al-admin/lib/authenticated_system.rb +124 -0
- data/examples/al-admin/lib/authenticated_test_helper.rb +113 -0
- data/examples/al-admin/lib/tasks/gettext.rake +35 -0
- data/examples/al-admin/po/en/al-admin.po +341 -0
- data/examples/al-admin/po/ja/al-admin.po +341 -0
- data/examples/al-admin/po/nl/al-admin.po +373 -0
- data/examples/al-admin/public/.htaccess +40 -0
- data/examples/al-admin/public/404.html +30 -0
- data/examples/al-admin/public/500.html +30 -0
- data/examples/al-admin/public/dispatch.cgi +10 -0
- data/examples/al-admin/public/dispatch.fcgi +24 -0
- data/examples/al-admin/public/dispatch.rb +10 -0
- data/examples/al-admin/public/favicon.ico +0 -0
- data/examples/al-admin/public/images/active-ldap.svg +6351 -0
- data/examples/al-admin/public/images/al-admin.png +0 -0
- data/examples/al-admin/public/images/al-admin.svg +6371 -0
- data/examples/al-admin/public/images/header-background.png +0 -0
- data/examples/al-admin/public/images/header-background.svg +99 -0
- data/examples/al-admin/public/images/rails.png +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_left_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_left_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_middle_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_middle_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_right_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/accordion_tab_right_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_bottom_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_bottom_middle.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_bottom_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_left_down_arrow.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_left_up_arrow.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_middle_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_middle_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_right_down_arrow.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_right_up_arrow.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_top_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_top_middle.gif +0 -0
- data/examples/al-admin/public/images/spinelz/balloon_top_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/barchart_h.gif +0 -0
- data/examples/al-admin/public/images/spinelz/barchart_v.gif +0 -0
- data/examples/al-admin/public/images/spinelz/button.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_default_handler.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_delete.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_next.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_next_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_pre.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_pre_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_private_icon.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calendar_schedule.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calender_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/calender_back_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_back_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_next.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_next_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_pre.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker2_pre_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_back_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_next.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_next_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_pre.gif +0 -0
- data/examples/al-admin/public/images/spinelz/datepicker_pre_second.gif +0 -0
- data/examples/al-admin/public/images/spinelz/grid_down.gif +0 -0
- data/examples/al-admin/public/images/spinelz/grid_state.gif +0 -0
- data/examples/al-admin/public/images/spinelz/grid_up.gif +0 -0
- data/examples/al-admin/public/images/spinelz/icon_day.gif +0 -0
- data/examples/al-admin/public/images/spinelz/icon_month.gif +0 -0
- data/examples/al-admin/public/images/spinelz/icon_week.gif +0 -0
- data/examples/al-admin/public/images/spinelz/menubar_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/menubar_subcontents_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_left_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_left_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_middle_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_right_active.gif +0 -0
- data/examples/al-admin/public/images/spinelz/navPanel_tab_right_inactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/select_date.gif +0 -0
- data/examples/al-admin/public/images/spinelz/selectabletable_selected.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_about.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_menu.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_sample.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabBottomInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleActive2.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabMiddleInactive2.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabTopInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sideBarBox_tabbar.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sortableTable_down.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sortableTable_normal.gif +0 -0
- data/examples/al-admin/public/images/spinelz/sortableTable_up.gif +0 -0
- data/examples/al-admin/public/images/spinelz/switcher_close.gif +0 -0
- data/examples/al-admin/public/images/spinelz/switcher_open.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_close.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabLeftActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabLeftInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabMiddleInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabRightActive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tabBox_tabRightInactive.gif +0 -0
- data/examples/al-admin/public/images/spinelz/tab_bar.gif +0 -0
- data/examples/al-admin/public/images/spinelz/table_back.gif +0 -0
- data/examples/al-admin/public/images/spinelz/timepicker_clock.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_close.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_max.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_middle.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_min.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_next.gif +0 -0
- data/examples/al-admin/public/images/spinelz/toolbar_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_dir.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_file.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_group.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_group_special.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_state.gif +0 -0
- data/examples/al-admin/public/images/spinelz/treeview_user.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_bottom_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_bottom_middle.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_bottom_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_close.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_max.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_middle_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_middle_right.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_min.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_top_left.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_top_middle.gif +0 -0
- data/examples/al-admin/public/images/spinelz/window_top_right.gif +0 -0
- data/examples/al-admin/public/javascripts/application.js +2 -0
- data/examples/al-admin/public/javascripts/controls.js +833 -0
- data/examples/al-admin/public/javascripts/dragdrop.js +942 -0
- data/examples/al-admin/public/javascripts/effects.js +1088 -0
- data/examples/al-admin/public/javascripts/prototype.js +2515 -0
- data/examples/al-admin/public/javascripts/spinelz/accordion.js +185 -0
- data/examples/al-admin/public/javascripts/spinelz/ajaxHistory.js +157 -0
- data/examples/al-admin/public/javascripts/spinelz/balloon.js +287 -0
- data/examples/al-admin/public/javascripts/spinelz/barchart.js +524 -0
- data/examples/al-admin/public/javascripts/spinelz/calendar.js +3012 -0
- data/examples/al-admin/public/javascripts/spinelz/colorpicker.js +128 -0
- data/examples/al-admin/public/javascripts/spinelz/datepicker.js +438 -0
- data/examples/al-admin/public/javascripts/spinelz/grid.js +1391 -0
- data/examples/al-admin/public/javascripts/spinelz/grid_resizeEx.js +100 -0
- data/examples/al-admin/public/javascripts/spinelz/grid_sortabletableEx.js +129 -0
- data/examples/al-admin/public/javascripts/spinelz/inplaceEditorEx.js +148 -0
- data/examples/al-admin/public/javascripts/spinelz/menubar.js +232 -0
- data/examples/al-admin/public/javascripts/spinelz/navPanel.js +170 -0
- data/examples/al-admin/public/javascripts/spinelz/selectableTable.js +433 -0
- data/examples/al-admin/public/javascripts/spinelz/sideBarBox.js +282 -0
- data/examples/al-admin/public/javascripts/spinelz/sideBarBox_effects.js +83 -0
- data/examples/al-admin/public/javascripts/spinelz/sortableTable.js +270 -0
- data/examples/al-admin/public/javascripts/spinelz/switcher.js +78 -0
- data/examples/al-admin/public/javascripts/spinelz/tabBox.js +469 -0
- data/examples/al-admin/public/javascripts/spinelz/timepicker.js +384 -0
- data/examples/al-admin/public/javascripts/spinelz/toolbar.js +152 -0
- data/examples/al-admin/public/javascripts/spinelz/treeview.js +703 -0
- data/examples/al-admin/public/javascripts/spinelz/window.js +641 -0
- data/examples/al-admin/public/javascripts/spinelz/window_resizeEx.js +130 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/builder.js +131 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/controls.js +835 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/dragdrop.js +944 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/effects.js +1090 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/json.js +139 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/prototype.js +2515 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/resize.js +215 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/scriptaculous.js +51 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/slider.js +278 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/spinelz_util.js +1266 -0
- data/examples/al-admin/public/javascripts/spinelz_lib/unittest.js +564 -0
- data/examples/al-admin/public/robots.txt +1 -0
- data/examples/al-admin/public/stylesheets/account.css +41 -0
- data/examples/al-admin/public/stylesheets/attributes.css +1 -0
- data/examples/al-admin/public/stylesheets/base.css +99 -0
- data/examples/al-admin/public/stylesheets/common.css +2 -0
- data/examples/al-admin/public/stylesheets/detail.css +36 -0
- data/examples/al-admin/public/stylesheets/directory.css +22 -0
- data/examples/al-admin/public/stylesheets/object-classes.css +6 -0
- data/examples/al-admin/public/stylesheets/rails.css +35 -0
- data/examples/al-admin/public/stylesheets/spinelz/accordion.css +59 -0
- data/examples/al-admin/public/stylesheets/spinelz/balloon.css +151 -0
- data/examples/al-admin/public/stylesheets/spinelz/calendar.css +564 -0
- data/examples/al-admin/public/stylesheets/spinelz/datepicker.css +175 -0
- data/examples/al-admin/public/stylesheets/spinelz/grid.css +137 -0
- data/examples/al-admin/public/stylesheets/spinelz/menubar.css +78 -0
- data/examples/al-admin/public/stylesheets/spinelz/modal.css +22 -0
- data/examples/al-admin/public/stylesheets/spinelz/navPanel.css +58 -0
- data/examples/al-admin/public/stylesheets/spinelz/selectableTable.css +28 -0
- data/examples/al-admin/public/stylesheets/spinelz/sideBarBox.css +82 -0
- data/examples/al-admin/public/stylesheets/spinelz/sortableTable.css +51 -0
- data/examples/al-admin/public/stylesheets/spinelz/switcher.css +23 -0
- data/examples/al-admin/public/stylesheets/spinelz/tabBox.css +94 -0
- data/examples/al-admin/public/stylesheets/spinelz/timepicker.css +508 -0
- data/examples/al-admin/public/stylesheets/spinelz/toolbar.css +82 -0
- data/examples/al-admin/public/stylesheets/spinelz/treeview.css +121 -0
- data/examples/al-admin/public/stylesheets/spinelz/window.css +140 -0
- data/examples/al-admin/public/stylesheets/structure.css +81 -0
- data/examples/al-admin/public/stylesheets/syntaxes.css +1 -0
- data/examples/al-admin/public/stylesheets/users.css +13 -0
- data/examples/al-admin/public/stylesheets/welcome.css +0 -0
- data/examples/al-admin/script/about +3 -0
- data/examples/al-admin/script/breakpointer +3 -0
- data/examples/al-admin/script/console +3 -0
- data/examples/al-admin/script/destroy +3 -0
- data/examples/al-admin/script/generate +3 -0
- data/examples/al-admin/script/performance/benchmarker +3 -0
- data/examples/al-admin/script/performance/profiler +3 -0
- data/examples/al-admin/script/plugin +3 -0
- data/examples/al-admin/script/process/inspector +3 -0
- data/examples/al-admin/script/process/reaper +3 -0
- data/examples/al-admin/script/process/spawner +3 -0
- data/examples/al-admin/script/runner +3 -0
- data/examples/al-admin/script/server +3 -0
- data/examples/al-admin/test/fixtures/users.yml +9 -0
- data/examples/al-admin/test/functional/account_controller_test.rb +24 -0
- data/examples/al-admin/test/functional/attributes_controller_test.rb +18 -0
- data/examples/al-admin/test/functional/directory_controller_test.rb +18 -0
- data/examples/al-admin/test/functional/object_classes_controller_test.rb +18 -0
- data/examples/al-admin/test/functional/syntaxes_controller_test.rb +18 -0
- data/examples/al-admin/test/functional/users_controller_test.rb +18 -0
- data/examples/al-admin/test/functional/welcome_controller_test.rb +18 -0
- data/examples/al-admin/test/run-test.sh +3 -0
- data/examples/al-admin/test/test_helper.rb +28 -0
- data/examples/al-admin/test/unit/user_test.rb +13 -0
- data/examples/al-admin/vendor/plugins/exception_notification/README +111 -0
- data/examples/al-admin/vendor/plugins/exception_notification/init.rb +1 -0
- data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifiable.rb +99 -0
- data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier.rb +67 -0
- data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb +77 -0
- data/examples/al-admin/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb +61 -0
- data/examples/al-admin/vendor/plugins/exception_notification/test/test_helper.rb +7 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml +1 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml +7 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml +16 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml +3 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml +2 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml +3 -0
- data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml +6 -0
- data/examples/config.yaml.example +5 -0
- data/examples/example.der +0 -0
- data/examples/example.jpg +0 -0
- data/examples/groupadd +41 -0
- data/examples/groupdel +35 -0
- data/examples/groupls +49 -0
- data/examples/groupmod +42 -0
- data/examples/lpasswd +55 -0
- data/examples/objects/group.rb +13 -0
- data/examples/objects/ou.rb +4 -0
- data/examples/objects/user.rb +20 -0
- data/examples/ouadd +38 -0
- data/examples/useradd +45 -0
- data/examples/useradd-binary +50 -0
- data/examples/userdel +34 -0
- data/examples/userls +50 -0
- data/examples/usermod +42 -0
- data/examples/usermod-binary-add +47 -0
- data/examples/usermod-binary-add-time +51 -0
- data/examples/usermod-binary-del +48 -0
- data/examples/usermod-lang-add +43 -0
- data/lib/active_ldap/acts/tree.rb +75 -0
- data/lib/active_ldap/adapter/base.rb +531 -0
- data/lib/active_ldap/adapter/ldap.rb +231 -0
- data/lib/active_ldap/adapter/ldap_ext.rb +69 -0
- data/lib/active_ldap/adapter/net_ldap.rb +292 -0
- data/lib/active_ldap/adapter/net_ldap_ext.rb +29 -0
- data/lib/active_ldap/association/belongs_to.rb +47 -0
- data/lib/active_ldap/association/belongs_to_many.rb +42 -0
- data/lib/active_ldap/association/children.rb +21 -0
- data/lib/active_ldap/association/collection.rb +83 -0
- data/lib/active_ldap/association/has_many.rb +31 -0
- data/lib/active_ldap/association/has_many_utils.rb +35 -0
- data/lib/active_ldap/association/has_many_wrap.rb +46 -0
- data/lib/active_ldap/association/proxy.rb +102 -0
- data/lib/active_ldap/associations.rb +172 -0
- data/lib/active_ldap/attributes.rb +211 -0
- data/lib/active_ldap/base.rb +1273 -0
- data/lib/active_ldap/callbacks.rb +19 -0
- data/lib/active_ldap/command.rb +49 -0
- data/lib/active_ldap/configuration.rb +147 -0
- data/lib/active_ldap/connection.rb +237 -0
- data/lib/active_ldap/distinguished_name.rb +251 -0
- data/lib/active_ldap/escape.rb +12 -0
- data/lib/active_ldap/get_text/parser.rb +159 -0
- data/lib/active_ldap/get_text.rb +8 -0
- data/lib/active_ldap/get_text_fallback.rb +53 -0
- data/lib/active_ldap/get_text_support.rb +26 -0
- data/lib/active_ldap/helper.rb +33 -0
- data/lib/active_ldap/human_readable.rb +112 -0
- data/lib/active_ldap/ldap_error.rb +74 -0
- data/lib/active_ldap/ldif.rb +52 -0
- data/lib/active_ldap/object_class.rb +93 -0
- data/lib/active_ldap/operations.rb +429 -0
- data/lib/active_ldap/populate.rb +44 -0
- data/lib/active_ldap/schema/syntaxes.rb +386 -0
- data/lib/active_ldap/schema.rb +530 -0
- data/lib/active_ldap/timeout.rb +75 -0
- data/lib/active_ldap/timeout_stub.rb +17 -0
- data/lib/active_ldap/user_password.rb +93 -0
- data/lib/active_ldap/validations.rb +171 -0
- data/lib/active_ldap.rb +982 -0
- data/po/en/active-ldap.po +3677 -0
- data/po/ja/active-ldap.po +3713 -0
- data/rails/plugin/active_ldap/README +54 -0
- data/rails/plugin/active_ldap/generators/model_active_ldap/USAGE +17 -0
- data/rails/plugin/active_ldap/generators/model_active_ldap/model_active_ldap_generator.rb +70 -0
- data/rails/plugin/active_ldap/generators/model_active_ldap/templates/fixtures.yml +11 -0
- data/rails/plugin/active_ldap/generators/model_active_ldap/templates/model_active_ldap.rb +3 -0
- data/rails/plugin/active_ldap/generators/model_active_ldap/templates/unit_test.rb +10 -0
- data/rails/plugin/active_ldap/generators/scaffold_active_ldap/scaffold_active_ldap_generator.rb +7 -0
- data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +20 -0
- data/rails/plugin/active_ldap/init.rb +26 -0
- data/test/al-test-utils.rb +362 -0
- data/test/command.rb +62 -0
- data/test/config.yaml.sample +6 -0
- data/test/run-test.rb +31 -0
- data/test/test-unit-ext/always-show-result.rb +28 -0
- data/test/test-unit-ext/backtrace-filter.rb +17 -0
- data/test/test-unit-ext/long-display-for-emacs.rb +25 -0
- data/test/test-unit-ext/priority.rb +163 -0
- data/test/test-unit-ext.rb +4 -0
- data/test/test_acts_as_tree.rb +57 -0
- data/test/test_adapter.rb +98 -0
- data/test/test_associations.rb +353 -0
- data/test/test_attributes.rb +79 -0
- data/test/test_base.rb +547 -0
- data/test/test_base_per_instance.rb +55 -0
- data/test/test_bind.rb +62 -0
- data/test/test_callback.rb +35 -0
- data/test/test_configuration.rb +40 -0
- data/test/test_connection.rb +59 -0
- data/test/test_connection_per_class.rb +58 -0
- data/test/test_connection_per_dn.rb +78 -0
- data/test/test_dn.rb +160 -0
- data/test/test_find.rb +96 -0
- data/test/test_groupadd.rb +50 -0
- data/test/test_groupdel.rb +46 -0
- data/test/test_groupls.rb +107 -0
- data/test/test_groupmod.rb +51 -0
- data/test/test_ldif.rb +35 -0
- data/test/test_lpasswd.rb +75 -0
- data/test/test_object_class.rb +56 -0
- data/test/test_reflection.rb +179 -0
- data/test/test_schema.rb +433 -0
- data/test/test_syntax.rb +329 -0
- data/test/test_user.rb +227 -0
- data/test/test_user_password.rb +93 -0
- data/test/test_useradd-binary.rb +61 -0
- data/test/test_useradd.rb +57 -0
- data/test/test_userdel.rb +48 -0
- data/test/test_userls.rb +91 -0
- data/test/test_usermod-binary-add-time.rb +64 -0
- data/test/test_usermod-binary-add.rb +63 -0
- data/test/test_usermod-binary-del.rb +66 -0
- data/test/test_usermod-lang-add.rb +59 -0
- data/test/test_usermod.rb +58 -0
- data/test/test_validation.rb +110 -0
- 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
|