My team has setup my ruby on rails app to work with devise ldap. Whenever a user logs in for the first time, it creates a new entry in the Users model and works completely fine. But whenever a user that already exist in the Users database tries to login it gives the following error:
NoMethodError in Devise::SessionsController#create
undefined method `to' for nil:NilClass
From looking at the server console, it seems like it is failing on this SQL statement:
SELECT `users`.* FROM `users` WHERE `users`.`username` = 'jDoe' LIMIT 1
I think the server is getting no results from that query.
But whenever I run that same sql statement in the ruby console, it gives the correct result (the record of the user attempting to login)
Here are some of my devise configs. I am new to devise and ruby (and I also didn't install the devise) so let me know if you need other config files to debug this problem.
initializers/devise.rb
Devise.setup do |config|
config.ldap_create_user = true
config.ldap_update_password = false
config.mailer_sender = "****"
require 'devise/orm/active_record'
config.authentication_keys = [ :username ]
config.case_insensitive_keys = [ :email ]
config.strip_whitespace_keys = [ :email ]
config.skip_session_storage = [:http_auth]
config.stretches = Rails.env.test? ? 1 : 10
config.reconfirmable = true
config.password_length = 8..128
config.sign_out_via = :delete
end
ldap.yml
authorizations: &AUTHORIZATIONS
group_base: ou=groups,dc=test,dc=com
required_groups:
- cn=admins,ou=groups,dc=test,dc=com
- cn=users,ou=groups,dc=test,dc=com
- ["moreMembers", "cn=users,ou=groups,dc=test,dc=com"]
require_attribute:
objectClass: inetOrgPerson
authorizationRole: postsAdmin
development:
host: ****
port: 636
attribute: uid
base: ou=People,dc=***,dc=com
admin_user: cn=admin,dc=test,dc=com
admin_password: admin_password
ssl: true
test:
host: localhost
port: 3389
attribute: cn
base: ou=people,dc=test,dc=com
admin_user: cn=admin,dc=test,dc=com
admin_password: admin_password
ssl: simple_tls
production:
host: localhost
port: 636
attribute: cn
base: ou=people,dc=test,dc=com
admin_user: cn=admin,dc=test,dc=com
admin_password: admin_password
ssl: start_tls
user.rb
class User < ActiveRecord::Base
devise :ldap_authenticatable, :rememberable, :trackable
attr_accessible :name, :cell, :email, :pref, :type, :username, :password, :password_confirmation, :remember_me
end
My problem happened because I had 2 tables that inherited from User (Client, and Runner) and they both tried to authenticate with the same ldap. When I took out the single table inheritance everything worked fine.
Related
I'm on Rails 5.2 and I have devise with LDAP setup and working in Development. Deploying to prod with capistrano though it errors out when put my username and password in. I get a nomethod error in devise::sessions::create. undefined method `[]' for nil:NilClass. This is the section that errors out:
end
ldap_options = params
ldap_config["ssl"] = :simple_tls if ldap_config["ssl"] === true
ldap_options[:encryption] = ldap_config["ssl"].to_sym if ldap_config["ssl"]
#ldap = Net::LDAP.new(ldap_options)
Here is my LDAP config:
authorizations: &AUTHORIZATIONS
required_groups:
- CN=GROUP1,OU=Users,OU=mysite,DC=ad,DC=com
## Environment
development:
host: mysite.com
port: 389
attribute: sAMAccountName
base: dc=ad,dc=com
admin_user: user
admin_password: password
ssl: false
<<: *AUTHORIZATIONS
production:
host: mysite.com
port: 389
attribute: sAMAccountName
base: dc=ad,dc=com
admin_user: user
admin_password: password
ssl: false
<<: *AUTHORIZATIONS
Any thoughts on why this only does this in production?
I figured it out! In my LDAP.yml file the production connection section was indented by one space. This was causing it to not load the config correctly. Aligning the productions ection to the left all the way fixed it. Everything is working now.
I have following login credential
EMAIL=xxxx#gmail.com and PASSWORD=test1234
and above credential is correct.
But while login, I am getting following error
LDAP: LDAP dn lookup: cn=xxxx#gmail.com
LDAP: LDAP search for login: cn=xxxx#gmail.com
LDAP: LDAP search yielded 0 matches
LDAP: Authorizing user cn=xxxx#gmail.com,ou=people,dc=test,dc=com
LDAP: Not authorized because not authenticated.
Completed 401 Unauthorized in 9ms
This is my configuration
For ldap.yml
authorizations: &AUTHORIZATIONS
allow_unauthenticated_bind: false
group_base: ou=groups,dc=test,dc=com
## Requires config.ldap_check_group_membership in devise.rb be true
# Can have multiple values, must match all to be authorized
required_groups:
# If only a group name is given, membership will be checked against "uniqueMember"
- cn=admins,ou=groups,dc=test,dc=com
- cn=users,ou=groups,dc=test,dc=com
# If an array is given, the first element will be the attribute to check against, the second the group name
- ["moreMembers", "cn=users,ou=groups,dc=test,dc=com"]
## Requires config.ldap_check_attributes in devise.rb to be true
## Can have multiple attributes and values, must match all to be authorized
require_attribute:
objectClass: inetOrgPerson
authorizationRole: postsAdmin
## Environment
development:
host: localhost
port: 389
attribute: cn
base: ou=people,dc=test,dc=com
admin_user: cn=admin,dc=test,dc=com
admin_password: admin_password
ssl: false
# <<: *AUTHORIZATIONS
and for device.rb
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# ==> LDAP Configuration
# config.ldap_logger = true
config.ldap_create_user = true
# config.ldap_update_password = true
config.ldap_config = "#{Rails.root}/config/ldap.yml"
config.ldap_check_group_membership = true
# config.ldap_check_group_membership_without_admin = false
config.ldap_check_attributes = true
config.ldap_use_admin_to_bind = true
config.ldap_ad_group_check = true
config.mailer_sender = 'please-change-me-at-config-initializers-devise#example.com'
require 'devise/orm/active_record'
config.case_insensitive_keys = [:email]
config.strip_whitespace_keys = [:email]
config.skip_session_storage = [:http_auth]
config.stretches = Rails.env.test? ? 1 : 10
config.reconfirmable = true
config.expire_all_remember_me_on_sign_out = true
config.password_length = 8..72
config.reset_password_within = 6.hours
config.sign_out_via = :delete
end
What wrong in above code? Please help me.
I'm using Devise 3.5.2 for authentication with the devise_ldap_authenticable gem on Rails 4.2.4. I've moved from the released 0.8.5 gem to the github master level (0.8.6).
From what I can tell, the LDAP plugin authentication code is not running. It's not writing any log messages.
I previously got regular database authentication to work for this application. I'm now trying to get LDAP authentication to work.
For example:
Started POST "/users/sign_in" for ::1 at 2015-11-23 16:05:14 -0500
ActiveRecord::SchemaMigration Load (87.6ms) SELECT schema_migrations.* FROM schema_migrations
Processing by Devise::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Mos...w==", "user"=>{"login"=>"leonsp", "password"=>"...", "remember_me"=>"0"}}
Completed 401 Unauthorized in 95ms (ActiveRecord: 0.0ms)
There should be LDAP log messages in there.
My user model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
#
# :database_authenticable is not enabled
devise :ldap_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :zxcvbnable
attr_accessible :email, :userid, :shortuserid, :login # etc...
# Virtual attribute for authenticating by either userid or email
# This is in addition to a real persisted field like 'userid'
attr_accessor :login
# ...
def self.find_for_database_authentication(warden_conditions)
Rails.logger.debug "Finding for database authentication"
conditions = warden_conditions.dup
login = conditions.delete(:login).try(:downcase)
if login.present?
Rails.logger.debug "Finding by login #{login}"
where(conditions.to_hash).find_by([
"lower(userid) = :value OR lower(shortuserid) = :value OR lower(email) = :value", { value: login }
])
else
Rails.logger.debug "Finding by conditions #{login}"
find_by(conditions.to_hash)
end
end
def self.find_for_ldap_authentication(warden_conditions)
Rails.logger.debug "Finding for ldap authentication"
conditions = warden_conditions.dup
login = conditions.delete(:login).try(:downcase)
if login.present?
Rails.logger.debug "Finding by login #{login}"
if login.include? "#"
conditions[:email] = login
super conditions
else
conditions[:userid] = login
super conditions
end
else
Rails.logger.debug "No login. Using default behaviour"
super
end
end
# ...
Here's the initializer for Devise. I added the monkeypatch at the front while debugging:
module Devise
module Strategies
class LdapAuthenticatable < Authenticatable
def authenticate!
Rails.logger.debug "=== Starting LDAP Authentication ==="
super
Rails.logger.debug "=== Done LDAP Authentication ==="
end
end
end
end
# Use this hook to configure devise mailer, warden hooks and so forth.
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|
# TODO: https://github.com/cschiewek/devise_ldap_authenticatable/issues/153
# ==> LDAP Configuration
# config.ldap_logger = true
# config.ldap_create_user = false
# config.ldap_update_password = true
# config.ldap_config = "#{Rails.root}/config/ldap.yml"
# config.ldap_check_group_membership = false
# config.ldap_check_group_membership_without_admin = false
# config.ldap_check_attributes = false
# config.ldap_use_admin_to_bind = false
# config.ldap_ad_group_check = false
config.ldap_logger = true
config.ldap_create_user = true
config.ldap_update_password = true
config.ldap_use_admin_to_bind = true
config.ldap_auth_username_builder = proc do |attribute, login, ldap|
username_string = "#{attribute}=#{login},#{ldap.base}"
Rails.logger.debug "Generated username as #{username_string}"
username_string
end
# ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating a user. The default is
# just :email. You can configure it to use [:username, :subdomain], so for
# authenticating a user, both parameters are required. Remember that those
# parameters are used only when authenticating and not when retrieving from
# session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present.
config.authentication_keys = [:login, :email, :userid]
# ...
I can successfully connect to the LDAP server on the machine using ldapsearch:
ldapsearch -x -W -D "cn=admin,dc=my_domain,dc=com" -H ldap://my_hostname.my_domain.com "(cn=leonsp)"
Here's the corresponding configuration the ldap.yml:
## Authorizations
# Uncomment out the merging for each environment that you'd like to include.
# You can also just copy and paste the tree (do not include the "authorizations") to each
# environment if you need something different per environment.
authorizations: &AUTHORIZATIONS
allow_unauthenticated_bind: false
group_base: ou=groups,dc=my_domain,dc=com
## Requires config.ldap_check_group_membership in devise.rb be true
# Can have multiple values, must match all to be authorized
required_groups:
## Requires config.ldap_check_attributes in devise.rb to be true
## Can have multiple attributes and values, must match all to be authorized
require_attribute:
objectClass: inetOrgPerson
## Environment
development:
host: ldap://my_hostname.my_domain.com
port: 389
attribute: cn
base: dc=my_domain,dc=com
admin_user: cn=admin,dc=my_domain,dc=com
admin_password: ...
ssl: none
# <<: *AUTHORIZATIONS
Am I correct in assuming that the LDAP plugin code for Devise is not running?
Why isn't it running?
Why aren't any of my debug statements being reached?
What additional methods can I monkeypatch with debug statements to debug the issue?
What other diagnostics can I collect?
Edit: I'm tracing the execution using the byebug debugger. So far I can tell that :ldap_authenticable shows up on the list of strategies during sign in, but doesn't seem to result in any :ldap_authenticable-specific code execution.
The main issue was that I had included optional parameters in config.authentication_keys. This made Devise::Strategies#parse_authentication_key_values fail silently. Only mandatory parameters should be included in authentication_keys.
Other issues related to the application being designed to allow users to log in with either their username or their email address. To allow for users to log in using either, I had to:
Override login_with in the User model
Override self.find_for_ldap_authentication in the User model
Rename the fake attr_accessor :login to attr_accessor :username in order to better distinguish it from the different meaning of :login in the gem
One last issue: Don't include protocol (e.g. ldaps://) in the host attributes of ldap.yml
For those coming to this thread still, I was able to get it to work by dropping down to gem version 0.8.4.
I can sign in using test code provided from net-ldap gem website, but same setting to login doesn't work with devise on Rails.
This is the server log when I try to log in on Rails using devise.
Started POST "/users/sign_in" for 127.0.0.1 at Fri Mar 22 10:53:39 -0700 2013
Processing by Devise::SessionsController#create as HTML
Parameters: {"commit"=>"Sign in", "authenticity_token"=>"bEmEPHuI8ob+O67hy0mpgGm12KzFnBNwRuhALAJzmCg=", "user"=>{"remember_me"=>"1", "email"=>"somerandomeusername#corp.bigasscorporation.com", "password"=>"[FILTERED]"}, "utf8"=>"✓"}
User Load (0.5ms) SELECT `users`.* FROM `users` WHERE `users`.`email` = 'somerandomeusername#corp.bigasscorporation.com' LIMIT 1
LDAP: LDAP dn lookup: mail=somerandomeusername#corp.bigasscorporation.com
LDAP: LDAP search for login: mail=somerandomeusername#corp.bigasscorporation.com
LDAP: Authorizing user mail=somerandomeusername#corp.bigasscorporation.com,OU=Users,OU=Users_and_Groups,DC=corp,DC=bigasscorporation,DC=com
LDAP: LDAP dn lookup: mail=somerandomeusername#corp.bigasscorporation.com
LDAP: LDAP search for login: mail=somerandomeusername#corp.bigasscorporation.com
DEPRECATION WARNING: an empty resource was given to Devise::Strategies::LdapAuthenticatable#validate. Please ensure the resource is not nil. (called from require at script/rails:6)
Completed 401 Unauthorized in 64ms
The below is some information about my environment
Ruby 1.8.7
Rails 3.2.13
Gems used
gem "devise", "~> 2.2.2"
gem "net-ldap", '~> 0.2.2'
gem "devise_ldap_authenticatable", '~> 0.6.1'
LDAP configs
config/ldap.yml
authorizations: &AUTHORIZATIONS
group_base: OU=Users,OU=Users_and_Groups,DC=corp,DC=somebigasscorporation,DC=com
required_groups:
- cn=Users,OU=Users_and_Groups,DC=corp,DC=somebigasscorporation,DC=com
- OU=Users,OU=Users_and_Groups,DC=corp,DC=somebigasscorporation,DC=com
- ["moreMembers", "cn=users,ou=groups,dc=test,dc=com"]
require_attribute:
objectClass: inetOrgPerson
authorizationRole: postsAdmin
## Enviornments
development:
host: xxx.corp.somebigasscorporation.com
port: 3268
attribute: mail
base: OU=Users,OU=Users_and_Groups,DC=corp,DC=somebigasscorporation,DC=com
# admin_user: cn=admin,dc=test,dc=com
# admin_password: admin_password
ssl: false
# <<: *AUTHORIZATIONS
config/initializers/devise.rb
Devise.setup do |config|
config.ldap_create_user = true
...
The test code that works
require 'rubygems'
require 'net/ldap'
require 'highline/import'
ldap = Net::LDAP.new
ldap.host = "xxx.corp.bigasscorporation.com"
ldap.port = "3268"
ldap.base = "OU=Users,OU=Users_and_Groups,DC=corp,DC=bigasscorporation,DC=com"
ldap.auth "somerandomuser#corp.bigasscorporation.com", "xxxXXXyyy"
if ldap.bind
p "Success!!"
p ldap
p ldap.base
p ldap.get_operation_result
else
p "Failed!"
p ldap.get_operation_result
end
# => "Success!!"
#<Net::LDAP:0x007fc393a22660 #host="xxx.corp.bigasscorporation.com", #port="3268", #verbose=false, #auth={:method=>:simple, :username=>"somerandomuser#corp.bigasscorporation.com", :password=>"xxxXXXyyy"}, #base="OU=Users,OU=Users_and_Groups,DC=corp,DC=bigasscorporation,DC=com", #encryption=nil, #open_connection=nil, #result=0>
"OU=Users,OU=Users_and_Groups,DC=corp,DC=bigasscorporation,DC=com"
#<OpenStruct code=0, message="Success">
I needed this in devise.rb
config.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| "#{login}" }
In case anyone else has this same issue, I found a slight problem with the ldap_auth_username_builder fix. I was also trying to set some authorizations on required attributes in the .yml file, and I was getting a lot of strange errors after ldap.search methods were being called by the gem. In particular, a lot of calling .try(:first) on a search that returned nil.
I ended up digging around in the gem's lib directory and tweaked some code for better logging. Turns out, when I added config.ldap_auth_username_builder, I was actually tricking the system to believe it found a record when it really wasn't. I wasn't able to specify attributes for these records because there was no record to begin with. If you guys are trying to do something similar, like check group membership or the like, I would suggest triple-checking that your attribute value in the ldap.yml file is set to a valid attr and that you're giving it the right information, so it can find what you need on the LDAP end.
Im trying to set up a Devise Authenticable LDAP login system. Right now, I can get it to work using all users. However, i would like to only use users within a certain group. To illustrate, as of now, using all users, the code looks like this:
production:
host: my.host.domain.com
port: 389
attribute: AccountName
base: cn=users,dc=my,dc=con,dc=to,dc=host
admin_user: adminuser
admin_password: password
ssl: false
So the following is the code i wrote to make it only work within the group "demo2" located within users. However, now it doesnt work with ANY user... Any suggestions?
production:
host: my.host.domain.com
port: 389
attribute: AccountName
base: cn=demo2,cn=users,dc=my,dc=con,dc=to,dc=host
admin_user: adminuser
admin_password: password
ssl: false
I believe you want to use required_groups in your ldap.yml. If you look at the template file you can see the example:
group_base: ou=groups,dc=test,dc=com
## Requires config.ldap_check_group_membership in devise.rb be true
# Can have multiple values, must match all to be authorized required_groups:
# If only a group name is given, membership will be checked against "uniqueMember"
- cn=admins,ou=groups,dc=test,dc=com