@@ -0,0 +1,9 | |||
|
1 | class AddAuthSourcesTimeout < ActiveRecord::Migration | |
|
2 | def up | |
|
3 | add_column :auth_sources, :timeout, :integer | |
|
4 | end | |
|
5 | ||
|
6 | def self.down | |
|
7 | remove_column :auth_sources, :timeout | |
|
8 | end | |
|
9 | end |
@@ -18,6 +18,7 | |||
|
18 | 18 | # Generic exception for when the AuthSource can not be reached |
|
19 | 19 | # (eg. can not connect to the LDAP) |
|
20 | 20 | class AuthSourceException < Exception; end |
|
21 | class AuthSourceTimeoutException < AuthSourceException; end | |
|
21 | 22 | |
|
22 | 23 | class AuthSource < ActiveRecord::Base |
|
23 | 24 | include Redmine::SubclassFactory |
@@ -18,6 +18,7 | |||
|
18 | 18 | require 'iconv' |
|
19 | 19 | require 'net/ldap' |
|
20 | 20 | require 'net/ldap/dn' |
|
21 | require 'timeout' | |
|
21 | 22 | |
|
22 | 23 | class AuthSourceLdap < AuthSource |
|
23 | 24 | validates_presence_of :host, :port, :attr_login |
@@ -25,6 +26,7 class AuthSourceLdap < AuthSource | |||
|
25 | 26 | validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true |
|
26 | 27 | validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true |
|
27 | 28 | validates_numericality_of :port, :only_integer => true |
|
29 | validates_numericality_of :timeout, :only_integer => true, :allow_blank => true | |
|
28 | 30 | validate :validate_filter |
|
29 | 31 | |
|
30 | 32 | before_validation :strip_ldap_attributes |
@@ -44,22 +46,26 class AuthSourceLdap < AuthSource | |||
|
44 | 46 | |
|
45 | 47 | def authenticate(login, password) |
|
46 | 48 | return nil if login.blank? || password.blank? |
|
47 | attrs = get_user_dn(login, password) | |
|
48 | 49 | |
|
49 | if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password) | |
|
50 | logger.debug "Authentication successful for '#{login}'" if logger && logger.debug? | |
|
51 | return attrs.except(:dn) | |
|
50 | with_timeout do | |
|
51 | attrs = get_user_dn(login, password) | |
|
52 | if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password) | |
|
53 | logger.debug "Authentication successful for '#{login}'" if logger && logger.debug? | |
|
54 | return attrs.except(:dn) | |
|
55 | end | |
|
52 | 56 | end |
|
53 |
rescue |
|
|
57 | rescue Net::LDAP::LdapError => e | |
|
54 | 58 | raise AuthSourceException.new(e.message) |
|
55 | 59 | end |
|
56 | 60 | |
|
57 | 61 | # test the connection to the LDAP |
|
58 | 62 | def test_connection |
|
59 | ldap_con = initialize_ldap_con(self.account, self.account_password) | |
|
60 | ldap_con.open { } | |
|
61 | rescue Net::LDAP::LdapError => e | |
|
62 | raise "LdapError: " + e.message | |
|
63 | with_timeout do | |
|
64 | ldap_con = initialize_ldap_con(self.account, self.account_password) | |
|
65 | ldap_con.open { } | |
|
66 | end | |
|
67 | rescue Net::LDAP::LdapError => e | |
|
68 | raise AuthSourceException.new(e.message) | |
|
63 | 69 | end |
|
64 | 70 | |
|
65 | 71 | def auth_method_name |
@@ -68,6 +74,16 class AuthSourceLdap < AuthSource | |||
|
68 | 74 | |
|
69 | 75 | private |
|
70 | 76 | |
|
77 | def with_timeout(&block) | |
|
78 | timeout = self.timeout | |
|
79 | timeout = 20 unless timeout && timeout > 0 | |
|
80 | Timeout.timeout(timeout) do | |
|
81 | return yield | |
|
82 | end | |
|
83 | rescue Timeout::Error => e | |
|
84 | raise AuthSourceTimeoutException.new(e.message) | |
|
85 | end | |
|
86 | ||
|
71 | 87 | def ldap_filter |
|
72 | 88 | if filter.present? |
|
73 | 89 | Net::LDAP::Filter.construct(filter) |
@@ -26,6 +26,9 | |||
|
26 | 26 | <p><label for="auth_source_custom_filter"><%=l(:field_ldap_filter)%></label> |
|
27 | 27 | <%= text_field 'auth_source', 'filter', :size => 60 %></p> |
|
28 | 28 | |
|
29 | <p><label for="auth_source_timeout"><%=l(:field_timeout)%></label> | |
|
30 | <%= text_field 'auth_source', 'timeout', :size => 4 %></p> | |
|
31 | ||
|
29 | 32 | <p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label> |
|
30 | 33 | <%= check_box 'auth_source', 'onthefly_register' %></p> |
|
31 | 34 | </div> |
@@ -329,6 +329,7 en: | |||
|
329 | 329 | field_multiple: Multiple values |
|
330 | 330 | field_ldap_filter: LDAP filter |
|
331 | 331 | field_core_fields: Standard fields |
|
332 | field_timeout: "Timeout (in seconds)" | |
|
332 | 333 | |
|
333 | 334 | setting_app_title: Application title |
|
334 | 335 | setting_app_subtitle: Application subtitle |
@@ -328,6 +328,7 fr: | |||
|
328 | 328 | field_multiple: Valeurs multiples |
|
329 | 329 | field_ldap_filter: Filtre LDAP |
|
330 | 330 | field_core_fields: Champs standards |
|
331 | field_timeout: "Timeout (en secondes)" | |
|
331 | 332 | |
|
332 | 333 | setting_app_title: Titre de l'application |
|
333 | 334 | setting_app_subtitle: Sous-titre de l'application |
@@ -114,6 +114,16 class AuthSourceLdapTest < ActiveSupport::TestCase | |||
|
114 | 114 | end |
|
115 | 115 | end |
|
116 | 116 | end |
|
117 | ||
|
118 | def test_authenticate_should_timeout | |
|
119 | auth_source = AuthSourceLdap.find(1) | |
|
120 | auth_source.timeout = 1 | |
|
121 | def auth_source.initialize_ldap_con(*args); sleep(5); end | |
|
122 | ||
|
123 | assert_raise AuthSourceTimeoutException do | |
|
124 | auth_source.authenticate 'example1', '123456' | |
|
125 | end | |
|
126 | end | |
|
117 | 127 | else |
|
118 | 128 | puts '(Test LDAP server not configured)' |
|
119 | 129 | end |
General Comments 0
You need to be logged in to leave comments.
Login now