CASify Redmine
July 22, 2009 on 7:54 am | In English, Planet | 4 CommentsRedmine is a project management web application. It’s written in Ruby using the Rails framework.
There is a CAS client available written in Ruby: RubyCAS-Client.
So, I’ve made some modifications on Redmine code in order to use the CAS service to authenticate Redmine users. Moreover, user information is updated from a LDAP every time the user login in the Redmine website (using Net::LDAP). And the fields to modify this data will be disabled on My account page.
Summarizing, I’ve uploaded a patch which provides this specific behaviour (this patch is done against the revision 2824 of Redmine). I know that it’s hardcoded, but it could be a base for further steps and I hope that it could be useful for someone else (or even for me in the future
). In the next paragraphs I’ll go into more technical details.
Instructions
Once you’ve installed the last version of Redmine (I’m using trunk revision 2824), you’ll need to install RubyCAS-Client (again from trunk because of some functions are not available at last stable 2.0.1):
./script/plugin install http://rubycas-client.googlecode.com/svn/trunk/rubycas-client
First of all, you should add the basic configuration for CAS at config/environment.rb:
CASClient::Frameworks::Rails::Filter.configure( :cas_base_url => 'https://localhost/cas/' )
Moreover, it’s needed to add the next line to the class AccountController at app/controllers/account_controller.rb:
before_filter CASClient::Frameworks::Rails::Filter
Then, in order to change the login behaviour you should modify the login action of the account controller. Change the method login at app/controllers/account_controller.rb:
require 'casclient' require 'casclient/frameworks/rails/filter' def login if session[:cas_user].empty? # Logout user self.logged_user = nil CASClient::Frameworks::Rails::Filter::redirect_to_cas_for_authentication(self) else cas_authentication(session[:cas_user]) end end
Now, it’s necessary to add the new method cas_authentication, which register an user if it’s the first time that makes login on the Redmine website:
def cas_authentication(cas_user) user = User.find_or_initialize_by_login(cas_user) if user.new_record? # Create on the fly user.login = cas_user user = update_user_data_from_ldap(user) user.status = User::STATUS_REGISTERED register_automatically(user) do onthefly_creation_failed(user) end else if user.active? user = update_user_data_from_ldap(user, true) successful_authentication(user) else account_pending end end end
The method update_user_data_from_ldap gets the user data from LDAP, and update user data on database if needed:
def update_user_data_from_ldap (user, save = false) ldap = Net::LDAP::new ldap.host = 'localhost' ldap.port = '389' treebase = 'dc=example,dc=com' filter = Net::LDAP::Filter.eq('uid', user.login) entry = ldap.search( :base => treebase, :filter => filter, :attributes => ['cn', 'sn', 'mail'] ).first user.firstname = entry.cn.first user.lastname = entry.sn.first user.mail = entry.mail.first user.save if save return user end
Following, you should modify the logout behaviour, changing the method logout of the same controller:
def logout cookies.delete :autologin Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged? self.logged_user = nil CASClient::Frameworks::Rails::Filter::logout(self) end
The last step is to disable the fields gotten from LDAP at My account page. Modify the app/views/my/account.rhtml file adding :disabled => true option:
<p><%= f.text_field :firstname, :required => true, :disabled => true %></p> <p><%= f.text_field :lastname, :required => true, :disabled => true %></p> <p><%= f.text_field :mail, :required => true, :disabled => true %></p>
Finally, you can get all this stuff from a single patch. Any comment or suggestion are, as usual, welcomed.
4 Comments »
RSS feed for comments on this post. TrackBack URI
Leave a comment
Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds.
Valid XHTML and CSS. ^Top^
Hello,
I use Redmine 0.8.4 (latest stable release)
CASifying Redmine will be great. But why do you write a function to connect to LDAP although LDAP configuration can be made in the Redmine interface ?
I’m dreaming of a SSO plugin for Redmine. But I’m not be able to do it.
Do you ?
Regards,
Neoh
PS: Sorry for my poor english, I’m french.
Comment by Neoh59 — August 14, 2009 #
I’m just using LDAP for a specific requirement. I need that the first time the user login Redmine, the information from LDAP about this use was stored on the database (morover every time the user login again I should update the data if that has changed on LDAP). Anyway, I could have a look at LDAP plugin, maybe it fulfils my requirements
About the SSO plugin, maybe this patch could be useful as example for that plugin. But for the moment it’s enough for my needs.
Comment by Manuel Rego Casasnovas — August 14, 2009 #
Awesome mate. Your patch saved us hours of work!!!
Comment by rubiojr — November 25, 2009 #