##@file # OpenID authentication backend file ##@class # OpenID authentication backend class. # The form must return a openid_identifier field package Lemonldap::NG::Portal::AuthOpenID; use strict; use Lemonldap::NG::Portal::Simple; use LWP::UserAgent; require Cache::FileCache; our $VERSION = '0.1'; ## @apmethod int authInit() # @return Lemonldap::NG::Portal constant sub authInit { my $self = shift; eval { require Net::OpenID::Consumer }; $self->abort( 'Unable to load Net::OpenID::Consumer', $@ ) if ($@); $self->{openIdSecret} ||= $self->{cipher}->encrypt(0); PE_OK; } ## @apmethod int extractFormInfo() # Read username return by OpenID authentication system. # @return Lemonldap::NG::Portal constant sub extractFormInfo { my $self = shift; my $ua = LWP::UserAgent->new(); # TODO : LWP options to use a proxy for example $self->{csr} = Net::OpenID::Consumer->new( ua => $ua, cache => $self->{refLocalStorage} || Cache::FileCache->new, args => $self, consumer_secret => $self->{openIdSecret}, required_root => $self->{portal}, ); my ( $url, $openid ); # 1. If no openid element has been detected $openid = $self->param('openid'); return PE_FIRSTACCESS unless ( $url = $self->param('openid_identifier') or $openid ); # 2. Check OpenID responses if ($openid) { my $csr = $self->{csr}; # Remote error unless ( $csr->is_server_response() ) { $self->{msg} = 'No OpenID valid message found' . $csr->err(); $self->lmLog( $self->{msg}, 'debug' ); return PE_BADCREDENTIALS; } # If confirmation is needed if ( my $setup_url = $csr->user_setup_url ) { $self->lmLog( 'OpenID confirmation needed', 'debug' ); print $self->redirect($setup_url); $self->quit(); } # Check if user has refused to share his authentication elsif ( $csr->user_cancel() ) { $self->{msg} = "OpenID request cancelled by user"; $self->lmLog( $self->{msg}, 'debug' ); return PE_FIRSTACCESS; } # TODO: check verified identity elsif ( $self->{vident} = $csr->verified_identity ) { $self->{user} = $self->{vident}->url(); $self->lmLog( "OpenID good authentication for $self->{user}", 'debug' ); $self->{mustRedirect} = 1; return PE_OK; } # Other errors else { $self->{msg} = 'OpenID error: ' . $csr->err; $self->lmLog( $self->{msg}, 'debug' ); return PE_ERROR; } } # 3. Check if an OpenID url has been submitted else { my $claimed_identity = $self->{csr}->claimed_identity($url); # Check if url is valid unless ($claimed_identity) { $self->{msg} = "OpenID error : " . $self->{csr}->err(); $self->lmLog( $self->{msg}, 'debug' ); return PE_BADCREDENTIALS; } # Build the redirection $self->lmLog( "OpenID redirection to $url", 'debug' ); my $check_url = $claimed_identity->check_url( return_to => $self->{portal} . '?openid=1' . ( $self->{_url} ? "&url=$self->{_url}" : '' ), trust_root => $self->{portal}, delayed_return => 1, ); # If UserDB uses OpenID, add "OpenID Simple Registration Extension" # compatible fields if ( $self->{userDB} =~ /^OpenID/ or $self->{stack}->[1]->[0]->{m} =~ /^OpenID/ ) { my ( @r, @o ); foreach my $k ( values %{ $self->{exportedVars} } ) { if ( $k =~ /^(?:(?:(?:full|nick)nam|languag|postcod|timezon)e|country|gender|email|dob)$/ ) { if (s/^!//) { push @r, $k } else { push @o, $k } } else { $self->lmLog( "Unknown \"OpenID Simple Registration Extension\" field name: $k", 'error' ); } } my @tmp = ( @r ? ( 'openid.sreg.required' => join( ',', @r ) ) : () ), ( @o ? ( 'openid.sreg.optional' => join( ',', @o ) ) : () ); OpenID::util::push_url_arg( \$check_url, @tmp ) if (@tmp); } print $self->redirect($check_url); $self->quit(); } PE_OK; } ## @apmethod int setAuthSessionInfo() # Set _user and authenticationLevel. # @return Lemonldap::NG::Portal constant sub setAuthSessionInfo { my $self = shift; $self->{sessionInfo}->{'_user'} = $self->{user}; $self->{sessionInfo}->{authenticationLevel} = $self->{openIdAuthnLevel}; PE_OK; } ## @apmethod int authenticate() # Does nothing. # @return Lemonldap::NG::Portal constant sub authenticate { PE_OK; } ## @apmethod int authFinish() # Does nothing. # @return Lemonldap::NG::Portal constant sub authFinish { PE_OK; } ## @apmethod int authLogout() # Does nothing # @return Lemonldap::NG::Portal constant sub authLogout { PE_OK; } ## @apmethod boolean authForce() # Does nothing # @return result sub authForce { return 0; } 1; __END__ =head1 NAME =encoding utf8 Lemonldap::NG::Portal::AuthOpenID - Perl extension for building Lemonldap::NG compatible portals with OpenID authentication. =head1 SYNOPSIS use Lemonldap::NG::Portal::SharedConf; my $portal = new Lemonldap::NG::Portal::Simple( configStorage => {...}, # See Lemonldap::NG::Portal authentication => 'OpenID', ); if($portal->process()) { # Write here the menu with CGI methods. This page is displayed ONLY IF # the user was not redirected here. print $portal->header('text/html; charset=utf8'); # DON'T FORGET THIS (see CGI(3)) print "..."; } else { # If the user enters here, IT MEANS THAT CAS REDIRECTION DOES NOT WORK print $portal->header('text/html; charset=utf8'); # DON'T FORGET THIS (see CGI(3)) print "