GPMSWiki/DeveloperDocumentation/MigrationHelp/SessionManagementChanges: Difference between revisions

From BRF-Software
Jump to navigation Jump to search
imported>MichaelDondrup
No edit summary
m (13 revisions)
 
(6 intermediate revisions by 2 users not shown)
Line 4: Line 4:
The new Sessionmanagement module is completely new and build from scratch. It supports hierarchical sessions,
The new Sessionmanagement module is completely new and build from scratch. It supports hierarchical sessions,
and also anonymous sessions.
and also anonymous sessions.
Complete redesign implies vast changes throughout the API. So expect some work.
Complete redesign implies vast changes throughout the API. Most functions do no longer exist or have changes.
So this thing is still a real pain in the ass.


== Classes ==
== Classes ==
Line 75: Line 76:
  once you have and authenticated Session::GPMS::<[[YourApp]]> the session can be resumed using <code><nowiki>Session::GPMS::<[[YourApp]]>->loadSingleton($masterSession);</nowiki></code> if it is a singleton session.
  once you have and authenticated Session::GPMS::<[[YourApp]]> the session can be resumed using <code><nowiki>Session::GPMS::<[[YourApp]]>->loadSingleton($masterSession);</nowiki></code> if it is a singleton session.


Here is a lengthy stub for a login script:
Here is a lengthy stub for a login script (taken from GenDB login script). The code fragment is encapsulated in a function, so ''return'' is used several times to exit the function.
 


