Rego’s Everyday Life

A blog about my work at Igalia.

CASify Redmine

Redmine 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.


Comments

On 09/08/14 10:54, **Neoh59** wrote:

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.

On 09/08/14 07:22, Manuel Rego Casasnovas wrote:

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.

On 09/11/25 12:15, rubiojr wrote:

Awesome mate. Your patch saved us hours of work!!!

On 10/04/15 16:44, lnomine wrote:

Hi,

Do you plan to use the 0.9.x version of Redmine? I’d love to see your patch working for the current version.

On 10/04/15 16:52, Manuel Rego Casasnovas wrote:

Maybe in the future we could update to this version, but not in the short term. Sorry.

I’ll keep you informed if we update the patch.