Some big changes (#595)

Replace uri_escape by WWW::Form::UrlEncoded
Replace PSGI:Request by inheritance from Plack::Request
This commit is contained in:
Xavier Guimard 2017-01-04 16:36:54 +00:00
parent 3146feeb3e
commit a09d12031e
35 changed files with 223 additions and 316 deletions

View File

@ -125,8 +125,8 @@ sub sendError {
# SOAP responses
if (
$req->accept =~ m#(?:application|text)/xml#
or ( $req->contentType
and $req->contentType =~ m#(?:application|text)/xml# )
or ( $req->content_type
and $req->content_type =~ m#(?:application|text)/xml# )
)
{
my $s = '<soapenv:Body>
@ -235,7 +235,7 @@ sub sendHtml {
my $sp = $self->staticPrefix;
$sp =~ s/\/*$/\//;
$htpl->param(
SCRIPT_NAME => $req->scriptname,
SCRIPT_NAME => $req->script_name,
STATIC_PREFIX => $sp,
AVAILABLE_LANGUAGES => $self->languages,
PORTAL => $self->portal,

View File

@ -3,10 +3,13 @@ package Lemonldap::NG::Common::PSGI::Request;
use strict;
use Mouse;
use JSON;
use Plack::Request;
use URI::Escape;
our $VERSION = '2.0.0';
our @ISA = ('Plack::Request');
# http :// server / path ? query # fragment
# m|(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?|;
@ -17,125 +20,78 @@ sub BUILD {
}
}
has HTTP_ACCEPT => ( is => 'ro', reader => 'accept' );
has HTTP_ACCEPT_ENCODING => ( is => 'ro', reader => 'encodings' );
has HTTP_ACCEPT_LANGUAGE => ( is => 'ro', reader => 'languages' );
has HTTP_AUTHORIZATION => ( is => 'ro', reader => 'authorization' );
has HTTP_COOKIE => ( is => 'ro', reader => 'cookies' );
has HTTP_HOST => ( is => 'ro', reader => 'hostname' );
has REFERER => ( is => 'ro', reader => 'referer' );
has REMOTE_ADDR => ( is => 'ro', isa => 'Str', reader => 'remote_ip' );
has REMOTE_PORT => ( is => 'ro', isa => 'Int', reader => 'port' );
has REQUEST_METHOD => ( is => 'ro', isa => 'Str', reader => 'method' );
has SCRIPT_NAME => ( is => 'ro', isa => 'Str', reader => 'scriptname' );
has SERVER_PORT => ( is => 'ro', isa => 'Int', reader => 'get_server_port' );
has X_ORIGINAL_URI => ( is => 'ro', isa => 'Str' );
has PATH_INFO => (
is => 'ro',
reader => 'path',
lazy => 1,
default => '',
trigger => sub {
my $tmp = $_[0]->{SCRIPT_NAME};
$_[0]->{PATH_INFO} =~ s|^$tmp|/|;
$_[0]->{PATH_INFO} =~ s|//+|/|g;
},
);
has REQUEST_URI => (
is => 'ro',
reader => 'uri',
lazy => 1,
default => '/',
trigger => sub {
my $uri = $_[0]->{X_ORIGINAL_URI} || $_[0]->{REQUEST_URI};
$_[0]->{unparsed_uri} = $uri;
$_[0]->{REQUEST_URI} = uri_unescape($uri);
$_[0]->{REQUEST_URI} =~ s|//+|/|g;
},
);
has unparsed_uri => ( is => 'rw', isa => 'Str' );
has 'psgi.errors' => ( is => 'rw', reader => 'stderr' );
# Authentication
has REMOTE_USER => (
is => 'rw',
reader => 'user',
trigger => sub {
$_[0]->{userData} = { $Lemonldap::NG::Handler::Main::tsv->{whatTotrace}
|| _whatToTrace => $_[0]->{REMOTE_USER}, };
},
);
has userData => ( is => 'rw', isa => 'HashRef', default => sub { {} } );
# Query parameters
has _params => ( is => 'rw', isa => 'HashRef', default => sub { {} } );
has QUERY_STRING => (
is => 'ro',
reader => 'query',
trigger => sub {
my $self = shift;
$self->_urlcode2params( $self->{QUERY_STRING} );
},
);
sub _urlcode2params {
my ( $self, $str ) = @_;
my @tmp = $str ? map { uri_unescape($_) } split( /&/, $str ) : ();
foreach my $s (@tmp) {
if ( $s =~ /^(.+?)=(.+)$/ ) { $self->{_params}->{$1} = $2; }
else { $self->{_params}->{$s} = 1; }
}
sub new {
my $self = Plack::Request::new(@_);
my $tmp = $self->script_name;
$self->env->{REQUEST_URI} = $self->env->{X_ORIGINAL_URI}
if ( $self->env->{X_ORIGINAL_URI} );
$self->env->{PATH_INFO} =~ s|^$tmp|/|;
$self->env->{PATH_INFO} =~ s|//+|/|g;
$self->{uri} = uri_unescape( $self->env->{REQUEST_URI} );
$self->{uri} =~ s|//+|/|g;
$self->{error} = 0;
$self->{respHeaders} = [];
return $self;
}
*param = *params;
sub uri { $_[0]->{uri} }
sub params {
my ( $self, $key, $value ) = @_;
return $self->_params unless ($key);
$self->_params->{$key} = $value if ( defined $value );
return $self->_params->{$key};
sub userData {
my($self,$v)=@_;
return $_[0]->{userData} = $v if($v);
return $_[0]->{userData} || { _whatToTrace => $_[0]->user, };
}
# POST management
#
# When CONTENT_LENGTH is set, store body in memory in `body` key
has 'psgix.input.buffered' => ( is => 'ro', reader => '_psgixBuffered', );
has 'psgi.input' => ( is => 'ro', reader => '_psgiInput', );
has body => ( is => 'rw', isa => 'Str', default => '' );
has CONTENT_TYPE => ( is => 'ro', isa => 'Str', reader => 'contentType', );
has CONTENT_LENGTH => (
is => 'ro',
reader => 'contentLength',
lazy => 1,
default => 0,
trigger => sub {
my $self = shift;
if ( $self->method eq 'GET' ) { $self->{body} = undef; }
elsif ( $self->method =~ /^(?:POST|PUT)$/ ) {
$self->{body} = '';
if ( $self->_psgixBuffered ) {
my $length = $self->{CONTENT_LENGTH};
while ( $length > 0 ) {
my $buffer;
$self->_psgiInput->read( $buffer,
( $length < 8192 ) ? $length : 8192 );
$length -= length($buffer);
$self->{body} .= $buffer;
}
sub respHeaders {
my ( $self, $respHeaders ) = @_;
$self->{respHeaders} = $respHeaders if ($respHeaders);
return $self->{respHeaders};
}
sub accept { $_[0]->env->{HTTP_ACCEPT} }
sub encodings { $_[0]->env->{HTTP_ACCEPT_ENCODING} }
sub languages { $_[0]->env->{HTTP_ACCEPT_LANGUAGE} }
sub authorization { $_[0]->env->{HTTP_AUTHORIZATION} }
sub hostname { $_[0]->env->{HTTP_HOST} }
sub referer { $_[0]->env->{REFERER} }
sub error {
my ( $self, $err ) = @_;
$self->{error} = $err if ($err);
return $self->{error};
}
sub read_body {
my $self = shift;
if ( $self->method eq 'GET' ) { return undef; }
elsif ( $self->method =~ /^(?:POST|PUT)$/ ) {
my $body = '';
if ( $self->env->{'_psgix.buffered'} ) {
my $length = $self->content_length;
while ( $length > 0 ) {
my $buffer;
$self->body->read( $buffer,
( $length < 8192 ) ? $length : 8192 );
$length -= length($buffer);
$body .= $buffer;
}
else {
$self->_psgiInput->read( $self->{body},
$self->{CONTENT_LENGTH}, 0 );
}
utf8::upgrade( $self->{body} );
}
else {
$self->body->read( $body, $self->content_length, 0 );
}
utf8::upgrade($body);
return $body;
}
);
has error => ( is => 'rw', isa => 'Str', default => '' );
}
has respHeaders => ( is => 'rw', isa => 'ArrayRef', default => sub { [] } );
*params = \&Plack::Request::param;
sub set_param {
my ( $self, $k, $v ) = @_;
$self->param;
$self->env->{'plack.request.merged'}->{$k} =
$self->env->{'plack.request.query'}->{$k} = $v;
}
sub wantJSON {
return 1
@ -147,7 +103,8 @@ sub wantJSON {
# JSON parser
sub jsonBodyToObj {
my $self = shift;
unless ( $self->contentType =~ /application\/json/ ) {
return $self->{json_body} if ( $self->{json_body} );
unless ( $self->content_type =~ /application\/json/ ) {
$self->error('Data is not JSON');
return undef;
}
@ -155,24 +112,12 @@ sub jsonBodyToObj {
$self->error('No data');
return undef;
}
return $self->body if ( ref( $self->body ) );
my $j = eval { from_json( $self->body ) };
my $j = eval { from_json( $self->read_body ) };
if ($@) {
$self->error("$@$!");
return undef;
}
return $self->{body} = $j;
}
sub parseBody {
my $self = shift;
if ( $self->contentType =~ /application\/json/ ) {
%{ $self->_params } =
( %{ $self->_params }, %{ $self->jsonBodyToObj } );
}
elsif ( $self->contentType =~ /^application\/x-www-form-urlencoded/ ) {
$self->_urlcode2params( $self->body );
}
return $self->{json_body} = $j;
}
1;
@ -244,10 +189,6 @@ Client TCP port.
HTTP method asked by client (GET/POST/PUT/DELETE).
=head3 scriptname
SCRIPT_NAME environment variable provided by HTTP server.
=head3 get_server_port
Server port.

View File

@ -118,8 +118,9 @@ sub handler {
if ( !@path and $self->defaultRoute ) {
@path = ( $self->defaultRoute );
}
my $res = $self->followPath( $req, $self->routes->{ $req->method }, \@path );
return $res ? $res : $self->sendError($req,'Bad request',400);
my $res =
$self->followPath( $req, $self->routes->{ $req->method }, \@path );
return $res ? $res : $self->sendError( $req, 'Bad request', 400 );
}
sub followPath {
@ -130,12 +131,12 @@ sub followPath {
return $routes->{$w}->( $self, $req, @$path );
}
my $res = $self->followPath( $req, $routes->{$w}, $path );
return $res if($res);
return $res if ($res);
unshift @$path, $w;
}
if ( $routes->{':'} ) {
my $v = shift @$path;
$req->params->{ $routes->{'#'} } = $v;
$req->set_param($routes->{'#'}, $v);
if ( ref( $routes->{':'} ) eq 'CODE' ) {
return $routes->{':'}->( $self, $req, @$path );
}

View File

@ -33,6 +33,7 @@ sub build_jail {
my @t =
$self->customFunctions ? split( /\s+/, $self->customFunctions ) : ();
foreach (@t) {
no warnings 'redefine';
$api->lmLog( "Custom function : $_", 'debug' );
my $sub = $_;
unless (/::/) {

View File

@ -66,7 +66,7 @@ sub handler {
my $hdrs = $req->{respHeaders};
$req->{respHeaders} = [];
my @convertedHdrs =
( 'Content-Length' => 0, Cookie => ( $req->cookies // '' ) );
( 'Content-Length' => 0, Cookie => ( $req->env->{HTTP_COOKIE} // '' ) );
my $i = 0;
while ( my $k = shift @$hdrs ) {
my $v = shift @$hdrs;

View File

@ -62,7 +62,7 @@ sub set_user {
sub header_in {
my ( $class, $header ) = @_;
$header ||= $class; # to use header_in as a method or as a function
return $request->{ cgiName($header) };
return $request->env->{ cgiName($header) };
}
## @method void set_header_in(hash headers)
@ -71,7 +71,7 @@ sub header_in {
sub set_header_in {
my ( $class, %headers ) = @_;
while ( my ( $h, $v ) = each %headers ) {
$request->{ cgiName($h) } = $v;
$request->env->{ cgiName($h) } = $v;
}
}
@ -81,7 +81,7 @@ sub set_header_in {
sub unset_header_in {
my ( $class, @headers ) = @_;
foreach my $h (@headers) {
delete $request->{ cgiName($h) };
delete $request->env->{ cgiName($h) };
}
}
@ -108,7 +108,7 @@ sub hostname {
# returns client IP address
# @return IP_Addr string client IP
sub remote_ip {
return $request->remote_ip;
return $request->address;
}
## @method boolean is_initial_req
@ -122,7 +122,7 @@ sub is_initial_req {
# gets the query string
# @return args string Query string
sub args {
return $request->query;
return $request->query_string;
}
## @method string uri
@ -140,21 +140,21 @@ sub uri {
# returns the URI, with arguments and with path portion normalized
# @return URI with normalized path portion
sub uri_with_args {
return $request->uri;
return $request->request_uri;
}
## @method string unparsed_uri
# returns the full original request URI, with arguments
# @return full original request URI, with arguments
sub unparsed_uri {
return $request->unparsed_uri;
return $request->request_uri;
}
## @method string get_server_port
# returns the port the server is receiving the current request on
# @return port string server port
sub get_server_port {
return $request->get_server_port;
return $request->port;
}
## @method string method

View File

@ -61,6 +61,7 @@ sub _run {
$self->lmLog(
"User not authenticated, Try in use, cancel redirection",
'debug' );
$req->userData( {} );
$req->respHeaders( [] );
$self->routes( $self->unAuthRoutes );
}

View File

@ -69,8 +69,8 @@ clean();
sub Lemonldap::NG::Handler::PSGI::handler {
my ( $self, $req ) = @_;
ok( $req->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
or explain( $req->{HTTP_REMOTE_USER}, 'dwho' );
ok( $req->env->{HTTP_AUTH_USER} eq 'dwho', 'Header is given to app' )
or explain( $req->env->{HTTP_AUTH_USER}, 'dwho' );
count(1);
return [ 200, [ 'Content-Type', 'text/plain' ], ['Hello'] ];
}

View File

@ -42,7 +42,7 @@ count(2);
# Check headers
%h = @{ $res->[1] };
ok( $h{'Auth-User'} eq 'dwho', 'Header Auth-User is set to "dwho"' )
or explain( $h, 'Auth-User => "dwho"' );
or explain( \%h, 'Auth-User => "dwho"' );
count(1);
# Denied query

View File

@ -10,6 +10,8 @@ our $client;
our $count = 1;
$Data::Dumper::Deparse = 1;
no warnings 'redefine';
my $module;
our $sessionId =
'f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545';
@ -84,7 +86,7 @@ has app => (
return $module->run(
{
configStorage => { type => 'File', dirName => 't' },
logLevel => 'warn',
logLevel => 'error',
cookieName => 'lemonldap',
securedCookie => 0,
https => 0,

View File

@ -97,7 +97,7 @@ sub getConfKey {
# when 'latest' => replace by last cfgNum
if ( $req->params('cfgNum') eq 'latest' ) {
my $tmp = $self->confAcc->lastCfg;
$req->params( 'cfgNum', $tmp );
$req->set_param( 'cfgNum', $tmp );
if ($Lemonldap::NG::Common::Conf::msg) {
$req->error($Lemonldap::NG::Common::Conf::msg);
return undef;
@ -107,7 +107,9 @@ sub getConfKey {
$req->error("cfgNum must be a number");
return undef;
}
unless ( defined $self->getConfByNum( $req->params('cfgNum'), @args ) ) {
unless (
defined $self->getConfByNum( scalar( $req->params('cfgNum') ), @args ) )
{
$req->error( "Configuration "
. $req->params('cfgNum')
. " is not available ("
@ -1050,7 +1052,8 @@ sub diff {
my ( $self, $req, @path ) = @_;
return $self->sendError( $req, 'to many arguments in path info', 400 )
if (@path);
my @cfgNum = ( $req->params('conf1'), $req->params('conf2') );
my @cfgNum =
( scalar( $req->param('conf1') ), scalar( $req->param('conf2') ) );
my @conf;
# Load the 2 configurations

View File

@ -123,7 +123,7 @@ sub scanTree {
$self->newConf->{cfgAuthor} =
$self->req->userData->{ $Lemonldap::NG::Handler::Main::tsv->{whatToTrace}
|| '_whatToTrace' } // "anonymous";
$self->newConf->{cfgAuthorIP} = $self->req->remote_ip;
$self->newConf->{cfgAuthorIP} = $self->req->address;
$self->newConf->{cfgDate} = time;
$self->newConf->{key} ||=
join( '', map { chr( int( rand(94) ) + 33 ) } ( 1 .. 16 ) );

View File

@ -150,7 +150,7 @@ sub notifications {
return $self->notification( $req, $notif, $type ) if ($notif);
# Case 2: list
my $params = $req->params();
my $params = $req->parameters();
my ( $notifs, $res );
$notifs = $self->notifAccess->$sub();

View File

@ -83,7 +83,7 @@ sub sessions {
my $mod = $self->getMod($req)
or return $self->sendError( $req, undef, 400 );
my $params = $req->params();
my $params = $req->parameters();
my $type = delete $params->{sessionType};
$type = $type eq 'global' ? 'SSO' : ucfirst($type);
@ -164,7 +164,7 @@ sub sessions {
my $total = ( keys %$res );
# 2.4 Special case doubleIp (users connected from more than 1 IP)
if ( $params->{doubleIp} ) {
if ( defined $params->{doubleIp} ) {
my %r;
# 2.4.1 Store user IP addresses in %r

View File

@ -300,6 +300,7 @@
"logout": "Logout",
"logoutServices": "Logout forward",
"logParams": "Logs",
"lwpSslOpts": "SSL options for server requests",
"macros": "Macros",
"mailBody": "Success mail content",
"mailCharset": "Charset",
@ -350,6 +351,7 @@
"newRule": "New rule",
"newValue": "New value",
"next": "Next",
"nginxCustomHandlers": "Custom Nginx handlers",
"noAjaxHook": "Keep redirections for Ajax",
"noDatas": "No datas to display",
"notABoolean": "Not a boolean",

View File

@ -300,6 +300,7 @@
"logout": "Déconnexion",
"logoutServices": "Transfert de la déconnexion",
"logParams": "Journalisation",
"lwpSslOpts": "Options SSL pour les requêtes serveur",
"macros": "Macros",
"mailBody": "Contenu du message de succès",
"mailCharset": "Charset",
@ -350,6 +351,7 @@
"newRule": "Nouvelle règle",
"newValue": "Nouvelle valeur",
"next": "Suivante",
"nginxCustomHandlers": "Handlers Nginx personnalisés",
"noAjaxHook": "Conserver les redirections pour Ajax",
"noDatas": "Aucune donnée à afficher",
"notABoolean": "Pas un booléen",

View File

@ -18,7 +18,7 @@ sub init {
sub extractFormInfo {
my ( $self, $req ) = @_;
unless ( $req->{user} = $req->{REMOTE_USER} ) {
unless ( $req->{user} = $req->env->{REMOTE_USER} ) {
$self->lmLog( 'Apache is not configured to authenticate users!',
'error' );
return PE_ERROR;

View File

@ -81,7 +81,7 @@ sub extractFormInfo {
# if we are receving SAML request or response
my $url = $req->uri;
my $request_method = $req->method;
my $content_type = $req->contentType;
my $content_type = $req->content_type;
# 1.1 SSO assertion consumer
if ( $url =~ $self->sloAssConsumerRe ) {
@ -774,7 +774,7 @@ sub extractFormInfo {
'debug' );
# Artifact request are sent with SOAP trough POST
my $art_request = $req->body;
my $art_request = $req->read_body;
my $art_response;
# Create Login object
@ -874,11 +874,12 @@ sub extractFormInfo {
. "<p><i>"
. $idp
. "</i></p>\n"
. ( $req->param("url")
. (
$req->param("url")
? "<input type=\"hidden\" name=\"url\" value=\""
. $req->param("url") . "\" />"
: '' )
. "<input type=\"hidden\" name=\"idp\" value=\"$idp\" />\n";
: ''
) . "<input type=\"hidden\" name=\"idp\" value=\"$idp\" />\n";
$self->p->info( $req, $html );
@ -1420,12 +1421,7 @@ sub getIDP {
my $idp;
my $idpName;
my $idp_cookie;
if ( $req->cookies
&& $req->cookies =~ /$self->{conf}->{samlIdPResolveCookie}=([^,; ]+)/o )
{
$idp_cookie = $1;
}
my $idp_cookie = $req->cookies->{ $self->{conf}->{samlIdPResolveCookie} };
# Case 1: Recover IDP from idp URL Parameter
unless ( $idp = $req->param("idp") ) {

View File

@ -25,7 +25,7 @@ sub init {
sub extractFormInfo {
my ( $self, $req ) = @_;
return PE_OK
if ( $req->user( $req->{ $self->SSLField } ) );
if ( $req->user( $req->env->{ $self->SSLField } ) );
if ( $req->{SSL_CLIENT_S_DN} ) {
$self->p->userError(
"$self->SSLField was not found in user certificate");

View File

@ -27,7 +27,7 @@ sub extractFormInfo {
$user_header = 'HTTP_' . uc($user_header);
$user_header =~ s/\-/_/g;
unless ( $req->{user} = $req->{$user_header} ) {
unless ( $req->{user} = $req->env->{$user_header} ) {
$self->lmLog( "No header " . $self->conf->{slaveUserHeader} . " found",
'error' );
return PE_USERNOTFOUND;

View File

@ -875,7 +875,6 @@ qq'<h3 trspan="oidcConsent,$display_name">The application $display_name would li
# Handle token endpoint
sub token {
my ( $self, $req ) = @_;
$req->parseBody if ( $req->method =~ /^post$/i );
$self->lmLog( "URL detected as an OpenID Connect TOKEN URL", 'debug' );
# Check authentication
@ -1034,7 +1033,6 @@ sub token {
sub userInfo {
my ( $self, $req ) = @_;
$self->lmLog( "URL detected as an OpenID Connect USERINFO URL", 'debug' );
$req->parseBody if ( $req->method =~ /^post$/i );
my $access_token = $self->getEndPointAccessToken($req);
@ -1091,7 +1089,6 @@ sub userInfo {
sub jwks {
my ( $self, $req ) = @_;
$self->lmLog( "URL detected as an OpenID Connect JWKS URL", 'debug' );
$req->parseBody if ( $req->method =~ /^post$/i );
my $jwks = { keys => [] };
@ -1117,7 +1114,7 @@ sub registration {
# TODO: check Initial Access Token
# Specific message to allow DOS detection
my $source_ip = $req->remote_ip;
my $source_ip = $req->address;
$self->lmLog( "OpenID Connect Registration request from $source_ip",
'warn' );
@ -1128,7 +1125,7 @@ sub registration {
}
# Get client metadata
my $client_metadata_json = $req->body;
my $client_metadata_json = $req->read_body;
unless ($client_metadata_json) {
return $self->p->sendError( $req, 'Missing POST datas', 400 );
}
@ -1221,7 +1218,6 @@ sub endSessionDone {
my ( $self, $req ) = @_;
$self->lmLog( "URL detected as an OpenID Connect END SESSION URL",
'debug' );
$req->parseBody if ( $req->method =~ /^post$/i );
$self->lmLog( "User is already logged out", 'debug' );
my $post_logout_redirect_uri = $req->param('post_logout_redirect_uri');
@ -1246,7 +1242,6 @@ sub checkSession {
my ( $self, $req ) = @_;
$self->lmLog( "URL detected as an OpenID Connect CHECK SESSION URL",
'debug' );
$req->parseBody if ( $req->method =~ /^post$/i );
my $portalPath = $self->conf->{portal};
$portalPath =~ s#^https?://[^/]+/?#/#;

View File

@ -147,7 +147,7 @@ sub run {
# if we are receving SAML request or response
my $url = $req->uri;
my $request_method = $req->param('issuerMethod') || $req->method;
my $content_type = $req->contentType();
my $content_type = $req->content_type();
my $idp_initiated = $req->param('IDPInitiated');
my $idp_initiated_sp = $req->param('sp');
my $idp_initiated_spConfKey = $req->param('spConfKey');
@ -981,7 +981,7 @@ sub artifactServer {
'debug' );
# Artifact request are sent with SOAP trough POST
my $art_request = $req->body;
my $art_request = $req->read_body;
my $art_response;
# Create Login object
@ -1022,7 +1022,7 @@ sub soapSloServer {
my ( $self, $req ) = @_;
my $url = $req->uri;
my $request_method = $req->param('issuerMethod') || $req->method;
my $content_type = $req->contentType();
my $content_type = $req->content_type();
$self->lmLog( "URL $url detected as an SLO URL", 'debug' );
@ -1269,7 +1269,7 @@ sub logout {
# for them.
# Redirect on logout page when all is done.
if ( $self->sendLogoutRequestToProviders( $req, $logout ) ) {
$self->{urldc} = $req->scriptname . "?logout=1";
$self->{urldc} = $req->script_name . "?logout=1";
return PE_OK;
}
@ -1401,7 +1401,7 @@ sub sloServer {
my ( $self, $req ) = @_;
my $url = $req->uri;
my $request_method = $req->param('issuerMethod') || $req->method;
my $content_type = $req->contentType();
my $content_type = $req->content_type();
$self->lmLog( "URL $url detected as an SLO URL", 'debug' );
# Check SAML Message
@ -1616,7 +1616,7 @@ sub attributeServer {
$self->lmLog( "URL $url detected as an attribute service URL", 'debug' );
# Attribute request are sent with SOAP trough POST
my $att_request = $req->body;
my $att_request = $req->read_body;
my $att_response;
# Process request

View File

@ -15,9 +15,9 @@ use JSON;
use LWP::UserAgent;
use MIME::Base64 qw/encode_base64 decode_base64/;
use Mouse;
use URI::Escape;
use WWW::Form::UrlEncoded qw/parse_urlencoded build_urlencoded/;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_REDIRECT);
our $VERSION = '2.0.0';
@ -216,8 +216,8 @@ sub getCallbackUri {
# Use authChoiceParam in redirect URL
if ( $req->param( $self->conf->{authChoiceParam} ) ) {
$callback_uri .= '&'
. $self->{authChoiceParam} . '='
. uri_escape( $req->param( $self->conf->{authChoiceParam} ) );
. build_urlencoded( $self->{authChoiceParam} =>
$req->param( $self->conf->{authChoiceParam} ) );
}
$self->lmLog( "OpenIDConnect Callback URI: $callback_uri", 'debug' );
@ -265,32 +265,23 @@ sub buildAuthorizationCodeAuthnRequest {
$nonceSession->update( { '_utime' => time } );
$nonce = $nonceSession->id;
}
$client_id = uri_escape($client_id);
$scope = uri_escape($scope);
$response_type = uri_escape($response_type);
$redirect_uri = uri_escape($redirect_uri);
$state = uri_escape($state) if defined $state;
$nonce = uri_escape($nonce) if defined $nonce;
$display = uri_escape($display) if defined $display;
$prompt = uri_escape($prompt) if defined $prompt;
$max_age = uri_escape($max_age) if defined $max_age;
$ui_locales = uri_escape($ui_locales) if defined $ui_locales;
$acr_values = uri_escape($acr_values) if defined $acr_values;
my $authn_uri =
$authorize_uri
. ( $authorize_uri =~ /\?/ ? '&' : '?' )
. "response_type=$response_type"
. "&client_id=$client_id"
. "&scope=$scope"
. "&redirect_uri=$redirect_uri";
$authn_uri .= "&state=$state" if defined $state;
$authn_uri .= "&nonce=$nonce" if defined $nonce;
$authn_uri .= "&display=$display" if defined $display;
$authn_uri .= "&prompt=$prompt" if defined $prompt;
$authn_uri .= "&max_age=$max_age" if $max_age;
$authn_uri .= "&ui_locales=$ui_locales" if defined $ui_locales;
$authn_uri .= "&acr_values=$acr_values" if defined $acr_values;
. build_urlencoded(
response_type => $response_type,
client_id => $client_id,
scope => $scope,
redirect_uri => $redirect_uri,
( defined $state ? ( state => $state ) : () ),
( defined $nonce ? ( nonce => $nonce ) : () ),
( defined $display ? ( display => $display ) : () ),
( defined $prompt ? ( prompt => $prompt ) : () ),
( $max_age ? ( max_age => $max_age ) : () ),
( defined $ui_locales ? ( ui_locales => $ui_locales ) : () ),
( defined $acr_values ? ( acr_values => $acr_values ) : () )
);
$self->lmLog(
"OpenIDConnect Authorization Code Flow Authn Request: $authn_uri",
@ -308,19 +299,14 @@ sub buildAuthorizationCodeAuthnRequest {
sub buildAuthorizationCodeAuthnResponse {
my ( $self, $redirect_uri, $code, $state, $session_state ) = @_;
my $response_url = $redirect_uri;
$response_url .= ( $redirect_uri =~ /\?/ ? '&' : '?' );
$response_url .= "code=" . uri_escape($code);
if ($state) {
$response_url .= "&state=" . uri_escape($state);
}
if ($session_state) {
$response_url .= "&session_state=" . uri_escape($session_state);
}
my $response_url =
$redirect_uri
. ( $redirect_uri =~ /\?/ ? '&' : '?' )
. build_urlencoded(
code => $code,
( $state ? ( state => $state ) : () ),
( $session_state ? ( session_state => $session_state ) : () )
);
return $response_url;
}
@ -338,20 +324,18 @@ sub buildImplicitAuthnResponse {
$session_state )
= @_;
my $response_url = "$redirect_uri#id_token=" . uri_escape($id_token);
if ($access_token) {
$response_url .=
"&token_type=bearer&access_token=" . uri_escape($access_token);
}
if ($expires_in) {
$response_url .= "&expires_in=" . uri_escape($expires_in);
}
if ($state) {
$response_url .= "&state=" . uri_escape($state);
}
if ($session_state) {
$response_url .= "&session_state=" . uri_escape($session_state);
}
my $response_url = "$redirect_uri#"
. build_urlencoded(
id_token => $id_token,
(
$access_token
? ( token_type => 'bearer', access_token => $access_token )
: ()
),
( $expires_in ? ( expires_in => $expires_in ) : () ),
( $state ? ( state => $state ) : () ),
( $session_state ? ( session_state => $session_state ) : () )
);
return $response_url;
}
@ -370,23 +354,18 @@ sub buildHybridAuthnResponse {
$id_token, $expires_in, $state, $session_state
) = @_;
my $response_url = "$redirect_uri#code=" . uri_escape($code);
if ($access_token) {
$response_url .=
"&token_type=bearer&access_token=" . uri_escape($access_token);
}
if ($id_token) {
$response_url .= "&id_token=" . uri_escape($id_token);
}
if ($expires_in) {
$response_url .= "&expires_in=" . uri_escape($expires_in);
}
if ($state) {
$response_url .= "&state=" . uri_escape($state);
}
if ($session_state) {
$response_url .= "&session_state=" . uri_escape($session_state);
}
my $response_url = "$redirect_uri#"
. build_urlencoded(
code => $code,
(
$access_token
? ( token_type => 'bearer', access_token => $access_token )
: ()
),
( $expires_in ? ( expires_in => $expires_in ) : () ),
( $state ? ( state => $state ) : () ),
( $session_state ? ( session_state => $session_state ) : () )
);
return $response_url;
}
@ -1025,18 +1004,18 @@ sub returnRedirectError {
my $urldc =
$redirect_url
. ( $fragment ? '#' : $redirect_url =~ /\?/ ? '&' : '?' )
. "error="
. uri_escape($error);
$urldc .= "&error_description=" . uri_escape($error_description)
if defined $error_description;
$urldc .= "&error_uri=" . uri_escape($error_uri) if defined $error_uri;
$urldc .= "&state=" . uri_escape($state) if defined $state;
$self->lmLog( "Redirect user to $urldc", 'debug' );
. build_urlencoded(
error => $error,
(
defined $error_description
? ( error_description => $error_description )
: ()
),
( defined $error_uri ? ( error_uri => $error_uri ) : () ),
( defined $state ? ( state => $state ) : () )
);
$req->urldc($urldc);
$req->steps( [] );
return PE_OK;
return PE_REDIRECT;
}
#sub returnJSONStatus {
@ -1331,11 +1310,12 @@ sub buildLogoutRequest {
my @tab = (qw(id_token_hint post_logout_redirect_uri state));
my @prms;
for ( my $i = 0 ; $i < 3 ; $i++ ) {
push @prms, "$tab[$i]=" . uri_escape( $args[$i] )
push @prms, $tab[$i], $args[$i]
if defined( $args[$i] );
}
my $response_url = $redirect_uri;
$response_url .= ( $response_url =~ /\?/ ? '&' : '?' ) . join( '&', @prms )
$response_url .=
( $response_url =~ /\?/ ? '&' : '?' ) . build_urlencoded(@prms)
if (@prms);
return $response_url;
}
@ -1351,7 +1331,7 @@ sub buildLogoutResponse {
if ($state) {
$response_url .= ( $redirect_uri =~ /\?/ ? '&' : '?' );
$response_url .= "state=" . uri_escape($state);
$response_url .= build_urlencoded( state => $state );
}
return $response_url;

View File

@ -76,7 +76,9 @@ sub goToPortal {
$req->urldc(
$self->conf->{remotePortal} . "?url="
. encode_base64(
$self->conf->{portal} . ( $req->query ? "?$req->query" : '' ), ''
$self->conf->{portal}
. ( $req->query_string ? '?' . $req->query_string : '' ),
''
)
);
return PE_REDIRECT;

View File

@ -411,7 +411,6 @@ sub checkMessage {
# 2.2.1. POST
if ( $content_type !~ /xml/ ) {
$req->parseBody unless ( %{ $req->params } );
$method = Lasso::Constants::HTTP_METHOD_POST;
$self->lmLog( "SAML method: HTTP-POST", 'debug' );
@ -462,7 +461,7 @@ sub checkMessage {
$self->lmLog( "SAML method: HTTP-SOAP", 'debug' );
# SOAP is always a request
$request = $req->body;
$request = $req->read_body;
$self->lmLog( "HTTP-SOAP: SAML Request $request", 'debug' );
}
@ -3023,13 +3022,13 @@ sub getQueryString {
my $query_string;
if ( $self->conf->{samlUseQueryStringSpecific} ) {
my @pairs = split( /&/, $req->param('issuerQuery') || $req->query );
my @pairs = split( /&/, $req->param('issuerQuery') || $req->query_string );
$query_string = join( ';', @pairs );
}
else {
# TODO: verify this
$query_string = $req->param('issuerQuery') || $req->query;
$query_string = $req->param('issuerQuery') || $req->query_string;
}
return $query_string;

View File

@ -18,7 +18,7 @@ our $VERSION = '2.0.0';
# @return true if remote IP is accredited in LL::NG conf
sub checkIP {
my ( $self, $req ) = @_;
my $remoteIP = $req->remote_ip;
my $remoteIP = $req->address;
return 1
if (!$self->conf->{slaveMasterIP}
|| $self->conf->{slaveMasterIP} =~ /\b$remoteIP\b/ );

View File

@ -371,10 +371,10 @@ sub getSkin {
# Fill sessionInfo to eval rule if empty (unauthenticated user)
$req->{sessionInfo}->{_url} ||= $req->{urldc};
$req->{sessionInfo}->{ipAddr} ||= $req->remote_ip;
$req->{sessionInfo}->{ipAddr} ||= $req->address;
# Load specific skin from skinRules
foreach my $rule ( @{ $self->conf->skinRules } ) {
foreach my $rule ( @{ $self->conf->{skinRules} } ) {
if ( $rule->[1]->( $req->sessionInfo ) ) {
$skin = $rule->[0];
$self->lmLog( "Skin $skin selected from skin rule", 'debug' );

View File

@ -37,10 +37,8 @@ sub init {
if ( my $path = $self->conf->{"issuerDB${type}Path"} ) {
$path =~ s/^.*?(\w+).*?$/$1/;
$self->path($path);
$self->addUnauthRoute( $path => { '*' => '_redirect' }, ['GET'] );
$self->addUnauthRoute( $path => { '*' => '_pRedirect' }, ['POST'] );
$self->addAuthRoute( $path => { '*' => "_forAuthUser" }, ['GET'] );
$self->addAuthRoute( $path => { '*' => "_pForAuthUser" }, ['POST'] );
$self->addUnauthRoute( $path => { '*' => '_redirect' }, ['GET','POST'] );
$self->addAuthRoute( $path => { '*' => "_forAuthUser" }, ['GET','POST'] );
}
else {
$self->lmLog( "No path declared for issuer $type. Skipping", 'debug' );
@ -54,16 +52,16 @@ sub init {
sub _redirect {
my ( $self, $req, @path ) = @_;
$self->lmLog( 'Processing _redirect', 'debug' );
my $prms = $req->params;
my $prms = $req->parameters;
foreach my $k ( keys %$prms ) {
$self->p->setHiddenFormValue( $req, $k, $prms->{$k}, '', 0 );
}
$self->p->setHiddenFormValue( $req, 'issuerMethod', $req->method, '', 0 );
$self->p->setHiddenFormValue( $req, 'issuerQuery', $req->query, '', 0 );
$self->p->setHiddenFormValue( $req, 'issuerQuery', $req->query_string, '', 0 );
$req->{urldc} =
$self->conf->{portal}
. $req->path
. ( $req->query ? '?' . $req->query : '' );
. ( $req->query_string ? '?' . $req->query_string : '' );
# TODO: launch normal process with 'run' at the end
return $self->p->do(
@ -82,13 +80,6 @@ sub _redirect {
);
}
sub _pRedirect {
my ( $self, $req, @path ) = @_;
$self->lmLog( '_pRedirect: parsing posted datas', 'debug' );
$req->parseBody;
return $self->_redirect( $req, @path );
}
# Case 3: authentified user, launch
sub _forAuthUser {
my ( $self, $req, @path ) = @_;
@ -106,13 +97,6 @@ sub _forAuthUser {
);
}
sub _pForAuthUser {
my ( $self, $req, @path ) = @_;
$self->lmLog( 'Parsing posted datas', 'debug' );
$req->parseBody;
return $self->_forAuthUser( $req, @path );
}
1;
__END__

View File

@ -39,7 +39,6 @@ sub process {
# For post requests, parse datas
sub restoreArgs {
my ( $self, $req ) = @_;
$req->parseBody;
$req->mustRedirect(1);
return PE_OK;
}
@ -66,11 +65,11 @@ sub controlUrl {
my $time = time() - $self->conf->{cipher}->decrypt($2);
if ( $time < 600 ) {
$self->lmLog( "Confirm parameter accepted $c", 'debug' );
$req->param( 'confirm', $c );
$req->set_param( 'confirm', $c );
}
else {
$self->lmLog( 'Confirmation to old, refused', 'notice' );
$req->param( 'confirm', 0 );
$req->set_param( 'confirm', 0 );
}
}
}
@ -131,7 +130,7 @@ sub controlUrl {
sub checkLogout {
my ( $self, $req ) = @_;
if ( $req->param('logout') ) {
if ( defined $req->param('logout') ) {
$req->steps(
[ @{ $self->beforeLogout }, 'authLogout', 'deleteSession' ] );
}
@ -194,7 +193,7 @@ sub deleteSession {
if ( !$req->urldc and !$req->postUrl ) {
$self->lmLog( 'No other target defined, redirect on logout',
'debug' );
$req->urldc( $req->scriptname . "?logout=1" );
$req->urldc( $req->script_name . "?logout=1" );
}
}
@ -292,7 +291,7 @@ sub setSessionInfo {
$req->{sessionInfo}->{_userDB} = $self->getModule( $req, "user" );
# Store IP address from remote address or X-FORWARDED-FOR header
$req->{sessionInfo}->{ipAddr} = $req->remote_ip;
$req->{sessionInfo}->{ipAddr} = $req->address;
# Date and time
if ( $self->conf->{updateSession} ) {

View File

@ -14,6 +14,7 @@ our $VERSION = '2.0.0';
package Lemonldap::NG::Portal::Main;
use strict;
use WWW::Form::UrlEncoded 'build_urlencoded';
# List constants
sub authProcess { qw(extractFormInfo getUser authenticate) }
@ -480,7 +481,9 @@ sub isTrustedUrl {
sub stamp {
my $self = shift;
return $self->conf->{cipher} ? $self->conf->{cipher}->encrypt( time() ) : 1;
my $res = $self->conf->{cipher} ? $self->conf->{cipher}->encrypt( time() ) : 1;
$res =~ s/\+/%2B/g;
return $res;
}
# Transfer POST data with auto submit
@ -524,7 +527,7 @@ sub setHiddenFormValue {
# Store value
if ($val) {
$key = $prefix . $key;
$val = encode_base64( $val, '' ) if $base64;
$val =~ s/\+/%2B/g;
$req->{portalHiddenFormValues}->{$key} = $val;
$self->lmLog( "Store $val in hidden key $key", 'debug' );
}

View File

@ -217,9 +217,7 @@ sub getNotifBack {
# Search for Lemonldap::NG cookie (ciphered)
my $id;
unless ($id = $req->cookies
and $id =~ s/$self->{conf}->{cookieName}=([^,; ]+)/$1/o )
{
unless ( $id = $req->cookies->{ $self->{conf}->{cookieName} } ) {
return $self->p->sendError( $req, 'No cookie found', 401 );
}
$id = $self->p->HANDLER->tsv->{cipher}->decrypt($id)
@ -237,9 +235,8 @@ sub getNotifBack {
if ($notifs) {
# Get accepted notifications
$req->parseBody;
my ( $refs, $checks ) = ( {}, {} );
my $prms = $req->params;
my $prms = $req->parameters;
foreach ( keys %$prms ) {
my $v = $prms->{$_};
if (s/^reference//) {

View File

@ -89,7 +89,7 @@ sub _register {
$req->datas->{registerInfo}->{mail} = $req->param('mail');
$req->datas->{registerInfo}->{firstname} = $req->param('firstname');
$req->datas->{registerInfo}->{lastname} = $req->param('lastname');
$req->datas->{registerInfo}->{ipAddr} = $req->remote_ip();
$req->datas->{registerInfo}->{ipAddr} = $req->address;
# Captcha for register form
# Only if register session does not already exist

View File

@ -105,7 +105,7 @@ SKIP: {
expectOK($res);
my $idpId = expectCookie($res);
# Post SAML artifact to SP
# Post SAML response to SP
ok(
$res->[2]->[0] =~
m#<form.+?action="http://auth.sp.com(.*?)".+?method="post"#,
@ -126,7 +126,7 @@ SKIP: {
length => length($s),
cookie => 'lemonldapidp=http://auth.idp.com/saml/metadata',
),
'Post artifact to SP'
'Post SAML response to SP'
);
my $spId = expectCookie($res);
expectRedirection( $res, 'http://auth.sp.com' );

View File

@ -76,13 +76,12 @@ count(1);
qr#^http://auth.rp.com/?\?openidconnectcallback=1\#(.*)$# );
my %prms = map { split /=/, $_ } split /&/, $query;
ok( $prms{id_token}, ' id_token found' );
ok( $prms{token_type}, ' token_type found' );
ok( $prms{session_state}, ' session_state found' );
ok( $prms{access_token}, ' access_token found' );
ok( $prms{state}, ' state found' );
ok( $prms{session_state}, ' session_state found' );
count(6);
count(5);
my $at;
ok( $at = $rp->p->_userDB->getUserInfo( 'op', $prms{access_token} ),

View File

@ -261,11 +261,11 @@ sub logout {
main::ok(
( defined( $c = main::getCookies($res)->{lemonldap} ) and not $c ),
'Cookie is deleted' )
or explain( $res->[1], "Set-Cookie => 'lemonldap='" );
or main::explain( $res->[1], "Set-Cookie => 'lemonldap='" );
main::ok( $res = $self->_get( '/', cookie => "lemonldap=$id" ),
'Disconnect request' )
or explain( $res, '[<code>,<hdrs>,<content>]' );
main::ok( $res->[0] == 401, 'Response is 401' ) or explain( $res, 401 );
main::ok( $res->[0] == 401, 'Response is 401' ) or main::explain( $res, 401 );
main::count(5);
}