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