diff options
-rw-r--r-- | app/controllers/auth_sources_controller.rb | 14 | ||||
-rw-r--r-- | app/models/auth_source.rb | 18 | ||||
-rw-r--r-- | app/models/auth_source_ldap.rb | 42 | ||||
-rw-r--r-- | app/views/users/new.html.erb | 18 | ||||
-rw-r--r-- | config/routes.rb | 3 | ||||
-rw-r--r-- | public/javascripts/application.js | 6 | ||||
-rw-r--r-- | test/functional/auth_sources_controller_test.rb | 16 | ||||
-rw-r--r-- | test/integration/routing/auth_sources_test.rb | 4 | ||||
-rw-r--r-- | test/unit/auth_source_ldap_test.rb | 24 |
9 files changed, 135 insertions, 10 deletions
diff --git a/app/controllers/auth_sources_controller.rb b/app/controllers/auth_sources_controller.rb index 8e2e1f001..e94df3c0e 100644 --- a/app/controllers/auth_sources_controller.rb +++ b/app/controllers/auth_sources_controller.rb @@ -72,6 +72,20 @@ class AuthSourcesController < ApplicationController redirect_to auth_sources_path end + def autocomplete_for_new_user + results = AuthSource.search(params[:term]) + + render :json => results.map {|result| { + 'value' => result[:login], + 'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})", + 'login' => result[:login].to_s, + 'firstname' => result[:firstname].to_s, + 'lastname' => result[:lastname].to_s, + 'mail' => result[:mail].to_s, + 'auth_source_id' => result[:auth_source_id].to_s + }} + end + private def find_auth_source diff --git a/app/models/auth_source.rb b/app/models/auth_source.rb index 784415863..d7098d5cb 100644 --- a/app/models/auth_source.rb +++ b/app/models/auth_source.rb @@ -48,6 +48,24 @@ class AuthSource < ActiveRecord::Base write_ciphered_attribute(:account_password, arg) end + def searchable? + false + end + + def self.search(q) + results = [] + AuthSource.all.each do |source| + begin + if source.searchable? + results += source.search(q) + end + rescue AuthSourceException => e + logger.error "Error while searching users in #{source.name}: #{e.message}" + end + end + results + end + def allow_password_changes? self.class.allow_password_changes? end diff --git a/app/models/auth_source_ldap.rb b/app/models/auth_source_ldap.rb index 5a32ffc2a..71625dd6b 100644 --- a/app/models/auth_source_ldap.rb +++ b/app/models/auth_source_ldap.rb @@ -64,6 +64,32 @@ class AuthSourceLdap < AuthSource "LDAP" end + # Returns true if this source can be searched for users + def searchable? + !account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send("attr_#{a}?")} + end + + # Searches the source for users and returns an array of results + def search(q) + q = q.to_s.strip + return [] unless searchable? && q.present? + + results = [] + search_filter = base_filter & Net::LDAP::Filter.begins(self.attr_login, q) + ldap_con = initialize_ldap_con(self.account, self.account_password) + ldap_con.search(:base => self.base_dn, + :filter => search_filter, + :attributes => ['dn', self.attr_login, self.attr_firstname, self.attr_lastname, self.attr_mail], + :size => 10) do |entry| + attrs = get_user_attributes_from_ldap_entry(entry) + attrs[:login] = AuthSourceLdap.get_attr(entry, self.attr_login) + results << attrs + end + results + rescue Net::LDAP::LdapError => e + raise AuthSourceException.new(e.message) + end + private def with_timeout(&block) @@ -84,6 +110,14 @@ class AuthSourceLdap < AuthSource nil end + def base_filter + filter = Net::LDAP::Filter.eq("objectClass", "*") + if f = ldap_filter + filter = filter & f + end + filter + end + def validate_filter if filter.present? && ldap_filter.nil? errors.add(:filter, :invalid) @@ -140,14 +174,8 @@ class AuthSourceLdap < AuthSource else ldap_con = initialize_ldap_con(self.account, self.account_password) end - login_filter = Net::LDAP::Filter.eq( self.attr_login, login ) - object_filter = Net::LDAP::Filter.eq( "objectClass", "*" ) attrs = {} - - search_filter = object_filter & login_filter - if f = ldap_filter - search_filter = search_filter & f - end + search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login) ldap_con.search( :base => self.base_dn, :filter => search_filter, diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 3eecf4a20..4eca39d07 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -10,3 +10,21 @@ <%= submit_tag l(:button_create_and_continue), :name => 'continue' %> </p> <% end %> + +<% if @auth_sources.present? && @auth_sources.any?(&:searchable?) %> + <%= javascript_tag do %> + observeAutocompleteField('user_login', '<%= escape_javascript autocomplete_for_new_user_auth_sources_path %>', { + select: function(event, ui) { + $('input#user_firstname').val(ui.item.firstname); + $('input#user_lastname').val(ui.item.lastname); + $('input#user_mail').val(ui.item.mail); + $('select#user_auth_source_id option').each(function(){ + if ($(this).attr('value') == ui.item.auth_source_id) { + $(this).attr('selected', true); + $('select#user_auth_source_id').trigger('change'); + } + }); + } + }); + <% end %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 6b45c65f0..9c77434b2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -309,6 +309,9 @@ RedmineApp::Application.routes.draw do member do get 'test_connection', :as => 'try_connection' end + collection do + get 'autocomplete_for_new_user' + end end match 'workflows', :controller => 'workflows', :action => 'index', :via => :get diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 592a4f873..3284c6a86 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -456,12 +456,12 @@ function updateBulkEditFrom(url) { }); } -function observeAutocompleteField(fieldId, url) { +function observeAutocompleteField(fieldId, url, options) { $(document).ready(function() { - $('#'+fieldId).autocomplete({ + $('#'+fieldId).autocomplete($.extend({ source: url, minLength: 2 - }); + }, options)); }); } diff --git a/test/functional/auth_sources_controller_test.rb b/test/functional/auth_sources_controller_test.rb index c64dca66d..c025db602 100644 --- a/test/functional/auth_sources_controller_test.rb +++ b/test/functional/auth_sources_controller_test.rb @@ -149,4 +149,20 @@ class AuthSourcesControllerTest < ActionController::TestCase assert_not_nil flash[:error] assert_include 'Something went wrong', flash[:error] end + + def test_autocomplete_for_new_user + AuthSource.expects(:search).with('foo').returns([ + {:login => 'foo1', :firstname => 'John', :lastname => 'Smith', :mail => 'foo1@example.net', :auth_source_id => 1}, + {:login => 'Smith', :firstname => 'John', :lastname => 'Doe', :mail => 'foo2@example.net', :auth_source_id => 1} + ]) + + get :autocomplete_for_new_user, :term => 'foo' + assert_response :success + assert_equal 'application/json', response.content_type + json = ActiveSupport::JSON.decode(response.body) + assert_kind_of Array, json + assert_equal 2, json.size + assert_equal 'foo1', json.first['value'] + assert_equal 'foo1 (John Smith)', json.first['label'] + end end diff --git a/test/integration/routing/auth_sources_test.rb b/test/integration/routing/auth_sources_test.rb index 76267f822..ce75633d1 100644 --- a/test/integration/routing/auth_sources_test.rb +++ b/test/integration/routing/auth_sources_test.rb @@ -51,5 +51,9 @@ class RoutingAuthSourcesTest < ActionController::IntegrationTest { :controller => 'auth_sources', :action => 'test_connection', :id => '1234' } ) + assert_routing( + { :method => 'get', :path => "/auth_sources/autocomplete_for_new_user" }, + { :controller => 'auth_sources', :action => 'autocomplete_for_new_user' } + ) end end diff --git a/test/unit/auth_source_ldap_test.rb b/test/unit/auth_source_ldap_test.rb index 60558f798..5e5f90b97 100644 --- a/test/unit/auth_source_ldap_test.rb +++ b/test/unit/auth_source_ldap_test.rb @@ -124,6 +124,30 @@ class AuthSourceLdapTest < ActiveSupport::TestCase auth_source.authenticate 'example1', '123456' end end + + def test_search_should_return_matching_entries + results = AuthSource.search("exa") + assert_equal 1, results.size + result = results.first + assert_kind_of Hash, result + assert_equal "example1", result[:login] + assert_equal "Example", result[:firstname] + assert_equal "One", result[:lastname] + assert_equal "example1@redmine.org", result[:mail] + assert_equal 1, result[:auth_source_id] + end + + def test_search_with_no_match_should_return_an_empty_array + results = AuthSource.search("wro") + assert_equal [], results + end + + def test_search_with_exception_should_return_an_empty_array + Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect') + + results = AuthSource.search("exa") + assert_equal [], results + end else puts '(Test LDAP server not configured)' end |