Browse Source

Init version of ldap auth/sync.

Martin Edenhofer 12 years ago
parent
commit
409dbe31d1
7 changed files with 168 additions and 28 deletions
  1. 2 0
      Gemfile
  2. 47 16
      app/models/user.rb
  3. 1 1
      lib/auth/env.rb
  4. 5 2
      lib/auth/internal.rb
  5. 111 7
      lib/auth/ldap.rb
  6. 1 1
      lib/auth/otrs.rb
  7. 1 1
      lib/auth/test.rb

+ 2 - 0
Gemfile

@@ -46,6 +46,8 @@ gem 'simple-rss'
 gem 'mysql2'
 #gem 'sqlite3'
 
+gem 'net-ldap'
+
 # Use unicorn as the web server
 # gem 'unicorn'
 

+ 47 - 16
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

+ 1 - 1
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']

+ 5 - 2
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 )

+ 111 - 7
lib/auth/ldap.rb

@@ -1,15 +1,119 @@
+require 'net/ldap'
+
 module Auth
 end
 module Auth::LDAP
-  def self.check( user, username, password, config )
-    
+  def self.check( username, password, config, user )
+
+    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
-    
-    # sync roles / groups
-#    return user
+    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 false
+    return user
   end
 end

+ 1 - 1
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?

+ 1 - 1
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'