<pre><nowiki>
<pre><nowiki>
sub login { 
 
     my $cgi = CGI->new();
     # resume master session and print HTTP header
    # get your parameters, or however they are named in your login page
     my $master_session;
    # ICICIC: to your needs
    my $login = $cgi->param('login');
    my $pass = $cgi->param('pass');
    my $changeproject = $cgi->param('changeproject'); # did we receive a change request
    my $projectname =  $cgi->param('project');  
 
    my $masterSession = undef; # this is the Master session
     my $myAppSession = undef; # this is the application specific session
     eval {
     eval {
# can we resume a session?
        $master_session = Session::Master->new();
$session = new Session::Master;
        print $master_session->header();
print $session->header();
     };
     };
    # errors are now raised by die-ing
     if (ref ($@) && $@->isa("Session::NoActiveSessionException")) {
    # if not:
        # we do not have an active session
     if (UNIVERSAL::isa($@, 'Session::NoActiveSessionException')) {
        # check whether this is the cookie-test invocation
    # First time login, nothing is there
        if ($q->param('cookie_test')) {
### Check for cookies?
if ($cgi->param('cookietest')) {
    #### TODO: ERROR: Your browser doesnt accept cookies


} else {
            # show error about not accepting cookies ....
    $session = new Session::Master (1); # initialize real-new session
           
    print $session->header(); # got a cookie with that
            return;
    my $url = $cgi->url().'?cookietest=1';
        }
    # ICICIC: other parameters must beadded here, probably
        else {
    print $cgi->start_html(-head=>$cgi->meta({-http_equiv=>'refresh',
            # first invocation, let's check whether the user accepts cookies
      -content=>"2;$url"}));
            $master_session = Session::Master->new(1);
    # print checking for cookies for the user
            my $url = $q->self_url()."?cookie_test=1";
    print $cgi->p("Checking cookies, please wait...");
            print $master_session->header();
    print $cgi->end_html();
            print $q->start_html(-head=>$q->meta({-http_equiv=>'refresh',
    return;
                                                  -content => "2;$url"}));
}
    } elsif ($@) {
### something else went wrong, error handling
      ## TODO: confess ...
    }
    # if somebody wants to change the project, throw some
    # data away:
   
    if ( $cgi->param('changeproject') ) {
$session->deleteParam('project_name');
$session->getApplicationFrame->release_project;


            print "Checking for cookies, you will be redirected to ".$q->a({-href=>$url},"this page.");
            print $q->end_html();
            return;
        }
    }
    elsif (ref($@)) {
        # some other error, show some error message to the user
        return;
     }
     }


### we got a Master session now, are we authenticated? 
     unless ($master_session->isAuthenticated()) {
     unless ($session->isAuthenticated) {
        unless ($q->param('login')) {


### we must retrieve uname pw from cgiparam:
            # show the login form
        # it's done at the top already
        # we must display the form first, if there is nothing!
unless ($login && $pass) { # also prevents passwordless accounts!
    ### TODO: show your login page
    return;
}
### get an Application specific session:
eval {
    $myAppSession = Session::GPMS::<YourAPP>-> # ICICIC:
create_with_gpms($masterSession, $login, $pass);
};
if ($@) {
# something's wrong, $@ tells what
        # TODO: output error $@, most likely wrong credentials
return;
}
### I'm authenticated now, show project selection page
        ### ICICIC: if your page does not support it
my @projects =  @{$myAppSession
    -> getApplicationFrame ->
    get_available_projects_by_project_class()};
# must also go here, if a project change is requested
if (@projects) {
   
    my @prjnames = map {{name=>$_->name}} @projects;
    @prjnames = sort { lc($a->{name}) cmp lc($b->{name}) } @prjnames;
  ### TODO: show the list


            return;
        }
        else {
            # try to authenticate the user and show the
            # project selection list
            eval {
                # this only creates a GPMS sub session, not a GenDB sub session
                # otherwise you would end up with a dangling GenDB sub session if the
                # user closes the window after authetication
                $master_session->addGPMSSubSession($q->param('login'), $q->param('pass'));
            };
            if ($@) {


} else {
                # failed to authenticate, show an error message to the use
    ### TODO: ERROR no projects found
               
}
                return;
            }
        }
    }


    # try to load an exiting GenDB session. This will return undef if no
    # GenDB sub session exists yet
    my $session = Session::GENDB->loadSingleton($master_session);


     } else {
     if ($q->param('project')) {
unless ($projectname) {
        if (ref($session)) {
    ### I'm authenticated now, show project selection page
            # we have a request to set a project and an active GenDB session
    ### Got no project name, so show the project selection page
            # there may be another GenDB window open, so we cannot change to
    return;
            # a different project
}
            # Session::GPMS::Application stored the selected project in the
            # session parameter 'project_name'
            if ($session->param('project_name')) {
                if ($session->param('project_name') ne $q->param('project')) {
                   
                    # print some error message about the concurrent project


# got project information from the CGI->param
                    return;
$myAppSession = Session::GPMS::<MyApp>->loadSingleton($masterSession); #ICICIC:
                }
         # can we resume the session?
            }
unless (defined $myAppSession) {
         }
             # NO! this time no exception, but undef returned (sic!)
        else {
    $myAppSession = Session::GPMS::<MyApp>->create($masterSession) #ICICIC:
             # no session yet, create a new one
}
            $session = Session::GENDB->create($master_session);
         # lookup the proect:
        }
my $project = $masterSession # only the GPMS-Session knows projects, a gpms session is
       
            -> getGPMSSubSession     # silently hidden in the masterSession
         # set the requested project
    -> getApplicationFrame
        my $project = $master_session->getGPMSSubSession()->
             -> gpms_master->Project
             getApplicationFrame()->
    -> init_arg($projectname);
            gpms_master()->
unless ($project) {
            Project->init_name($q->param('project'));
    # there is no such project or so
        $session->setProject($project);
    # ICICIC: ERROR: no such project
   
} else {
            # finally:
    $myAppSession->setProject($project);
}
     }
     }
 
 
    if (ref($session) && $session->param('project_name')) {
       
        # session is initialized and project is set, show the main window
        # of your application
    }
    else {
        # no project selected yet, display project selection
      
      
     print more page output .... # ICICIC:
     }
   
 
}
</nowiki></pre>
 
 
== API ==


The API has been rewritten to be consistent and easy to use.


=== Session parameter ===


Session parameters are handled by a number of functions:


</nowiki></pre>
* '''param(name, [value])''' gets or sets a session parameter
* '''hasParam(name)''' returns true if a session parameter with the given name exists
* '''deleteParam(name)''' removed the session parameter
* '''getParams()''' returns a list of all session parameter names


Certain functions of the old API are not supported anymore; they can be easily substituted by short chunks of code. Permanent session parameters are also not implemented in the new session management due to its limited use and added maintance overhead.


== API ==
=== Other discontinued methods ===


The API is completely different from the old one, especially when it come to initialization of sessions. The only function that is unchanged is '''param''', fortunately ;-) It can be used to get and set session parameters.  
These methods were specific for GPMS based sessions in the former session management; they counterparts (if existing) are implemented for Session::GPMS::Application sub classes only.


* The function '''params''' delivered all parameter values as key value pairs. This function does no longer exist. To replace it:
* '''query''' use CGI->new() to get a CGI object. The CGI module stores an internal copy of an initialized object during page processing, so calling new several times has only very little to no overhead
** get all session parameters by using function ''getParams'' to get all parameter names
* '''master''' used to return the /!\ application frame /!\ in the previous session management and was removed for the obvious reason. If you are using a sub class of Session::GPMS::Application, use ''getApplicationFrame()'' to get the application frame of the session and use the ''application_master()'' and ''gpms_master()'' methods to get the master objects.
** use ''param'' on each name and store the value
* '''login_name''','''password''' etc. Also use the application frame to get these values. Keep in mind that the new sessionmanagement uses role accounts. You have to use the ''real_login()'' method of the application frame to get the name of the user. Passwords are only stored as hashes. Clear text passwords '''ARE NOT AVAILABLE ANYMORE'''.
* Deleted: '''param_permanent''' any more, because you don't want it :-| If you do, blame Burkhard and suffer
* Deleted: '''query''' used to return the CGI object of the session management, should be re-implemented in Extension class
* Deleted: '''master''' returned the gpms master of the session, was removed
* the function '''delete_param''' removes the param value, which was before done by setting the param to undef
*

Latest revision as of 07:18, 26 October 2011

Changes in the Session Management

The new Sessionmanagement module is completely new and build from scratch. It supports hierarchical sessions, and also anonymous sessions. Complete redesign implies vast changes throughout the API. Most functions do no longer exist or have changes. So this thing is still a real pain in the ass.

Classes

You can have multiple sessions of different classes. An application specific subclass of Session::GPMS is mandatory. It has to be placed in the application subdirectory share/perl/Session/GPMS.

Here is a simple example:


package Session::GPMS::EMMAII;

=head1 NAME

Session::GPMS::EMMA2

=head1 DESCRIPTION

Sample implementation of a Session::GPMS::Application class, using GPMS::Application_Frame::Sample

=cut

use strict;
use warnings;
use GPMS::Application_Frame::EMMAII;

use base qw(Session::GPMS::Application);


1;

### Begin Class Methods ###

sub AppFrameClass {
    # we use GPMS::Application_Frame::EMMAII
    return "GPMS::Application_Frame::EMMAII";
}

sub NeedSingleton {
    # we cannot have multiple instances of an EMMA apllications at the moment
    # this could change in the future....
    return 1;
}

### End Class Methods ###

__END__


Initialization of Sessions

With the new session management there are normally al least two session:

  • Session::Master -- the root session from which others can be derived
  • Session::GPMS::<YourApp> -- an application-specific session, sublclass of Session::GPMS as given above

The session management supports multiple sessions of the same type, if your application does not: use singleton session. currently most of our apps only support a single instance.

To retrieve valid sessions for your application the following steps have to be performed:

  1. No Session at all -> check if browser accepts cookies
  2. Check if you can resume Session::Master, if not: new Session::Master. The master session already sets the cookie.
  3. Check if you are having an authenticated session, that is have already given login/passw
  4. If not: get credentials and
  5. authenticate, getting a Session::GPMS::<YourApp>
  6. Set the project for this session
once you have and authenticated Session::GPMS::<YourApp> the session can be resumed using Session::GPMS::<[[YourApp]]>->loadSingleton($masterSession); if it is a singleton session.

