summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/auth_sources_controller.rb14
-rw-r--r--app/models/auth_source.rb18
-rw-r--r--app/models/auth_source_ldap.rb42
-rw-r--r--app/views/users/new.html.erb18
-rw-r--r--config/routes.rb3
-rw-r--r--public/javascripts/application.js6
-rw-r--r--test/functional/auth_sources_controller_test.rb16
-rw-r--r--test/integration/routing/auth_sources_test.rb4
-rw-r--r--test/unit/auth_source_ldap_test.rb24
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