OpenPlugin::Session - Save and retrieve session data


OpenPlugin documentation Contained in the OpenPlugin distribution.

Index


Code Index:

NAME

Top

OpenPlugin::Session - Save and retrieve session data

SYNOPSIS

Top

 my $session = {}
 $session->{camel_humps}     = "one";
 $session->{fish_in_the_sea} = "lots";
 $session->{stooges}         = "three";

 my $session_id = $OP->session->save( $session );

 ...

 my $session = $OP->session->fetch( $session_id );
 print "Humps:   $session->{camel_humps}\n";       # Prints "one"
 print "Fish:    $session->{fish_in_the_sea}\n";   # Prints "lots"
 print "Stooges: $session->{stooges}\n";           # Prints "three"

DESCRIPTION

Top

Sessions provide a means to save information across requests, for a given user. Typically, any values created or retrieved for a user are lost after the request. With sessions, one can store that data for later retrieval, as long as the user (or, more specifically, the browser) can later provide the unique key associated with the data.

METHODS

Top

fetch( $session_id )

Given a session_id, retrieve an existing session.

Returns a hashref containing all the session data, or undef if the session has expired.

save( \%session_data, [ { id = $id, $expires => $date } ] )>

Save a session. If a session is already open, the existing session id is used. If a session id does not yet exist, a new one is created.

Returns the ID of the session saved, or undef on failure.

Basic parameters -- drivers may define others:

session_id()

Returns session ID for the current session. If the session is new or not yet saved this will return undef.

create()

Create a new session. It is not necessary to call this function to use sessions -- fetch and save do the same thing. This function can be used when you wish to create a session and get your session id, but aren't ready to save anything yet.

Returns the ID of the session created.

delete( [ $session_id ] )

Delete an existing session. If no parameters are passed, it defaults to deleting the last session opened. You may pass in a session_id to explicitly delete a particular session.

Returns the ID of the session deleted if successful.

BUGS

Top

None known.

TO DO

Top

The interface for this module is not complete. Methods for accessing the sessions creation, modified, and accessed time should be created. Also one for finding when it will expire.

I'm also pondering the creation of an OO interface for this module, much like CGI::Session. Instead of passing a hashref to the save() function, you'd use some sort of param() method to save each piece of data. Maybe.

SEE ALSO

Top

OpenPlugin

Apache::Session

CGI::Session

COPYRIGHT

Top

AUTHORS

Top

Eric Andreychek <eric@openthought.net>


OpenPlugin documentation Contained in the OpenPlugin distribution.

package OpenPlugin::Session;

# $Id: Session.pm,v 1.46 2003/04/28 17:43:49 andreychek Exp $

use strict;
use base                  qw( OpenPlugin::Plugin );

$OpenPlugin::Session::VERSION = sprintf("%d.%02d", q$Revision: 1.46 $ =~ /(\d+)\.(\d+)/);

sub OP   { return $_[0]->{_m}{OP} }
sub type { return 'session' }

# API

# Returns the session id
sub session_id {
    my ( $self ) = @_;
    return $self->state->{ session_id };
}

# This sub is defined in the individial drivers
sub get_session_data { return undef };

# Initiate a session and return a session_id
sub create {
    my ( $self ) = @_;
    my $session_id;

    my $session = $self->get_session_data();

    $session = $self->_init_session_data( $session, {} );

    if ( $self->_validate_session( $session ) ) {
        $session_id = $session->{_session_id};
    }
    else {
        $session_id = undef;
    }

    untie %{ $session };

    return $session_id;
}

# Retrieve all the values stored within a particular session
sub fetch {
    my ( $self, $session_id ) = @_;
    my $session_vals;

    $session_id = $self->_validate_session_id( $session_id );

    # Don't do anything if we weren't passed a valid session_id
    unless ( $session_id ) {
        $self->OP->log->info( "Invalid session_id given, can't fetch session.");
        return undef;
    }

    my $session = $self->get_session_data( $session_id );

    return undef unless defined $session;

    $session = $self->_init_session_data( $session, {} );

    # Verify that this session is legitimate
    if( $self->_validate_session( $session ) ) {

        $session->{_accessed} = time();

        # Set the session values, then untie the session
        foreach my $key ( keys %{ $session } ) {
            $session_vals->{ $key } = $session->{ $key };
        }
        untie %{ $session };
    }
    else {
        $session_vals = undef;
    }

    return $session_vals;

}

# Save data to a session
sub save {
    my ( $self, $data, $params ) = @_;

    # Make sure we were actually sent some values to save..
    unless ( scalar keys %{ $data } ) {
        $self->OP->log->info( "No session information to be saved." );
        return undef;
    }

    unless ( $params->{'id'}) {
        $params->{'id'} = $self->session_id() || $self->create();
    }

    # Validate the session ID if we were passed one (the ID itself, not the
    # data)
    $params->{'id'} = $self->_validate_session_id( $params->{'id'} ) if
                                                            $params->{'id'};

    # Initiate the session
    my $session = $self->get_session_data( $params->{'id'} );
    $session = $self->_init_session_data( $session, $params );

    # Make sure our session is good
    if ( $self->_validate_session( $session ) ) {

        # Set the session values.  Values starting with _ are readonly for
        # everything but this module
        foreach my $key ( keys %{ $data } ) {
            next if $data->{ $key } =~ m/^_/;

            $session->{ $key } = $data->{ $key };
        }
        $session->{_accessed} = time();

        $self->OP->log->debug( "Saving session ($session->{_session_id}).");
        untie %{ $session };
    }

    return $params->{ id };
}

sub delete {
    my ( $self, $session_id ) = @_;

    unless ( $session_id ) {
        $session_id = $self->session_id();
    }
    my $session = $self->get_session_data( $session_id );

    # Untaint the session_id so it can be properly deleted
    $session_id = $session->{_session_id};
    $session->{_session_id} = $self->_validate_session_id( $session_id );

    tied( %{ $session } )->delete if $session->{_session_id};

    return $session_id;
}

# Set up some values for our session
sub _init_session_data {
    my ( $self, $session, $params ) = @_;

    # When, if at all, the session will expire
    $session->{'_expires'} =
                        $params->{ expires } ||
                        $self->OP->config->{'plugin'}{'session'}{'expires'};

    # If _start exists, we've done this already
    return $session if exists $session->{'_start'};

    $self->OP->log->info( "Initiating session data.");

    # Time the session was created
    $session->{'_start'} = time();

    # Time the session was last accessed
    $session->{'_accessed'} = time();

    return $session;
}

# Validate and untaint the session ID given to us
sub _validate_session_id {
    my ( $self, $session_id ) = @_;

    $session_id ||= "";

    # Does our session ID look legitimate?
    # TODO -- this will only work for MD5 session ID's
    # Apache::Session only supports MD5.  But should something that validates
    #  the session ID string be part of the individual driver instead of here?
    #if( $self->OP->config->{plugin}{session}{parameters}{Generate} eq "MD5" ) {
        if ( $session_id =~ m/^([a-fA-F0-9]{32}$)/ ) {
            return $1;
        }
        else {
            $self->OP->log->info( "The session ID ($session_id) is an " .
                                  "invalid MD5 string.");
            return undef;
        }
    #}
    #else {
    #    $self->OP->log->log(1,  "Not using MD5 sessions, unable to validate " .
    #                            "session id ($session_id).  Continuing "      .
    #                            "anyway...");
    #    return $session_id;
    #}
}

# Validate the properties and expiration of our session
sub _validate_session {
    my ( $self, $session ) = @_;

    my $invalid = 0;

    return undef unless $session;

    # Make sure we have these already..
    $invalid = 1 unless (( exists $session->{_start} )          &&
                         ( exists $session->{_accessed} )       &&
                         ( exists $session->{_session_id} )     &&
                         ( exists $session->{_expires} ));

    # An expiration of -1 means it doesn't expire
    unless ( $session->{_expires} =~ /^-1$/ ) {

        $invalid = 1 if (time >
                    OpenPlugin::Utility->expire_calc( $session->{_expires},
                                                      $session->{_accessed} ));

    }

    if ( $invalid ) {
        $self->OP->log->info( "Session ($session->{_session_id}) is invalid." );

        # Untaint the session_id before we expire it
        $session->{_session_id} = $self->_validate_session_id( $session->{_session_id} );

        tied( %{ $session } )->delete if $session->{_session_id};
        return undef;
    }
    else {
        $self->OP->log->debug( "Session ($session->{_session_id}) is valid." );
        $self->state( 'session_id', $session->{_session_id} );
        return 1;
    }
}


1;

__END__