Authen-Ticket

 view release on metacpan or  search on metacpan

lib/Authen/Ticket.pm  view on Meta::CPAN

# $Id: Ticket.pm,v 1.8 1999/11/18 21:22:32 jgsmith Exp $
#
# Copyright (c) 1999, Texas A&M University
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. Neither the name of the University nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTERS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

package Authen::Ticket;

use Apache ();
use Apache::Constants (qw/OK DECLINED FORBIDDEN/);
use Apache::URI ();
use CGI::Cookie ();

use vars (qw#$VERSION @ISA#);

$VERSION = '0.02';
@ISA = ( );

sub handler ($$) {
  my $class = shift;
  my $r = shift;
  my $log = $r->log;

  Apache->request($r);  # set it to make sure it is set...

  $log->debug("Callback: " . $r->current_callback);

  if($r->current_callback eq "PerlHandler" ||
     $r->current_callback eq "PerlFixupHandler") {
    my $cclass = "$class\:\:Client";
    $class .= "::Server";
    my $self = $class->new($r);

    my $ticketinfo;

    unless($self->{request_uri}) {
      $self->no_cookie_error;
      return OK;
    }

    my $userinfo = $self->get_userinfo;

    unless($$userinfo{user} || $$userinfo{security}) {
      my $client = $cclass->new($r);
      if($client->{ticket}) {
        $userinfo->{security} = 'strong';
        if($client->{ticket}->{ip}) {
          my(@bip) = split('.', $client->{ticket}->{ip});
 
          if(($bip[0] < 128 && !($bip[1] || $bip[2] || $bip[3]))
           ||($bip[0] < 192 && !(           $bip[2] || $bip[3]))
           ||($bip[0] < 224 && !(                      $bip[3])))
            { $userinfo->{security} = 'med'; }
        } else {
          $userinfo->{security} = 'weak';
        }
        $userinfo->{user} = $client->{ticket}->{uid};
      }
    }

    unless($$userinfo{user} && $$userinfo{password}) {
      $self->no_user_password_error;
      return OK;
    }

    unless($$userinfo{duration} > 0) {
      $self->no_user_password_error(
          "Duration must be a number greater than zero."
      );
      return OK;
    }

    unless($ticketinfo = $self->authenticate($userinfo)) {
      $self->no_user_password_error(
          "Either the username or password are incorrect."
      );
      return OK;
    }

    $ticketinfo->{fields} = join(',',keys %$ticketinfo);
    $ticketinfo->{uid}  = $$userinfo{user};

    if($userinfo->{security} ne 'weak') {
      my $ip = $self->connection->remote_ip;
      my(@rip) = ($ip =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/);
      if($userinfo->{security} eq 'med') {
        if($rip[0] < 128)    { $rip[1] = $rip[2] = $rip[3] = 0; }
        elsif($rip[0] < 192) {           $rip[2] = $rip[3] = 0; }
        elsif($rip[0] < 224) {                     $rip[3] = 0; }
      }
      $$ticketinfo{ip} = join('.', @rip);
    }

    my $cookiev = $self->encode_cookie(
                    $self->construct_cookie(%$ticketinfo,
                             'expiry' => time + $$userinfo{duration} * 60) );

    #
    # sign ticket if signing is available...
    #
    my $sc = eval { $self->sign_ticket($cookiev); };
    if($@) {
      $self->debug("Eval results: [$@]");
    } else {
      $cookiev = $sc;
    }
    
    $self->go_to_url(CGI::Cookie->new(-name => $$self{TicketName},
                                      -value => $cookiev,
                                      -domain => $$self{TicketDomain},
                                      -path => '/'
                                     ));
    return OK;
  } elsif($r->current_callback eq "PerlAccessHandler") {
    $class .= "::Client";

    return OK unless $r->is_main;

    my $checkedout = 1;

    # we want to be able to include the ticket as part of the URL if needed
    # this needs to work even with a POST...

    my $self = $class->new($r);
 
    unless($self->{ticket} && $self->{ticket}->{expiry} > time) {
      # bad ticket... need another
      $checkedout = 0;
      $log->debug("No ticket or ticket expired...");
    }

    if($self->{ticket} && $self->{ticket}->{ip}) {
      my(@bip) = split('.', $self->{ticket}->{ip});
      my(@rip) = split('.', $r->connection->remote_ip);
  
      if(   $bip[0] < 128 && !($bip[1] || $bip[2] || $bip[3]))
        { $rip[1] = $rip[2] = $rip[3] = 0; }
      elsif($bip[0] < 192 && !(           $bip[2] || $bip[3]))
        {           $rip[2] = $rip[3] = 0; }
      elsif($bip[0] < 224 && !(                      $bip[3]))
        {                     $rip[3] = 0; }

      for(my $i = 0; $i < 4; $i++) {
        if($bip[$i] != $rip[$i]) {
          # bad ticket... need another
          $checkedout = 0;
          $log->debug("IP addresses don't match");
          last;
        }
      }
    }

    unless($checkedout) {
      $log->debug("Ticket didn't check out");
      my $uri = Apache::URI->parse($r, $r->uri);
      $uri->scheme('http');
      $uri->hostname($r->get_server_name);
  
      $uri->port($r->get_server_port);
      $uri->query(scalar $r->args);

      $log->debug("Ticket `request_uri' being set to `" .
                  $uri->unparse . "'");

      # read in content if it exists...  even for a GET
  
      $self->err_headers_out->add('Set-Cookie' =>
        CGI::Cookie->new(-name => 'request_uri',
                         -value => $uri->unparse,
                         -domain => $self->{TicketDomain},
                         -path => '/'
                        )
        );
      return FORBIDDEN;
    }

    $r->connection->user($self->{ticket}->{uid});
    return OK;
  } else {
    return DECLINED;
  }
}

1;
=pod

=head1 NAME

Authen::Ticket - Perl extension for implementing ticket authentication

=head1 SYNOPSIS

  PerlHandler Authen::Ticket

or

  PerlAccessHandler Authen::Ticket
  ErrorDocument     403  http://ticket.tamu.edu/TicketMaster/

=head1 DESCRIPTION

Authen::Ticket provides the mod_perl framework for using the
Authen::Ticket::Server and Authen::Ticket::Client classes as
Apache handlers.

To create custom handlers, derive a class (My::Authen) from
Authen::Ticket:

  package My::Authen;
  use vars (qw/@ISA/);
  @ISA = (qw/Authen::Ticket/);

In addition to My::Authen, the server and client classes are also
required:

  package My::Authen::Server;
  use vars (qw/@ISA/);
  @ISA = (qw/Authen::Ticket::Server/);

  sub authenticate {
    my($self, $u) = @_;
    my $t = { };

    # do stuff
    return $t;   # hash ref to ticket contents
  }

  package My::Authen::Client;



( run in 0.491 second using v1.01-cache-2.11-cpan-39bf76dae61 )