module Picnic::Authentication::Cas

Picnic::Authentication::Cas provides basic CAS (Central Authentication System) authentication for your Camping app.

To learn more about CAS, see rubycas-client.googlecode.com and www.ja-sig.org/products/cas.

The module defines a service method that intercepts every request to check for CAS authentication. If the user has already been authenticated, the request proceeds as normal and the authenticated user's username is made available under <tt>@state. Otherwise the request is redirected to your CAS server for authentication.

Getting Started

To activate CAS authentication for your application:

  1. Picnic-fy your Camping app (e.g: Camping.goes :your_app; YourApp.picnic!)

  2. Call YourApp.authenticate_using :cas.

  3. In your app's configuration YAML file add something like this:

    authentication:
       cas_base_url: https://login.example.com/cas

    Where the value for </tt>cas_base_url</tt> is the URL of your CAS server.

  4. That's it. Now whenever a user tries to access any of your controller's actions, the request will be checked for CAS authentication. If the user is authenticated, their username is availabe in @state. Note that there is currently no way to apply CAS authentication only to certain controllers or actions. When enabled, CAS authentication applies to your entire application, except for items placed in the /public subdirectory (CSS files, JavaScripts, images, etc.). The public directory does not require CAS authentication, so anyone can access its contents.

Public Class Methods

append_features(mod) click to toggle source

For some reason the Module#included callback is just not working for me, so I had to resort to overriding ::append_features(). If anyone has any ideas why, please let me know!

Calls superclass method
# File lib/picnic/authentication.rb, line 152
def self.append_features(mod)
  super
  
  require 'camping/db'
  require 'camping/session'
  
  $: << File.dirname(File.expand_path(__FILE__))+"/../../../rubycas-client2/lib" # for development
  require 'rubycas-client'
end
included(mod) click to toggle source
# File lib/picnic/authentication.rb, line 191
def self.included(mod)
  mod.module_eval do
    include Cas::Session
  end
end

Public Instance Methods

service(*a) click to toggle source
# File lib/picnic/authentication.rb, line 197
def service(*a)
  $LOG.debug "Running CAS filter for request #{a.inspect}..."
  
  if @env['PATH_INFO'] =~ /^\/public\/.*/
    $LOG.debug "Access to items in /public subdirectory does not require CAS authentication."
    return super(*a)
  end
  if @state[:cas_username]
    $LOG.debug "Local CAS session exists for user #{@state[:cas_username]}."
    return super(*a)
  end
          
  client = CASClient::Client.new($CONF[:authentication].merge(:logger => $LOG))
  
  ticket = @input[:ticket]
  
  cas_login_url = client.add_service_to_login_url(read_service_url(@env))
  
  if ticket
    if ticket =~ /^PT-/
      st = CASClient::ProxyTicket.new(ticket, read_service_url(@env), @input[:renew])
    else
      st = CASClient::ServiceTicket.new(ticket, read_service_url(@env), @input[:renew])
    end
    
    $LOG.debug "Got CAS ticket: #{st.inspect}"
    
    client.validate_service_ticket(st)
    if st.is_valid?
      $LOG.info "CAS ticket #{st.ticket.inspect} is valid. Opening local CAS session for user #{st.response.user.inspect}."
      @state[:cas_username] = st.response.user
      return super(*a)
    else
      $LOG.warn "CAS ticket #{st.ticket.inspect} is INVALID. Redirecting back to CAS server at #{cas_login_url.inspect} for authentication."
      @state[:cas_username] = nil
      redirect cas_login_url
      s = self
    end
  else
    $LOG.info "User is unauthenticated and no CAS ticket found. Redirecting to CAS server at #{cas_login_url.inspect} for authentication."
    @state[:cas_username] = nil
    redirect cas_login_url
    s = self
  end
  s
end

Private Instance Methods

read_service_url(env) click to toggle source
# File lib/picnic/authentication.rb, line 245
def read_service_url(env)
  if $CONF[:authentication][:service_url] 
    $CONF[:authentication][:service_url]
  else
    env['REQUEST_URI'].gsub(/service=[^&]*[&]?/,'').gsub(/ticket=[^&]*[&]?/,'')
  end
end