Archive for November, 2007

Hooking custom authentication into Radiant CMS

November 6, 2007

radiant.png

I’m working on a site running Radiant CMS. Instead of authenticating users with the Radiant database we want to use Crowd.

I need to do 2 things
1) have User include CrowdAuthenticator
2) override the existing implementation of User.authenticate

The solution took a while, but it turned out to be fairly simple.

Create a new radiant extension

$ script/generate extension crowd_authenticator

Create a CrowdAuthenticator module in the lib directory under the extension. The CrowdAuthenticator module has a method to authenticate users by calling Ruby code that wraps the Crowd SOAP API. It also handles updating the Radiant database with the user information from Crowd.

The extension injects my module into the Radiant User model.

class CrowdAuthenticatorExtension < Radiant::Extension
  def activate
    User.send :include, CrowdAuthenticator
  end
end

Radiant looks for the user in the database. I want to change the definition of User.authentication without editing the Radiant code.

class User < ActiveRecord::Base
  ...
  def self.authenticate(login, password)
    find_by_login_and_password(login, sha1(password))
  end
  ...
end

I was having trouble trying to “override” the “static” authenticate method. Fortunately Simon Harris helped out with Alias a Static Method in Ruby

When my extension loads it replaces User.authenticate with CrowdAuthenticator.crowd_login.

# crowd_authenticator.rb
require 'crowd'

module CrowdAuthenticator
  def self.included(base)
    base.extend ClassMethods

    base.class_eval do
      class << self
        alias_method :old_authenticate, :authenticate
        alias_method :authenticate, :crowd_login
      end
    end
  end

  module ClassMethods

    def crowd_login(login, password)
      crowd_user = get_crowd_user login, password
      return nil if crowd_user.nil?

      find_or_create_user crowd_user
    end

    def get_crowd_user(login, password)
      # call api
      # check groups
      crowd_user
    end

    def find_or_create_user(crowd_user)
      # create radiant user if necessary
      # update radiant user with info from crowd_user
      # save and return user
    end
  end
end

I also added some new routes to disable the existing pages for creating, and editing users, since these are now managed through Crowd.