2016-09-30 07:15:17 +02:00
|
|
|
package Lemonldap::NG::Handler::Lib::AuthBasic;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Exporter;
|
2019-09-20 22:47:48 +02:00
|
|
|
use Digest::SHA qw(sha256_hex);
|
2016-09-30 07:15:17 +02:00
|
|
|
use MIME::Base64;
|
|
|
|
use HTTP::Headers;
|
2017-02-28 07:34:54 +01:00
|
|
|
|
|
|
|
#use SOAP::Lite; # link protected portalRequest
|
|
|
|
use Lemonldap::NG::Common::UserAgent;
|
|
|
|
use Lemonldap::NG::Common::FormEncode;
|
2016-09-30 07:15:17 +02:00
|
|
|
use Lemonldap::NG::Common::Session;
|
|
|
|
|
2022-04-07 22:39:19 +02:00
|
|
|
our $VERSION = '2.0.15';
|
2016-09-30 07:15:17 +02:00
|
|
|
our @ISA = ('Exporter');
|
|
|
|
our @EXPORT = qw(fetchId retrieveSession createSession hideCookie goToPortal);
|
|
|
|
our @EXPORT_OK = @EXPORT;
|
2019-09-29 12:50:46 +02:00
|
|
|
our $_ua;
|
2016-09-30 07:15:17 +02:00
|
|
|
|
2022-04-07 22:39:19 +02:00
|
|
|
sub ua {
|
|
|
|
my ($class) = @_;
|
|
|
|
return $_ua if $_ua;
|
|
|
|
$_ua = Lemonldap::NG::Common::UserAgent->new( {
|
|
|
|
lwpOpts => $class->tsv->{lwpOpts},
|
|
|
|
lwpSslOpts => $class->tsv->{lwpSslOpts}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
return $_ua;
|
|
|
|
}
|
|
|
|
|
2016-09-30 07:15:17 +02:00
|
|
|
## @rmethod protected fetchId
|
|
|
|
# Get user session id from Authorization header
|
|
|
|
# Unlike usual processing, session id is computed from user creds,
|
|
|
|
# so that it remains secret but handler can easily get it.
|
|
|
|
# It is still changed from time to time - once a day - to prevent from
|
|
|
|
# using indefinitely a session id disclosed accidentally or maliciously.
|
|
|
|
# @return session id
|
|
|
|
sub fetchId {
|
2017-03-28 23:07:49 +02:00
|
|
|
my ( $class, $req ) = @_;
|
|
|
|
if ( my $creds = $req->env->{'HTTP_AUTHORIZATION'} ) {
|
2016-09-30 07:15:17 +02:00
|
|
|
$creds =~ s/^Basic\s+//;
|
|
|
|
my @date = localtime;
|
|
|
|
my $day = $date[5] * 366 + $date[7];
|
2019-09-10 22:30:03 +02:00
|
|
|
return Digest::SHA::sha256_hex( $creds . $day );
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
## @rmethod protected boolean retrieveSession(id)
|
|
|
|
# Tries to retrieve the session whose index is id,
|
|
|
|
# and if needed, ask portal to create it through a SOAP request
|
|
|
|
# @return true if the session was found, false else
|
|
|
|
sub retrieveSession {
|
2017-03-28 23:07:49 +02:00
|
|
|
my ( $class, $req, $id ) = @_;
|
2016-09-30 07:15:17 +02:00
|
|
|
|
|
|
|
# First check if session already exists
|
2017-03-28 23:07:49 +02:00
|
|
|
if ( my $res =
|
|
|
|
$class->Lemonldap::NG::Handler::Main::retrieveSession( $req, $id ) )
|
2017-02-28 10:49:37 +01:00
|
|
|
{
|
|
|
|
return $res;
|
|
|
|
}
|
2016-09-30 07:15:17 +02:00
|
|
|
|
|
|
|
# Then ask portal to create it
|
2017-03-28 23:07:49 +02:00
|
|
|
if ( $class->createSession( $req, $id ) ) {
|
|
|
|
return $class->Lemonldap::NG::Handler::Main::retrieveSession( $req,
|
|
|
|
$id );
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-06 10:04:00 +01:00
|
|
|
## @rmethod protected boolean createSession(id)
|
2019-10-15 17:36:09 +02:00
|
|
|
# Send a create session request to the Portal
|
2016-09-30 07:15:17 +02:00
|
|
|
# @return true if the session is created, else false
|
|
|
|
sub createSession {
|
2017-03-28 23:07:49 +02:00
|
|
|
my ( $class, $req, $id ) = @_;
|
2016-09-30 07:15:17 +02:00
|
|
|
|
2019-10-15 17:36:09 +02:00
|
|
|
# Add client IP as X-Forwarded-For IP in request
|
2017-03-28 23:07:49 +02:00
|
|
|
my $xheader = $req->env->{'HTTP_X_FORWARDED_FOR'};
|
2016-09-30 07:15:17 +02:00
|
|
|
$xheader .= ", " if ($xheader);
|
2017-03-28 23:07:49 +02:00
|
|
|
$xheader .= $req->{env}->{REMOTE_ADDR};
|
2016-09-30 07:15:17 +02:00
|
|
|
|
2017-02-28 07:34:54 +01:00
|
|
|
#my $soapHeaders = HTTP::Headers->new( "X-Forwarded-For" => $xheader );
|
|
|
|
## TODO: use adminSession or sessions
|
|
|
|
#my $soapClient = SOAP::Lite->proxy(
|
|
|
|
# $class->tsv->{portal}->() . '/sessions',
|
|
|
|
# default_headers => $soapHeaders
|
|
|
|
#)->uri('urn:Lemonldap/NG/Common/PSGI/SOAPService');
|
2016-09-30 07:15:17 +02:00
|
|
|
|
2017-03-28 23:07:49 +02:00
|
|
|
my $creds = $req->env->{'HTTP_AUTHORIZATION'};
|
2016-09-30 07:15:17 +02:00
|
|
|
$creds =~ s/^Basic\s+//;
|
|
|
|
my ( $user, $pwd ) = ( decode_base64($creds) =~ /^(.*?):(.*)$/ );
|
2017-02-15 07:41:50 +01:00
|
|
|
$class->logger->debug("AuthBasic authentication for user: $user");
|
2016-09-30 07:15:17 +02:00
|
|
|
|
2017-02-28 07:34:54 +01:00
|
|
|
#my $soapRequest = $soapClient->getCookies( $user, $pwd, $id );
|
2017-02-28 10:49:37 +01:00
|
|
|
my $url = $class->tsv->{portal}->() . "/sessions/global/$id?auth";
|
|
|
|
$url =~ s#//sessions/#/sessions/#g;
|
2017-03-28 23:07:49 +02:00
|
|
|
my $get = HTTP::Request->new( POST => $url );
|
|
|
|
$get->header( 'X-Forwarded-For' => $xheader );
|
|
|
|
$get->header( 'Content-Type' => 'application/x-www-form-urlencoded' );
|
|
|
|
$get->header( Accept => 'application/json' );
|
|
|
|
$get->content(
|
2017-02-28 07:34:54 +01:00
|
|
|
build_urlencoded(
|
|
|
|
user => $user,
|
|
|
|
password => $pwd,
|
2019-09-10 10:15:17 +02:00
|
|
|
secret => $class->tsv->{cipher}->encrypt(time),
|
|
|
|
(
|
|
|
|
$class->tsv->{authChoiceAuthBasic}
|
|
|
|
? ( $class->tsv->{authChoiceParam} =>
|
|
|
|
$class->tsv->{authChoiceAuthBasic} )
|
|
|
|
: ()
|
|
|
|
)
|
2017-02-28 07:34:54 +01:00
|
|
|
)
|
|
|
|
);
|
2017-03-28 23:07:49 +02:00
|
|
|
my $resp = $class->ua->request($get);
|
2017-02-28 07:34:54 +01:00
|
|
|
|
|
|
|
if ( $resp->is_success ) {
|
|
|
|
$class->userLogger->notice("Good REST authentication for $user");
|
|
|
|
return 1;
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
else {
|
2017-02-28 07:34:54 +01:00
|
|
|
$class->userLogger->warn(
|
|
|
|
"Authentication failed for $user: " . $resp->status_line );
|
|
|
|
return 0;
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
2017-02-28 07:34:54 +01:00
|
|
|
|
|
|
|
## Catch SOAP errors
|
|
|
|
#if ( $soapRequest->fault ) {
|
|
|
|
# $class->abort( "SOAP request to the portal failed: "
|
|
|
|
# . $soapRequest->fault->{faultstring} );
|
|
|
|
#}
|
|
|
|
#else {
|
|
|
|
# my $res = $soapRequest->result();
|
|
|
|
|
|
|
|
# # If authentication failed, display error
|
|
|
|
# if ( $res->{errorCode} ) {
|
|
|
|
# $class->userLogger->notice( "Authentication failed for $user: "
|
|
|
|
# . $soapClient->error( $res->{errorCode}, 'en' )->result() );
|
|
|
|
# return 0;
|
|
|
|
# }
|
|
|
|
# else {
|
|
|
|
# return 1;
|
|
|
|
# }
|
|
|
|
#}
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
## @rmethod protected void hideCookie()
|
|
|
|
# Hide user credentials to the protected application
|
|
|
|
sub hideCookie {
|
2017-03-28 23:07:49 +02:00
|
|
|
my ( $class, $req ) = @_;
|
2017-02-15 07:41:50 +01:00
|
|
|
$class->logger->debug("removing Authorization header");
|
2017-03-28 23:07:49 +02:00
|
|
|
$class->unset_header_in( $req, 'Authorization' );
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
## @rmethod protected int goToPortal(string url, string arg)
|
|
|
|
# If user is asked to authenticate, return $class->AUTH_REQUIRED,
|
|
|
|
# else redirect him to the portal to display some message defined by $arg
|
|
|
|
# @param $url Url requested
|
|
|
|
# @param $arg optionnal GET parameters
|
2019-10-15 17:36:09 +02:00
|
|
|
# @return AUTH_REDIRECT or AUTH_REQUIRED constant
|
2016-09-30 07:15:17 +02:00
|
|
|
sub goToPortal {
|
2017-03-28 23:07:49 +02:00
|
|
|
my ( $class, $req, $url, $arg ) = @_;
|
2016-09-30 07:15:17 +02:00
|
|
|
if ($arg) {
|
2017-03-28 23:07:49 +02:00
|
|
|
return $class->Lemonldap::NG::Handler::Main::goToPortal( $req, $url,
|
|
|
|
$arg );
|
2016-09-30 07:15:17 +02:00
|
|
|
}
|
|
|
|
else {
|
2017-03-28 23:07:49 +02:00
|
|
|
$class->set_header_out( $req,
|
2016-09-30 07:15:17 +02:00
|
|
|
'WWW-Authenticate' => 'Basic realm="LemonLDAP::NG"' );
|
|
|
|
return $class->AUTH_REQUIRED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|