Difference between revisions of "Add a parallel authentication routine"
m |
m |
||
Line 47: | Line 47: | ||
'''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 | '''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 | ||
− | ==== | + | ====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) | However, this means that we get a text input item, not what we want (see the comment above about users typing in opaque strings) | ||
Line 142: | Line 142: | ||
</pre> | </pre> | ||
+ | ====Problem: needs data set at the beginning of each session==== | ||
This works well, however there is a small snag - how are we setting <code>$c->notes->get('this_puid');</code>? | This works well, however there is a small snag - how are we setting <code>$c->notes->get('this_puid');</code>? | ||
− | ===Step 3: Altering | + | ===Step 3: Altering Session.pm=== |
Well, this is all done in <code>perl_lib/EPrints/Session.pm</code>, specifically in <code>_current_user_auth_cookie</code>: | Well, this is all done in <code>perl_lib/EPrints/Session.pm</code>, specifically in <code>_current_user_auth_cookie</code>: | ||
<pre> | <pre> | ||
Line 221: | Line 222: | ||
</pre> | </pre> | ||
− | ==== | + | ====So how do I save ''two'' fields at once?==== |
There is one small problem, although we can change the puid held in the user object, we're not updating the nice piece of text held in <code>puid_inst</code> | There is one small problem, although we can change the puid held in the user object, we're not updating the nice piece of text held in <code>puid_inst</code> | ||
Revision as of 16:04, 22 March 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.
How To Do It
Step 1: The very basics
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', }, { 'name' => 'puid_inst', '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
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)
Step 2: 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 ) = @_; my $inst; if ($value) { $inst = $session->{current_user}->get_value('puid_inst'); return $session->make_text( "Your defined authenticating institution is <strong>$inst</strong>") if ($inst); return $session->make_text( "You have an inatitution defined, but it is un-named"); } else { return $session->make_text("You have no automatic authentication"); } } sub select_puid { my ( $field, $session, $puid, $dataset, $staff, $hidden_fields, $user, $basename ) = @_; my $c = $session->{request}->connection; my $inst = $user->get_value('puid_inst'); my $this_puid = $c->notes->get('this_puid'); my $this_inst = $c->notes->get($this_puid); unless ($puid) { if ($this_puid) { my $fragment = $session->make_doc_fragment; $fragment->appendChild( $session->make_text( "You may change your authenticating institution:") ); my $element = $session->make_element( "select", name => $field->get_name ); $element->appendChild( $session->render_single_option( "", "None", 1 ) ); $element->appendChild( $session->render_single_option( $this_puid, $this_inst, 0 ) ); $fragment->appendChild($element); return $fragment; } ## end of $this_puid else { return ( $session->make_doc_fragment("You have nothing defined") ); } } else { if ( $this_puid && ( $this_puid ne $puid ) ) { my $fragment = $session->make_doc_fragment; $fragment->appendChild( $session->make_text( "You may change your authenticating institution:") ); my $element = $session->make_element( "select", name => "id3_".$field->get_name ); $element->appendChild( $session->render_single_option( "", "None", 0 ) ); $element->appendChild( $session->render_single_option( "$puid", $user->get_value('puid_inst'), 1 ) ); $element->appendChild( $session->render_single_option( $this_puid, $this_inst, 0 ) ); $fragment->appendChild($element); return $fragment; } else { return render_puid( $session, $field, $puid ); } ## end of $puid eq $this_puid } ## end of unless ($puid) {} else... }
Problem: needs data set at the beginning of each session
This works well, however there is a small snag - how are we setting $c->notes->get('this_puid');
?
Step 3: Altering Session.pm
Well, this is all done in perl_lib/EPrints/Session.pm
, specifically in _current_user_auth_cookie
:
sub _current_user_auth_cookie { my( $self ) = @_; if( !defined $self->{request} ) { # not a cgi script. return undef; } # Hack to ensure that the Repository Junction details are set before # the local EP3 cookie stuff takes over my %cookie; my ($puid, $orgname); # we won't have the cookie for the page after login. my $c = $self->{request}->connection; unless ( $c->notes->get( "this_puid" ) ) { warn "Not got RJunction cookie"; my $rj_cookie = EPrints::Apache::AnApache::cookie( $self->get_request, "depot_authen" ); if( defined $rj_cookie ) { %cookie = split /&/, $rj_cookie; ## Do clever stuff to extract $puid and $orgname from the cookie ## $c->notes->set( "this_puid", $puid ); $c->notes->set( $puid, $orgname ); } ## end of cookie defined } ## end of "if note set" ## end of first bit of hack, back to normal, but the coding is slightly altered my $userid = $c->notes->get( "userid" ); $c->notes->set( "userid", 'undef' ); my $user; # Check for a userid set in the session notes area if( EPrints::Utils::is_set( $userid ) && $userid ne 'undef' ) { $user = EPrints::DataObj::User->new( $self, $userid ); } # if not, check for an EPrints user session cookie unless ( $user ) { my $cookie = EPrints::Apache::AnApache::cookie( $self->get_request, "eprints_session" ); if ( $cookie ) { my $remote_addr = $c->get_remote_host; $userid = $self->{database}->get_ticket_userid( $cookie, $remote_addr ); $user = EPrints::DataObj::User->new( $self, $userid ) if EPrints::Utils::is_set( $userid ); } } # If still not, can we authenticate with a puid? unless ( $user ) { if ( $c->notes->get( "this_puid" ) ) { $user = EPrints::DataObj::User::user_with_puid( $self, $c->notes->get( "this_puid" ) ); } } # if we get a user, by hook or by crook, set some details in the notes section if ( $user ) { if ( $user->get_value('puid') ) { $c->notes->set( 'puid' , $user->get_value('puid' ) ); $c->notes->set( $user->get_value('puid') , $user->get_value('puid_inst') ); } return $user; } # if we really cannot find a user, return undef (no user found) return undef; }
So how do I save two fields at once?
There is one small problem, although we can change the puid held in the user object, we're not updating the nice piece of text held in puid_inst
Step 4: Altering the way the submitted data is handled
Step 4 is to save the name of the authenticting institute as well as the puid, so that we can display the appropriate name.
We need another subroutine in archives/ARCHIVEID/cfg/cfg.d/user_fields.pl:
{ 'name' => 'puid', 'type' => 'text', 'render_single_value' => \&render_puid, 'render_input' => \&select_puid, ''' 'fromform' => \&fromform_puid,''' },
and the corresponding method:
sub fromform_puid { my ( $value, $session ) = @_; # get the user object from the session my $c = $session->{request}->connection; my $inst = $c->notes->get($value); my $user = $session->current_user; # save the text associated with the puid in the sesion notes $user->set_value('puid_inst', $inst ); $user->commit; # return the value, unchanged return $value; }
There is a problem
It's not working :sad: