Patch #3358 » advanced_ldap_auth_r6417.diff
| app/views/ldap_auth_sources/_form.rhtml (working copy) | ||
|---|---|---|
| 9 | 9 |
<%= text_field 'auth_source', 'host' %></p> |
| 10 | 10 | |
| 11 | 11 |
<p><label for="auth_source_port"><%=l(:field_port)%> <span class="required">*</span></label> |
| 12 |
<%= text_field 'auth_source', 'port', :size => 6 %> <%= check_box 'auth_source', 'tls' %> LDAPS</p> |
|
| 12 |
<%= text_field 'auth_source', 'port', :size => 6 %> |
|
| 13 |
<%= check_box 'auth_source', 'tls' %> LDAPS |
|
| 14 |
<%= check_box 'auth_source', 'starttls' %> START_TLS</p> |
|
| 15 | ||
| 16 |
<p><label for="auth_source_protocol_version"><%=l(:field_protocol_version)%></label> |
|
| 17 |
<%= select 'auth_source', 'protocol_version', [2, 3] %></p> |
|
| 13 | 18 | |
| 14 | 19 |
<p><label for="auth_source_account"><%=l(:field_account)%></label> |
| 15 |
<%= text_field 'auth_source', 'account' %></p>
|
|
| 20 |
<%= text_field 'auth_source', 'account' %></p> |
|
| 16 | 21 | |
| 17 | 22 |
<p><label for="auth_source_account_password"><%=l(:field_password)%></label> |
| 18 | 23 |
<%= password_field 'auth_source', 'account_password', :name => 'ignore', |
| ... | ... | |
| 20 | 25 |
:onfocus => "this.value=''; this.name='auth_source[account_password]';", |
| 21 | 26 |
:onchange => "this.name='auth_source[account_password]';" %></p> |
| 22 | 27 | |
| 28 |
<p><label for="auth_source_require_cert"><%=l(:field_require_cert)%></label> |
|
| 29 |
<%= select 'auth_source', 'require_cert', [ [l(:field_require_cert_never), 0], |
|
| 30 |
[l(:field_require_cert_hard), 1], |
|
| 31 |
[l(:field_require_cert_demand), 2], |
|
| 32 |
[l(:field_require_cert_allow), 3], |
|
| 33 |
[l(:field_require_cert_try), 4] ] %></p> |
|
| 34 | ||
| 23 | 35 |
<p><label for="auth_source_base_dn"><%=l(:field_base_dn)%> <span class="required">*</span></label> |
| 24 | 36 |
<%= text_field 'auth_source', 'base_dn', :size => 60 %></p> |
| 25 | 37 | |
| 38 |
<p><label for="auth_source_filter"><%=l(:field_filter)%></label><%= text_field 'auth_source', 'filter' %></p> |
|
| 39 | ||
| 40 |
<p><label for="auth_source_dereference"><%=l(:field_dereference)%></label> |
|
| 41 |
<%= select 'auth_source', 'dereference', [ [l(:field_dereference_never), 0], |
|
| 42 |
[l(:field_dereference_searching), 1], |
|
| 43 |
[l(:field_dereference_finding), 2], |
|
| 44 |
[l(:field_dereference_always), 3] ] %></p> |
|
| 45 | ||
| 26 | 46 |
<p><label for="auth_source_onthefly_register"><%=l(:field_onthefly)%></label> |
| 27 | 47 |
<%= check_box 'auth_source', 'onthefly_register' %></p> |
| 28 | 48 |
</div> |
| app/models/auth_source_ldap.rb (working copy) | ||
|---|---|---|
| 14 | 14 |
# You should have received a copy of the GNU General Public License |
| 15 | 15 |
# along with this program; if not, write to the Free Software |
| 16 | 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 |
# Modified by Daniel Marczisovszky (marczi@dev-labs.com) |
|
| 18 |
# to allow dereferencing aliases, START_TLS |
|
| 17 | 19 | |
| 18 |
require 'net/ldap'
|
|
| 20 |
require 'ldap' |
|
| 19 | 21 |
require 'iconv' |
| 20 | 22 | |
| 21 | 23 |
class AuthSourceLdap < AuthSource |
| 22 | 24 |
validates_presence_of :host, :port, :attr_login |
| 23 | 25 |
validates_length_of :name, :host, :maximum => 60, :allow_nil => true |
| 24 |
validates_length_of :account, :account_password, :base_dn, :maximum => 255, :allow_nil => true
|
|
| 26 |
validates_length_of :account, :base_dn, :filter, :maximum => 255, :allow_nil => true
|
|
| 25 | 27 |
validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true |
| 26 |
validates_numericality_of :port, :only_integer => true |
|
| 27 |
|
|
| 28 |
validates_numericality_of :port, :protocol_version, :only_integer => true
|
|
| 29 |
|
|
| 28 | 30 |
before_validation :strip_ldap_attributes |
| 29 | 31 |
|
| 30 | 32 |
def after_initialize |
| 31 | 33 |
self.port = 389 if self.port == 0 |
| 34 |
self.protocol_version = 3 if self.protocol_version == 0 |
|
| 32 | 35 |
end |
| 33 | 36 |
|
| 34 | 37 |
def authenticate(login, password) |
| ... | ... | |
| 39 | 42 |
logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
|
| 40 | 43 |
return attrs.except(:dn) |
| 41 | 44 |
end |
| 42 |
rescue Net::LDAP::LdapError => text |
|
| 43 |
raise "LdapError: " + text |
|
| 45 |
# rescue Net::LDAP::LdapError => text |
|
| 46 |
# raise "LdapError: " + text |
|
| 47 |
rescue LDAP::Error => text |
|
| 48 |
raise "LDAP Auth Error: " + text |
|
| 44 | 49 |
end |
| 45 | 50 | |
| 46 | 51 |
# test the connection to the LDAP |
| 47 | 52 |
def test_connection |
| 48 | 53 |
ldap_con = initialize_ldap_con(self.account, self.account_password) |
| 49 |
ldap_con.open { }
|
|
| 50 |
rescue Net::LDAP::LdapError => text |
|
| 51 |
raise "LdapError: " + text |
|
| 54 |
rescue LDAP::Error => text |
|
| 55 |
raise "LDAP Error: " + text |
|
| 56 |
# ldap_con.open { }
|
|
| 57 |
# rescue Net::LDAP::LdapError => text |
|
| 58 |
# raise "LdapError: " + text |
|
| 52 | 59 |
end |
| 53 | 60 |
|
| 54 | 61 |
def auth_method_name |
| ... | ... | |
| 64 | 71 |
end |
| 65 | 72 |
|
| 66 | 73 |
def initialize_ldap_con(ldap_user, ldap_password) |
| 67 |
options = { :host => self.host,
|
|
| 68 |
:port => self.port, |
|
| 69 |
:encryption => (self.tls ? :simple_tls : nil) |
|
| 70 |
} |
|
| 71 |
options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
|
|
| 72 |
Net::LDAP.new options |
|
| 74 |
# options = { :host => self.host,
|
|
| 75 |
# :port => self.port, |
|
| 76 |
# :encryption => (self.tls ? :simple_tls : nil) |
|
| 77 |
# } |
|
| 78 |
# options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
|
|
| 79 |
# Net::LDAP.new options |
|
| 80 |
logger.debug "Connecting to #{self.host}:#{self.port}, tls=#{self.tls}" if logger && logger.debug?
|
|
| 81 |
if self.tls |
|
| 82 |
conn = LDAP::SSLConn.new(self.host, self.port, self.starttls) |
|
| 83 |
else |
|
| 84 |
conn = LDAP::Conn.new(self.host, self.port) |
|
| 85 |
end |
|
| 86 |
logger.debug "Dereference set option" if logger && logger.debug? |
|
| 87 |
conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, self.protocol_version) |
|
| 88 |
conn.set_option(LDAP::LDAP_OPT_DEREF, self.dereference) |
|
| 89 |
if self.tls && self.starttls |
|
| 90 |
logger.debug "Certificate set option" if logger && logger.debug? |
|
| 91 |
conn.set_option(LDAP::LDAP_OPT_X_TLS_REQUIRE_CERT, self.require_cert) |
|
| 92 |
end |
|
| 93 | ||
| 94 |
logger.debug "Trying to bind" if logger && logger.debug? |
|
| 95 |
if !ldap_user.blank? || !ldap_password.blank? then |
|
| 96 |
logger.debug "Bind as user #{ldap_user}" if logger && logger.debug?
|
|
| 97 |
conn.bind(ldap_user, ldap_password) |
|
| 98 |
else |
|
| 99 |
logger.debug "Anonymous bind" if logger && logger.debug? |
|
| 100 |
conn.bind |
|
| 101 |
end |
|
| 102 |
rescue LDAP::Error => text |
|
| 103 |
logger.debug "LDAP Connect Error: #{$!}" if logger && logger.debug?
|
|
| 104 |
raise |
|
| 73 | 105 |
end |
| 74 | 106 | |
| 75 | 107 |
def get_user_attributes_from_ldap_entry(entry) |
| ... | ... | |
| 95 | 127 |
# Check if a DN (user record) authenticates with the password |
| 96 | 128 |
def authenticate_dn(dn, password) |
| 97 | 129 |
if dn.present? && password.present? |
| 98 |
initialize_ldap_con(dn, password).bind |
|
| 130 |
begin |
|
| 131 |
logger.debug "Trying to login as #{dn}" if logger && logger.debug?
|
|
| 132 |
initialize_ldap_con(dn, password) |
|
| 133 |
rescue LDAP::Error => bindError |
|
| 134 |
logger.debug "Login failed: #{bindError}" if logger && logger.debug?
|
|
| 135 |
return nil |
|
| 136 |
end |
|
| 137 |
# initialize_ldap_con(dn, password).bind |
|
| 99 | 138 |
end |
| 100 | 139 |
end |
| 101 | 140 | |
| 102 | 141 |
# Get the user's dn and any attributes for them, given their login |
| 103 | 142 |
def get_user_dn(login) |
| 104 |
ldap_con = initialize_ldap_con(self.account, self.account_password) |
|
| 105 |
login_filter = Net::LDAP::Filter.eq( self.attr_login, login ) |
|
| 106 |
object_filter = Net::LDAP::Filter.eq( "objectClass", "*" ) |
|
| 143 |
# ldap_con = initialize_ldap_con(self.account, self.account_password)
|
|
| 144 |
# login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
|
|
| 145 |
# object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
|
|
| 107 | 146 |
attrs = {}
|
| 108 | 147 |
|
| 109 |
ldap_con.search( :base => self.base_dn, |
|
| 110 |
:filter => object_filter & login_filter, |
|
| 111 |
:attributes=> search_attributes) do |entry| |
|
| 148 | ||
| 149 |
# Ticket #1913 by Adi Kriegisch (adi@cg.tuwien.ac.at) |
|
| 150 |
if self.account.include? "$login" then |
|
| 151 |
logger.debug "LDAP-Auth with User login" if logger && logger.debug? |
|
| 152 |
ldap_con = initialize_ldap_con(self.account.sub("$login", encode(login)), password)
|
|
| 153 |
else |
|
| 154 |
logger.debug "LDAP-Auth with Admin User" if logger && logger.debug? |
|
| 155 |
ldap_con = initialize_ldap_con(self.account, self.account_password) |
|
| 156 |
end |
|
| 157 | ||
| 158 |
if self.filter.empty? |
|
| 159 |
filter = self.attr_login + "=" + encode(login) |
|
| 160 |
else |
|
| 161 |
filter = self.filter.gsub("$login", encode(login))
|
|
| 162 |
end |
|
| 163 |
|
|
| 164 |
|
|
| 165 |
# ldap_con.search( :base => self.base_dn, |
|
| 166 |
# :filter => object_filter & login_filter, |
|
| 167 |
# :attributes=> search_attributes) do |entry| |
|
| 168 |
logger.debug "Search in DN: #{self.base_dn} with filter: #{filter}" if logger && logger.debug?
|
|
| 169 |
ldap_con.search( self.base_dn, LDAP::LDAP_SCOPE_SUBTREE, filter, |
|
| 170 |
(onthefly_register? ? ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail] : ['dn'])) { |entry|
|
|
| 112 | 171 | |
| 113 | 172 |
if onthefly_register? |
| 114 | 173 |
attrs = get_user_attributes_from_ldap_entry(entry) |
| ... | ... | |
| 117 | 176 |
end |
| 118 | 177 | |
| 119 | 178 |
logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
|
| 120 |
end |
|
| 179 |
# end |
|
| 180 |
} |
|
| 121 | 181 | |
| 122 | 182 |
attrs |
| 123 | 183 |
end |
| ... | ... | |
| 127 | 187 |
entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name] |
| 128 | 188 |
end |
| 129 | 189 |
end |
| 190 | ||
| 191 |
def encode(value) |
|
| 192 |
value = value.gsub("\\", "\\\\5c")
|
|
| 193 |
value = value.gsub("*", "\\\\2a")
|
|
| 194 |
value = value.gsub("(", "\\\\28")
|
|
| 195 |
value = value.gsub(")", "\\\\29")
|
|
| 196 |
value = value.gsub("\000", "\\\\00")
|
|
| 197 |
end |
|
| 130 | 198 |
end |
| db/migrate/20110812010830_add_auth_sources_filter_deref_advtls.rb (revision 0) | ||
|---|---|---|
| 1 |
class AddAuthSourcesFilterDerefAdvtls < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
add_column :auth_sources, :starttls, :boolean, :default => false, :null => false |
|
| 4 |
add_column :auth_sources, :filter, :string |
|
| 5 |
add_column :auth_sources, :dereference, :integer |
|
| 6 |
add_column :auth_sources, :require_cert, :integer |
|
| 7 |
add_column :auth_sources, :protocol_version, :integer, :default => 3, :null => false |
|
| 8 |
end |
|
| 9 | ||
| 10 |
def self.down |
|
| 11 |
remove_column :auth_sources, :starttls |
|
| 12 |
remove_column :auth_sources, :filter |
|
| 13 |
remove_column :auth_sources, :dereference |
|
| 14 |
remove_column :auth_sources, :require_cert |
|
| 15 |
remove_column :auth_sources, :protocol_version |
|
| 16 |
end |
|
| 17 |
end |
|
| config/locales/de.yml (working copy) | ||
|---|---|---|
| 281 | 281 |
field_port: Port |
| 282 | 282 |
field_account: Konto |
| 283 | 283 |
field_base_dn: Base DN |
| 284 |
field_protocol_version: Protokol Version |
|
| 285 |
field_filter: Filter |
|
| 286 |
field_dereference: LDAP Aliase dereferenzieren |
|
| 287 |
field_dereference_never: Nie (standard) |
|
| 288 |
field_dereference_searching: Beim suchen |
|
| 289 |
field_dereference_finding: Beim finden |
|
| 290 |
field_dereference_always: Immer |
|
| 291 |
field_require_cert: Zertifikat verlangen |
|
| 292 |
field_require_cert_never: Nie |
|
| 293 |
field_require_cert_hard: Hart |
|
| 294 |
field_require_cert_demand: Anfordern |
|
| 295 |
field_require_cert_allow: Erlauben |
|
| 296 |
field_require_cert_try: Versuchen |
|
| 284 | 297 |
field_attr_login: Mitgliedsname-Attribut |
| 285 | 298 |
field_attr_firstname: Vorname-Attribut |
| 286 | 299 |
field_attr_lastname: Name-Attribut |
| config/locales/en.yml (working copy) | ||
|---|---|---|
| 266 | 266 |
field_port: Port |
| 267 | 267 |
field_account: Account |
| 268 | 268 |
field_base_dn: Base DN |
| 269 |
field_protocol_version: Protocol version |
|
| 270 |
field_filter: Filter |
|
| 271 |
field_dereference: Dereference LDAP aliases |
|
| 272 |
field_dereference_never: Never (default) |
|
| 273 |
field_dereference_searching: Searching |
|
| 274 |
field_dereference_finding: Finding |
|
| 275 |
field_dereference_always: Always |
|
| 276 |
field_require_cert: Require certificate |
|
| 277 |
field_require_cert_never: Never |
|
| 278 |
field_require_cert_hard: Hard |
|
| 279 |
field_require_cert_demand: Demand |
|
| 280 |
field_require_cert_allow: Allow |
|
| 281 |
field_require_cert_try: Try |
|
| 269 | 282 |
field_attr_login: Login attribute |
| 270 | 283 |
field_attr_firstname: Firstname attribute |
| 271 | 284 |
field_attr_lastname: Lastname attribute |