git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11080 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/2.3.0
@@ -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 |
@@ -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 |
@@ -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, |
@@ -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 %> |
@@ -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 |
@@ -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)); | |||
}); | |||
} | |||
@@ -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 |
@@ -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 |
@@ -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 |