##// END OF EJS Templates
Auto-populate fields while creating a new user with LDAP (#10286)....
Jean-Philippe Lang -
r10850:7b8ebb7e3ffc
parent child
Show More
@@ -72,6 +72,20 class AuthSourcesController < ApplicationController
72 redirect_to auth_sources_path
72 redirect_to auth_sources_path
73 end
73 end
74
74
75 def autocomplete_for_new_user
76 results = AuthSource.search(params[:term])
77
78 render :json => results.map {|result| {
79 'value' => result[:login],
80 'label' => "#{result[:login]} (#{result[:firstname]} #{result[:lastname]})",
81 'login' => result[:login].to_s,
82 'firstname' => result[:firstname].to_s,
83 'lastname' => result[:lastname].to_s,
84 'mail' => result[:mail].to_s,
85 'auth_source_id' => result[:auth_source_id].to_s
86 }}
87 end
88
75 private
89 private
76
90
77 def find_auth_source
91 def find_auth_source
@@ -48,6 +48,24 class AuthSource < ActiveRecord::Base
48 write_ciphered_attribute(:account_password, arg)
48 write_ciphered_attribute(:account_password, arg)
49 end
49 end
50
50
51 def searchable?
52 false
53 end
54
55 def self.search(q)
56 results = []
57 AuthSource.all.each do |source|
58 begin
59 if source.searchable?
60 results += source.search(q)
61 end
62 rescue AuthSourceException => e
63 logger.error "Error while searching users in #{source.name}: #{e.message}"
64 end
65 end
66 results
67 end
68
51 def allow_password_changes?
69 def allow_password_changes?
52 self.class.allow_password_changes?
70 self.class.allow_password_changes?
53 end
71 end
@@ -64,6 +64,32 class AuthSourceLdap < AuthSource
64 "LDAP"
64 "LDAP"
65 end
65 end
66
66
67 # Returns true if this source can be searched for users
68 def searchable?
69 !account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send("attr_#{a}?")}
70 end
71
72 # Searches the source for users and returns an array of results
73 def search(q)
74 q = q.to_s.strip
75 return [] unless searchable? && q.present?
76
77 results = []
78 search_filter = base_filter & Net::LDAP::Filter.begins(self.attr_login, q)
79 ldap_con = initialize_ldap_con(self.account, self.account_password)
80 ldap_con.search(:base => self.base_dn,
81 :filter => search_filter,
82 :attributes => ['dn', self.attr_login, self.attr_firstname, self.attr_lastname, self.attr_mail],
83 :size => 10) do |entry|
84 attrs = get_user_attributes_from_ldap_entry(entry)
85 attrs[:login] = AuthSourceLdap.get_attr(entry, self.attr_login)
86 results << attrs
87 end
88 results
89 rescue Net::LDAP::LdapError => e
90 raise AuthSourceException.new(e.message)
91 end
92
67 private
93 private
68
94
69 def with_timeout(&block)
95 def with_timeout(&block)
@@ -84,6 +110,14 class AuthSourceLdap < AuthSource
84 nil
110 nil
85 end
111 end
86
112
113 def base_filter
114 filter = Net::LDAP::Filter.eq("objectClass", "*")
115 if f = ldap_filter
116 filter = filter & f
117 end
118 filter
119 end
120
87 def validate_filter
121 def validate_filter
88 if filter.present? && ldap_filter.nil?
122 if filter.present? && ldap_filter.nil?
89 errors.add(:filter, :invalid)
123 errors.add(:filter, :invalid)
@@ -140,14 +174,8 class AuthSourceLdap < AuthSource
140 else
174 else
141 ldap_con = initialize_ldap_con(self.account, self.account_password)
175 ldap_con = initialize_ldap_con(self.account, self.account_password)
142 end
176 end
143 login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
144 object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
145 attrs = {}
177 attrs = {}
146
178 search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login)
147 search_filter = object_filter & login_filter
148 if f = ldap_filter
149 search_filter = search_filter & f
150 end
151
179
152 ldap_con.search( :base => self.base_dn,
180 ldap_con.search( :base => self.base_dn,
153 :filter => search_filter,
181 :filter => search_filter,
@@ -10,3 +10,21
10 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
10 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
11 </p>
11 </p>
12 <% end %>
12 <% end %>
13
14 <% if @auth_sources.present? && @auth_sources.any?(&:searchable?) %>
15 <%= javascript_tag do %>
16 observeAutocompleteField('user_login', '<%= escape_javascript autocomplete_for_new_user_auth_sources_path %>', {
17 select: function(event, ui) {
18 $('input#user_firstname').val(ui.item.firstname);
19 $('input#user_lastname').val(ui.item.lastname);
20 $('input#user_mail').val(ui.item.mail);
21 $('select#user_auth_source_id option').each(function(){
22 if ($(this).attr('value') == ui.item.auth_source_id) {
23 $(this).attr('selected', true);
24 $('select#user_auth_source_id').trigger('change');
25 }
26 });
27 }
28 });
29 <% end %>
30 <% end %>
@@ -309,6 +309,9 RedmineApp::Application.routes.draw do
309 member do
309 member do
310 get 'test_connection', :as => 'try_connection'
310 get 'test_connection', :as => 'try_connection'
311 end
311 end
312 collection do
313 get 'autocomplete_for_new_user'
314 end
312 end
315 end
313
316
314 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
317 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
@@ -456,12 +456,12 function updateBulkEditFrom(url) {
456 });
456 });
457 }
457 }
458
458
459 function observeAutocompleteField(fieldId, url) {
459 function observeAutocompleteField(fieldId, url, options) {
460 $(document).ready(function() {
460 $(document).ready(function() {
461 $('#'+fieldId).autocomplete({
461 $('#'+fieldId).autocomplete($.extend({
462 source: url,
462 source: url,
463 minLength: 2
463 minLength: 2
464 });
464 }, options));
465 });
465 });
466 }
466 }
467
467
@@ -149,4 +149,20 class AuthSourcesControllerTest < ActionController::TestCase
149 assert_not_nil flash[:error]
149 assert_not_nil flash[:error]
150 assert_include 'Something went wrong', flash[:error]
150 assert_include 'Something went wrong', flash[:error]
151 end
151 end
152
153 def test_autocomplete_for_new_user
154 AuthSource.expects(:search).with('foo').returns([
155 {:login => 'foo1', :firstname => 'John', :lastname => 'Smith', :mail => 'foo1@example.net', :auth_source_id => 1},
156 {:login => 'Smith', :firstname => 'John', :lastname => 'Doe', :mail => 'foo2@example.net', :auth_source_id => 1}
157 ])
158
159 get :autocomplete_for_new_user, :term => 'foo'
160 assert_response :success
161 assert_equal 'application/json', response.content_type
162 json = ActiveSupport::JSON.decode(response.body)
163 assert_kind_of Array, json
164 assert_equal 2, json.size
165 assert_equal 'foo1', json.first['value']
166 assert_equal 'foo1 (John Smith)', json.first['label']
167 end
152 end
168 end
@@ -51,5 +51,9 class RoutingAuthSourcesTest < ActionController::IntegrationTest
51 { :controller => 'auth_sources', :action => 'test_connection',
51 { :controller => 'auth_sources', :action => 'test_connection',
52 :id => '1234' }
52 :id => '1234' }
53 )
53 )
54 assert_routing(
55 { :method => 'get', :path => "/auth_sources/autocomplete_for_new_user" },
56 { :controller => 'auth_sources', :action => 'autocomplete_for_new_user' }
57 )
54 end
58 end
55 end
59 end
@@ -124,6 +124,30 class AuthSourceLdapTest < ActiveSupport::TestCase
124 auth_source.authenticate 'example1', '123456'
124 auth_source.authenticate 'example1', '123456'
125 end
125 end
126 end
126 end
127
128 def test_search_should_return_matching_entries
129 results = AuthSource.search("exa")
130 assert_equal 1, results.size
131 result = results.first
132 assert_kind_of Hash, result
133 assert_equal "example1", result[:login]
134 assert_equal "Example", result[:firstname]
135 assert_equal "One", result[:lastname]
136 assert_equal "example1@redmine.org", result[:mail]
137 assert_equal 1, result[:auth_source_id]
138 end
139
140 def test_search_with_no_match_should_return_an_empty_array
141 results = AuthSource.search("wro")
142 assert_equal [], results
143 end
144
145 def test_search_with_exception_should_return_an_empty_array
146 Net::LDAP.stubs(:new).raises(Net::LDAP::LdapError, 'Cannot connect')
147
148 results = AuthSource.search("exa")
149 assert_equal [], results
150 end
127 else
151 else
128 puts '(Test LDAP server not configured)'
152 puts '(Test LDAP server not configured)'
129 end
153 end
General Comments 0
You need to be logged in to leave comments. Login now