1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
package Lemonldap::NG::Handler::Lib::AuthBasic;
use strict;
use Exporter;
use Digest::SHA qw(sha256_hex);
use MIME::Base64;
use HTTP::Headers;
#use SOAP::Lite; # link protected portalRequest
use Lemonldap::NG::Common::UserAgent;
use Lemonldap::NG::Common::FormEncode;
use Lemonldap::NG::Common::Session;
our $VERSION = '2.19.0';
our @ISA = ('Exporter');
our @EXPORT = qw(fetchId retrieveSession createSession hideCookie goToPortal);
our @EXPORT_OK = @EXPORT;
our $_ua;
sub ua {
my ($class) = @_;
return $_ua if $_ua;
$_ua = Lemonldap::NG::Common::UserAgent->new( {
lwpOpts => $class->tsv->{lwpOpts},
lwpSslOpts => $class->tsv->{lwpSslOpts}
}
);
return $_ua;
}
## @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 {
my ( $class, $req ) = @_;
if ( my $creds = $req->env->{'HTTP_AUTHORIZATION'} ) {
$creds =~ s/^Basic\s+//;
my $pepper = int( time / $class->tsv->{timeout} ) . $class->tsv->{keyH};
return sha256_hex( $creds . $pepper );
}
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 {
my ( $class, $req, $id ) = @_;
# First check if session already exists
if ( my $res =
$class->Lemonldap::NG::Handler::Main::retrieveSession( $req, $id ) )
{
return $res;
}
# Then ask portal to create it
if ( $class->createSession( $req, $id ) ) {
return $class->Lemonldap::NG::Handler::Main::retrieveSession( $req,
$id );
}
else {
return 0;
}
}
## @rmethod protected boolean createSession(id)
# Send a create session request to the Portal
# @return true if the session is created, else false
sub createSession {
my ( $class, $req, $id ) = @_;
# Add client IP as X-Forwarded-For IP in request
my $xheader = $req->env->{'HTTP_X_FORWARDED_FOR'};
$xheader .= ", " if ($xheader);
$xheader .= $req->address;
#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');
my $creds = $req->env->{'HTTP_AUTHORIZATION'};
$creds =~ s/^Basic\s+//;
my ( $user, $pwd ) = ( decode_base64($creds) =~ /^(.*?):(.*)$/ );
$class->logger->debug("AuthBasic authentication for user: $user");
#my $soapRequest = $soapClient->getCookies( $user, $pwd, $id );
my $url = $class->tsv->{portal}->($req) . "/sessions/global/$id?auth";
$url =~ s#//sessions/#/sessions/#g;
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(
build_urlencoded(
user => $user,
password => $pwd,
secret => $class->tsv->{cipher}->encrypt(time),
(
$class->tsv->{authChoiceAuthBasic}
? ( $class->tsv->{authChoiceParam} =>
$class->tsv->{authChoiceAuthBasic} )
: ()
)
)
);
my $resp = $class->ua->request($get);
if ( $resp->is_success ) {
$class->auditLog(
$req,
message => "Good REST authentication for $user",
code => "REST_AUTHENTICATION_SUCCESS",
user => $user,
);
return 1;
}
else {
$class->auditLog(
$req,
message =>
( "Authentication failed for $user: " . $resp->status_line ),
code => "REST_AUTHENTICATION_FAILURE",
user => $user,
status => $resp->status_line,
);
return 0;
}
}
## @rmethod protected void hideCookie()
# Hide user credentials to the protected application
sub hideCookie {
my ( $class, $req ) = @_;
$class->logger->debug("removing Authorization header");
$class->unset_header_in( $req, 'Authorization' );
}
## @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
# @return AUTH_REDIRECT or AUTH_REQUIRED constant
sub goToPortal {
my ( $class, $req, $url, $arg ) = @_;
if ($arg) {
return $class->Lemonldap::NG::Handler::Main::goToPortal( $req, $url,
$arg );
}
else {
$class->set_header_out( $req,
'WWW-Authenticate' => 'Basic realm="LemonLDAP::NG"' );
return $class->AUTH_REQUIRED;
}
}
1;
|