diff --git a/lemonldap-ng-handler/MANIFEST b/lemonldap-ng-handler/MANIFEST index 6d09ce686..af2457b9f 100644 --- a/lemonldap-ng-handler/MANIFEST +++ b/lemonldap-ng-handler/MANIFEST @@ -11,6 +11,7 @@ lib/Lemonldap/NG/Handler/API/ApacheMP2.pm lib/Lemonldap/NG/Handler/API/CGI.pm lib/Lemonldap/NG/Handler/API/ExperimentalNginx.pm lib/Lemonldap/NG/Handler/API/PSGI.pm +lib/Lemonldap/NG/Handler/API/PSGI/Server.pm lib/Lemonldap/NG/Handler/CGI.pm lib/Lemonldap/NG/Handler/Main.pm lib/Lemonldap/NG/Handler/Main/Jail.pm @@ -46,6 +47,8 @@ t/50-Lemonldap-NG-Handler-SecureToken.t t/51-Lemonldap-NG-Handler-Zimbra.t t/52-Lemonldap-NG-Handler-AuthBasic.t t/60-Lemonldap-NG-Handler-PSGI.t +t/61-Lemonldap-NG-Handler-PSGI-Server.t +t/62-Lemonldap-NG-Handler-Nginx.t t/99-pod.t t/lmConf-1.js t/sessions/f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545 diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API.pm index 91f704a74..3cead6ffb 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API.pm @@ -42,9 +42,18 @@ sub AUTOLOAD { # - Apache (modperl1), # - Nginx if ( !$mode or $func eq 'newRequest' ) { + + #print STDERR "FONCTION $func\n"; + #for ( my $i = 0 ; $i < 7 ; $i++ ) { + # print STDERR " $i: " . ( caller($i) )[0] . "\n"; + #} $mode = - ( ( caller(1) )[0] eq 'Lemonldap::NG::Handler::Nginx' ) - ? 'PSGI' + ( + ( caller(1) )[0] =~ + /^Lemonldap::NG::Handler::(?:Nginx|PSGI::Server)$/ + or ( caller(6) )[0] =~ + /^Lemonldap::NG::Handler::(?:Nginx|PSGI::Server)$/ + ) ? 'PSGI/Server' : ( ( caller(0) )[0] =~ /^Lemonldap::NG::Handler::PSGI/ or ( diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API/PSGI/Server.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API/PSGI/Server.pm new file mode 100644 index 000000000..b44e10214 --- /dev/null +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/API/PSGI/Server.pm @@ -0,0 +1,21 @@ +package Lemonldap::NG::Handler::API::PSGI::Server; + +use strict; + +use base 'Lemonldap::NG::Handler::API::PSGI'; + +# In server mode, headers are not passed to a PSGI application but returned +# to the server + +## @method void set_header_in(hash headers) +# sets or modifies request headers +# @param headers hash containing header names => header value +sub set_header_in { + my ( $class, %headers ) = @_; + for my $k ( keys %headers ) { + $Lemonldap::NG::Handler::API::PSGI::request->{respHeaders}->{$k} = + $headers{$k}; + } +} + +1; diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Nginx.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Nginx.pm index 5b1cec68a..f73e28423 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Nginx.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/Nginx.pm @@ -75,3 +75,154 @@ sub router { } 1; + +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +Lemonldap::NG::Handler::Nginx - Lemonldap::NG FastCGI handler for Nginx. + +=head1 SYNOPSIS + +FastCGI server: + + use Lemonldap::NG::Handler::Nginx; + Lemonldap::NG::Handler::Nginx->run( {} ); + +Launch it with plackup: + + plackup -s FCGI --listen /tmp/llng.sock --no-default-middleware + +Configure Nginx: + + http { + log_format lm_combined '$remote_addr - $lmremote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent"'; + + server { + server_name test1.example.com; + access_log /log/file lm_combined + + # Internal authentication request + location = /lmauth { + internal; + include /etc/nginx/fastcgi_params; + fastcgi_pass __PSGISERVERSOCKET__; + + # Drop post datas + fastcgi_pass_request_body off; + fastcgi_param CONTENT_LENGTH ""; + + # Keep original hostname + fastcgi_param HOST $http_host; + + # Keep original request (LLNG server will received /llauth) + fastcgi_param X_ORIGINAL_URI $request_uri; + } + + # Client requests + location / { + + # Activate access control + auth_request /lmauth; + + # Set logs + auth_request_set $lmremote_user $upstream_http_lm_remote_user; + auth_request_set $lmlocation $upstream_http_location; + error_page 401 $lmlocation; + try_files $uri $uri/ =404; + + # Add as many 3-lines block as max number of headers returned by + # configuration + auth_request_set $headername1 $upstream_http_headername1; + auth_request_set $headervalue1 $upstream_http_headervalue1; + #proxy_set_header $headername1 $headervalue1; + # OR + #fastcgi_param $fheadername1 $headervalue1; + + auth_request_set $headername2 $upstream_http_headername2; + auth_request_set $headervalue2 $upstream_http_headervalue2; + #proxy_set_header $headername2 $headervalue2; + # OR + #fastcgi_param $fheadername2 $headervalue2; + + auth_request_set $headername3 $upstream_http_headername3; + auth_request_set $headervalue3 $upstream_http_headervalue3; + #proxy_set_header $headername3 $headervalue3; + # OR + #fastcgi_param $fheadername3 $headervalue3; + } + } + + + +=head1 DESCRIPTION + +Lemonldap::NG is a modular Web-SSO based on Apache::Session modules. It +simplifies the build of a protected area with a few changes in the application. + +It manages both authentication and authorization and provides headers for +accounting. So you can have a full AAA protection for your web space as +described below. + +Lemonldap::NG::Handler::Nginx provides a FastCGI server that can be used by +Nginx as authentication server. + +=head1 SEE ALSO + +L, L, +L + +=head1 AUTHOR + +=over + +=item Clement Oudot, Eclem.oudot@gmail.comE + +=item François-Xavier Deltombe, Efxdeltombe@gmail.com.E + +=item Xavier Guimard, Ex.guimard@free.frE + +=back + +=head1 BUG REPORT + +Use OW2 system to report bug or ask for features: +L + +=head1 DOWNLOAD + +Lemonldap::NG is available at +L + +=head1 COPYRIGHT AND LICENSE + +=over + +=item Copyright (C) 2016 by Xavier Guimard, Ex.guimard@free.frE + +=item Copyright (C) 2012-2015 by François-Xavier Deltombe, Efxdeltombe@gmail.com.E + +=item Copyright (C) 2006-2012 by Clement Oudot, Eclem.oudot@gmail.comE + +=back + +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see L. + +=cut diff --git a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Server.pm b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Server.pm index ad2714cd6..e38c7e574 100644 --- a/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Server.pm +++ b/lemonldap-ng-handler/lib/Lemonldap/NG/Handler/PSGI/Server.pm @@ -13,8 +13,8 @@ extends 'Lemonldap::NG::Handler::PSGI'; sub _run { my ($self) = @_; return sub { - my $req = Lemonldap::NG::Common::PSGI::Request( $_[0] ); - my $res = $self->router($req); + my $req = Lemonldap::NG::Common::PSGI::Request->new( $_[0] ); + my $res = $self->_authAndTrace($req); push @{ $res->[1] }, %{ $req->{respHeaders} }; return $res; }; diff --git a/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t b/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t new file mode 100644 index 000000000..196bf7418 --- /dev/null +++ b/lemonldap-ng-handler/t/61-Lemonldap-NG-Handler-PSGI-Server.t @@ -0,0 +1,84 @@ +#!/usr/bin/env perl -I pl/lib + +use Test::More; +use JSON; +use Data::Dumper; +use MIME::Base64; + +require 't/test-psgi-lib.pm'; + +init('Lemonldap::NG::Handler::PSGI::Server'); + +my $res; + +# Unauthentified query +ok( $res = $client->_get('/'), 'Unauthentified query' ); +ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); +my %h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com/?url=' + . encode_base64( 'http://test1.example.com/', '' ), + 'Redirection points to portal' + ) + or explain( + \%h, + 'Location => http://auth.example.com/?url=' + . encode_base64( 'http://test1.example.com/', '' ) + ); + +count(4); + +# Authentified queries +# -------------------- + +# Authorizated query +ok( + $res = $client->_get( + '/', + undef, + undef, +'lemonldap=f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Authentified query' +); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 ); + +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"'); +count(1); + +# Denied query +ok( + $res = $client->_get( + '/deny', + undef, + undef, +'lemonldap=f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Denied query' +); +ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 ); + +count(2); + +# Bad cookie +ok( + $res = $client->_get( + '/deny', + undef, + 'manager.example.com', +'lemonldap=e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Bad cookie' +); +ok( $res->[0] == 302, 'Code is 302' ) or explain( $res->[0], 302 ); +unlink('t/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'); + +count(2); + +done_testing( count() ); + diff --git a/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t b/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t new file mode 100644 index 000000000..7e9f28e03 --- /dev/null +++ b/lemonldap-ng-handler/t/62-Lemonldap-NG-Handler-Nginx.t @@ -0,0 +1,85 @@ +#!/usr/bin/env perl -I pl/lib + +use Test::More; +use JSON; +use Data::Dumper; +use MIME::Base64; + +require 't/test-psgi-lib.pm'; + +init('Lemonldap::NG::Handler::Nginx'); + +my $res; + +# Unauthentified query +ok( $res = $client->_get('/'), 'Unauthentified query' ); +ok( ref($res) eq 'ARRAY', 'Response is an array' ) or explain( $res, 'array' ); +ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 ); +my %h = @{ $res->[1] }; +ok( + $h{Location} eq 'http://auth.example.com/?url=' + . encode_base64( 'http://test1.example.com/', '' ), + 'Redirection points to portal' + ) + or explain( + \%h, + 'Location => http://auth.example.com/?url=' + . encode_base64( 'http://test1.example.com/', '' ) + ); + +count(4); + +# Authentified queries +# -------------------- + +# Authorizated query +ok( + $res = $client->_get( + '/', + undef, + undef, +'lemonldap=f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Authentified query' +); +ok( $res->[0] == 200, 'Code is 200' ) or explain( $res->[0], 200 ); + +count(2); + +# Check headers +%h = @{ $res->[1] }; +ok($h{'Headername1'} eq 'Auth-User','Headername1 is set to "Auth-User"') or explain($h,'Headername1 => "Auth-User"'); +ok($h{'Headervalue1'} eq 'dwho','Headervalue1 is set to "dwho"') or explain($h,'Headervalue1 => "dwho"'); +count(2); + +# Denied query +ok( + $res = $client->_get( + '/deny', + undef, + undef, +'lemonldap=f5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Denied query' +); +ok( $res->[0] == 403, 'Code is 403' ) or explain( $res->[0], 403 ); + +count(2); + +# Bad cookie +ok( + $res = $client->_get( + '/deny', + undef, + 'manager.example.com', +'lemonldap=e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545' + ), + 'Bad cookie' +); +ok( $res->[0] == 401, 'Code is 401' ) or explain( $res->[0], 401 ); +unlink('t/sessions/lock/Apache-Session-e5eec18ebb9bc96352595e2d8ce962e8ecf7af7c9a98cb9a43f9cd181cf4b545.lock'); + +count(2); + +done_testing( count() ); + diff --git a/lemonldap-ng-manager/MANIFEST b/lemonldap-ng-manager/MANIFEST index 7a32bbb9b..bd29ad91e 100644 --- a/lemonldap-ng-manager/MANIFEST +++ b/lemonldap-ng-manager/MANIFEST @@ -172,7 +172,6 @@ t/80-attributes.t t/90-translations.t t/99-pod.t t/conf/lmConf-1.js -t/conf/lmConf-2.js t/jsonfiles/01-base-tree.json t/jsonfiles/02-base-tree-all-nodes-opened.json t/jsonfiles/12-modified.json