Auto-populate fields while creating a new user with LDAP (#10286).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11080 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
eba4efc9d0
commit
7b8ebb7e3f
|
@ -72,6 +72,20 @@ class AuthSourcesController < ApplicationController
|
||||||
redirect_to auth_sources_path
|
redirect_to auth_sources_path
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def find_auth_source
|
def find_auth_source
|
||||||
|
|
|
@ -48,6 +48,24 @@ class AuthSource < ActiveRecord::Base
|
||||||
write_ciphered_attribute(:account_password, arg)
|
write_ciphered_attribute(:account_password, arg)
|
||||||
end
|
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?
|
def allow_password_changes?
|
||||||
self.class.allow_password_changes?
|
self.class.allow_password_changes?
|
||||||
end
|
end
|
||||||
|
|
|
@ -64,6 +64,32 @@ class AuthSourceLdap < AuthSource
|
||||||
"LDAP"
|
"LDAP"
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def with_timeout(&block)
|
def with_timeout(&block)
|
||||||
|
@ -84,6 +110,14 @@ class AuthSourceLdap < AuthSource
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def base_filter
|
||||||
|
filter = Net::LDAP::Filter.eq("objectClass", "*")
|
||||||
|
if f = ldap_filter
|
||||||
|
filter = filter & f
|
||||||
|
end
|
||||||
|
filter
|
||||||
|
end
|
||||||
|
|
||||||
def validate_filter
|
def validate_filter
|
||||||
if filter.present? && ldap_filter.nil?
|
if filter.present? && ldap_filter.nil?
|
||||||
errors.add(:filter, :invalid)
|
errors.add(:filter, :invalid)
|
||||||
|
@ -140,14 +174,8 @@ class AuthSourceLdap < AuthSource
|
||||||
else
|
else
|
||||||
ldap_con = initialize_ldap_con(self.account, self.account_password)
|
ldap_con = initialize_ldap_con(self.account, self.account_password)
|
||||||
end
|
end
|
||||||
login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
|
|
||||||
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
|
|
||||||
attrs = {}
|
attrs = {}
|
||||||
|
search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login)
|
||||||
search_filter = object_filter & login_filter
|
|
||||||
if f = ldap_filter
|
|
||||||
search_filter = search_filter & f
|
|
||||||
end
|
|
||||||
|
|
||||||
ldap_con.search( :base => self.base_dn,
|
ldap_con.search( :base => self.base_dn,
|
||||||
:filter => search_filter,
|
:filter => search_filter,
|
||||||
|
|
|
@ -10,3 +10,21 @@
|
||||||
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
|
<%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% 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
|
member do
|
||||||
get 'test_connection', :as => 'try_connection'
|
get 'test_connection', :as => 'try_connection'
|
||||||
end
|
end
|
||||||
|
collection do
|
||||||
|
get 'autocomplete_for_new_user'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
|
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() {
|
$(document).ready(function() {
|
||||||
$('#'+fieldId).autocomplete({
|
$('#'+fieldId).autocomplete($.extend({
|
||||||
source: url,
|
source: url,
|
||||||
minLength: 2
|
minLength: 2
|
||||||
});
|
}, options));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,4 +149,20 @@ class AuthSourcesControllerTest < ActionController::TestCase
|
||||||
assert_not_nil flash[:error]
|
assert_not_nil flash[:error]
|
||||||
assert_include 'Something went wrong', flash[:error]
|
assert_include 'Something went wrong', flash[:error]
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -51,5 +51,9 @@ class RoutingAuthSourcesTest < ActionController::IntegrationTest
|
||||||
{ :controller => 'auth_sources', :action => 'test_connection',
|
{ :controller => 'auth_sources', :action => 'test_connection',
|
||||||
:id => '1234' }
|
:id => '1234' }
|
||||||
)
|
)
|
||||||
|
assert_routing(
|
||||||
|
{ :method => 'get', :path => "/auth_sources/autocomplete_for_new_user" },
|
||||||
|
{ :controller => 'auth_sources', :action => 'autocomplete_for_new_user' }
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -124,6 +124,30 @@ class AuthSourceLdapTest < ActiveSupport::TestCase
|
||||||
auth_source.authenticate 'example1', '123456'
|
auth_source.authenticate 'example1', '123456'
|
||||||
end
|
end
|
||||||
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
|
else
|
||||||
puts '(Test LDAP server not configured)'
|
puts '(Test LDAP server not configured)'
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue