Commit eb4b7216 authored by Yadd's avatar Yadd
Browse files

#595 in progress

parent f91910ca
......@@ -66,18 +66,21 @@ has QUERY_STRING => (
reader => 'query',
trigger => sub {
my $self = shift;
$self->{QUERY_STRING} = uri_unescape( $self->{QUERY_STRING} );
my @tmp =
$self->{QUERY_STRING}
? split /&/, $self->{QUERY_STRING}
: ();
foreach my $s (@tmp) {
if ( $s =~ /^(.+?)=(.+)$/ ) { $self->{_params}->{$1} = $2; }
else { $self->{_params}->{$s} = 1; }
}
$self->_urlcode2params( $self->{QUERY_STRING} );
},
);
sub _urlcode2params {
my ( $self, $str ) = shift;
my @tmp = $str ? map { uri_unescape($_) } split( /&/, $str ) : ();
foreach my $s (@tmp) {
if ( $s =~ /^(.+?)=(.+)$/ ) { $self->{_params}->{$1} = $2; }
else { $self->{_params}->{$s} = 1; }
}
}
*param = *params;
sub params {
my ( $self, $key, $value ) = @_;
return $self->_params unless ($key);
......@@ -145,6 +148,17 @@ sub jsonBodyToObj {
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 );
}
}
1;
__END__
......
......@@ -4,10 +4,77 @@ use strict;
use Mouse;
use Lemonldap::NG::Portal::Main::Constants;
use Lemonldap::NG::Portal::Main::Request;
use MIME::Base64;
our $VERSION = '2.0.0';
# Auth process
# First process block: check args
# -------------------------------
# For post requests, parse datas
sub restoreArgs {
my ( $self, $req ) = @_;
$req->parseBody;
return ( %{ $req->params } ? PE_OK : PE_FORMEMPTY );
}
# Verify url parameter
sub controlUrl {
my ( $self, $req ) = @_;
$req->datas->{_url} ||= '';
if ( my $url = $req->param('url') ) {
# REJECT NON BASE64 URL except for CAS IssuerDB
if ( $self->get_module('issuer') ne "CAS" ) {
if ( $url =~ m#[^A-Za-z0-9\+/=]# ) {
$self->lmLog(
"Value must be in BASE64 (param: url | value: $url)",
"warn" );
return PE_BADURL;
}
$req->datas->{urldc} = decode_base64($url);
$req->datas->{urldc} =~ s/[\r\n]//sg;
}
else { $req->datas->{urldc} = $url; }
# For logout request, test if Referer comes from an authorizated site
my $tmp =
( $req->param('logout') ? $ENV{HTTP_REFERER} : $req->datas->{urldc} );
# XSS attack
if (
$self->checkXSSAttack(
$req->param('logout') ? 'HTTP Referer' : 'urldc',
$req->datas->{urldc}
)
)
{
delete $req->datas->{urldc};
return PE_BADURL;
}
# Non protected hosts
if ( $tmp and !$self->isTrustedUrl($tmp) ) {
$self->lmLog(
"URL contains a non protected host (param: "
. ( $req->param('logout') ? 'HTTP Referer' : 'urldc' )
. " | value: $tmp)",
"warn"
);
delete $req->datas->{urldc};
return PE_BADURL;
}
$req->datas->{_url} = $url;
}
PE_OK;
}
# Second block: auth process (call auth or userDB object)
# -------------------------------------------------------
sub extractFormInfo {
my $self = shift;
return $self->_authentication->extractFormInfo(@_);
......@@ -23,7 +90,8 @@ sub authenticate {
return $self->_authentication->authenticate(@_);
}
# Session data providing
# Third block: Session data providing
# -----------------------------------
sub setSessionInfo {
my ( $self, $req ) = @_;
......@@ -43,7 +111,8 @@ sub setSessionInfo {
$req->{sessionInfo}->{_utime} ||= time();
$req->{sessionInfo}->{startTime} =
strftime( "%Y%m%d%H%M%S", localtime() );
$req->{sessionInfo}->{_lastSeen} = time() if $self->conf->{timeoutActivity};
$req->{sessionInfo}->{_lastSeen} = time()
if $self->conf->{timeoutActivity};
}
# Get environment variables matching exportedVars
......@@ -64,147 +133,147 @@ sub setSessionInfo {
}
sub setMacros {
my ( $self, $req ) = @_;
foreach ( sort keys %{ $self->_macros } ) {
$req->{sessionInfo}->{$_} = $self->_macros->{$_}->($req);
}
PE_OK;
my ( $self, $req ) = @_;
foreach ( sort keys %{ $self->_macros } ) {
$req->{sessionInfo}->{$_} = $self->_macros->{$_}->($req);
}
PE_OK;
}
sub setGroups {
my ( $self, $req ) = @_;
return $self->_userDB->setGroups(@_);
my ( $self, $req ) = @_;
return $self->_userDB->setGroups(@_);
}
sub setPersistentSessionInfo {
my ( $self, $req ) = @_;
my ( $self, $req ) = @_;
# Do not restore infos if session already opened
unless ( $req->{id} ) {
my $key = $req->{sessionInfo}->{ $self->conf->{whatToTrace} };
# Do not restore infos if session already opened
unless ( $req->{id} ) {
my $key = $req->{sessionInfo}->{ $self->conf->{whatToTrace} };
return PE_OK unless ( $key and length($key) );
return PE_OK unless ( $key and length($key) );
my $persistentSession = $self->getPersistentSession($key);
my $persistentSession = $self->getPersistentSession($key);
if ($persistentSession) {
$self->lmLog( "Persistent session found for $key", 'debug' );
foreach my $k ( keys %{ $persistentSession->data } ) {
if ($persistentSession) {
$self->lmLog( "Persistent session found for $key", 'debug' );
foreach my $k ( keys %{ $persistentSession->data } ) {
# Do not restore some parameters
next if $k =~ /^_(?:utime|session_(?:u?id|kind))$/;
$self->lmLog( "Restore persistent parameter $k", 'debug' );
$req->{sessionInfo}->{$k} = $persistentSession->data->{$k};
}
}
}
# Do not restore some parameters
next if $k =~ /^_(?:utime|session_(?:u?id|kind))$/;
$self->lmLog( "Restore persistent parameter $k", 'debug' );
$req->{sessionInfo}->{$k} = $persistentSession->data->{$k};
}
}
}
PE_OK;
PE_OK;
}
sub setLocalGroups {
my ( $self, $req ) = @_;
foreach ( sort keys %{ $self->_groups } ) {
if ( $self->_groups->{$_}->($req) ) ) {
$req->{sessionInfo}->{groups} .=
$self->conf->{multiValuesSeparator} . $_;
$req->{sessionInfo}->{hGroups}->{$_}->{name} = $_;
}
}
# Clear values separator at the beginning
if ( $req->{sessionInfo}->{groups} ) {
$req->{sessionInfo}->{groups} =~
s/^\Q$self->conf->{multiValuesSeparator}\E//o;
}
PE_OK;
my ( $self, $req ) = @_;
foreach ( sort keys %{ $self->_groups } ) {
if ( $self->_groups->{$_}->($req) ) )
{
$req->{sessionInfo}->{groups} .=
$self->conf->{multiValuesSeparator} . $_;
$req->{sessionInfo}->{hGroups}->{$_}->{name} = $_;
}
}
# Clear values separator at the beginning
if ( $req->{sessionInfo}->{groups} ) {
$req->{sessionInfo}->{groups} =~
s/^\Q$self->conf->{multiValuesSeparator}\E//o;
}
PE_OK;
}
sub store {
my ( $self, $req ) = @_;
# Now, user is authenticated => inform handler
$req->userData($req->sessionInfo);
# Create second session for unsecure cookie
if($self->conf->{securedCookie} == 2 ) {
my $session2 = $self->getApacheSession( undef, 1 );
my %infos = %{ $req->{sessionInfo} };
$infos{_httpSessionType} = 1;
$session2->update( \%infos );
$req->{sessionInfo}->{_httpSession} = $session2->id;
}
# Main session
my $session = $self->getApacheSession( $req->{id}, 0, $self->{force} );
return PE_APACHESESSIONERROR unless ($session);
# Compute unsecure cookie value if needed
if ( $self->conf->{securedCookie} == 3 ) {
$req->{sessionInfo}->{_httpSession} =
$self->conf->{cipher}->encryptHex( $self->{id}, "http" );
}
# Fill session
my $infos = {};
foreach my $k ( keys %{ $req->{sessionInfo} } ) {
next unless defined $req->{sessionInfo}->{$k};
my $displayValue = $req->{sessionInfo}->{$k};
if ( $self->conf->{hiddenAttributes} =~ /\b$k\b/ ) {
$displayValue = '****';
}
$self->lmLog( "Store $displayValue in session key $k", 'debug' );
$self->_dump($displayValue) if ref($displayValue);
$infos->{$k} = $self->{sessionInfo}->{$k};
}
$session->update($infos);
PE_OK;
my ( $self, $req ) = @_;
# Now, user is authenticated => inform handler
$req->userData( $req->sessionInfo );
# Create second session for unsecure cookie
if ( $self->conf->{securedCookie} == 2 ) {
my $session2 = $self->getApacheSession( undef, 1 );
my %infos = %{ $req->{sessionInfo} };
$infos{_httpSessionType} = 1;
$session2->update( \%infos );
$req->{sessionInfo}->{_httpSession} = $session2->id;
}
# Main session
my $session = $self->getApacheSession( $req->{id}, 0, $self->{force} );
return PE_APACHESESSIONERROR unless ($session);
# Compute unsecure cookie value if needed
if ( $self->conf->{securedCookie} == 3 ) {
$req->{sessionInfo}->{_httpSession} =
$self->conf->{cipher}->encryptHex( $self->{id}, "http" );
}
# Fill session
my $infos = {};
foreach my $k ( keys %{ $req->{sessionInfo} } ) {
next unless defined $req->{sessionInfo}->{$k};
my $displayValue = $req->{sessionInfo}->{$k};
if ( $self->conf->{hiddenAttributes} =~ /\b$k\b/ ) {
$displayValue = '****';
}
$self->lmLog( "Store $displayValue in session key $k", 'debug' );
$self->_dump($displayValue) if ref($displayValue);
$infos->{$k} = $self->{sessionInfo}->{$k};
}
$session->update($infos);
PE_OK;
}
sub buildCookie {
my ( $self, $req ) = @_;
push @{ $req->respCookies },
$self->cookie(
name => $self->{cookieName},
value => $self->{id},
domain => $self->{domain},
path => "/",
secure => $self->{securedCookie},
HttpOnly => $self->{httpOnly},
expires => $self->{cookieExpiration},
@_,
);
if ( $self->conf->{securedCookie} >= 2 ) {
push @{ $req->respCookies },
$self->cookie(
name => $self->{cookieName} . "http",
value => $self->{sessionInfo}->{_httpSession},
domain => $self->{domain},
path => "/",
secure => 0,
HttpOnly => $self->{httpOnly},
expires => $self->{cookieExpiration},
@_,
);
}
PE_OK;
my ( $self, $req ) = @_;
push @{ $req->respCookies }, $self->cookie(
name => $self->{cookieName},
value => $self->{id},
domain => $self->{domain},
path => "/",
secure => $self->{securedCookie},
HttpOnly => $self->{httpOnly},
expires => $self->{cookieExpiration},
@_,
);
if ( $self->conf->{securedCookie} >= 2 ) {
push @{ $req->respCookies },
$self->cookie(
name => $self->{cookieName} . "http",
value => $self->{sessionInfo}->{_httpSession},
domain => $self->{domain},
path => "/",
secure => 0,
HttpOnly => $self->{httpOnly},
expires => $self->{cookieExpiration},
@_,
);
}
PE_OK;
}
sub cookie {
my ($self,%h) = @_;
my @res;
$req[0] = "$h{name}" or die("name required");
my $res[0] .= "=$h{value}";
foreach (qw(domain path expires max_age)) {
my $f = $_;
s/_/-/g;
push @res, "$_=$h{$f}" if($h{$f});
}
return join('; ',@res);
my ( $self, %h ) = @_;
my @res;
$req[0] = "$h{name}" or die("name required");
my $res[0] .= "=$h{value}";
foreach (qw(domain path expires max_age)) {
my $f = $_;
s/_/-/g;
push @res, "$_=$h{$f}" if ( $h{$f} );
}
return join( '; ', @res );
}
1;
......@@ -5,12 +5,22 @@ use Mouse;
extends 'Lemonldap::NG::Common::PSGI::Request';
# List of methods to call
has steps => ( is => 'rw' );
has datas => ( is => 'rw' );
# Datas shared between methods
has datas => ( is => 'rw', default => sub { {} } );
# Session datas when created
has id => ( is => 'rw' );
has sessionInfo => ( is => 'rw' );
# Response cookies (list of strings built by cookie())
has respCookies => ( is => 'rw' );
# Template to display (if not defined, login or menu)
has template => ( is => 'rw' );
sub wantJSON {
return $_[0]->accept =~ m#(?:application|text)/json# ? 1 : 0;
}
......
......@@ -2,17 +2,17 @@
# Serve request part of Lemonldap::NG portal
#
# Methods:
# - handler(): verify that portal configuration is the same that the
# underlying handler configuration before launching
# Lemonldap::NG::Common::PSGI::Router::handler() (which parse
# routes)
# - handler(): verify that portal configuration is the same that the
# underlying handler configuration before launching
# Lemonldap::NG::Common::PSGI::Router::handler() (which parse
# routes)
#
# Entry points:
# - "/test": * authenticated() for already authenticated users
# * pleaseAuth() for others
# - "/": * login() ~first access
# * postLogin(), same for POST requests
# * authenticatedRequest() for authenticated users
# - "/test": - authenticated() for already authenticated users
# - pleaseAuth() for others
# - "/": - login() ~first access
# - postLogin(), same for POST requests
# - authenticatedRequest() for authenticated users
package Lemonldap::NG::Portal::Main::Run;
use strict;
......@@ -61,9 +61,9 @@ sub login {
return $req->do(
$req,
[
'rememberArgs', @{ $self->beforeAuth },
&authProcess, @{ $self->betweenAuthAndDatas },
&sessionDatas, @{ $self->afterdatas },
'controlUrl', @{ $self->beforeAuth },
&authProcess, @{ $self->betweenAuthAndDatas },
&sessionDatas, @{ $self->afterdatas },
]
);
}
......@@ -73,7 +73,7 @@ sub postLogin {
return $req->do(
$req,
[
'restoreArgs', @{ $self->beforeAuth },
'restoreArgs', 'controlUrl' @{ $self->beforeAuth },
&authProcess, @{ $self->betweenAuthAndDatas },
&sessionDatas, @{ $self->afterdatas },
]
......@@ -109,7 +109,7 @@ sub do {
}
else {
if ($err) {
return $self->sendHtml( $req, 'login.tpl' );
return $self->sendHtml( $req, $req->template || 'login' );
}
else {
return $self->autoRedirect($req);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment