Device LDAP Authenticable using group within cn? - ruby-on-rails

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

Related

Devise issue with LDAP Auth in Prod Only

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.

rails devise ldap_authentication cannot find user

I am trying to use thise gem according base documentation (https://github.com/cschiewek/devise_ldap_authenticatable)
this is my devise.rb config
# ==> LDAP Configuration
config.ldap_logger = true
config.ldap_create_user = true
config.ldap_config = "#{Rails.root}/config/ldap.yml"
and this is my ldap.yml
authorizations: &AUTHORIZATIONS
allow_unauthenticated_bind: true
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: myACtiveDirectory.server
port: 389
attribute: userPrincipalName
base: dc=mycompanydomain,dc=com
admin_user: cn=admin,dc=test,dc=com
admin_password: admin_password
ssl: false
# <<: *AUTHORIZATIONS
when I try to login, what I want to achieve is login with email and pass and if user is not present in local DB, create record.
In logs I see that LDAP cannot find user according userPrincipalName and it is always twice (is it trying twice before it fails?)
LDAP: LDAP dn lookup: userPrincipalName=mirob#mycompanydomain.com
LDAP: LDAP search for login: userPrincipalName=mirob#mycompanydomain.com
LDAP: LDAP search yielded 0 matches
LDAP: Authorizing user userPrincipalName=mirob#mycompanydomain.com,dc=mycompanydomain,dc=com
LDAP: Not authorized because not authenticated.
LDAP: LDAP dn lookup: userPrincipalName=mirob#mycompanydomain.com
LDAP: LDAP search for login: userPrincipalName=mirob#mycompanydomain.com
LDAP: LDAP search yielded 0 matches
LDAP: Authorizing user userPrincipalName=mirob#mycompanydomain.com,dc=mycompanydomain,dc=com
LDAP: Not authorized because not authenticated.
When I use script from this question to test (I changed sAMAccountName to principal name and I dont merge username with domain) I can login to LDAP so connection is working
Ldap is not working with Devise
any idea what is wrong with my devise ldap setup?
EDIT:
ok I found that ldap_authenticable is searching for DN what in my case (Active Directory setup) is CN=Complete Name,CN=Users,CN=mydomain,CN=com
The question is how can I search for email instead of Complete Name as I cant update AD for all users to put email into name field?
ok I found this answer and it helped me
Correct configuration for devise_ldap_authenticatable
I just needed to add this line to devise.rb and I can simple authenticate according to userPrincipalName
config.ldap_auth_username_builder = Proc.new() {|attribute, login, ldap| login}

LDAP group membership authentication - Rails

I am new to LDAP and been playing around with this for a while now. I have a rails app where I need to authenticate a user if he is from a group. I tried few params but got nothing to work. Any help would be appreciated. Thanks in advance.
Here is the devise_ldap settings and terminal output.
devise_ldap settings - ldap.yml
authorizations: &AUTHORIZATIONS
group_base: dc=skcript,dc=com
required_groups:
- ou=try1,dc=skcript,dc=com
- ["moreMembers", "cn=users,ou=groups,dc=test,dc=com"]
require_attribute:
objectClass: inetOrgPerson
authorizationRole: postsAdmin
development:
host: localhost
port: 389
attribute: "uid"
base: ou=try1,dc=skcript,dc=com
admin_user: cn=admin,dc=skcript,dc=com
admin_password: password
ssl: false
<<: *AUTHORIZATIONS
This should work :
authorizations: &AUTHORIZATIONS
group_base: dc=skcript,dc=com
require_attribute:
objectClass: inetOrgPerson
authorizationRole: postsAdmin
uniqueMember: ou=try1,dc=skcript,dc=com
required_groups checks your group dn against the uniqueMember attribute by default.
Check that the attribute storing group data in your LDAP server has the same name.
Don't forget to set this in your devise.rb :
config.ldap_check_attributes = true
For more information : https://github.com/cschiewek/devise_ldap_authenticatable/issues/96

Multiple LDAP Connections With Devise

I'm trying to modify an existing rails application that uses devise to check against an LDAP connection. I need to check against multiple different LDAP connections. Basically my user base is split between 2 or 3 different active directories and I'd like to be able to supply an array of connection information objects and have it run through the connections until it gets a response or fails. Is this possible with devise?
It is! Kind of. I hacked together a solution recently, not sure if it will be much help to you now.
First, you need to use devise_ldap_authenticatable. Once you have this installed, you can make some updates to the initialize method in the connection.rb file in this gem to accept either one configuration or many.
def initialize(params = {})
ldap_configs = YAML.load(ERB.new(File.read(::Devise.ldap_config || "#{Rails.root}/config/ldap.yml")).result)[Rails.env]
ldap_configs = ldap_configs.is_a?(Hash) ? [ldap_configs] : ldap_configs
The next part is up to you. Due to my constraints (usernames existing in both directories), I forced a user to enter a valid domain before looping through the connections. You might not have this constraint. In either case, just loop through the configs. Once you bind successfully, break the loop. The config values will be stored in the #ldap variable that is initialized here -
ldap_configs.each do |ldap_config|
#Maybe not needed if you don't have usernames in each directory. #domain is a user-entered value
if #domain == ldap_config["domain"]
#This should all stay the same, until you check the bindings
if #ldap.bind
#If it binds, break the loop. the values in #ldap will be stored
break
else
#keep looping
end
end
end
Next, make sure the ldap.yml file that devise_ldap_authenticatable generated is configured with all of your connections, including the domain, if needed -
## Environment
development:
-
host: "localhost1.com"
port: "389"
attribute: uid
base: dc=my-domain,dc=com
admin_user: cn=admin,dc=my-domain,dc=com
admin_password: admin_password
ssl: false
domain: "FIRST"
-
host: "localhost2.com"
port: "389"
attribute: uid
base: dc=my-domain,dc=com
admin_user: cn=admin,dc=my-domain,dc=com
admin_password: admin_password
ssl: false
domain: "SECOND"
I built on Steve's answer with the following that seems to work well. The benefit with this is that it wraps the original code and adds functionality to it. You can keep the ldap.yml file the same and add a hosts key with an array of hosts to the YAML to exercise this.
Note that I rescue the connection error in the loop. It will still throw when it attempts, again, to make the connection that the library would already try to make.
module Devise
module LDAP
module ConnectionExtensions
def initialize(params = {})
super
ldap_config = YAML.load(File.read("#{Rails.root}/config/ldap.yml"))[Rails.env]
ldap_config["hosts"]&.each do |host|
begin
#ldap.host = host
break if #ldap.bind
rescue Net::LDAP::Error => e
DeviseLdapAuthenticatable::Logger.send(e)
next
end
end
end
end
class Connection
prepend ConnectionExtensions
end
end
end
And here is the sample YAML file:
development:
host: localhost1.com
hosts:
- localhost1.com
- localhost2.com
port: 389
attribute: uid
base: dc=my-domain,dc=com
admin_user: cn=admin,dc=my-domain,dc=com
admin_password: admin_password
ssl: false

devise_ldap_authenticatable + devise: can authenticate via ldap with test script, but doesn't work with devise

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.

Resources