Permalink
Browse files

init check in. rewrote the ldap strategy based on the omniauth 1.0

spec. Added spec test for ldap strategy
  • Loading branch information...
0 parents commit 5656da80d4193e0d0584f44bac493a87695e580f @pyu10055 pyu10055 committed Oct 24, 2011
@@ -0,0 +1,2 @@
+.project
+coverage
@@ -0,0 +1,11 @@
+source 'http://rubygems.org'
+
+gemspec
+
+group :development, :test do
+ gem 'guard'
+ gem 'guard-rspec'
+ gem 'guard-bundler'
+ gem 'growl'
+ gem 'rb-fsevent'
+end
@@ -0,0 +1,79 @@
+PATH
+ remote: .
+ specs:
+ omniauth-ldap (1.0.0.beta1)
+ net-ldap (~> 0.2.2)
+ omniauth (~> 1.0.0.beta1)
+ pyu-ruby-sasl (~> 0.0.3.1)
+ rubyntlm (~> 0.1.1)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ archive-tar-minitar (0.5.2)
+ columnize (0.3.4)
+ diff-lcs (1.1.3)
+ ffi (1.0.9)
+ growl (1.0.3)
+ guard (0.8.8)
+ thor (~> 0.14.6)
+ guard-bundler (0.1.3)
+ bundler (>= 1.0.0)
+ guard (>= 0.2.2)
+ guard-rspec (0.5.0)
+ guard (>= 0.8.4)
+ hashie (1.2.0)
+ libnotify (0.5.7)
+ ffi (= 1.0.9)
+ linecache19 (0.5.12)
+ ruby_core_source (>= 0.1.4)
+ multi_json (1.0.3)
+ net-ldap (0.2.2)
+ omniauth (1.0.0.beta1)
+ hashie
+ rack
+ pyu-ruby-sasl (0.0.3.3)
+ rack (1.3.5)
+ rack-test (0.6.1)
+ rack (>= 1.0)
+ rb-fsevent (0.4.3.1)
+ rspec (2.7.0)
+ rspec-core (~> 2.7.0)
+ rspec-expectations (~> 2.7.0)
+ rspec-mocks (~> 2.7.0)
+ rspec-core (2.7.1)
+ rspec-expectations (2.7.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.7.0)
+ ruby-debug-base19 (0.11.25)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby_core_source (>= 0.1.4)
+ ruby-debug19 (0.11.6)
+ columnize (>= 0.3.1)
+ linecache19 (>= 0.5.11)
+ ruby-debug-base19 (>= 0.11.19)
+ ruby_core_source (0.1.5)
+ archive-tar-minitar (>= 0.5.2)
+ rubyntlm (0.1.1)
+ simplecov (0.5.4)
+ multi_json (~> 1.0.3)
+ simplecov-html (~> 0.5.3)
+ simplecov-html (0.5.3)
+ thor (0.14.6)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ growl
+ guard
+ guard-bundler
+ guard-rspec
+ libnotify
+ omniauth-ldap!
+ rack-test
+ rb-fsevent
+ rspec (~> 2.6)
+ ruby-debug19
+ simplecov
@@ -0,0 +1,11 @@
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec" }
+end
+
+
+guard 'bundler' do
+ watch('Gemfile')
+ watch(/^.+\.gemspec/)
+end
@@ -0,0 +1,68 @@
+# OmniAuth LDAP
+
+**Note:** This gem is designed to work with the in-beta OmniAuth 1.0
+library. It will not be officially released on RubyGems.org until
+OmniAuth 1.0 is released.
+
+== LDAP
+
+Use the LDAP strategy as a middleware in your application:
+
+ use OmniAuth::Strategies::LDAP,
+ :title => "My LDAP",
+ :host => '10.101.10.1',
+ :port => 389,
+ :method => :plain,
+ :base => 'dc=intridea, dc=com',
+ :uid => 'sAMAccountName',
+ :name_proc => Proc.new {|name| name.gsub(/@.*$/,'')}
+ :bind_dn => 'default_bind_dn'
+ :password => 'password'
+
+All of the listed options are required, with the exception of :name_proc, :bind_dn, and :password.
+Allowed values of :method are: :plain, :ssl, :tls.
+
+:bind_dn and :password is the default credentials to perform user lookup.
+ most LDAP servers require that you supply a complete DN as a binding-credential, along with an authenticator
+ such as a password. But for many applications, you often don’t have a full DN to identify the user.
+ You usually get a simple identifier like a username or an email address, along with a password.
+ Since many LDAP servers don't allow anonymous access, search function will require a bound connection,
+ :bind_dn and :password will be required for searching on the username or email to retrieve the DN attribute
+ for the user. If the LDAP server allows anonymous access, you don't need to provide these two parameters.
+
+:uid is the LDAP attribute name for the user name in the login form.
+ typically AD would be 'sAMAccountName' or 'UserPrincipalName', while OpenLDAP is 'uid'.
+
+:name_proc allows you to match the user name entered with the format of the :uid attributes.
+ For example, value of 'sAMAccountName' in AD contains only the windows user name. If your user prefers using
+ email to login, a name_proc as above will trim the email string down to just the windows login name.
+ In summary, use :name_proc to fill the gap between the submitted username and LDAP uid attribute value.
+
+:try_sasl and :sasl_mechanisms are optional. :try_sasl [true | false], :sasl_mechanisms ['DIGEST-MD5' | 'GSS-SPNEGO']
+ Use them to initialize a SASL connection to server. If you are not familiar with these authentication methods,
+ please just avoid them.
+
+Direct users to '/auth/ldap' to have them authenticated via your company's LDAP server.
+
+
+## License
+
+Copyright (C) 2011 by Ping Yu and Intridea, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
@@ -0,0 +1,9 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
+require 'rspec/core/rake_task'
+
+desc 'Default: run specs.'
+task :default => :spec
+
+desc "Run specs"
+RSpec::Core::RakeTask.new
@@ -0,0 +1,4 @@
+require "omniauth-ldap/version"
+require "omniauth-ldap/adaptor"
+require 'omniauth/strategies/ldap'
+
@@ -0,0 +1,158 @@
+#this code borrowed pieces from activeldap and net-ldap
+
+require 'rack'
+require 'net/ldap'
+require 'net/ntlm'
+require 'uri'
+
+module OmniAuth
+ module LDAP
+ class Adaptor
+ class LdapError < StandardError; end
+ class ConfigurationError < StandardError; end
+ class AuthenticationError < StandardError; end
+ class ConnectionError < StandardError; end
+
+ VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password, :try_sasl, :sasl_mechanisms, :uid, :base, :allow_anonymous]
+
+ MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
+
+ METHOD = {
+ :ssl => :simple_tls,
+ :tls => :start_tls,
+ :plain => nil,
+ }
+
+ attr_accessor :bind_dn, :password
+ attr_reader :connection, :uid, :base
+
+ def initialize(configuration={})
+ @disconnected = false
+ @bound = false
+ @configuration = configuration.dup
+ @configuration[:allow_anonymous] ||= false
+ @logger = @configuration.delete(:logger)
+ message = []
+ MUST_HAVE_KEYS.each do |name|
+ message << name if configuration[name].nil?
+ end
+ raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
+ VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
+ instance_variable_set("@#{name}", configuration[name])
+ end
+
+ method = ensure_method(@method)
+ config = {
+ :host => @host,
+ :port => @port,
+ :encryption => method
+ }
+ @uri = construct_uri(@host, @port, @method != :plain)
+
+ @bind_method = @try_sasl ? "sasl" : @allow_anonymous ? 'anonymous' : 'simple'
+ @bind_method = 'anonymous' unless @bind_dn && @password
+
+ @auth = sasl_auths.first if @bind_method == 'sasl'
+ @bind_method = 'simple' unless @auth
+ @auth ||= { :method => @bind_method,
+ :username => @bind_dn,
+ :password => @passowrd
+ }
+ config[:auth] = @auth
+ @connection = Net::LDAP.new(config)
+ end
+
+ #:base => "dc=yourcompany, dc=com",
+ # :filter => "(mail=#{user})",
+ # :password => psw
+ def bind_as(args = {})
+ result = false
+ @connection.open { |me|
+ rs = search args
+ if rs and rs.first and dn = rs.first.dn
+ password = args[:password]
+ method = args[:method]
+ password = password.call if password.respond_to?(:call)
+ if method == 'sasl'
+ result = rs if bind(sasl_auths(args))
+ else
+ result = rs if bind(:method => :simple, :username => dn,
+ :password => password)
+ end
+ end
+ }
+ result
+ end
+
+ private
+
+ def ensure_port(method)
+ if method == :ssl
+ URI::LDAPS::DEFAULT_PORT
+ else
+ URI::LDAP::DEFAULT_PORT
+ end
+ end
+
+ def ensure_method(method)
+ method ||= "plain"
+ normalized_method = method.to_s.downcase.to_sym
+ return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
+
+ available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
+ format = "%s is not one of the available connect methods: %s"
+ raise ConfigurationError, format % [method.inspect, available_methods]
+ end
+
+ def sasl_auths(options={})
+ auths = []
+ sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
+ sasl_mechanisms.each do |mechanism|
+ normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
+ sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
+ next unless respond_to?(sasl_bind_setup, true)
+ initial_credential, challenge_response = send(sasl_bind_setup, options)
+
+ auths << {
+ :method => :sasl,
+ :initial_credential => initial_credential,
+ :mechanism => mechanism,
+ :challenge_response => challenge_response,
+ }
+ end
+ end
+
+ def sasl_bind_setup_digest_md5(options)
+ bind_dn = options[:username]
+ initial_credential = ""
+ challenge_response = Proc.new do |cred|
+ pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password
+ sasl = SASL.new("DIGEST-MD5", pref)
+ response = sasl.receive("challenge", cred)
+ response[1]
+ end
+ [initial_credential, challenge_response]
+ end
+
+ def sasl_bind_setup_gss_spnego(options)
+ bind_dn = options[:username]
+ psw = [bind_dn, options[:password]||@password]
+ raise LdapError.new( "invalid binding information" ) unless (bind_dn && psw)
+
+ nego = proc {|challenge|
+ t2_msg = Net::NTLM::Message.parse( challenge )
+ bind_dn, domain = bind_dn.split('\\').reverse
+ t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
+ t3_msg = t2_msg.response( {:user => bind_dn, :password => psw}, {:ntlmv2 => true} )
+ t3_msg.serialize
+ }
+ [Net::NTLM::Message::Type1.new.serialize, nego]
+ end
+
+ def construct_uri(host, port, ssl)
+ protocol = ssl ? "ldaps" : "ldap"
+ URI.parse("#{protocol}://#{host}:#{port}").to_s
+ end
+ end
+ end
+end
@@ -0,0 +1,5 @@
+module OmniAuth
+ module LDAP
+ VERSION = "1.0.0.beta1"
+ end
+end
Oops, something went wrong.

0 comments on commit 5656da8

Please sign in to comment.