Last week I was successful getting Devise and Omniauth-ldap to work together to integrate with Active Directory, but I decided that the solution was not enterprise-ready because there is currently no way to override the login page and make it look like the rest of my application.
Now I think I have the solution, and it was a little bit easier too. Devise + devise_ldap_authenticatable. Here is the walkthrough for what worked for me.
Adding your Gems
For this project I am going to add the following lines to my gemfile:
gem "nifty-generators", :git => 'https://github.com/ryanb/nifty-generators.git' gem 'devise' gem 'devise_ldap_authenticatable', :git => 'git://github.com/cschiewek/devise_ldap_authenticatable.git' gem 'annotate', :git => 'git://github.com/ctran/annotate_models.git'
The annotate gem and the nifty-generators gem aren't necessary, but I like to use them. Nifty-generators will make the flash notices that you'll need later on in the project so you might consider using it if you're just following along. Now run bundle to get everything set up.
Setting up nifty-generators
There are a couple steps that I'm going to go through to set up nifty-generators which are optional if you decide that you don't mind having your rails project look ugly.
Also, make sure you edit config/routes.rb and add a path to root:rails generate nifty:config
rails generate nifty:layout
rails generate nifty:scaffold Change title:string description:string
cp public/stylesheets/application.css app/assets/stylesheets/nifty.css
rm public/index.html
cat app/views/layouts/application.html.erb | sed \
's/javascript_include_tag :defaults/javascript_include_tag "application"/' \
> /tmp/what.txt
cp /tmp/what.txt app/views/layouts/application.html.erb
rake db:migrate
root :to => 'changes#index'At this point you should have a working application that will allow you to create, edit, and delete changes. It should look nice and neat. Make sure that you have the ability to delete objects, and if you do not check the line in app/views/layouts/application.html.erb to make sure that javascript_include_tag is including "application" not :defaults. A tell-tale sign that javascript is the source of your delete problem is that when you try to delete a change you're not even given the "are you sure" prompt.
Configuring devise for user management
Now we need to create a model for our users, and we'll call that model User. We will use devise because then we'll have all of the helpers created for things like checking if the current user is authenticated. This part is amazingly simple.
Bam! That's it. Now you have a user model with all kinds of neat options. If you didn't want to perform Active Directory authentication you would be almost done now with just some work to customize the views.rails generate devise:installrails generate devise Userrake db:migrate
Setting up devise_ldap_authenticatable
First we need to generate the devise_ldap_authenticatable installation.
rails generate devise_ldap_authenticatable:installThere are options that you can set, but I don't need them. The gem assumes that your user model is called User which mine is.
Now we need to set the LDAP parameters used to connect to Active Directory. This is kept config/ldap.yml.
development: host: domaincontroller.domain.com port: 389 attribute: sAMAccountName base: dc=domain,dc=com admin_user: cn=admin,dc=test,dc=com admin_password: admin_password ssl: false # <<: *AUTHORIZATIONS
The most important thing here is that you set the attribute to sAMAccountName and that you have the full DN and password of your binding user. There are options here to force certain group membership or make sure that certain attributes are set, but I haven't played with that yet. Right now, anybody that is a member of the domain is able to log into my app.
The last thing we need to do is make some edits to config/initializers/devise.rb which has some new options that were added by the devise_ldap_authenticatable installer. This is why my configuration looks like:
# ==> 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 = false config.ldap_check_attributes = false config.ldap_use_admin_to_bind = true # config.ldap_ad_group_check = false
Look a little further in the document to find these lines and change them to match mine:
config.authentication_keys = [ :username ] config.case_insensitive_keys = [ :email, :username ]
Changes to the user model
We want to keep track of some Active Directory attributes, so we need to make a few changes to our user model. Let's add a firstname, lastname, username, and displayname field.
rails generate migration add_fields_to_users firstname:string lastname:string username:string displayname:string
rake db:migrate
This is when I like to use annotate so that I can keep track of what I've done to my models. Anyway, lets take a look at the app/models/user.rb file. We need to update our attributes accessible, and add some methods to pull in Active Directory attributes and save them with our user.
attr_accessible :username, :email, :password, :password_confirmation, :remember_me, :firstname, :lastname, :displayname before_save :get_ldap_lastname, :get_ldap_firstname, :get_ldap_displayname, :get_ldap_email def get_ldap_lastname Rails::logger.info("### Getting the users last name") tempname = Devise::LdapAdapter.get_ldap_param(self.username,"sn") puts "\tLDAP returned lastname of " + tempname self.lastname = tempname end def get_ldap_firstname Rails::logger.info("### Getting the users first name") tempname = Devise::LdapAdapter.get_ldap_param(self.username,"givenname") puts "\tLDAP returned firstname of " + tempname self.firstname = tempname end def get_ldap_displayname Rails::logger.info("### Getting the users display name") tempname = Devise::LdapAdapter.get_ldap_param(self.username,"displayname") self.displayname = tempname end def get_ldap_email Rails::logger.info("### Getting the users email address") tempmail = Devise::LdapAdapter.get_ldap_param(self.username,"mail") self.email = tempmail end
Changes to the views
The final change is to make sure that our view is going to reflect whether or not a user is logged in and provide some basic information about the logged in user. I added this right under the title in app/views/changes/index.html.erb
<% if user_signed_in? %> Welcome <%= current_user.firstname %>, you last logged on <%= current_user.last_sign_in_at %> from <%= current_user.last_sign_in_ip %> <% else %> <%= link_to "login", new_user_session_path %> <% end %>
We also need to make a critical change to the login form. Right now it is still asking for an email address and it wont let you get away with typing a username into the email field. So we need to have devise show us our view so that we can edit it.
rails generate devise:views
Now edit app/views/devise/sessions/new.html.erb. Change the email label to say username and change the email_field to text_field.
Also, if you want to have the user redirect to your root path, then you need to add something like this to config/routes.rb
match '/changes' => 'changes#index', :as => 'user_root'
2 comments:
Thanks
thanks for sharing ur AD integration experience. much appreciated. how long did all of this take to implement? and is there any cost required to buy additional AD servers?
Post a Comment