diff --git a/app/controllers/ldap_settings_controller.rb b/app/controllers/ldap_settings_controller.rb old mode 100644 new mode 100755 index cd79f2d..e6d0742 --- a/app/controllers/ldap_settings_controller.rb +++ b/app/controllers/ldap_settings_controller.rb @@ -114,7 +114,7 @@ def js_request? end def update_ldap_setting_from_params - %w(user group).each do |e| + %w(user group person).each do |e| params[:ldap_setting]["#{e}_fields_to_sync"] = params["#{e}_fields_to_sync"] params[:ldap_setting]["#{e}_ldap_attrs"] = params["#{e}_ldap_attrs"] end if params[:ldap_setting] diff --git a/app/helpers/ldap_settings_helper.rb b/app/helpers/ldap_settings_helper.rb old mode 100644 new mode 100755 index 1f836ae..0448d70 --- a/app/helpers/ldap_settings_helper.rb +++ b/app/helpers/ldap_settings_helper.rb @@ -113,6 +113,23 @@ def user_fields end end + def person_fields + return [] unless Redmine::Plugin.installed?(:redmine_people) + + has_person_ldap_attrs = @ldap_setting.has_person_ldap_attrs? + + Person::STANDARD_FIELDS.map do |f| + SyncField.new( + f, + l("label_people_#{f}"), + false, + @ldap_setting.sync_person_fields? && @ldap_setting.person_fields_to_sync.include?(f.to_s), + has_person_ldap_attrs ? @ldap_setting.person_ldap_attrs[f.to_s] : '', + '' + ) + end + end + def options_for_base_settings options = [[l(:option_custom), '']] options += base_settings.collect {|k, h| [h['name'], k] }.sort diff --git a/app/models/ldap_setting.rb b/app/models/ldap_setting.rb old mode 100644 new mode 100755 index 674acc9..022c693 --- a/app/models/ldap_setting.rb +++ b/app/models/ldap_setting.rb @@ -26,11 +26,11 @@ class LdapSetting include ActiveModel::AttributeMethods # LDAP_DESCRIPTORS - LDAP_ATTRIBUTES = %w( groupname member user_memberid user_groups groupid parent_group primary_group group_parentid member_group group_memberid account_flags ) - CLASS_NAMES = %w( class_user class_group ) + LDAP_ATTRIBUTES = %w( groupname member user_memberid user_groups groupid parent_group primary_group group_parentid member_group group_memberid account_flags) + CLASS_NAMES = %w( class_user class_group class_person ) FLAGS = %w( create_groups create_users active ) COMBOS = %w( group_membership nested_groups sync_on_login dyngroups users_search_scope ) - OTHERS = %w( account_disabled_test user_fields_to_sync group_fields_to_sync user_ldap_attrs group_ldap_attrs fixed_group admin_group required_group group_search_filter groupname_pattern groups_base_dn dyngroups_cache_ttl ) + OTHERS = %w( account_disabled_test user_fields_to_sync group_fields_to_sync person_fields_to_sync user_ldap_attrs group_ldap_attrs person_ldap_attrs fixed_group admin_group required_group group_search_filter groupname_pattern groups_base_dn dyngroups_cache_ttl ) validates_presence_of :auth_source_ldap_id validates_presence_of :class_user, :class_group, :groupname @@ -55,6 +55,7 @@ class LdapSetting validate :validate_group_filter validate :validate_user_fields_to_sync, :validate_user_ldap_attrs validate :validate_group_fields_to_sync, :validate_group_ldap_attrs + validate :validate_person_fields_to_sync, :validate_person_ldap_attrs if Redmine::Plugin.installed?(:redmine_people) before_validation :strip_names, :set_ldap_attrs, :set_fields_to_sync @@ -66,7 +67,7 @@ class LdapSetting safe_attributes *(LDAP_ATTRIBUTES + CLASS_NAMES + FLAGS + COMBOS + OTHERS) define_attribute_methods LDAP_ATTRIBUTES + CLASS_NAMES + FLAGS + COMBOS + OTHERS - [:login, *User::STANDARD_FIELDS].each {|f| module_eval("def #{f}; auth_source_ldap.attr_#{f}; end") } + [:login, *User::STANDARD_FIELDS].each {|f| module_eval("def #{f}; auth_source_ldap.attr_#{f}; end")} def id @auth_source_ldap_id @@ -120,6 +121,10 @@ def sync_group_fields? has_group_fields_to_sync? end + def sync_person_fields? + has_person_fields_to_sync? + end + def sync_dyngroups? has_dyngroups? end @@ -157,6 +162,11 @@ def user_ldap_attrs_to_sync(fields = user_fields_to_sync) (fields||[]).map {|f| user_ldap_attrs[f] || (send(f.to_sym) if respond_to?(f.to_sym)) } end + # Returns an array of ldap attributes to used when syncing the person fields + def person_ldap_attrs_to_sync(fields = person_fields_to_sync) + (fields||[]).map {|f| person_ldap_attrs[f] || (send(f.to_sym) if respond_to?(f.to_sym)) } + end + # Returns an array of ldap attributes to used when syncing the group fields def group_ldap_attrs_to_sync (group_fields_to_sync||[]).map {|f| group_ldap_attrs[f] } @@ -177,6 +187,8 @@ def group_field(ldap_attr) # Returns the user field name for the given ldap attribute def user_field(ldap_attr) ldap_attr = ldap_attr.to_s + user_ldap_attrs.reverse_merge!(person_ldap_attrs) if Redmine::Plugin.installed?(:redmine_people) + result = @user_standard_ldap_attrs.find {|(k, v)| v.downcase == ldap_attr }.try(:first) result ||= user_ldap_attrs.find {|(k, v)| v.downcase == ldap_attr }.try(:first) end @@ -226,7 +238,6 @@ def safe_attributes=(attrs, user = User.current) def save return false if invalid? - self.settings = delete_unsafe_attributes(@attributes, User.current) end @@ -308,14 +319,29 @@ def validate_group_fields_to_sync validate_fields group_fields_to_sync, GroupCustomField.all, group_ldap_attrs end + def validate_person_ldap_attrs + validate_ldap_attrs person_ldap_attrs, Person::STANDARD_FIELDS + end + + def validate_person_fields_to_sync + validate_fields person_fields_to_sync, Person::STANDARD_FIELDS, person_ldap_attrs + end + def validate_ldap_attrs(ldap_attrs, fields) - field_ids = fields.map {|f| f.id.to_s } + field_ids = fields.map {|f| f.try(:id) ? f.id.to_s : f.to_s } ldap_attrs.each do |k, v| if !field_ids.include?(k) errors.add :user_group_fields, :invalid unless errors.added? :user_group_fields, :invalid elsif v.present? && v !~ /\A[a-z][a-z0-9-]*\z/i - field_name = fields.find {|f| f.id == k.to_i }.name + field_name = '' + fields.each do |f| + if f.try(:id) + field_name = f.name if f.id.to_i == k.to_i + else + field_name = f if f.to_s == k.to_s + end + end errors.add :base, :invalid_ldap_attribute, :field => field_name end end @@ -339,11 +365,13 @@ def validate_fields(fields_to_sync, fields, attrs) def set_fields_to_sync self.user_fields_to_sync ||= [] self.group_fields_to_sync ||= [] + self.person_fields_to_sync ||= [] end def set_ldap_attrs self.user_ldap_attrs ||= {} self.group_ldap_attrs ||= {} + self.person_ldap_attrs ||= {} end def strip_names diff --git a/app/views/ldap_settings/_synchronization_actions.html.erb b/app/views/ldap_settings/_synchronization_actions.html.erb old mode 100644 new mode 100755 index 2ca022d..285f76d --- a/app/views/ldap_settings/_synchronization_actions.html.erb +++ b/app/views/ldap_settings/_synchronization_actions.html.erb @@ -31,7 +31,7 @@ - <% { :user => user_fields, :group => group_fields }.each do |k, fields| %> + <% { :user => user_fields, :group => group_fields, :person => person_fields }.each do |k, fields| %> <% if fields.any? -%> @@ -48,7 +48,7 @@ <%= check_box_tag "#{k}_fields_to_sync[]", field.id, field.synchronize?, :class => 'sync' %> - <%= text_field_tag "#{k}_ldap_attrs[#{field.id}]", field.ldap_attribute, :size => 12, :disabled => field.id.is_a?(String) %> + <%= text_field_tag "#{k}_ldap_attrs[#{field.id}]", field.ldap_attribute, :size => 12, :disabled => (field.id.is_a?(String) && k != :person) %> <%=h field.default_value %> diff --git a/config/locales/en.yml b/config/locales/en.yml old mode 100644 new mode 100755 index a41e8c9..8bf22ae --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -22,6 +22,8 @@ en: label_ldap_attributes_on_a_user: "LDAP attributes on a user" label_ldap_attributes_on_a_group: "LDAP attributes on a group" label_log_messages: "Log messages" + label_person: "Person" + label_people_department_id: "Department" button_test: "Test" button_execute: "Execute" diff --git a/lib/ldap_sync/entity_manager.rb b/lib/ldap_sync/entity_manager.rb old mode 100644 new mode 100755 index 9cae2d7..853d184 --- a/lib/ldap_sync/entity_manager.rb +++ b/lib/ldap_sync/entity_manager.rb @@ -23,6 +23,7 @@ def connect_as_user?; setting.account.include?('$login'); end private def get_user_fields(username, user_data=nil, options={}) fields_to_sync = setting.user_fields_to_sync + if options.try(:fetch, :include_required, false) custom_fields = user_required_custom_fields.map {|cf| cf.id.to_s } fields_to_sync -= (User::STANDARD_FIELDS + custom_fields) @@ -30,6 +31,13 @@ def get_user_fields(username, user_data=nil, options={}) end ldap_attrs_to_sync = setting.user_ldap_attrs_to_sync(fields_to_sync) + if Redmine::Plugin.installed?(:redmine_people) + person_fields_to_sync = setting.person_fields_to_sync + fields_to_sync = fields_to_sync + person_fields_to_sync + ldap_attrs_to_sync = ldap_attrs_to_sync + setting.person_ldap_attrs_to_sync(person_fields_to_sync) + departments = Department.all.pluck(:id, :name) + end + user_data ||= with_ldap_connection do |ldap| find_user(ldap, username, ldap_attrs_to_sync) end @@ -38,6 +46,9 @@ def get_user_fields(username, user_data=nil, options={}) user_fields = user_data.inject({}) do |fields, (attr, value)| f = setting.user_field(attr) if f && fields_to_sync.include?(f) + if (f.to_s == 'department_id' && Redmine::Plugin.installed?(:redmine_people)) + value, = departments.select {|k,v| v == value.first.to_s} unless value.nil? || value.first.blank? + end fields[f] = value.first unless value.nil? || value.first.blank? end fields diff --git a/lib/ldap_sync/infectors.rb b/lib/ldap_sync/infectors.rb old mode 100644 new mode 100755 index 9dd2c1a..a7e20cf --- a/lib/ldap_sync/infectors.rb +++ b/lib/ldap_sync/infectors.rb @@ -1,7 +1,8 @@ module LdapSync::Infectors Dir[File.join(File.dirname(__FILE__), "infectors", "*.rb")].each do |file| - require_dependency file; infected_name = File.basename(file, ".rb").classify + next if infected_name == 'Person' && !Redmine::Plugin.installed?(:redmine_people) + require_dependency file; _module = const_get(infected_name) _class = Kernel.const_get(infected_name) _class.send(:include, _module) unless _class.included_modules.include? _module diff --git a/lib/ldap_sync/infectors/auth_source_ldap.rb b/lib/ldap_sync/infectors/auth_source_ldap.rb old mode 100644 new mode 100755 index be06620..9040d86 --- a/lib/ldap_sync/infectors/auth_source_ldap.rb +++ b/lib/ldap_sync/infectors/auth_source_ldap.rb @@ -63,7 +63,6 @@ def sync_users with_ldap_connection do |_| ldap_users[:disabled].each do |login| user = self.users.where("LOWER(login) = ?", login.mb_chars.downcase).first - if user.try(:active?) if user.lock! change user.login, "-- Locked active user '#{user.login}' (#{user.name})" @@ -95,8 +94,9 @@ def sync_user(user, is_new_user = false, options = {}) sync_groups = !options[:try_to_login] || setting.sync_groups_on_login? sync_fields = !is_new_user && (!options[:try_to_login] || setting.sync_fields_on_login?) - user_data, flags = if options[:try_to_login] && setting.has_account_flags? && sync_fields - user_data = find_user(ldap, user.login, setting.user_ldap_attrs_to_sync + ns(:account_flags)) + user_data, flags = if (options[:try_to_login] && setting.has_account_flags?) || sync_fields + user_attributes = setting.user_ldap_attrs_to_sync + ns(:account_flags) + setting.person_ldap_attrs_to_sync + user_data = find_user(ldap, user.login, user_attributes) [user_data, user_data.present? ? user_data[n(:account_flags)].first : :deleted] end @@ -161,11 +161,12 @@ def sync_user_groups(user) end def sync_user_fields(user, user_data = nil) - return unless setting.active? && setting.sync_user_fields? - - user.synced_fields = get_user_fields(user.login, user_data) + return unless setting.active? && (setting.sync_user_fields? || setting.sync_person_fields?) + user_fields = get_user_fields(user.login, user_data) + user.synced_fields = user_fields if user.save + sync_person_fields(user, user_fields) if Redmine::Plugin.installed?(:redmine_people) user else error_message = if user.email_is_taken @@ -179,6 +180,15 @@ def sync_user_fields(user, user_data = nil) end end + def sync_person_fields(user, user_fields) + person = Person.find(user.id) + person.synced_fields = user_fields + + unless person.save + error "Could not sync person '#{user.login}': \"#{person.errors.full_messages.join('", "')}\""; nil + end + end + def sync_user_status(user, flags, disabled) if flags && (flags == :deleted || account_disabled?(flags)) user.lock! @@ -243,7 +253,7 @@ def find_or_create_group(groupname, group_data = nil) end def find_local_user(username) - user = ::User.where("LOWER(#{User.table_name}.login) = ?", username.mb_chars.downcase).first + user = ::User.where("LOWER(login) = ?", username.mb_chars.downcase).first if user.present? && user.auth_source_id != self.id trace "-- Skipping user '#{user.login}': it already exists on a different auth_source" return nil, true diff --git a/lib/ldap_sync/infectors/person.rb b/lib/ldap_sync/infectors/person.rb new file mode 100755 index 0000000..b5e1af4 --- /dev/null +++ b/lib/ldap_sync/infectors/person.rb @@ -0,0 +1,32 @@ +# encoding: utf-8 +# Copyright (C) 2011-2013 The Redmine LDAP Sync Authors +# +# This file is part of Redmine LDAP Sync. +# +# Redmine LDAP Sync is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Redmine LDAP Sync is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Redmine LDAP Sync. If not, see . +module LdapSync::Infectors::Person + ::Person::STANDARD_FIELDS = %w( phone job_title department_id workday_length ) + + module InstanceMethods + + def synced_fields=(attrs) + self.information_attributes = attrs.slice(*::Person::STANDARD_FIELDS) + end + + end + + def self.included(receiver) + receiver.send(:include, InstanceMethods) + end +end diff --git a/lib/ldap_sync/infectors/user.rb b/lib/ldap_sync/infectors/user.rb old mode 100644 new mode 100755 diff --git a/test/functional/ldap_settings_controller_test.rb b/test/functional/ldap_settings_controller_test.rb index 4ac0c72..2e3a000 100644 --- a/test/functional/ldap_settings_controller_test.rb +++ b/test/functional/ldap_settings_controller_test.rb @@ -87,7 +87,7 @@ def test_should_disable_an_invalid_ldap_setting # We should have ldap_setting = LdapSetting.find_by_auth_source_ldap_id(2) - assert_equal nil, ldap_setting.member_group, 'LdapSetting is not the same' + assert_nil ldap_setting.member_group, 'LdapSetting is not the same' assert !ldap_setting.active?, "LdapSetting must be disabled" end @@ -222,4 +222,4 @@ def test_should_validate_on_test assert_no_match /ldap_test\.rb/, response.body, 'Should not throw an error' end -end \ No newline at end of file +end