ReverseProxy-FormFiller-0.2/0000755000175000017500000000000012131606373013705 5ustar fxfxReverseProxy-FormFiller-0.2/Makefile.PL0000644000175000017500000000156512131260320015652 0ustar fxfxuse strict; use warnings; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'ReverseProxy::FormFiller', AUTHOR => q{FX Deltombe }, VERSION_FROM => 'lib/ReverseProxy/FormFiller.pm', ABSTRACT_FROM => 'lib/ReverseProxy/FormFiller.pm', ($ExtUtils::MakeMaker::VERSION >= 6.3002 ? ('LICENSE'=> 'perl') : ()), PL_FILES => {}, PREREQ_PM => { 'Test::More' => 0, 'Apache2::Filter' => 0, 'Apache2::Const' => 0, 'Apache2::RequestUtil' => 0, 'Apache2::RequestRec' => 0, 'Apache2::Response' => 0, 'Apache2::Log' => 0, 'URI::Escape' => 0, }, dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', }, clean => { FILES => 'ReverseProxy-FormFiller-*' }, ); ReverseProxy-FormFiller-0.2/lib/0000755000175000017500000000000012131606373014453 5ustar fxfxReverseProxy-FormFiller-0.2/lib/ReverseProxy/0000755000175000017500000000000012131606373017130 5ustar fxfxReverseProxy-FormFiller-0.2/lib/ReverseProxy/FormFiller.pm0000644000175000017500000003321712131606120021523 0ustar fxfxpackage ReverseProxy::FormFiller; use strict; use Apache2::Filter; use Apache2::Const -compile => qw(:common); use Apache2::RequestUtil; use Apache2::RequestRec; use Apache2::Log; use URI::Escape; our $VERSION = '0.2'; my $globalParams; sub logError { my ($r, $error) = @_; $r->log->error("ReverseProxy::FormFiller is not configured, since $error"); } sub getParams { my $r = shift; my $paramFile = $r->dir_config('FormFillerParamFile'); unless (defined $paramFile) { &logError($r, "there is no perl var FormFillerParamFile defined in Apache config"); return 0; } unless (defined $globalParams->{$paramFile}) { if (open F, $paramFile) { local $/ = undef; my $paramContent = ; close F; my $params; eval "\$params = {$paramContent}"; if ($@) { &logError($r, "$paramFile content doesn't seem to be a valid perl hash"); $globalParams->{$paramFile} = 0; } else { $params->{form} ||= 'form:first'; $params->{submit} ||= 'false'; $params->{secretFormData} ||= {}; $params->{publicFormData} ||= {}; %{ $params->{secretFormData} } = ( %{ $params->{publicFormData} }, %{ $params->{secretFormData} } ); $globalParams->{$paramFile} = $params; } } else { &logError($r, "Apache can't read $paramFile"); $globalParams->{$paramFile} = 0; } } return $globalParams->{$paramFile}; } ## forge javascript to fill and submit form sub js { my $params = shift; my ($form, $submit) = ($params->{form}, $params->{submit}); eval "\$form = $form"; eval "\$submit = $submit"; my $js = " var form = jQuery('$form')\n" . " form.attr('autocomplete', 'off')\n"; while ( my ($name, $value) = each %{ $params->{publicFormData} } ) { eval "\$value = $value"; $js .= " form.find('input[name=$name], select[name=$name], textarea[name=$name]').val('$value')\n"; } $js .= $submit eq "true" ? " form.submit()\n" : $submit ne "false" ? " form.find('$submit').click()\n" : ""; $js = "\n"; $js = "\n$js" if ($params->{jQueryUrl}); return $js; } ## filter applied to response body sub output { my $f = shift; my $buffer; my $params = &getParams($f->r); # Filter only html reponse body unless ( (!defined $f->r->content_type or $f->r->content_type =~ /html/i) && $params ) { $f->print($buffer) while ($f->read($buffer)); return Apache2::Const::OK; } my $body = $f->ctx || ""; $body .= $buffer while ($f->read($buffer)); unless ($f->seen_eos) { $f->ctx($body); } else { $f->r->subprocess_env; my $js = &js($params); $body =~ s/(<\/head>)/$js$1/i or $body =~ s/()/$1$js/i; $f->print($body); } return Apache2::Const::OK; } ## filter applied to request body sub input { my $f = shift; my $buffer; my $params = &getParams($f->r); # Filter only POST request body unless ($f->r->method eq "POST" && $params) { $f->print($buffer) while ($f->read($buffer)); return Apache2::Const::OK; } my $body = $f->ctx || ""; $body .= $buffer while ($f->read($buffer)); unless ($f->seen_eos) { $f->ctx($body); } else { $f->r->subprocess_env; while ( my ($name, $value) = each %{ $params->{secretFormData} } ) { eval "\$value = $value"; $name = uri_escape $name; $value = uri_escape $value; $body =~ s/$name=.*?((?:&|$))/$name=$value$1/; } $f->print($body); } return Apache2::Const::OK; } 1; __END__ =head1 NAME ReverseProxy::FormFiller - Let Apache fill and submit any html form in place of the user =head1 VERSION Version 0.2 =head1 SYNOPSIS ReverseProxy::FormFiller makes an Apache server, positioned as a frontal server or as a reverse-proxy, fill and (possibly) submit html forms in place of users. This is particularly intended for authentication forms, if you want users to be authenticated with some account, but if you don't want them to know and type any password. But it also works with any html POST form. ReverseProxy::FormFiller is based on Apache2 mod_perl filters. So, you have to enable mod_perl. =head2 Basic Example Assume you want all users requesting auth.example.com to be authenticated as "jdoe", but you don't want to publish jdoe's password. If auth.example.com's authentication form is located at http://auth.example.com/login.php and looks like
login:
password:
create an Apache virtualhost called myauth.example.com, looking like : ServerName myauth.example.com PerlModule ReverseProxy::FormFiller PerlSetVar FormFillerParamFile "/etc/apache2/FormFiller/example" ProxyPass / http://auth.example.com/ ProxyPassReverse / http://auth.example.com/ RequestHeader unset Accept-Encoding Header unset Content-Length PerlOutputFilterHandler ReverseProxy::FormFiller::output PerlInputFilterHandler ReverseProxy::FormFiller::input and create a ReverseProxy::FormFiller config file at /etc/apache2/FormFiller/example, looking like form => '#authForm', submit => "true", publicFormData => { login => "jdoe", password => "fake", }, secretFormData => { password => "secret", }, =head2 Elaborate example Assume you want some people to be authenticated as "user", and some other as "admin". Besides, assume just submit form does not work, but it is necessary to click on the button, since it will execute a javascript function. Finally, assume jQuery is not loaded by the web page displaying the form. /etc/apache2/FormFiller/example will look like jQueryUrl => 'http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js', form => '#authForm', submit => "button[type=submit]", publicFormData => { login => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin" : "user"', password => "fake", }, secretFormData => { password => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin-secret" : "user-secret"', }, =head2 Screwy example Assume you have two authentication forms in the same page, one for the morning and another one for the afternoon : /etc/apache2/FormFiller/example will look like form => '(localtime)[2] >= 12 ? "#morningForm" : "#afternoonForm"', submit => "false", publicFormData => { login => "jdoe", # so, user believe he'll be authenticated as "jdoe" password => "fake", }, secretFormData => { # but actually, he'll be authenticated as "admin" or as "user" login => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin" : "user"', password => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin-secret" : "user-secret"', }, =head1 Details of Apache config =head2 Load Module This is done by PerlModule ReverseProxy::FormFiller This directive has to appear once in Apache config. It can be set in server config or in a container. =head2 Set config parameters This is done by PerlSetVar FormFillerParamFile "/etc/apache2/FormFiller/example" This directive can be set in server config or in a any container directive (as a container, a container or a container). It is applied only to requests matching the corresponding container directive. This directive can be set several times, so a single server can manage several forms (typically, on different virtualhosts, but you can also manage several forms in the same virtualhost). =head2 Filter response body When Apache has received the response from the remote server (if Apache is used as a reverse-proxy) or from the backend server (if used as a frontend), it rewrites html so as to fill the form and possibly submitting it or clicking on a button. Actually, this is done not by directly overwriting the form, but by including some javascript filling and submitting the form. This is done by the directive PerlOutputFilterHandler ReverseProxy::FormFiller::output Besides, ReverseProxy::FormFiller::output can not (or not yet) read zipped contents, so HTTP request headers "Content-encoding" have to be removed. This is done by the directive RequestHeader unset Accept-Encoding And ReverseProxy::FormFiller::output can not (or not yet) set Content-Length response header to the modified response body's length. So, remove Content-Length response header to avoid some bugs: Header unset Content-Length For performances, it is better to handle only html pages containing the aimed form. So, you should place these directives in a container directive matching the form URL (as a directive), so as not to filter any html content. =head2 Filter request body When Apache receives a POST request from a client, it rewrites request POST body, replacing empty or fake data with secret data. This is done by the directive PerlInputFilterHandler ReverseProxy::FormFiller::input For performances, it is better to handle only requests to the form "action" URL. So, you should place this directive in a container directive matching this URL (as a directive), so as not to filter any request. =head1 ReverseProxy::FormFiller config parameters ReverseProxy::FormFiller config file looks similar to a .ini file, but it is not. Actually it is simply a hash content. So, don't forget commas ! In case of syntax error, you'll have a message " content doesn't seem to be a valid perl hash" in Apache error logs. =head2 jQueryUrl URL to load jQuery, since ReverseProxy::FormFiller response filter relies on jQuery (any version >= 1.0) Optional: if empty or not defined, jQuery is supposed to be already loaded in the web page =head2 form jQuery selector to the form to fill. For example : form => "form#authForm", or form => "form:last", Optional: if empty or not defined, first form in web page will be filled - i.e., form => "form:first", This field may rely on perl functions and Apache environment vars, e.g form => '(localtime)[2] >= 12 ? "#morningForm" : "#afternoonForm"', or form => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "#adminForm" : "#userForm"', =head2 submit To enable form autosubmit, or to automatically click on a button. It may be "true" (autosubmit enabled), "false" (autosubmit disabled), or a jQuery selector to the button to click on (this is sometimes useful, when clicking runs a javasript function). It also may rely on perl functions and Apache environment vars (as same as "form" parameter). Optional: if empty or not defined, autosubmit is disabled - that is, default value is "false". For example, submit => "true", or submit => 'button#login', =head2 publicFormData Form fields to fill in html form : these data will be seen by user. Additionnaly, these fields will be controled in POST request when the form will be submitted, to prevent malicious users to change any value. As same as "submit" and "form" parameters, field values can rely on perl functions and Apache environment vars. For example, publicFormData => { company => "SnakeOilsInc", user => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin" : "user"', password => "hidden" }, Note that these data are filled through jQuery method '.val()', so it works only with text inputs, password inputs, select tags and textarea, but not with checkbox and radio buttons. But all inputs =head2 secretFormData Form fields to fill in request body, in addition or in overload to publicFormData. The main with between publicFormData is that these data will not be filled in the html form, so users can't see them. Field values can rely on perl functions and Apache environment vars. secretFormData => { password => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "admin-secret" : "user-secret"', }, =head1 AUTHOR FX Deltombe, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc ReverseProxy::FormFiller You can also look for information at: =over 4 =item * RT: CPAN's request tracker L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 ACKNOWLEDGEMENTS =head1 LICENSE AND COPYRIGHT Copyright 2013 FX Deltombe. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information. ReverseProxy-FormFiller-0.2/t/0000755000175000017500000000000012131606373014150 5ustar fxfxReverseProxy-FormFiller-0.2/t/00-load.t0000644000175000017500000000031712130757541015475 0ustar fxfx#!perl -T use Test::More tests => 1; BEGIN { use_ok( 'ReverseProxy::FormFiller' ) || print "Bail out! "; } diag( "Testing ReverseProxy::FormFiller $ReverseProxy::FormFiller::VERSION, Perl $], $^X" ); ReverseProxy-FormFiller-0.2/t/manifest.t0000644000175000017500000000042012130757541016142 0ustar fxfx#!perl -T use strict; use warnings; use Test::More; unless ( $ENV{RELEASE_TESTING} ) { plan( skip_all => "Author tests not required for installation" ); } eval "use Test::CheckManifest 0.9"; plan skip_all => "Test::CheckManifest 0.9 required" if $@; ok_manifest(); ReverseProxy-FormFiller-0.2/t/pod.t0000644000175000017500000000035012130757541015120 0ustar fxfx#!perl -T use strict; use warnings; use Test::More; # Ensure a recent version of Test::Pod my $min_tp = 1.22; eval "use Test::Pod $min_tp"; plan skip_all => "Test::Pod $min_tp required for testing POD" if $@; all_pod_files_ok(); ReverseProxy-FormFiller-0.2/t/pod-coverage.t0000644000175000017500000000104712130757541016715 0ustar fxfxuse strict; use warnings; use Test::More; # Ensure a recent version of Test::Pod::Coverage my $min_tpc = 1.08; eval "use Test::Pod::Coverage $min_tpc"; plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage" if $@; # Test::Pod::Coverage doesn't require a minimum Pod::Coverage version, # but older versions don't recognize some common documentation styles my $min_pc = 0.18; eval "use Pod::Coverage $min_pc"; plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage" if $@; all_pod_coverage_ok(); ReverseProxy-FormFiller-0.2/MANIFEST0000644000175000017500000000036212131606374015040 0ustar fxfxChanges MANIFEST Makefile.PL README lib/ReverseProxy/FormFiller.pm t/00-load.t t/manifest.t t/pod-coverage.t t/pod.t example/apache-vhost example/FormFillerConfig META.yml Module meta-data (added by MakeMaker) ReverseProxy-FormFiller-0.2/README0000644000175000017500000000255112131260017014557 0ustar fxfxReverseProxy-FormFiller ReverseProxy::FormFiller makes an Apache server, positioned as a frontal server or as a reverse-proxy, fill and (possibly) submit html forms in place of users. This is particularly intended for authentication forms, if you want users to be authenticated with some account, but if you don't want them to know and type any password. But it also works with any html form. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc ReverseProxy::FormFiller You can also look for information at: RT, CPAN's request tracker http://rt.cpan.org/NoAuth/Bugs.html?Dist=ReverseProxy-FormFiller AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/ReverseProxy-FormFiller CPAN Ratings http://cpanratings.perl.org/d/ReverseProxy-FormFiller Search CPAN http://search.cpan.org/dist/ReverseProxy-FormFiller/ LICENSE AND COPYRIGHT Copyright (C) 2013 FX Deltombe This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information. ReverseProxy-FormFiller-0.2/example/0000755000175000017500000000000012131606373015340 5ustar fxfxReverseProxy-FormFiller-0.2/example/FormFillerConfig0000644000175000017500000000334012131263110020436 0ustar fxfx# ReverseProxy::FormFiller config example # Put this file somewhere on your server - typically in # /etc/apache2/FormFiller/ -, adapt it and point to it in Apache2 config, # through a directive like # PerlSetVar FormFillerParamFile "" # jQuery selector to the form to fill # optional: if not defined, first form in web page will be filled form => "form:first", # may rely on perl functions and Apache environment vars, e.g #form => '(localtime)[2] >= 12 ? "#morningForm" : "#afternoonForm"', # To enable form auto-submit, or to automatically click on a button # may be true (enabled), false (disabled), or a jQuery selector to the button to click on submit => "true", # may also rely on perl functions and Apache environment vars # optional, default value: false # URL to load jQuery, # since ReverseProxy::FormFiller response filter relies on jQuery (any version >= 1.0) # optional: if not defined, jQuery is supposed to be already loaded in the web page jQueryUrl => "", # Form fields to fill in response body - filled data will be seen by user # Fields value can rely on perl functions and Apache environment vars publicFormData => { company => "SnakeOilsInc", user => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "user" : $ENV{REMOTE_USER} =~ /dwho/ ? "admin" : "nobody"', password => "hidden" }, # Form fields to fill in request body - filled data will not be seen by user # Fields value can rely on perl functions and Apache environment vars # Note that publicFormData are also re-filled in request body, # to prevent malicious users to tamper them secretFormData => { password => '$ENV{REMOTE_USER} =~ /(rtyler|msmith)/ ? "user-secret" : $ENV{REMOTE_USER} =~ /dwho/ ? "admin-secret" : ""', }, ReverseProxy-FormFiller-0.2/example/apache-vhost0000644000175000017500000000135212131262460017641 0ustar fxfx# ReverseProxy::FormFiller apache2 vhost example # Put this file into /etc/apache2/sites-available/ and adapt it ServerName myauth.example.com ProxyPass / http://auth.example.com/ ProxyPassReverse / http://auth.example.com/ # previous directives are not linked to ReverseProxy::FormFiller # following directives are specific to ReverseProxy::FormFiller PerlModule ReverseProxy::FormFiller PerlSetVar FormFillerParamFile "/etc/apache2/FormFiller/example" RequestHeader unset Accept-Encoding PerlOutputFilterHandler ReverseProxy::FormFiller::output PerlInputFilterHandler ReverseProxy::FormFiller::input ReverseProxy-FormFiller-0.2/Changes0000644000175000017500000000023112131606064015171 0ustar fxfxRevision history for ReverseProxy-FormFiller 0.1 2013/04/10 First version 0.2 2013/04/11 Fix bug with content-length, update doc ReverseProxy-FormFiller-0.2/META.yml0000644000175000017500000000143612131606373015162 0ustar fxfx--- #YAML:1.0 name: ReverseProxy-FormFiller version: 0.2 abstract: Let Apache fill and submit any html form in place of the user author: - FX Deltombe license: perl distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: Apache2::Const: 0 Apache2::Filter: 0 Apache2::Log: 0 Apache2::RequestRec: 0 Apache2::RequestUtil: 0 Apache2::Response: 0 Test::More: 0 URI::Escape: 0 no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.55_02 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4