Here is a lengthy stub for a login script (taken from GenDB login script). The code fragment is encapsulated in a function, so return is used several times to exit the function.



    # resume master session and print HTTP header
    my $master_session;
    eval {
        $master_session = Session::Master->new();
        print $master_session->header();
    };
    if (ref ($@) && $@->isa("Session::NoActiveSessionException")) {
        # we do not have an active session
        # check whether this is the cookie-test invocation
        if ($q->param('cookie_test')) {

            # show error about not accepting cookies ....
            
            return;
        }
        else {
            # first invocation, let's check whether the user accepts cookies
            $master_session = Session::Master->new(1);
            my $url = $q->self_url()."?cookie_test=1";
            print $master_session->header();
            print $q->start_html(-head=>$q->meta({-http_equiv=>'refresh',
                                                  -content => "2;$url"}));

            print "Checking for cookies, you will be redirected to ".$q->a({-href=>$url},"this page.");
            print $q->end_html();
            return;
        }
    }
    elsif (ref($@)) {
        # some other error, show some error message to the user
        return;
    }

    unless ($master_session->isAuthenticated()) {
        unless ($q->param('login')) {

            # show the login form

            return;
        }
        else {
            # try to authenticate the user and show the
            # project selection list
            eval {
                # this only creates a GPMS sub session, not a GenDB sub session
                # otherwise you would end up with a dangling GenDB sub session if the
                # user closes the window after authetication
                $master_session->addGPMSSubSession($q->param('login'), $q->param('pass'));
            };
            if ($@) {

                # failed to authenticate, show an error message to the use
                
                return;
            }
        }
    }

    # try to load an exiting GenDB session. This will return undef if no
    # GenDB sub session exists yet
    my $session = Session::GENDB->loadSingleton($master_session);

    if ($q->param('project')) {
        if (ref($session)) {
            # we have a request to set a project and an active GenDB session
            # there may be another GenDB window open, so we cannot change to
            # a different project
            # Session::GPMS::Application stored the selected project in the
            # session parameter 'project_name'
            if ($session->param('project_name')) {
                if ($session->param('project_name') ne $q->param('project')) {
                     
                    # print some error message about the concurrent project

                    return;
                }
            }
        }
        else {
            # no session yet, create a new one
            $session = Session::GENDB->create($master_session);
        }
        
        # set the requested project
        my $project = $master_session->getGPMSSubSession()->
            getApplicationFrame()->
            gpms_master()->
            Project->init_name($q->param('project'));
        $session->setProject($project);
    }

    if (ref($session) && $session->param('project_name')) {
        
        # session is initialized and project is set, show the main window
        # of your application
    } 
    else {
 
        # no project selected yet, display project selection
    
    } 


API

The API has been rewritten to be consistent and easy to use.

Session parameter

Session parameters are handled by a number of functions:

  • param(name, [value]) gets or sets a session parameter
  • hasParam(name) returns true if a session parameter with the given name exists
  • deleteParam(name) removed the session parameter
  • getParams() returns a list of all session parameter names

Certain functions of the old API are not supported anymore; they can be easily substituted by short chunks of code. Permanent session parameters are also not implemented in the new session management due to its limited use and added maintance overhead.

Other discontinued methods

These methods were specific for GPMS based sessions in the former session management; they counterparts (if existing) are implemented for Session::GPMS::Application sub classes only.

  • query use CGI->new() to get a CGI object. The CGI module stores an internal copy of an initialized object during page processing, so calling new several times has only very little to no overhead
  • master used to return the /!\ application frame /!\ in the previous session management and was removed for the obvious reason. If you are using a sub class of Session::GPMS::Application, use getApplicationFrame() to get the application frame of the session and use the application_master() and gpms_master() methods to get the master objects.
  • login_name,password etc. Also use the application frame to get these values. Keep in mind that the new sessionmanagement uses role accounts. You have to use the real_login() method of the application frame to get the name of the user. Passwords are only stored as hashes. Clear text passwords ARE NOT AVAILABLE ANYMORE.