From 409dbe31d14ee78e807530312a07fc234bbae200 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 7 Feb 2013 22:24:03 +0100 Subject: [PATCH] Init version of ldap auth/sync. --- Gemfile | 2 + app/models/user.rb | 63 ++++++++++++++++------ lib/auth/env.rb | 2 +- lib/auth/internal.rb | 7 ++- lib/auth/ldap.rb | 122 +++++++++++++++++++++++++++++++++++++++---- lib/auth/otrs.rb | 2 +- lib/auth/test.rb | 2 +- 7 files changed, 170 insertions(+), 30 deletions(-) diff --git a/Gemfile b/Gemfile index 0d40d3c2a..d15f785e2 100644 --- a/Gemfile +++ b/Gemfile @@ -46,6 +46,8 @@ gem 'simple-rss' gem 'mysql2' #gem 'sqlite3' +gem 'net-ldap' + # Use unicorn as the web server # gem 'unicorn' diff --git a/app/models/user.rb b/app/models/user.rb index b951628ae..2268b9a0c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -43,15 +43,17 @@ class User < ApplicationModel return if !password || password == '' # try to find user based on login - user = User.where( :login => username, :active => true ).first + user = User.where( :login => username.downcase, :active => true ).first # try second lookup with email if !user - user = User.where( :email => username, :active => true ).first + user = User.where( :email => username.downcase, :active => true ).first end - # no user found - return nil if !user + # check failed logins + if user +# return if user.faild_login > 10 + end # use auth backends config = { @@ -65,12 +67,22 @@ class User < ApplicationModel :adapter => 'env', }, :ldap => { - :adapter => 'ldap', - :host => 'somehost', - :port => '3333', - :base_dn => 'some base dn', - :bind_user => 'some bind user', - :bind_pw => 'some pw', + :adapter => 'ldap', + :host => 'localhost', + :port => 389, + :bind_dn => 'cn=Manager,dc=example,dc=org', + :bind_pw => 'example', + :uid => 'mail', + :base => 'dc=example,dc=org', + :always_filter => '', + :always_roles => ['Admin', 'Agent'], + :always_groups => ['Users'], + :sync_params => { + :firstname => 'givenName', + :lastname => 'sn', + :email => 'mail', + :login => 'mail', + }, }, :otrs => { :adapter => 'otrs', @@ -87,15 +99,33 @@ class User < ApplicationModel }, }, } + + # try to login against configure auth backends + user_auth = nil config.each {|key, c| file = "auth/#{c[:adapter]}" require file - user_auth = Auth.const_get("#{c[:adapter].to_s.upcase}").check( user, username, password, c ) - return user_auth if user_auth + user_auth = Auth.const_get("#{c[:adapter].to_s.upcase}").check( username, password, c, user ) + + # auth ok + if user_auth + + # update last login + + + # reset login failed + + + return user_auth + end } + # set login failed +1 + + # auth failed - return false + sleep 1 + return user_auth end def self.create_from_hash!(hash) @@ -113,7 +143,8 @@ class User < ApplicationModel :note => hash['info']['description'], :source => hash['provider'], :roles => roles, - :created_by_id => 1 + :updated_by_id => 1, + :created_by_id => 1, ) end @@ -122,11 +153,11 @@ class User < ApplicationModel return if !username || username == '' # try to find user based on login - user = User.where( :login => username, :active => true ).first + user = User.where( :login => username.downcase, :active => true ).first # try second lookup with email if !user - user = User.where( :email => username, :active => true ).first + user = User.where( :email => username.downcase, :active => true ).first end # check if email address exists diff --git a/lib/auth/env.rb b/lib/auth/env.rb index 3b6ad0809..d30e01bb1 100644 --- a/lib/auth/env.rb +++ b/lib/auth/env.rb @@ -1,7 +1,7 @@ module Auth end module Auth::ENV - def self.check( user, username, password, config ) + def self.check( username, password, config, user ) # try to find user based on login if ENV['REMOTE_USER'] diff --git a/lib/auth/internal.rb b/lib/auth/internal.rb index 249b48b31..923eb3136 100644 --- a/lib/auth/internal.rb +++ b/lib/auth/internal.rb @@ -1,8 +1,11 @@ module Auth end module Auth::INTERNAL - def self.check( user, username, password, config ) - + def self.check( username, password, config, user ) + + # return if no user exists + return nil if !user + # sha auth check if user.password =~ /^\{sha2\}/ crypted = Digest::SHA2.hexdigest( password ) diff --git a/lib/auth/ldap.rb b/lib/auth/ldap.rb index efbeca773..d1dd4a427 100644 --- a/lib/auth/ldap.rb +++ b/lib/auth/ldap.rb @@ -1,15 +1,119 @@ +require 'net/ldap' + module Auth end module Auth::LDAP - def self.check( user, username, password, config ) - - # ldap connect - - # ldap bind - - # sync roles / groups -# return user + def self.check( username, password, config, user ) - return false + scope = Net::LDAP::SearchScope_WholeSubtree + + # ldap connect + ldap = Net::LDAP.new( :host => config[:host], :port => config[:port] ) + + # set auth data if needed + if config[:bind_dn] && config[:bind_pw] + ldap.auth config[:bind_dn], config[:bind_pw] + end + + # ldap bind + if !ldap.bind + puts "NOTICE: Can't connect/bind to '#{host}', #{ldap.get_operation_result.code}, #{ldap.get_operation_result.message}" + return + end + + # search user + filter = "(#{config[:uid]}=#{username})" + if config[:always_filter] + filter = "(&#{filter}#{config[:always_filter]})" + end + user_dn = nil + user_data = {} + ldap.search( :base => config[:base], :filter => filter, :scope => scope ) do |entry| + user_data = {} + user_dn = entry.dn + + # remember attributes for :sync_params + entry.each do |attribute, values| + user_data[ attribute.to_sym ] = '' + values.each do |value| + user_data[ attribute.to_sym ] = value + end + end + end + + if user_dn == nil + puts "NOTICE: ldap entry found for user '#{username}' with filter #{filter} failed!" + return nil + end + + # try ldap bind with user credentals + auth = ldap.authenticate user_dn, password + if !ldap.bind( auth ) + puts "NOTICE: ldap bind with '#{user_dn}' failed!" + return false + end + + # create/update user + if config[:sync_params] + user_attributes = { + :source => 'ldap', + :updated_by_id => 1, + } + config[:sync_params].each {| local_data, ldap_data | + if user_data[ ldap_data.to_sym ] + user_attributes[ local_data.to_sym] = user_data[ ldap_data.to_sym ] + end + } + if !user + user_attributes[:created_by_id] = 1 + user = User.create( user_attributes ) + puts "NOTICE: user created '#{user.login}'" + else + user.update_attributes( user_attributes ) + puts "NOTICE: user updated '#{user.login}'" + end + end + + # return if it was not possible to create user + return if !user + + # sync roles + # FIXME + + # sync groups + # FIXME + + # set always roles + if config[:always_roles] + role_ids = user.role_ids + config[:always_roles].each {|role_name| + role = Role.where( :name => role_name ).first + next if !role + if !role_ids.include?( role.id ) + role_ids.push role.id + end + } + user.role_ids = role_ids + user.save + end + + # set always groups + if config[:always_groups] + group_ids = user.group_ids + config[:always_groups].each {|group_name| + group = Group.where( :name => group_name ).first + next if !group + if !group_ids.include?( group.id ) + group_ids.push group.id + end + } + user.group_ids = group_ids + user.save + end + + # take session down + # - not needed, done by Net::LDAP - + + return user end end \ No newline at end of file diff --git a/lib/auth/otrs.rb b/lib/auth/otrs.rb index e7bf7b891..ee7c4c426 100644 --- a/lib/auth/otrs.rb +++ b/lib/auth/otrs.rb @@ -1,7 +1,7 @@ module Auth end module Auth::OTRS - def self.check( user, username, password, config ) + def self.check( username, password, config, user ) endpoint = Setting.get('import_otrs_endpoint') return false if !endpoint || endpoint.empty? diff --git a/lib/auth/test.rb b/lib/auth/test.rb index 237046a72..66b679b65 100644 --- a/lib/auth/test.rb +++ b/lib/auth/test.rb @@ -1,7 +1,7 @@ module Auth end module Auth::TEST - def self.check( user, username, password, config ) + def self.check( username, password, config, user ) # development systems if !ENV['RAILS_ENV'] || ENV['RAILS_ENV'] == 'development'