Difference between revisions of "Add a parallel authentication routine"
m |
(→needs re-edited below) |
||
Line 186: | Line 186: | ||
$session->{"request"}, | $session->{"request"}, | ||
"Set-Cookie" => $cookie ); | "Set-Cookie" => $cookie ); | ||
+ | }; | ||
+ | |||
+ | </pre> | ||
+ | |||
+ | ===Displaying the method of logging in=== | ||
+ | It may be beneficial for our users to know how they have logged in. This is done in the dynamic template: <code>archives/''ARCHIVEID''/cfg/cfg.d/dynamic_template.pl</code> | ||
+ | <pre> | ||
+ | $c->{dynamic_template}->{function} = sub | ||
+ | { | ||
+ | my( $session, $parts ) = @_; | ||
+ | |||
+ | my $user = $session->current_user; | ||
+ | if( defined $user ) | ||
+ | { | ||
+ | # new stuff, only used if there is a current user | ||
+ | my $cookie_puid = $session->puid_from_cookie; | ||
+ | my $phrase_id = 'dynamic:logged_in_local'; | ||
+ | if( defined $cookie_puid ) | ||
+ | { | ||
+ | my $user_puid = $user->get_value( "puid" ); | ||
+ | if( $cookie_puid eq $user_puid ) | ||
+ | { | ||
+ | $phrase_id = 'dynamic:logged_in_puid'; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | $phrase_id = 'dynamic:logged_in_local_mismatch'; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # Normal routine | ||
+ | $parts->{login_status} = $session->html_phrase( | ||
+ | "dynamic:logged_in", | ||
+ | user => $session->html_phrase( $phrase_id, user=>$user->render_description ), | ||
+ | tools => render_toolbar($session) ); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | $parts->{login_status} = $session->html_phrase( | ||
+ | "dynamic:not_logged_in" ); | ||
+ | } | ||
}; | }; | ||
Revision as of 07:52, 21 September 2007
This is a real-world example.
Contents
goal
Use an external agency that may define a persistent User ID (a "puid") for someone visiting the repository.
explanation
An eprints user may have an identifying puid associated with their account. If a user arrives with a piud that matches one held in the tables, that user profile is loaded. If not, use the standard eprints login system is used to identify the user.
There are situations where a user may come in with a different puid (visiting a different institution, moving to a different institution, a change at institutional level, etc), and a user should be able to change the puid associated with their account.
Note: puids are opaque, so humans do not recognise the opaque string as meaning anything, and asking a user to type in a puid is prone to typo-errors. However, puids are assigned by an institution, so I made sure I got the name of the institution that authenticated the user along with the puid.
A puid will be in the format of 1234:5678#My+Institution
How To Do It
Prepare the user object
Step 1 is to set up the basic requirement: We need fields in the user object (archives/ARCHIVEID/cfg/cfg.d/user_fields.pl)
{ 'name' => 'puid', 'type' => 'text', },
and the corresponding entries in the workflow and phrases files:
Workflows (archives/ARCHIVEID/cfg/workflows/user/default.xml)
<component type="Field::Multi"> <title><epc:phrase ref="user_section_account" /></title> <epc:if test="usertype != 'minuser'"><field ref="email"/></epc:if> <field ref="hideemail"/> <field ref="password"/> <field ref="puid" /> <!-- NEW ITEM --> </component>
phrases (archives/ARCHIVEID/cfg/lang/en/phrases/user_fields.xml):
<epp:phrase id="user_fieldname_puid">Devolved Authentication</epp:phrase> <epp:phrase id="user_fieldhelp_puid">This option determines which (if any) institution is recognised as providing the correct devolved authentication for this depositor.</epp:phrase>
NOTE If you alter the user_fields.pl file, you need to rebuild the database, which means wiping and restarting (or doing clever stuff with SQL), so make sure you do all this before you start putting data into the repository
Overriding the Login system
In EPrints 3.0.3+ you can create a subroutine for an alternative login procedure. Place the following code in archives/ARCHIVEID/cfg/cfg.d/myMethods.pl
:
###################################################################### =pod if( $self->get_repository->can_call( 'get_current_user' ) ) { $self->{current_user} = $self->get_repository->call( 'get_current_user', $self ); } An optional call that allows one to get the current eprints user-object by some method. If this method is available, it is called B<instead of> the internal eprint methods. =cut ###################################################################### $c->{get_current_user} = sub { my( $session ) = @_; if( !defined $session->{request} ) { # not a cgi script. return undef; } # check for a eprints session cookie my $user = $session->_current_user_auth_cookie; if( defined $user ) { return $user; } # get my puid my $puid = $session->puid_from_cookie; return undef unless defined $puid; # can we get a user? my $user = EPrints::DataObj::User::user_with_puid( $session,$puid ); if( defined $user ) { $session->{via_depot} = 1; } return $user; };
There are two new functions in here: $session->puid_from_cookie;
and EPrints::DataObj::User::user_with_puid
. Both of them are also defined in archives/ARCHIVEID/cfg/cfg.d/myMethods.pl
###################################################################### =pod =item $puid = $session->puid_from_cookie; returns the puid from our own cookie Used in both the dynamic template and C<&get_current_user():> =cut ###################################################################### sub EPrints::Session::puid_from_cookie { my( $self ) = @_; my $cookie = EPrints::Apache::AnApache::cookie( $self->get_request, "myCookie" ); if( !defined $cookie ) { return undef; } my %info = split( '&', $cookie ); # do cookie validation checks here my $puid = $info{user}; $puid=~s/\%([0-9A-F]{2})/eval "chr(0x$1)"/egi; return $puid; } ###################################################################### =pod =item $user = EPrints::DataObj::User::user_with_puid( $session, $puid ) Return the EPrints::user with the specified $puid, or undef if they are not found. =cut ###################################################################### sub EPrints::DataObj::User::user_with_puid { my( $session, $puid ) = @_; my $user_ds = $session->get_repository->get_dataset( "user" ); my $searchexp = new EPrints::Search( session=>$session, dataset=>$user_ds ); $searchexp->add_field( $user_ds->get_field( "puid" ), $puid ); my $searchid = $searchexp->perform_search; my @records = $searchexp->get_records(0,1); $searchexp->dispose(); return $records[0]; }
and finally $session->{via_depot} = 1
just sets a flag in the session.
Tidy up on logout
We need to account for users who are working from public computers: we can't leave our authentication cookie lying around for someone else to accidentally pick up on, so lets provide extra log-out functionality (again, eprints 3.0.3+).
Another function for archives/ARCHIVEID/cfg/cfg.d/myMethods.pl
:
###################################################################### =pod if( $session->get_repository->can_call( 'on_logout' ) ) { $session->get_repository->call( 'on_logout', $session ); } A method that can be called as part of the logout routine. It happens before any of the standard EPrints logout routines are called. =cut ###################################################################### $c->{on_logout} = sub { my( $session ) = @_; ## clear the Repository Junction cookie my $cookie = $session->{query}->cookie( -name => 'depot_authen', -domain => 'edina.ac.uk', -expires => time(), -value => '', ); EPrints::Apache::AnApache::header_out( $session->{"request"}, "Set-Cookie" => $cookie ); };
Displaying the method of logging in
It may be beneficial for our users to know how they have logged in. This is done in the dynamic template: archives/ARCHIVEID/cfg/cfg.d/dynamic_template.pl
$c->{dynamic_template}->{function} = sub { my( $session, $parts ) = @_; my $user = $session->current_user; if( defined $user ) { # new stuff, only used if there is a current user my $cookie_puid = $session->puid_from_cookie; my $phrase_id = 'dynamic:logged_in_local'; if( defined $cookie_puid ) { my $user_puid = $user->get_value( "puid" ); if( $cookie_puid eq $user_puid ) { $phrase_id = 'dynamic:logged_in_puid'; } else { $phrase_id = 'dynamic:logged_in_local_mismatch'; } } # Normal routine $parts->{login_status} = $session->html_phrase( "dynamic:logged_in", user => $session->html_phrase( $phrase_id, user=>$user->render_description ), tools => render_toolbar($session) ); } else { $parts->{login_status} = $session->html_phrase( "dynamic:not_logged_in" ); } };
needs re-edited below
Field rendering Problem
However, this means that we get a text input item, not what we want (see the comment above about users typing in opaque strings)
Changing the way the field is rendered
In Step 2 we alter the way the object deals with the 'puid' field. the user field is changed and we define alternative methods for rendering the field (as a fragment of xhtml) and for changing the value:
{ 'name' => 'puid', 'type' => 'text', 'render_single_value' => \&render_puid, 'render_input' => \&select_puid, },
In archives/ARCHIVEID/cfg/cfg.d/user_fields.pl
we also add the two methods:
sub render_puid { my ( $session, $field, $value ) = @_; if(!EPrints::Utils::is_set($value)) { return $session->make_text("You have no automatic authentication"); } # split the value into two parts, on the first # (there may be more than one..) my ( $rawpuid, $inst) = split /#/, $value, 2; if( !EPrints::Utils::is_set($inst) ) { return $session->make_text( "You have an institution defined, but it is un-named"); } $inst =~ s/\+/ /g; # we should have an institutionl name set, but just in case... return $session->make_text( "Your defined authenticating institution is $inst") } sub select_puid { my ( $field, $session, $puid, $dataset, $staff, $hidden_fields, $user, $basename ) = @_; my @order = ('none'); my $desc = { none => 'No PUID stuff' }; my $values = { 'none' => '' }; my $default = 'none'; if( EPrints::Utils::is_set( $value ) ) { push @order, 'old'; $values->{old} = $value; my ($uid, $org) = split /\#/, $value; $org =~ s/\+/ /g; $desc->{old} = "Current value: $org"; $default = 'old'; } my $cookie_puid = $session->puid_from_cookie; if( defined $cookie_puid && $cookie_puid ne $value ) { push @order, 'new'; $values->{new} = $cookie_puid; my ($uid, $org) = split /\#/, $cookie_puid; $org =~ s/\+/ /g; $desc->{new} = "New value: $org"; } my $div = $session->make_element( "div" ); foreach my $bit ( @order ) { my $div2 = $session->make_element( "div" ); my $label = $session->make_element( "label" ); $div->appendChild( $div2 ); $div2->appendChild( $label ); my %iopts = ( type=>'radio', name=>$basename, value=>$values->{$bit} ); if( $default eq $bit ) { $iopts{checked} = 'checked'; } $label->appendChild( $session->make_element( "input", %iopts ) ); $label->appendChild( $session->make_text( " ".$desc->{$bit} ) ); } return $div; }