Apache-AuthCookie-3.20000755000766000024 012251436463 14625 5ustar00mschoutstaff000000000000README100644000766000024 157112251436463 15572 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20OVERVIEW Apache::AuthCookie allows you to intercept a user's first unauthenticated access to a protected document. The user will be presented with a custom form where they can enter authentication credentials. The credentials are posted to the server where AuthCookie verifies them and returns a session key. The session key is returned to the user's browser as a cookie. As a cookie, the browser will pass the session key on every subsequent accesses. AuthCookie will verify the session key and re-authenticate the user. All you have to do is write a custom module that inherits from AuthCookie. See the POD documentation for more details. INSTALLATION This module uses the Apache::Test framework for testing. As a result, any other Apache::Test parameters can be used when generating the Makefile. perl Makefile.PL -apxs /usr/sbin/apxs make make test make install Changes100644000766000024 3735612251436463 16237 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20Revision history for Apache::AuthCookie 3.20 2013-12-09 - login_form: return OK for mobile IE 10, which also ignores content for FORBIDDEN response. - test .pl registry scripts: do not try to load mod_perl.pm - escape html tags in destination. 3.19 2012-12-28 - split out CGI data handling into ::AuthCookie::Params modules - use Apache::Request/Apache2::Request from libapreq if available. Otherwise, fall back to CGI.pm for handling CGI data. - improve "removed cookie" debug log message - add dependencies: autobox, Class::Load - allow username to be '0' - login_form: return OK for SymbianOS, which ignores content for FORBIDDEN responses. - add login_form_status() to override HTTP status returned by login form - recognize_user: return DECLINED if user is not recognized 3.18 2011-01-24 - remove 3.17's test skip hacks, and bump Apache::Test prerequisite to v1.35 which fixes this issue. - fix MANIFEST.SKIP to ignore generated t/conf/mime.types - remove dist.ini, weaver.ini from dists - fixed t/real.t to use correct -withtestmore import syntax - rename sample authcookie handlers to Sample::Apache and Sample::Apache2 namespaces 3.17 2011-01-19 - skip the test suite if running as root. Apache::Test 1.34 fails the test suite if running as root instead of skipping it. By skipping, AuthCookie can be installed via CPAN.pm as root. 3.16 2011-01-19 - require Apache::Test 1.32 - fixes ubuntu build issue - remove mod_perl/mod_perl2 related prereq's from META.yml. The correct mod perl version is not known until Makefile.PL is run. CPAN.pm should not try to install either one until it is known which one is appropriate. (RT 64926) 3.15 2010-08-27 - enable Dist::Zilla Manifest plugin - add FAQ - add FAQ entry on how to protect an entire site/document root - recognize_user: return DECLINED if user is already set - refactor P3P header generation into send_p3p($r) so subclasses can overload it 3.14 2010-04-12 - MP2: doc updates: remove beta warnings, change Apache::AuthCookie to Apache2::Authcookie where appopriate. - docs: change my email to my cpan address - docs: remove POST limitations reference (handled by POST to GET conversion) - sign dist with Module::Signature - add signature test - MP1: perltidy Apache::AuthCookie sources. - update mod_perl2 prereq version (still 2.0.0 RC5, but version number was incorrect in Makefile.PL) - use Dist::Zilla for building the dist 3.13 2010-04-12 - removed: bad dist Version: 3.12 - Makefile.PL If no mod_perl version is found, just require mod_perl2. This makes sure that CPAN testers will get the right dependencies. way. Also set up PREREQ_PM properly for mod_perl version 1. Version: 3.11 - Fix tiny pod doc error. - Escape CR and LF in 'destination' field to prevent possible XSS attack [Steffen Schwigon] Version: 3.10 - Bug Fix: when copying user from prev request, check that $r->prev is defined, not just that $r->is_initial_request is true. Version: 3.09 - POD doc fixes. - MP2: remove _check_request_req() - this was only necessary when running under both MP1 and MP2. Package name change eliminates the need for this. - test suite converted to Test::More style test suites. - descriptive test descriptions added - make login() stash credentials in $r->pnotes("${AuthName}Creds") so that the login form can access the user-supplied credentials if the login fails. - bug fix: use of Apache2::URI::unescape_url() does not handle '+' to ' ' conversion. This caused problems for credentials that contain spaces. - MP2: remove mod_perl features from "use mod_perl2" line. This is no longer supported by mod_perl2. - MP2: _get_form_data() - switch to CGI.pm to handle form data (fixes several form data handling bugs) - In a subrequest, copy $r->prev->user to $r->user (or r->connection->user for MP1). - remove Apache2::AuthCookie::Util - no longer necessary - multi-valued form fields are now handled properly in POST -> GET conversion - MP2: require CGI.pm 3.12 or later Version: 3.08 - fix "authorize user" error log that was missing a debug level check (thanks Barry) - fix test cases 3, 6, 18 for Win32 - clean up t/real.t Version: 3.07 *** mod_perl2 users: THIS RELEASE IS INCOMPATIBLE WITH PAST RELEASES *** *** If you are running mod_perl2, you must update to at least *** *** mod_perl 2.0.0 RC5. The mod_perl2 version of AuthCookie has been *** *** renamed to Apache2::AuthCookie *** ** MP2: RENAME AuthCookie.pm.mp2 to Apache2::AuthCookie. - MP2: Update module, and tests for mod_perl 2.0.0 RC5. mod_perl2 users MUST use Apache2::AuthCookie now. - Require Apache::Test 1.22 - Add support for ${auth_name}SessionTimeout configuration paramter which will re-issue the ticket with the expires parameter set to the value of this configuration setting for each request. This is useful for idle-timeout. - POD fixes. - MP2: fix uninitialized warnings if no POST/GET data (RT 11371) - make sure recognize_user() returns an Apache constant in all cases. Returns DECLINED in cases where we were returning undef before. (Thanks Vivek) - Add support for MS HttpOnly cookie property. Version: 3.06 ** BUG FIX: AuthNameSatisfy (Any|All) directives were broken. AuthCookie was using AuthCookieSatisfy rather than ${auth_name}Satisfy. If you used this feature and had an "AuthCookieSatisfy" directive in your config file, you MUST change this to ${auth_name}Satisfy. E.g.: "WhateverSatisfy All" - created better test cases for AuthNameSatisfy directives. - when redirecting, set Location with headers_out() not err_headers_out(). apache prefers Location in headers_out, even if the status code is not 200. - MP2: Apache::unescape_url() -> Apache::URI::unescape_url() - check for mod_perl 1.9913 or later for Apache::URI (Frederick Moyer) - Remove set status in login.pl which caused malformed custom error document (Frederick Moyer) - Add support for ${auth_name}CookieName to change the name of the cookie used for each auth name. Default remains ${auth_name}_${auth_type} if not set. - make some debug log_error() calls conditional on $debug Version: 3.05 - Fix POD documentation bug (thanks Steve van der Burg) - login(): set Location header with err_headers_out rather than headers_out (Casey West) - put cookie removal code in remove_cookie() method, put cache handling code in handle_cache() (Mark A. Hershberger) - reorganized tree to support multiple mod_perl versions. - rewrote tests to use Apache::Test framework from CPAN. - fix POD errors in authorize() documentation. - initial support for mod_perl version 2 - mp2: check for Apache::RequestRec arg so that unported subclasses throw exceptions. Version: 3.04 - add _convert_to_get() to login_form(), and make POST -> GET conversion skip credentials and destination data so only extra data is copied. This ensures that "destination" wont contain the login data. Version: 3.03 - various POD typos fixed (Eric Cholet) - Add support for ${AuthName}P3P which will set up a P3P header that will be sent with the cookie. - fix undefined warning in _convert_to_get (David K Trudgett) - fix potential cookie clobbering if cookie was set in earlier handler phase in send_cookie() (Carlyn Hicks). - various undefined value warnings eliminated Version: 3.02 - Add support for AuthNameSatisfy directive (can be Any/All, default: Any) - Move cookie path setting into get_cookie_path() so that users can overload this function if they desire (Thanks Raj Chandran) - POST -> GET conversion was broken (r->content called twice). Fixed. Version: 3.01 - adopted support for custom_errors() hook from michael@bizsystems.com. - Fixed incorrect documentation in authorize() (thanks to David Young). - login() handler changes: o if "destination" isnt in posted data, set AuthCookieReason to no_cookie and return to login_form (previously just returned SERVER_ERROR). o if authen_cred() returns false, set AuthCookieReason to bad_credentials and return to the login form. o try to handle POST -> GET conversion. - CGI::Util dependency removed (these are internal subroutines for CGI.pm) - ${AuthName}Path will default to "/" if it is not specified (MSIE 6.0 wont set cookies without path) - fix login() handler change so that destination doesnt get lost on subsequent login attempts (thanks Phillip Molter) Version: 3.00 - New maintiner: Michael Schout - changed to hard coded $VERSION rather than RCS Revision style. - Revamped testing code to use Apache::test (with minor mods). - Added support for ${AuthName}Expires parameter that can be used to make your cookie persistent. - CGI::Util is now used for parsing the "Expires" parameter. If you are missing this package, you probably need to upgrade your CGI.pm package. Version: 2.011 Date: 2000/06/17 08:01:19 Several people have requested a mechanism for the login script to be able to tell the difference between a failed login and a first attempt at a login. Typically one can figure this out by whether a cookie has been sent or not, so I've put some information to that effect in $r->prev->subprocess_env(). See the login script docs for specifics. I also cleaned up the documentation for the login script. Version: 2.010 Date: 2000/06/17 01:37:16 (merged changes from sub-releases into main CVS branch. Been living in CVS hell. My CVS-hubris got the better of me, tried to do things with tags & soforth and wound up severely confused. Thus the version numbers jumped a bit.) Made some minor documentation updates. Version: 2.009 Date: 2000/06/14 15:54:00 *** empty log message *** Version: 2.008 Date: 2000/06/14 15:36:12 Instead of replacing the Set-Cookie header when we set the cookie, add to it. This means we won't clobber other folks' unrelated cookies when we set ours. It also means you need at least mod_perl version 1.24 (I think). [rlocke@infiniteinfo.com (Robert Locke)] Version: 2.007.002.001 Date: 2000/05/14 18:06:30 Added a PerlSetVar *Cache parameter that you can set to a true value to allow cacheing. Also removed cache control from regular requests - cacheing still happens on login & logout. [asparks@cpd.harris.com (Alan Sparks) and dtaylor@vialogix.com (Drew Taylor)] Split off the login-form-displaying code into its own method, called login_form(). Can be overridden in subclasses. The default method uses $r->custom_response() to display the login. [richter@ecos.de (Gerald Richter)] Added documentation for the send_cookie() method. Version: 2.007.001.001 Date: 2000/04/29 20:03:51 Created the send_cookie() method, which just creates the cookie and calls $r->err_header_out to send it to the browser. It is occasionally useful to override this method if you need to change the value of the cookie before sending it out (for instance, to update a timestamp). [mschout@gkg.net (Michael J Schout)] Changed the _cookie_string() method to cookie_string(). Added a PerlSetVar configuration directive "{YourAuth}Secure" which will secure cookies (i.e. only send them over a secure connection). [mschout@gkg.net (Michael J Schout)] Version: 2.007 Date: 2000/04/15 15:27:02 branches: 2.7.1; 2.7.2; If the browser sends a cookie but it's not one related to our authentication, we formerly sent a blank cookie to the authentication methods. Now we act as if no cookie was sent. [asparks@cpd.harris.com (Alan Sparks)] Fixed a server error that occurred when a certain user was required, but a different valid user was logged in. [efujii@miis.edu (Eduardo Fujii)] Added a couple more debug statements that can help figure out what's happening when your auth isn't working. Improved some of the docs. Added some tricks to Makefile.PL to make my life easier. Changed the action of the example login forms from LOGIN to /LOGIN. [michael@bizsystems.com (Michael)] Version: 2.006 Date: 2000/03/26 18:28:32 Added the key() method, which will return the user's current session key, if any. This can be handy inside a method that implements a C directive check (like the C method discussed above) if you put any extra information like clearances or whatever into the session key. Added method-by-method documentation for each method in AuthCookie. Version: 2.005 Date: 2000/03/24 15:20:30 Removed the deprecated methods ->authen and ->authz. If you have configurations that use these methods, you must change to the newer ->authenticate and ->authorize methods. Changed a couple of 'Sample's in the documentation to 'Sample::AuthCookieHandler'. [asparks@cpd.harris.com (Alan Sparks)] Version: 2.004 Date: 2000/03/15 20:53:20 Added documentation about the ability to set cookie domains. That ability actually appeared in 2.002, but I forgot to document it or add notes to the Changes file. Version: 2.003 Date: 2000/03/14 21:08:02 Now returns FORBIDDEN instead of AUTH_REQUIRED when authorization fails or when a user tries to access a protected doc. Version: 2.002 Date: 2000/03/14 17:46:42 Added an internal _cookie_string method that helps construct cookie strings. This shouldn't change any functionality, but makes my job easier. Added a couple of Makefile.PL questions that set the user & group tests should run under. Version: 2.001 Date: 2000/02/11 04:46:59 The login forms may now use the POST method instead of the GET method. This is a big deal, because with GET the user's credentials get logged to access logs, they remain in the user's browser history, and so on. Thanks to cholet@logilune.com (Eric Cholet) for the patch and prodding. There is now a proper test suite, which will fire up an httpd and make requests of it. The test code is adapted from Eric's old example (eg/) suite. I've added a logout() method to help unset cookies. The example logout.pl now uses logout(). Thanks to Aaron Ross (ross@mathforum.com). Version: 2.000 Date: 2000/02/02 13:18:23 First released version, bumped up revision number to 2.0 Created indentify() and authorize() methods to replace authen() and authz(). authen() and authz() are now deprecated and will disappear in a later version. AuthType can now contain colons [adi@certsite.com (Adi)] Nonexistent method calls (via 'require' directive) are no longer shielded in authorization stage - if it fails it fails, and you get a server error. Multiple 'require' directives should work now - previously only the first directive was respected. Changed lots of documentation to reflect the above interface changes. Version: 1.002 Date: 2000/01/27 22:07:13 - Now owned by Ken Williams (ken@forum.swarthmore.edu) - Created indentify() and authorize() methods to replace authen() and authz(). authen() and authz() are now deprecated and will disappear in a later version. - AuthType can now contain colons [adi@certsite.com (Adi)] - Nonexistent method calls (via 'require' directive) are no longer shielded in authorization stage - if it fails it fails, and you get a server error. - Multiple 'require' directives should work now - previously only the first directive was respected. Version: 1.001 Date: 2000/01/25 01:21:05 Eric's original version from CPAN t000755000766000024 012251436463 15011 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20SMOKE100755000766000024 222712251436463 15760 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t#!/Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/bin/perl eval 'exec /Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/bin/perl -S $0 ${1+"$@"}' if $running_under_some_shell; # WARNING: this file is generated, do not edit # generated on Mon Aug 5 15:10:37 2013 # 01: /Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/lib/site_perl/5.16.3/darwin-thread-multi-2level/Apache/TestConfig.pm:961 # 02: /Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/lib/site_perl/5.16.3/darwin-thread-multi-2level/Apache/TestConfig.pm:1051 # 03: /Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/lib/site_perl/5.16.3/darwin-thread-multi-2level/Apache/TestSmoke.pm:775 # 04: /Users/mschout/perl5/perlbrew/perls/perl-5.16.3-apache-2.4/lib/site_perl/5.16.3/darwin-thread-multi-2level/Apache/TestMM.pm:143 # 05: Makefile.PL:50 # 06: Makefile.PL:8 BEGIN { eval { require blib && blib->import; } } use strict; use warnings FATAL => 'all'; use lib qw( /Users/mschout/Dropbox/git/apache-authcookie/../Apache-Test/lib /Users/mschout/Dropbox/git/apache-authcookie/t/lib ); use Apache::TestSmoke; Apache::TestSmoke->new(@ARGV)->run;LICENSE100644000766000024 4365312251436463 15746 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2000 by Ken Williams. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program 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 1, 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2000 by Ken Williams. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End real.t100755000766000024 2743412251436463 16316 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t# TODO: handle line-endings better. Perhaps we should just look for an # identifying part of each page rather than trying to do an exact match # of the entire page. The problem is on win32, some responses come back with # dos-style line endings (not all of them though). Not sure what MacOS does # and I don't have a Mac to test with. Currently, we just strip CR's out of # responses to make the tests pass on Unix and Win32. use strict; use warnings FATAL => 'all'; use lib 'lib'; use Apache::Test '-withtestmore'; use Apache::TestUtil; use Apache::TestRequest qw(GET POST GET_BODY); Apache::TestRequest::user_agent( reset => 1, requests_redirectable => 0 ); plan tests => 49, need_lwp; ok 1; # we loaded. # TODO: the test descriptions should be things other than 'test #' here. # check that /docs/index.html works. If this fails, the test environment did # not configure properly. { my $url = '/docs/index.html'; my $data = strip_cr(GET_BODY $url); like($data, qr/Get the protected document/s, '/docs/index.html seems to work'); } # test no_cookie failure { my $url = '/docs/protected/get_me.html'; my $r = GET $url; like($r->content, qr/Failure reason: 'no_cookie'/s, 'no_cookie works'); } # should succeed with redirect. { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'programmer', credential_1 => 'Hero' ]); is($r->code, 302, 'login produces redirect'); is($r->header('Location'), '/docs/protected/get_me.html', 'redirect header exists, and contains expected url'); } # get protected document with valid cookie. Should succeed. { my $uri = '/docs/protected/get_me.html'; my $r = GET( $uri, Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero;' ); is($r->code, '200', 'get protected document'); like($r->content, qr/Congratulations, you got past AuthCookie/s, 'check protected document content'); } # should fail with no_cookie { my $url = '/docs/protected/get_me.html'; my $dat = strip_cr(GET_BODY($url)); like($dat, qr/Failure reason: 'no_cookie'/s, 'test failure reason: no_cookie'); } # should have a Set-Cookie header that expired at epoch. { my $url = '/docs/logout.pl'; my $r = GET($url); my $data = $r->header('Set-Cookie'); my $expected = 'Sample::AuthCookieHandler_WhatEver=; expires=Mon, 21-May-1971 00:00:00 GMT; path=/'; is($data, $expected, 'logout tries to delete the cookie'); } # check the session key { my $data = GET_BODY( '/docs/echo_cookie.pl', Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero;' ); is(strip_cr($data), 'programmer:Hero', 'session key contains expected data'); } # should fail because of 'require user programmer' { my $r = GET( '/docs/protected/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:duck;' ); is($r->code, '403', 'user "some-user" is not authorized'); } # Should redirect to /docs/protected/get_me.html { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'programmer', credential_1 => 'Heroo' ]); is($r->code, 302, 'programmer:Heroo login replies with redirect'); is($r->header('Location'), '/docs/protected/get_me.html', 'programmer:Heroo location header contains expected URL'); is($r->header('Set-Cookie'), 'Sample::AuthCookieHandler_WhatEver=programmer:Heroo; path=/', 'programmer:Heroo cookie header contains expected data'); } # should get the login form back (bad_cookie). { my $data = GET_BODY( '/docs/protected/get_me.html', Cookie=>'Sample::AuthCookieHandler_WhatEver=programmer:Heroo' ); like($data, qr/Failure reason: 'bad_cookie'/, 'invalid cookie'); } # should get the login form back (bad_credentials) { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'fail', credential_1 => 'Hero' ]); like($r->content, qr/Failure reason: 'bad_credentials'/, 'invalid credentials'); } # check that the destination is right. { my $r = POST('/LOGIN', [ destination => '/docs/authany/get_me.html', credential_0 => 'some-user', credential_1 => 'mypassword' ]); is($r->header('Location'), '/docs/authany/get_me.html', 'Location header is correct'); is($r->header('Set-Cookie'), 'Sample::AuthCookieHandler_WhatEver=some-user:mypassword; path=/', 'Set-Cookie header is correct'); is($r->code, 302, 'redirect code is correct'); } # should fail because all requirements are not met { my $r = GET( '/docs/authall/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:mypassword' ); is($r->code(), 403, 'unauthorized if requirements are not met'); } # test POST to GET conversion { my $r = POST('/docs/protected/get_me.html', [ foo => 'bar' ]); like($r->content, qr#"/docs/protected/get_me\.html\?foo=bar"#, 'POST -> GET conversion works'); } # same test at #16, but in GET mode. Should succeed { my $data = GET_BODY('/docs/protected/get_me.html?foo=bar'); like($data, qr#"/docs/protected/get_me\.html\?foo=bar"#, 'input query string exists in desintation'); } # should succeed (any requirement is met) { my $r = GET( '/docs/authany/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=some-user:mypassword' ); like($r->content, qr/Congratulations, you got past AuthCookie/, 'AuthAny access allowed'); } # any requirement, username=0 works. { my $r = GET( '/docs/authany/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=0:mypassword' ); like($r->content, qr/Congratulations, you got past AuthCookie/, 'username=0 access allowed'); } # login with username=0 works { my $r = POST('/LOGIN', [ destination => '/docs/authany/get_me.html', credential_0 => '0', credential_1 => 'mypassword' ]); is($r->code, 302, 'username=0 login produces redirect'); is($r->header('Location'), '/docs/authany/get_me.html', 'redirect header exists, and contains expected url'); } # should fail: AuthAny and NONE of the requirements are met. { my $r = GET( '/docs/authany/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=nouser:mypassword' ); is($r->code, 403, 'AuthAny forbidden'); } # Should succeed and cookie should have HttpOnly attribute { my $r = POST('/LOGIN-HTTPONLY', [ destination => '/docs/protected/get_me.html', credential_0 => 'programmer', credential_1 => 'Heroo' ]); is($r->header('Location'), '/docs/protected/get_me.html', 'HttpOnly location header'); is($r->header('Set-Cookie'), 'Sample::AuthCookieHandler_WhatEver=programmer:Heroo; path=/; HttpOnly', 'cookie contains HttpOnly attribute'); is($r->code, 302, 'check redirect response code'); } # test SessionTimeout { my $r = GET( '/docs/stimeout/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero' ); like($r->header('Set-Cookie'), qr/^Sample::AuthCookieHandler_WhatEver=.*expires=.+/, 'Set-Cookie contains expires property'); } # should return bad credentials page, and credentials should be in a comment. # We are checking here that $r->prev->pnotes('WhatEverCreds') works. { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'fail', credential_1 => 'Hero' ]); like($r->content, qr/creds: fail Hero/s, 'WhatEverCreds pnotes works'); } # regression - Apache2::URI::unescape_url() does not handle '+' to ' ' # conversion. { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'fail', credential_1 => 'one two' ]); like($r->content, qr/creds: fail one two/, 'read form data handles "+" conversion'); } # variation of '+' to ' ' regression. Make sure we do not remove encoded # '+' { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'fail', credential_1 => 'one+two' ]); like($r->content, qr/creds: fail one\+two/, 'read form data handles "+" conversion with encoded +'); } # XSS attack prevention. make sure embedded \r, \n, \t is escaped in the destination. { my $r = POST('/LOGIN', [ destination => "/docs/protected/get_me.html\r\nX-Test-Bar: True\r\nX-Test-Foo: True\r\n", credential_0 => 'programmer', credential_1 => 'Hero' ]); ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection'); ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection'); # try with escaped CRLF also. $r = POST('/LOGIN', [ destination => "/docs/protected/get_me.html%0d%0aX-Test-Foo: True%0d%0aX-Test-Bar: True\r\n", credential_0 => 'programmer', credential_1 => 'Hero' ]); ok(!defined $r->header('X-Test-Foo'), 'anti XSS injection with escaped CRLF'); ok(!defined $r->header('X-Test-Bar'), 'anti XSS injection with escaped CRLF'); } # embedded html tags in destination { my $r = POST('/LOGIN', [ destination => '">
Embedded Form
' ]); like $r->content, qr{"%22%3E%3Cform method=%22post%22%3EEmbedded Form%3C/form%3E"}; } # embedded script tags { my $r = POST('/LOGIN', [ destination => q{">} ]); ok index($r->content, q{}) == -1; } # make sure '/' in password is preserved. { my $r = POST('/LOGIN', [ destination => '/docs/protected/get_me.html', credential_0 => 'fail', credential_1 => 'one/two' ]); like($r->content, qr/creds: fail one\/two/, 'read form data handles "/" conversion with encoded +'); } # make sure multi-valued form data is preserved. { my $r = POST('/docs/protected/xyz', [ one => 'abc', one => 'def' ]); # check and make sure we are at the login form now. like($r->content, qr/Failure reason: 'no_cookie'/, 'login form was returned'); # check for multi-valued form data. like($r->content, qr/one=abc&one=def/, 'post conversion perserves multi-valued fields'); } # make sure $ENV{REMOTE_USER} gets set up { my $r = GET('/docs/protected/echo_user.pl', Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero' ); like($r->content, qr/User: programmer/); } # test login form response status=OK with SymbianOS { my $orig_agent = Apache::TestRequest::user_agent()->agent; # should get a 403 response by default my $r = GET('/docs/protected/get_me.html'); is $r->code, 403; like $r->content, qr/\bcredential_0\b/, 'got login form'; Apache::TestRequest::user_agent() ->agent('Mozilla/5.0 (SymbianOS/9.1; U; [en]; Series60/3.0 NokiaE60/4.06.0) AppleWebKit/413 (KHTML, like Gecko) Safari/413'); # should get a 200 response for SymbianOS $r = GET('/docs/protected/get_me.html'); is $r->code, 200; like $r->content, qr/\bcredential_0\b/, 'got login form'; Apache::TestRequest::user_agent()->agent($orig_agent); } { # recognize user my $body = GET_BODY('/docs/echo-user.pl', Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero'); is $body, 'programmer'; } # remove CR's from a string. Win32 apache apparently does line ending # conversion, and that can cause test cases to fail because output does not # match expected because expected has UNIX line endings, and OUTPUT has dos # style line endings. sub strip_cr { my $data = shift; $data =~ s/\r//gs; return $data; } # vim: ft=perl ts=4 ai et sw=4 META.yml100644000766000024 123412251436463 16157 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20--- abstract: 'Perl Authentication and Authorization via cookies' author: - 'Michael Schout ' build_requires: {} dynamic_config: 0 generated_by: 'Dist::Zilla version 4.300032, CPAN::Meta::Converter version 2.130880' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Apache-AuthCookie requires: Apache::Test: 1.35 CGI: 3.12 Class::Load: 0.03 autobox: 1.10 resources: bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie homepage: http://search.cpan.org/dist/Apache-AuthCookie/ repository: git://github.com/mschout/apache-authcookie.git version: 3.20 MANIFEST100644000766000024 156212251436463 16043 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20Changes LICENSE MANIFEST MANIFEST.SKIP META.yml Makefile.PL README README.modperl2 SIGNATURE lib/Apache/AuthCookie.pm lib/Apache/AuthCookie/Autobox.pm lib/Apache/AuthCookie/FAQ.pod lib/Apache/AuthCookie/Params.pm lib/Apache/AuthCookie/Params/Base.pm lib/Apache/AuthCookie/Util.pm lib/Apache2/AuthCookie.pm lib/Apache2/AuthCookie/Params.pm t/SMOKE t/Skeleton/AuthCookieHandler.pm t/TEST.PL t/autobox.t t/conf/extra.conf.in t/htdocs/docs/authall/get_me.html t/htdocs/docs/authany/get_me.html t/htdocs/docs/cookiename/get_me.html t/htdocs/docs/echo-user.pl t/htdocs/docs/echo_cookie.pl t/htdocs/docs/index.html t/htdocs/docs/login.pl t/htdocs/docs/logout.pl t/htdocs/docs/protected/echo_user.pl t/htdocs/docs/protected/get_me.html t/htdocs/docs/stimeout/get_me.html t/lib/Sample/Apache/AuthCookieHandler.pm t/lib/Sample/Apache2/AuthCookieHandler.pm t/real.t t/signature.t t/startup.pl TEST.PL100644000766000024 61712251436463 16151 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t#!perl use strict; use warnings FATAL => 'all'; use lib qw(t/lib blib/lib); use Apache::TestRunPerl (); # mod_auth can cause test failures if in the wrong spot in the # Apache chain, so we just make sure it does not get added. (test case: static # mod_perl build and everything else as a DSO). Apache::TestConfig::autoconfig_skip_module_add('mod_auth.c'); Apache::TestRunPerl->new->run(@ARGV); SIGNATURE100644000766000024 642712251436463 16203 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20This file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.70. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA1 10cf60e6ba4d8ca7dcbc7d484e40a9868f0aac02 Changes SHA1 c5a20423d4cd77d244aaa2284a66e8bdc02aaedd LICENSE SHA1 fba0ca1507cb861f782f8c74dc484cb7c6bacc6d MANIFEST SHA1 65ac9daeb4b3a197c69eb953500174519565b7b1 MANIFEST.SKIP SHA1 4239563c7f4276d94c6d35433cb561bd574a9887 META.yml SHA1 5b5d0d04447e4814ff7b91584e094175ac84e141 Makefile.PL SHA1 b9945378262a25db34dcdba06da956a52876188b README SHA1 ccbc46a0385aabadd1e6f4a22f8d4ebb11b44901 README.modperl2 SHA1 2b758b3d1e19809f93820eccad49ab615de0b6cb lib/Apache/AuthCookie.pm SHA1 35ed58ee9739ffed71e37e9dafcb0e7db0125825 lib/Apache/AuthCookie/Autobox.pm SHA1 ff6ee02991e8025f4fb4c51e7886e1f86232a584 lib/Apache/AuthCookie/FAQ.pod SHA1 055775e30ffb7918d295400f71927aaa08eebda8 lib/Apache/AuthCookie/Params.pm SHA1 10ee65206cbf5ec4612b04b856eb9976670397a4 lib/Apache/AuthCookie/Params/Base.pm SHA1 325b016af5c12f35583f99d407d77299a91ae0b6 lib/Apache/AuthCookie/Util.pm SHA1 49d2de9cfd13c0970565af13198549a1c8baa2e7 lib/Apache2/AuthCookie.pm SHA1 a97386fabd5f94a3304624a90f736acf490f19f5 lib/Apache2/AuthCookie/Params.pm SHA1 2bf2c365ca98a98a5981e9c9949bf124dcb232fd t/SMOKE SHA1 3ac8de46e7bba83f6969caec3c9c14cbd99881cb t/Skeleton/AuthCookieHandler.pm SHA1 b1f854e6edecbdd44fc7b8db719e0fe21d9340d1 t/TEST.PL SHA1 290c96de9cbeafe5cc6ad7f3a47d706e740ba28f t/autobox.t SHA1 05df71f07c9f902f70e248add4f310e49c3f1ae6 t/conf/extra.conf.in SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/authall/get_me.html SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/authany/get_me.html SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/cookiename/get_me.html SHA1 ab2ac35a231e02c62fe61dda6800a6fa10e65686 t/htdocs/docs/echo-user.pl SHA1 7cba67da7906c24dcd1b14a9572ff4544b567444 t/htdocs/docs/echo_cookie.pl SHA1 ff64131e263980ea9575b71dc05c5aa2063e135d t/htdocs/docs/index.html SHA1 0dba04a9de174ab9881cfe575d1d23bb5fc588a8 t/htdocs/docs/login.pl SHA1 b9eca1b328da7d703abaec2d6a6d5751866843ac t/htdocs/docs/logout.pl SHA1 b37a85d16cbb2342b407f2ba70b8a61aa1ca67bb t/htdocs/docs/protected/echo_user.pl SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/protected/get_me.html SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/stimeout/get_me.html SHA1 d8a8ea1ebe037a4dea4ad8d1c5b0704b2d43e854 t/lib/Sample/Apache/AuthCookieHandler.pm SHA1 fcdda94993dec73cada9a87d5318b05078cb17ab t/lib/Sample/Apache2/AuthCookieHandler.pm SHA1 8c5edd0e2ea9cfb38b2aad703d931bbde2298aec t/real.t SHA1 61cea839dd94aaaeb301ccac9b83cde4c5c91b42 t/signature.t SHA1 e91bf0ef7d63322eaf15ca7d9907c6db47ce90ca t/startup.pl -----BEGIN PGP SIGNATURE----- Version: GnuPG/MacGPG2 v2.0.17 (Darwin) Comment: GPGTools - http://gpgtools.org iEUEARECAAYFAlKmPTMACgkQ+CqvSzp9LOxxAgCXRv0gHHkF21jeh372GmWvaXrE 3ACfX8HvR3fwrl0w5n58sBc6PBYQlTE= =Mc01 -----END PGP SIGNATURE----- Makefile.PL100644000766000024 415512251436463 16665 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20use ExtUtils::MakeMaker; use File::Spec; use File::Copy; my @CLEAN_FILES = (); my $mp_version = mod_perl_version(); test_configure(); my %makeconf = ( 'NAME' => 'Apache::AuthCookie', 'VERSION_FROM' => 'lib/Apache/AuthCookie.pm', 'PREREQ_PM' => { 'Apache::Test' => 1.35, 'Test::More' => 0, 'CGI' => 0, 'Class::Load' => 0.03, 'autobox' => 1.10 }, 'clean' => { FILES => "@CLEAN_FILES" } ); if (MM->can('signature_target')) { $makeconf{SIGN} = 1; } if ($mp_version == 2) { # 1.999022 == 2.0.0 RC5. mod_perl -> mod_perl2 renamed here. $makeconf{PREREQ_PM}{mod_perl2} = '1.999022'; # CGI.pm 3.12 is required to work with mod_perl2 $makeconf{PREREQ_PM}{CGI} = '3.12'; } elsif ($mp_version == 1) { $makeconf{PREREQ_PM}{mod_perl} = '1.27'; } WriteMakefile(%makeconf); # inspired by Apache::Peek 1.01 sub test_configure { if (eval { require Apache::TestMM }) { # enable "make test" Apache::TestMM->import(qw(test clean)); # accept configs from command line. Apache::TestMM::filter_args(); Apache::TestMM::generate_script('t/TEST'); push @CLEAN_FILES, 't/TEST'; } else { # overload test rule with a no-op warn "***: You should install Apache::Test to do real testing\n"; *MY::test = \&skip_no_apache_test; } } sub mod_perl_version { # try to figure out what version of mod_perl is installed. eval { require mod_perl }; unless ($@) { if ($mod_perl::VERSION >= 1.99) { # mod_perl 2 prior to RC5 (1.99_21 or earlier) die "mod_perl 2.0.0 RC5 or later is required for this module"; } return 1; } eval { require mod_perl2; }; unless ($@) { return 2; } # we didn't fine a supported version issue a warning, and assume version 2. warn "no supported mod_perl version was found\n"; return 2; } sub skip_no_apache_test { return <<'EOF'; test:: @echo \*** This test suite requires Apache::Test available from CPAN EOF } autobox.t100755000766000024 52612251436463 17005 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t#!/usr/bin/env perl # # tests for Apache::AuthCookie::Autobox # use strict; use Test::More tests => 8; # don't use_ok, this needs to load at compile time. use Apache::AuthCookie::Autobox; ok ' '->is_blank; ok ''->is_blank; ok "\t"->is_blank; ok "\n"->is_blank; ok "\r\n"->is_blank; ok undef->is_blank; ok !0->is_blank; ok !'a'->is_blank; startup.pl100644000766000024 6012251436463 17144 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/tuse lib qw(../blib/lib blib/lib lib t/lib); 1; MANIFEST.SKIP100644000766000024 53712251436463 16571 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20^blib/ ^Makefile$ ^Makefile\.old$ ^pm_to_blib$ ^\.git/ ^\.gitignore$ ^t/TEST$ ^t/db/ ~$ error_log ^dist\.ini ^weaver\.ini ^t/httpd.loc # Apache::Test generated files ^t/conf/extra.conf$ ^t/conf/httpd.conf$ ^t/conf/mime.types$ ^t/conf/modperl_inc.pl$ ^t/conf/modperl_startup.pl$ ^t/conf/apache_test_config.pm$ ^t/htdocs/index.html$ ^t/logs/ ^t/TEST$ signature.t100644000766000024 175512251436463 17347 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t#!perl -w ## Test that our SIGNATURE file is valid use Test::More; use strict; if (!$ENV{TEST_SIGNATURE}) { plan skip_all => "Set the environment variable TEST_SIGNATURE to enable this test."; } elsif (!eval { require Module::Signature; 1 }) { plan skip_all => "Please install Module::Signature so that you can verify ". "the integrity of this and other distributions."; } elsif (!-e 'SIGNATURE') { plan skip_all => "SIGNATURE file was not found"; } elsif (-s 'SIGNATURE' == 0) { plan skip_all => "SIGNATURE file was empty"; } elsif (!eval { require Socket; Socket::inet_aton('pgp.mit.edu') }) { plan skip_all => "Cannot connect to the keyserver to check module signature"; } else { plan tests => 1; } my $ret = Module::Signature::verify(); SKIP: { skip "Module::Signature cannot verify", 1 if $ret eq Module::Signature::CANNOT_VERIFY(); cmp_ok $ret, '==', Module::Signature::SIGNATURE_OK(), "Valid signature"; } README.modperl2100644000766000024 264212251436463 17315 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20**** NOTICE: Version 3.07 is incompatible with previous releases. *** As of release 3.07, you must be running mod_perl 2.0.0 RC5 or later. Please note that in mod_perl 2.0.0 RC5 many packages were reneamed. See the mod_perl documentation for updating your code for 2.0.0 RC5. ***** Notes for migrating from mod_perl version 1.x to mod_perl version 2: API changes: In order to avoid requiring the GlobalRequest option under mod_perl 2, the interface to several of the public AuthCookie methods needed to change. Every method that called Apache->request internally, now expects $r to be passed in as the first argument. The affected methods are: modperl 1.x API modperl 2.x API -------------- ---------------- handle_cache() handle_cache($r) remove_cookie() remove_cookie($r) login_form() login_form($r) send_cookie($ses_key) send_cookie($r, $ses_key) key() key($r) So, for example, instead of $auth_type->login_form(), you now need to call $auth_type->login_form($r). Also Note that the Apache interface for retrieving the auth type and username has changed in mod_perl 2, so you will need to update these calls in your AuthCookie subclass as well: modperl 1.x modperl 2.x -------------------- ---------------- $r->auth_type $r->ap_auth_type $r->connection->user $r->user $Id$ conf000755000766000024 012251436463 15736 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/textra.conf.in100644000766000024 1134512251436463 20521 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/confPerlRequire @ServerRoot@/startup.pl PerlModule Sample::Apache::AuthCookieHandler PerlModule Apache::Registry PerlSwitches -I@ServerRoot@/lib PerlModule Sample::Apache2::AuthCookieHandler PerlModule ModPerl::Registry PerlSetVar WhatEverPath / PerlSetVar WhatEverLoginScript /docs/login.pl PerlSetVar AuthCookieDebug 3 PerlSetVar WhatEverCookieName Sample::AuthCookieHandler_WhatEver AllowOverride All # These documents require user to be logged in. AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize AuthType Sample::Apache2::AuthCookieHandler PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize require user programmer # must satisfy any requirement PerlSetVar WhatEverSatisfy Any AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize AuthType Sample::Apache2::AuthCookieHandler PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize Require user some-user Require user programmer Require user 0 # must satisfy all requirements PerlSetVar WhatEverSatisfy All AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize AuthType Sample::Apache2::AuthCookieHandler PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize Require user some-user Require user programmer PerlSetVar WhatEverSessionTimeout +10m AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize AuthType Sample::Apache2::AuthCookieHandler PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize Require user some-user Require user programmer # These documents don't require logging in, but allow it. AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlFixupHandler Sample::Apache::AuthCookieHandler->recognize_user AuthType Sample::Apache2::AuthCookieHandler PerlFixupHandler Sample::Apache2::AuthCookieHandler->recognize_user SetHandler perl-script Options +ExecCGI AuthName WhatEver PerlHandler Apache::Registry AuthType Sample::Apache::AuthCookieHandler PerlFixupHandler Sample::Apache::AuthCookieHandler->recognize_user PerlResponseHandler ModPerl::Registry AuthType Sample::Apache2::AuthCookieHandler PerlFixupHandler Sample::Apache2::AuthCookieHandler->recognize_user # This is the action of the login.pl script above. AuthName WhatEver SetHandler perl-script AuthType Sample::Apache::AuthCookieHandler PerlHandler Sample::Apache::AuthCookieHandler->login AuthType Sample::Apache2::AuthCookieHandler PerlResponseHandler Sample::Apache2::AuthCookieHandler->login # login action that sets HttpOnly PerlSetVar WhatEverHttpOnly On AuthName WhatEver SetHandler perl-script AuthType Sample::Apache::AuthCookieHandler PerlHandler Sample::Apache::AuthCookieHandler->login AuthType Sample::Apache2::AuthCookieHandler PerlResponseHandler Sample::Apache2::AuthCookieHandler->login SetHandler perl-script PerlHandler Apache::Status # vim: filetype=apache sw=2 ts=2 ai et docs000755000766000024 012251436463 17225 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocslogin.pl100755000766000024 343612251436463 21043 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs#!/usr/bin/perl use strict; use constant MP2 => ($mod_perl::VERSION >= 1.99); my $r = MP2 ? Apache2::RequestUtil->request : Apache->request; # Setting the status to 200 here causes the default apache 403 page to be # appended to the custom error document. We understand but the user may not # $r->status(200); my $uri = $r->prev->uri; my $creds = $r->prev->pnotes("WhatEverCreds"); # if there are args, append that to the uri my $args = $r->prev->args; if ($args) { $uri .= "?$args"; } my $reason = $r->prev->subprocess_env("AuthCookieReason"); my $form = < Enter Login and Password HERE # output creds in a comment so the test case can see them. if (defined $creds) { $form .= "\n"; } $form .= <

This is a secure document

Failure reason: '$reason'. Please enter your login and password to authenticate.

Login:
Password:
HERE $r->no_cache(1); my $x = length($form); $r->content_type("text/html"); $r->headers_out->set("Content-length","$x"); $r->headers_out->set("Pragma", "no-cache"); unless (MP2) { $r->send_http_header; } $r->print ($form); logout.pl100755000766000024 114312251436463 21235 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsuse strict; use constant MODPERL2 => ($mod_perl::VERSION >= 1.99); if (MODPERL2) { require Apache2::Access; } my $r = MODPERL2 ? Apache2::RequestUtil->request : Apache->request; my $auth_type = $r->auth_type; # Delete the cookie, etc. $auth_type->logout($r); $r->content_type("text/html"); $r->status(200); unless (MODPERL2) { $r->send_http_header; } print < Logged Out

You have been logged out and the cookie deleted from you browser.

Go ahead and try it again.

EOF Apache000755000766000024 012251436463 16475 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/libAuthCookie.pm100644000766000024 10657112251436463 21300 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apachepackage Apache::AuthCookie; { $Apache::AuthCookie::VERSION = '3.20'; } # ABSTRACT: Perl Authentication and Authorization via cookies use strict; use Carp; use mod_perl qw(1.07 StackedHandlers MethodHandlers Authen Authz); use Apache::Constants qw(:common M_GET FORBIDDEN OK REDIRECT); use Apache::AuthCookie::Params; use Apache::AuthCookie::Util; use Apache::AuthCookie::Autobox; use Apache::Util qw(escape_uri); sub recognize_user ($$) { my ($self, $r) = @_; # only check if user is not already set return DECLINED unless $r->connection->user->is_blank; my $debug = $r->dir_config("AuthCookieDebug") || 0; my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name); return DECLINED if $auth_type->is_blank or $auth_name->is_blank; return DECLINED if $r->header_in('Cookie')->is_blank; my $cookie_name = $self->cookie_name($r); my ($cookie) = $r->header_in('Cookie') =~ /$cookie_name=([^;]+)/; $r->log_error("cookie $cookie_name is $cookie") if $debug >= 2; return DECLINED unless $cookie; my ($user, @args) = $auth_type->authen_ses_key($r, $cookie); if (!$user->is_blank and scalar @args == 0) { $r->log_error("user is $user") if $debug >= 2; # if SessionTimeout is on, send new cookie with new Expires. if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $self->send_cookie($cookie, { expires => $expires }); } $r->connection->user($user); } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $user, @args); } return $user->is_blank ? DECLINED : OK; } sub cookie_name { my ($self, $r) = @_; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; my $cookie_name = $r->dir_config("${auth_name}CookieName") || "${auth_type}_${auth_name}"; return $cookie_name; } sub handle_cache { my $self = shift; my $r = Apache->request; my $auth_name = $r->auth_name; return unless $auth_name; unless ($r->dir_config("${auth_name}Cache")) { $r->no_cache(1); $r->err_header_out(Pragma => 'no-cache'); } } sub remove_cookie { my $self = shift; my $r = Apache->request; my $debug = $r->dir_config("AuthCookieDebug") || 0; my $cookie_name = $self->cookie_name($r); my $str = $self->cookie_string( request => $r, key => $cookie_name, value => '', expires => 'Mon, 21-May-1971 00:00:00 GMT' ); $r->err_headers_out->add("Set-Cookie" => "$str"); $r->log_error("removed cookie $cookie_name") if $debug >= 2; } # convert current request to GET sub _convert_to_get { my ($self, $r) = @_; return unless $r->method eq 'POST'; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->log_error("Converting POST -> GET") if $debug >= 2; my $args = $self->params($r); my @pairs = (); for my $name ($args->param) { # we dont want to copy login data, only extra data next if $name eq 'destination' or $name =~ /^credential_\d+$/; for my $v ($args->param($name)) { push @pairs, escape_uri($name) . '=' . escape_uri($v); } } $r->args(join '&', @pairs) if scalar(@pairs) > 0; $r->method('GET'); $r->method_number(M_GET); $r->headers_in->unset('Content-Length'); } sub params { my ($self, $r) = @_; return Apache::AuthCookie::Params->new($r); } sub login ($$) { my ($self, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name); my $params = $self->params($r); $self->_convert_to_get($r) if $r->method eq 'POST'; unless (defined $params->param('destination')) { $r->log_error("No key 'destination' found in form data"); $r->subprocess_env('AuthCookieReason', 'no_cookie'); return $auth_type->login_form; } # Get the credentials from the data posted by the client my @credentials; for (my $i = 0 ; defined $params->param("credential_$i") ; $i++) { my $key = "credential_$i"; my $val = $params->param("credential_$i"); $r->log_error("$key $val") if $debug >= 2; push @credentials, $val; } # save creds in pnotes in case login form script wants to use them. $r->pnotes("${auth_name}Creds", \@credentials); # Exchange the credentials for a session key. my $ses_key = $self->authen_cred($r, @credentials); unless ($ses_key) { $r->log_error("Bad credentials") if $debug >= 2; $r->subprocess_env('AuthCookieReason', 'bad_credentials'); $r->uri($self->untaint_destination($params->param('destination'))); return $auth_type->login_form; } if ($debug >= 2) { if (defined $ses_key) { $r->log_error("ses_key $ses_key"); } else { $r->log_error("ses_key undefined"); } } $self->send_cookie($ses_key); $self->handle_cache; $r->header_out( "Location" => $self->untaint_destination($params->param('destination'))); return REDIRECT; } sub untaint_destination { my ($self, $dest) = @_; return Apache::AuthCookie::Util::escape_destination($dest); } sub logout($$) { my ($self, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $self->remove_cookie; $self->handle_cache; #my %args = $r->args; #if (exists $args{'redirect'}) { # $r->err_header_out("Location" => $args{'redirect'}); # return REDIRECT; #} else { # $r->status(200); # return OK; #} } sub authenticate ($$) { my ($auth_type, $r) = @_; my $auth_user; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->log_error("auth_type " . $auth_type) if ($debug >= 3); unless ($r->is_initial_req) { if (defined $r->prev) { # we are in a subrequest. Just copy user from previous request. $r->connection->user($r->prev->connection->user); } return OK; } if ($r->auth_type ne $auth_type) { # This location requires authentication because we are being called, # but we don't handle this AuthType. $r->log_error("AuthType mismatch: $auth_type =/= " . $r->auth_type) if $debug >= 3; return DECLINED; } # Ok, the AuthType is $auth_type which we handle, what's the authentication # realm's name? my $auth_name = $r->auth_name; $r->log_error("auth_name " . $auth_name) if $debug >= 2; unless ($auth_name) { $r->log_reason("AuthName not set, AuthType=$auth_type", $r->uri); return SERVER_ERROR; } # Get the Cookie header. If there is a session key for this realm, strip # off everything but the value of the cookie. my $cookie_name = $auth_type->cookie_name($r); my ($ses_key_cookie) = ($r->header_in("Cookie") || "") =~ /$cookie_name=([^;]+)/; $ses_key_cookie = "" unless defined($ses_key_cookie); $r->log_error("ses_key_cookie " . $ses_key_cookie) if ($debug >= 1); $r->log_error("uri " . $r->uri) if ($debug >= 2); if ($ses_key_cookie) { my ($auth_user, @args) = $auth_type->authen_ses_key($r, $ses_key_cookie); if (!$auth_user->is_blank and scalar @args == 0) { # We have a valid session key, so we return with an OK value. # Tell the rest of Apache what the authentication method and # user is. $r->connection->auth_type($auth_type); $r->connection->user($auth_user); $r->log_error("user authenticated as $auth_user") if $debug >= 1; # if SessionTimeout is on, send cookie with new expires if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $auth_type->send_cookie($ses_key_cookie, { expires => $expires }); } return OK; } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $auth_user, @args); } else { # There was a session key set, but it's invalid for some reason. So, # remove it from the client now so when the credential data is posted # we act just like it's a new session starting. $auth_type->remove_cookie; $r->subprocess_env('AuthCookieReason', 'bad_cookie'); } } else { $r->subprocess_env('AuthCookieReason', 'no_cookie'); } # They aren't authenticated, and they tried to get a protected # document. Send them the authen form. return $auth_type->login_form; } sub login_form { my $self = shift; my $r = Apache->request or die "no request"; my $auth_name = $r->auth_name; $self->_convert_to_get($r) if $r->method eq 'POST'; # There should be a PerlSetVar directive that gives us the URI of # the script to execute for the login form. my $authen_script; unless ($authen_script = $r->dir_config($auth_name . "LoginScript")) { $r->log_reason("PerlSetVar '${auth_name}LoginScript' not set", $r->uri); return SERVER_ERROR; } #$r->log_error("Redirecting to $authen_script"); my $status = $self->login_form_status($r); $status = FORBIDDEN unless defined $status; if ($status == OK) { # custom_response doesn't work for OK, DONE, or DECLINED in apache 1.x $r->internal_redirect($authen_script); } else { $r->custom_response($status, $authen_script); } return $status; } sub login_form_status { my ($self, $r) = @_; my $ua = $r->headers_in->get('User-Agent') or return FORBIDDEN; if (Apache::AuthCookie::Util::understands_forbidden_response($ua)) { return FORBIDDEN; } else { return OK; } } sub satisfy_is_valid { my ($auth_type, $r, $satisfy) = @_; $satisfy = lc $satisfy; if ($satisfy eq 'any' or $satisfy eq 'all') { return 1; } else { my $auth_name = $r->auth_name; $r->log_reason("PerlSetVar ${auth_name}Satisfy $satisfy invalid", $r->uri); return 0; } } sub get_satisfy { my ($auth_type, $r) = @_; my $auth_name = $r->auth_name; return lc $r->dir_config("${auth_name}Satisfy") || 'all'; } sub authorize ($$) { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->log_error('authorize() for ' . $r->uri()) if ($debug >= 3); return OK unless $r->is_initial_req; #only the first internal request if ($r->auth_type ne $auth_type) { $r->log_error($auth_type . " auth type is " . $r->auth_type) if ($debug >= 3); return DECLINED; } my $reqs_arr = $r->requires or return DECLINED; my $user = $r->connection->user; if ($user->is_blank) { # authentication failed $r->log_reason("No user authenticated", $r->uri); return FORBIDDEN; } my $satisfy = $auth_type->get_satisfy($r); return SERVER_ERROR unless $auth_type->satisfy_is_valid($r, $satisfy); my $satisfy_all = $satisfy eq 'all'; my ($forbidden); foreach my $req (@$reqs_arr) { my ($requirement, $args) = split /\s+/, $req->{requirement}, 2; $args = '' unless defined $args; $r->log_error("requirement := $requirement, $args") if $debug >= 2; if (lc($requirement) eq 'valid-user') { if ($satisfy_all) { next; } else { return OK; } } if ($requirement eq 'user') { if ($args =~ m/\b$user\b/) { next if $satisfy_all; return OK; # satisfy any } $forbidden = 1; next; } # Call a custom method my $ret_val = $auth_type->$requirement($r, $args); $r->log_error("$auth_type->$requirement returned $ret_val") if $debug >= 3; if ($ret_val == OK) { next if $satisfy_all; return OK; # satisfy any } # Nothing succeeded, deny access to this user. $forbidden = 1; } return $forbidden ? FORBIDDEN : OK; } sub send_cookie { my ($self, $ses_key, $cookie_args) = @_; my $r = Apache->request(); $cookie_args = {} unless defined $cookie_args; my $cookie_name = $self->cookie_name($r); my $cookie = $self->cookie_string( request => $r, key => $cookie_name, value => $ses_key, %$cookie_args ); # add P3P header if user has configured it. $self->send_p3p($r); $r->err_headers_out->add("Set-Cookie" => $cookie); } # send a P3P header if it is configured as ${auth_name}P3P sub send_p3p { my ($self, $r) = @_; my $auth_name = $r->auth_name; if (my $p3p = $r->dir_config("${auth_name}P3P")) { $r->err_header_out(P3P => $p3p); } } sub cookie_string { my $self = shift; # if passed 3 args, we have old-style call. if (scalar(@_) == 3) { carp "deprecated old style call to " . __PACKAGE__ . "::cookie_string()"; my ($r, $key, $value) = @_; return $self->cookie_string(request => $r, key => $key, value => $value); } # otherwise assume named parameters. my %p = @_; for (qw/request key/) { croak "missing required parameter $_" unless defined $p{$_}; } # its okay if value is undef here. my $r = $p{request}; $p{value} = '' unless defined $p{value}; my $string = sprintf '%s=%s', @p{ 'key', 'value' }; my $auth_name = $r->auth_name; if (my $expires = $p{expires} || $r->dir_config("${auth_name}Expires")) { $expires = Apache::AuthCookie::Util::expires($expires); $string .= "; expires=$expires"; } $string .= '; path=' . ($self->get_cookie_path($r) || '/'); #$r->log_error("Attribute ${auth_name}Path not set") unless $path; if (my $domain = $r->dir_config("${auth_name}Domain")) { $string .= "; domain=$domain"; } if ($r->dir_config("${auth_name}Secure")) { $string .= '; secure'; } # HttpOnly is an MS extension. See # http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp if ($r->dir_config("${auth_name}HttpOnly")) { $string .= '; HttpOnly'; } return $string; } sub key { my $self = shift; my $r = Apache->request; my $allcook = ($r->header_in("Cookie") || ""); my $cookie_name = $self->cookie_name($r); return ($allcook =~ /(?:^|\s)$cookie_name=([^;]*)/)[0]; } sub get_cookie_path { my $self = shift; my $r = shift || Apache->request; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Path"); } 1; =pod =head1 NAME Apache::AuthCookie - Perl Authentication and Authorization via cookies =head1 VERSION version 3.20 =head1 SYNOPSIS Make sure your mod_perl is at least 1.24, with StackedHandlers, MethodHandlers, Authen, and Authz compiled in. # In httpd.conf or .htaccess: PerlModule Sample::Apache::AuthCookieHandler PerlSetVar WhatEverPath / PerlSetVar WhatEverLoginScript /login.pl # use to alter how "require" directives are matched. Can be "Any" or "All". # If its "Any", then you must only match Any of the "require" directives. If # its "All", then you must match All of the require directives. # # Default: All PerlSetVar WhatEverSatisfy Any # The following line is optional - it allows you to set the domain # scope of your cookie. Default is the current domain. PerlSetVar WhatEverDomain .yourdomain.com # Use this to only send over a secure connection PerlSetVar WhatEverSecure 1 # Use this if you want user session cookies to expire if the user # doesn't request a auth-required or recognize_user page for some # time period. If set, a new cookie (with updated expire time) # is set on every request. PerlSetVar WhatEverSessionTimeout +30m # to enable the HttpOnly cookie property, use HttpOnly. # this is an MS extension. See # http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp PerlSetVar WhatEverHttpOnly 1 # Usually documents are uncached - turn off here PerlSetVar WhatEverCache 1 # Use this to make your cookies persistent (+2 hours here) PerlSetVar WhatEverExpires +2h # Use to make AuthCookie send a P3P header with the cookie # see http://www.w3.org/P3P/ for details about what the value # of this should be PerlSetVar WhatEverP3P "CP=\"...\"" # These documents require user to be logged in. AuthType Sample::Apache::AuthCookieHandler AuthName WhatEver PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize require valid-user # These documents don't require logging in, but allow it. AuthType Sample::Apache::AuthCookieHandler AuthName WhatEver PerlFixupHandler Sample::Apache::AuthCookieHandler->recognize_user # This is the action of the login.pl script above. AuthType Sample::Apache::AuthCookieHandler AuthName WhatEver SetHandler perl-script PerlHandler Sample::Apache::AuthCookieHandler->login =head1 DESCRIPTION B allows you to intercept a user's first unauthenticated access to a protected document. The user will be presented with a custom form where they can enter authentication credentials. The credentials are posted to the server where AuthCookie verifies them and returns a session key. The session key is returned to the user's browser as a cookie. As a cookie, the browser will pass the session key on every subsequent accesses. AuthCookie will verify the session key and re-authenticate the user. All you have to do is write a custom module that inherits from AuthCookie. Your module is a class which implements two methods: =over 4 =item C Verify the user-supplied credentials and return a session key. The session key can be any string - often you'll use some string containing username, timeout info, and any other information you need to determine access to documents, and append a one-way hash of those values together with some secret key. =item C Verify the session key (previously generated by C, possibly during a previous request) and return the user ID. This user ID will be fed to C<$r-Econnection-Euser()> to set Apache's idea of who's logged in. =back By using AuthCookie versus Apache's built-in AuthBasic you can design your own authentication system. There are several benefits. =over 4 =item 1. The client doesn't *have* to pass the user credentials on every subsequent access. If you're using passwords, this means that the password can be sent on the first request only, and subsequent requests don't need to send this (potentially sensitive) information. This is known as "ticket-based" authentication. =item 2. When you determine that the client should stop using the credentials/session key, the server can tell the client to delete the cookie. Letting users "log out" is a notoriously impossible-to-solve problem of AuthBasic. =item 3. AuthBasic dialog boxes are ugly. You can design your own HTML login forms when you use AuthCookie. =item 4. You can specify the domain of a cookie using PerlSetVar commands. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverDomain .yourhost.com into your server setup file and your access cookies will span all hosts ending in C<.yourhost.com>. =item 5. You can optionally specify the name of your cookie using the C directive. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverCookieName MyCustomName into your server setup file and your cookies for this AuthCookie realm will be named MyCustomName. Default is AuthType_AuthName. =item 6. By default users must satisfy ALL of the C directives. If you want authentication to succeed if ANY C directives are met, use the C directive. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverSatisfy Any into your server startup file and authentication for this realm will succeed if ANY of the C directives are met. =back This is the flow of the authentication handler, less the details of the redirects. Two REDIRECT's are used to keep the client from displaying the user's credentials in the Location field. They don't really change AuthCookie's model, but they do add another round-trip request to the client. =for html

 (-----------------------)     +---------------------------------+
 ( Request a protected   )     | AuthCookie sets custom error    |
 ( page, but user hasn't )---->| document and returns            |
 ( authenticated (no     )     | FORBIDDEN. Apache abandons      |      
 ( session key cookie)   )     | current request and creates sub |      
 (-----------------------)     | request for the error document. |<-+
                               | Error document is a script that |  |
                               | generates a form where the user |  |
                 return        | enters authentication           |  |
          ^------------------->| credentials (login & password). |  |
         / \      False        +---------------------------------+  |
        /   \                                   |                   |
       /     \                                  |                   |
      /       \                                 V                   |
     /         \               +---------------------------------+  |
    /   Pass    \              | User's client submits this form |  |
   /   user's    \             | to the LOGIN URL, which calls   |  |
   | credentials |<------------| AuthCookie->login().            |  |
   \     to      /             +---------------------------------+  |
    \authen_cred/                                                   |
     \ function/                                                    |
      \       /                                                     |
       \     /                                                      |
        \   /            +------------------------------------+     |
         \ /   return    | Authen cred returns a session      |  +--+
          V------------->| key which is opaque to AuthCookie.*|  |
                True     +------------------------------------+  |
                                              |                  |
               +--------------------+         |      +---------------+
               |                    |         |      | If we had a   |
               V                    |         V      | cookie, add   |
  +----------------------------+  r |         ^      | a Set-Cookie  |
  | If we didn't have a session|  e |T       / \     | header to     |
  | key cookie, add a          |  t |r      /   \    | override the  |
  | Set-Cookie header with this|  u |u     /     \   | invalid cookie|
  | session key. Client then   |  r |e    /       \  +---------------+
  | returns session key with   |  n |    /  pass   \               ^    
  | successive requests        |    |   /  session  \              |
  +----------------------------+    |  /   key to    \    return   |
               |                    +-| authen_ses_key|------------+
               V                       \             /     False
  +-----------------------------------+ \           /
  | Tell Apache to set Expires header,|  \         /
  | set user to user ID returned by   |   \       /
  | authen_ses_key, set authentication|    \     /
  | to our type (e.g. AuthCookie).    |     \   /
  +-----------------------------------+      \ /
                                              V
         (---------------------)              ^
         ( Request a protected )              |
         ( page, user has a    )--------------+
         ( session key cookie  )
         (---------------------)


 *  The session key that the client gets can be anything you want.  For
    example, encrypted information about the user, a hash of the
    username and password (similar in function to Digest
    authentication), or the user name and password in plain text
    (similar in function to HTTP Basic authentication).

    The only requirement is that the authen_ses_key function that you
    create must be able to determine if this session_key is valid and
    map it back to the originally authenticated user ID.

=for html 
=head1 METHODS C has several methods you should know about. Here is the documentation for each. =) =over 4 =item * authenticate() This method is one you'll use in a server config file (httpd.conf, .htaccess, ...) as a PerlAuthenHandler. If the user provided a session key in a cookie, the C method will get called to check whether the key is valid. If not, or if there is no key provided, we redirect to the login form. =item * authorize() This will step through the C directives you've given for protected documents and make sure the user passes muster. The C and C directives are handled for you. You can implement custom directives, such as C, by defining a method called C in your subclass, which will then be called. The method will be called as C<$r-Especies($r, $args)>, where C<$args> is everything on your C line after the word C. The method should return OK on success and FORBIDDEN on failure. =item * authen_cred() You must define this method yourself in your subclass of C. Its job is to create the session key that will be preserved in the user's cookie. The arguments passed to it are: sub authen_cred ($$\@) { my $self = shift; # Package name (same as AuthName directive) my $r = shift; # Apache request object my @cred = @_; # Credentials from login form ...blah blah blah, create a session key... return $session_key; } The only limitation on the session key is that you should be able to look at it later and determine the user's username. You are responsible for implementing your own session key format. A typical format is to make a string that contains the username, an expiration time, whatever else you need, and an MD5 hash of all that data together with a secret key. The hash will ensure that the user doesn't tamper with the session key. More info in the Eagle book. =item * authen_ses_key() You must define this method yourself in your subclass of Apache::AuthCookie. Its job is to look at a session key and determine whether it is valid. If so, it returns the username of the authenticated user. sub authen_ses_key ($$$) { my ($self, $r, $session_key) = @_; ...blah blah blah, check whether $session_key is valid... return $ok ? $username : undef; } Optionally, return an array of 2 or more items that will be passed to method custom_errors. It is the responsibility of this method to return the correct response to the main Apache module. =item * custom_errors($r,@_) Note: this interface is experimental. This method handles the server response when you wish to access the Apache custom_response method. Any suitable response can be used. this is particularly useful when implementing 'by directory' access control using the user authentication information. i.e. /restricted /one user is allowed access here /two not here /three AND here The authen_ses_key method would return a normal response when the user attempts to access 'one' or 'three' but return (NOT_FOUND, 'File not found') if an attempt was made to access subdirectory 'two'. Or, in the case of expired credentials, (AUTH_REQUIRED,'Your session has timed out, you must login again'). example 'custom_errors' sub custom_errors { my ($self,$r,$CODE,$msg) = @_; # return custom message else use the server's standard message $r->custom_response($CODE, $msg) if $msg; return($CODE); } where CODE is a valid code from Apache::Constants =item * login() This method handles the submission of the login form. It will call the C method, passing it C<$r> and all the submitted data with names like C<"credential_#">, where # is a number. These will be passed in a simple array, so the prototype is C<$self-Eauthen_cred($r, @credentials)>. After calling C, we set the user's cookie and redirect to the URL contained in the C<"destination"> submitted form field. =item * login_form() This method is responsible for displaying the login form. The default implementation will make an internal redirect and display the URL you specified with the C configuration directive. You can overwrite this method to provide your own mechanism. =item * login_form_status($r) This method returns the HTTP status code that will be returned with the login form response. The default behaviour is to return FORBIDDEN, except for some known browsers which ignore HTML content for FORBIDDEN responses (e.g.: SymbianOS). You can override this method to return custom codes. Note that FORBIDDEN is the most correct code to return as the given request was not authorized to view the requested page. You should only change this if FORBIDDEN does not work. =item * logout() This is simply a convenience method that unsets the session key for you. You can call it in your logout scripts. Usually this looks like C<$r-Eauth_type-Elogout($r);>. =item * send_cookie($session_key) By default this method simply sends out the session key you give it. If you need to change the default behavior (perhaps to update a timestamp in the key) you can override this method. =item * recognize_user() If the user has provided a valid session key but the document isn't protected, this method will set C<$r-Econnection-Euser> anyway. Use it as a PerlFixupHandler, unless you have a better idea. =item * key() This method will return the current session key, if any. This can be handy inside a method that implements a C directive check (like the C method discussed above) if you put any extra information like clearances or whatever into the session key. =item * untaint_destination($self, $uri) This method returns a modified version of the destination parameter before embedding it into the response header. Per default it escapes CR, LF and TAB characters of the uri to avoid certain types of security attacks. You can override it to more limit the allowed destinations, e.g., only allow relative uris, only special hosts or only limited set of characters. =back =head1 EXAMPLE For an example of how to use Apache::AuthCookie, you may want to check out the test suite, which runs AuthCookie through a few of its paces. The documents are located in t/eg/, and you may want to peruse t/real.t to see the generated httpd.conf file (at the bottom of real.t) and check out what requests it's making of the server (at the top of real.t). =head1 THE LOGIN SCRIPT You will need to create a login script (called login.pl above) that generates an HTML form for the user to fill out. You might generate the page using an Apache::Registry script, or an HTML::Mason component, or perhaps even using a static HTML page. It's usually useful to generate it dynamically so that you can define the 'destination' field correctly (see below). The following fields must be present in the form: =over 4 =item 1. The ACTION of the form must be /LOGIN (or whatever you defined in your server configuration as handled by the ->login() method - see example in the SYNOPSIS section). =item 2. The various user input fields (username, passwords, etc.) must be named 'credential_0', 'credential_1', etc. on the form. These will get passed to your authen_cred() method. =item 3. You must define a form field called 'destination' that tells AuthCookie where to redirect the request after successfully logging in. Typically this value is obtained from C<$r-Eprev-Euri>. See the login.pl script in t/eg/. =back In addition, you might want your login page to be able to tell why the user is being asked to log in. In other words, if the user sent bad credentials, then it might be useful to display an error message saying that the given username or password are invalid. Also, it might be useful to determine the difference between a user that sent an invalid auth cookie, and a user that sent no auth cookie at all. To cope with these situations, B will set C<$r-Esubprocess_env('AuthCookieReason')> to one of the following values. =over 4 =item I The user presented no cookie at all. Typically this means the user is trying to log in for the first time. =item I The cookie the user presented is invalid. Typically this means that the user is not allowed access to the given page. =item I The user tried to log in, but the credentials that were passed are invalid. =back You can examine this value in your login form by examining C<$r-Eprev-Esubprocess_env('AuthCookieReason')> (because it's a sub-request). Of course, if you want to give more specific information about why access failed when a cookie is present, your C method can set arbitrary entries in C<$r-Esubprocess_env>. =head1 THE LOGOUT SCRIPT If you want to let users log themselves out (something that can't be done using Basic Auth), you need to create a logout script. For an example, see t/htdocs/docs/logout.pl. Logout scripts may want to take advantage of AuthCookie's C method, which will set the proper cookie headers in order to clear the user's cookie. This usually looks like C<$r-Eauth_type-Elogout($r);>. Note that if you don't necessarily trust your users, you can't count on cookie deletion for logging out. You'll have to expire some server-side login information too. AuthCookie doesn't do this for you, you have to handle it yourself. =head1 ABOUT SESSION KEYS Unlike the sample AuthCookieHandler, you have you verify the user's login and password in C, then you do something like: my $date = localtime; my $ses_key = MD5->hexhash(join(';', $date, $PID, $PAC)); save C<$ses_key> along with the user's login, and return C<$ses_key>. Now C looks up the C<$ses_key> passed to it and returns the saved login. I use Oracle to store the session key and retrieve it later, see the ToDo section below for some other ideas. =head2 TO DO =over 4 =item * It might be nice if the logout method could accept some parameters that could make it easy to redirect the user to another URI, or whatever. I'd have to think about the options needed before I implement anything, though. =back =head1 HISTORY Originally written by Eric Bartley versions 2.x were written by Ken Williams =head1 SEE ALSO L, L, L. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut __END__ # vim: sw=2 ts=2 ai et index.html100644000766000024 16112251436463 21340 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs Get the protected document Apache2000755000766000024 012251436463 16557 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/libAuthCookie.pm100644000766000024 10641212251436463 21354 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache2package Apache2::AuthCookie; { $Apache2::AuthCookie::VERSION = '3.20'; } # ABSTRACT: Perl Authentication and Authorization via cookies use strict; use Carp; use CGI '3.12'; use mod_perl2 '1.99022'; use Apache::AuthCookie::Util; use Apache2::AuthCookie::Params; use Apache2::RequestRec; use Apache2::RequestUtil; use Apache2::Log; use Apache2::Access; use Apache2::Response; use Apache2::Util; use Apache::AuthCookie::Autobox; use APR::Table; use Apache2::Const qw(:common M_GET HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY HTTP_OK); sub recognize_user { my ($self, $r) = @_; # only check if user is not already set return DECLINED unless $r->user->is_blank; my $debug = $r->dir_config("AuthCookieDebug") || 0; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; return DECLINED if $auth_type->is_blank or $auth_name->is_blank; return DECLINED if $r->headers_in->get('Cookie')->is_blank; my $cookie = $self->key($r); my $cookie_name = $self->cookie_name($r); $r->server->log_error("cookie $cookie_name is $cookie") if $debug >= 2; return DECLINED if $cookie->is_blank; my ($user,@args) = $auth_type->authen_ses_key($r, $cookie); if (!$user->is_blank and scalar @args == 0) { $r->server->log_error("user is $user") if $debug >= 2; # send cookie with update expires timestamp if session timeout is on if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $self->send_cookie($r, $cookie, {expires => $expires}); } $r->user($user); } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $user, @args); } return $user->is_blank ? DECLINED : OK; } sub cookie_name { my ($self, $r) = @_; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; my $cookie_name = $r->dir_config("${auth_name}CookieName") || "${auth_type}_${auth_name}"; return $cookie_name; } sub handle_cache { my ($self, $r) = @_; my $auth_name = $r->auth_name; return unless $auth_name; unless ($r->dir_config("${auth_name}Cache")) { $r->no_cache(1); $r->err_headers_out->set(Pragma => 'no-cache'); } } sub remove_cookie { my ($self, $r) = @_; my $cookie_name = $self->cookie_name($r); my $debug = $r->dir_config("AuthCookieDebug") || 0; my $str = $self->cookie_string( request => $r, key => $cookie_name, value => '', expires => 'Mon, 21-May-1971 00:00:00 GMT' ); $r->err_headers_out->add("Set-Cookie" => "$str"); $r->server->log_error("removed cookie $cookie_name") if $debug >= 2; } # convert current request to GET sub _convert_to_get { my ($self, $r) = @_; return unless $r->method eq 'POST'; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("Converting POST -> GET") if $debug >= 2; my $args = $self->params($r); my @pairs = (); for my $name ($args->param) { # we dont want to copy login data, only extra data next if $name eq 'destination' or $name =~ /^credential_\d+$/; for my $v ($args->param($name)) { push @pairs, escape_uri($r, $name) . '=' . escape_uri($r, $v); } } $r->args(join '&', @pairs) if scalar(@pairs) > 0; $r->method('GET'); $r->method_number(M_GET); $r->headers_in->unset('Content-Length'); } sub escape_uri { my ($r, $string) = @_; return Apache2::Util::escape_path($string, $r->pool); } # get GET or POST data and return hash containing the data. sub params { my ($self, $r) = @_; return Apache2::AuthCookie::Params->new($r); } sub login { my ($self, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; my $params = $self->params($r); if ($r->method eq 'POST') { $self->_convert_to_get($r); } unless (defined $params->param('destination')) { $r->server->log_error("No key 'destination' found in form data"); $r->subprocess_env('AuthCookieReason', 'no_cookie'); return $auth_type->login_form($r); } # Get the credentials from the data posted by the client my @credentials; for (my $i = 0; defined $params->param("credential_$i"); $i++) { my $key = "credential_$i"; my $val = $params->param($key); $r->server->log_error("$key $val") if $debug >= 2; push @credentials, $val; } # save creds in pnotes so login form script can use them if it wants to $r->pnotes("${auth_name}Creds", \@credentials); # Exchange the credentials for a session key. my $ses_key = $self->authen_cred($r, @credentials); unless ($ses_key) { $r->server->log_error("Bad credentials") if $debug >= 2; $r->subprocess_env('AuthCookieReason', 'bad_credentials'); $r->uri($self->untaint_destination($params->param('destination'))); return $auth_type->login_form($r); } if ($debug >= 2) { defined $ses_key ? $r->server->log_error("ses_key $ses_key") : $r->server->log_error("ses_key undefined"); } $self->send_cookie($r, $ses_key); $self->handle_cache($r); if ($debug >= 2) { $r->server->log_error("redirect to ", $params->param('destination')); } $r->headers_out->set( "Location" => $self->untaint_destination($params->param('destination'))); return HTTP_MOVED_TEMPORARILY; } sub untaint_destination { my ($self, $dest) = @_; return Apache::AuthCookie::Util::escape_destination($dest); } sub logout { my ($self,$r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $self->remove_cookie($r); $self->handle_cache($r); } sub authenticate { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("auth_type " . $auth_type) if ($debug >= 3); unless ($r->is_initial_req) { if (defined $r->prev) { # we are in a subrequest. Just copy user from previous request. $r->user( $r->prev->user ); } return OK; } if ($debug >= 3) { $r->server->log_error("r=$r authtype=". $r->auth_type); } if ($r->auth_type ne $auth_type) { # This location requires authentication because we are being called, # but we don't handle this AuthType. $r->server->log_error("AuthType mismatch: $auth_type =/= ".$r->auth_type) if $debug >= 3; return DECLINED; } # Ok, the AuthType is $auth_type which we handle, what's the authentication # realm's name? my $auth_name = $r->auth_name; $r->server->log_error("auth_name $auth_name") if $debug >= 2; unless ($auth_name) { $r->server->log_error("AuthName not set, AuthType=$auth_type", $r->uri); return SERVER_ERROR; } # Get the Cookie header. If there is a session key for this realm, strip # off everything but the value of the cookie. my $ses_key_cookie = $auth_type->key($r) || ''; $r->server->log_error("ses_key_cookie " . $ses_key_cookie) if $debug >= 1; $r->server->log_error("uri " . $r->uri) if $debug >= 2; if ($ses_key_cookie) { my ($auth_user, @args) = $auth_type->authen_ses_key($r, $ses_key_cookie); if (!$auth_user->is_blank and scalar @args == 0) { # We have a valid session key, so we return with an OK value. # Tell the rest of Apache what the authentication method and # user is. $r->ap_auth_type($auth_type); $r->user($auth_user); $r->server->log_error("user authenticated as $auth_user") if $debug >= 1; # send new cookie if SessionTimeout is on if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $auth_type->send_cookie($r, $ses_key_cookie, {expires => $expires}); } return OK; } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $auth_user, @args); } else { # There was a session key set, but it's invalid for some reason. So, # remove it from the client now so when the credential data is posted # we act just like it's a new session starting. $auth_type->remove_cookie($r); $r->subprocess_env('AuthCookieReason', 'bad_cookie'); } } else { $r->subprocess_env('AuthCookieReason', 'no_cookie'); } # This request is not authenticated, but tried to get a protected # document. Send client the authen form. return $auth_type->login_form($r); } sub login_form { my ($self, $r) = @_; my $auth_name = $r->auth_name; if ($r->method eq 'POST') { $self->_convert_to_get($r); } # There should be a PerlSetVar directive that gives us the URI of # the script to execute for the login form. my $authen_script; unless ($authen_script = $r->dir_config($auth_name . "LoginScript")) { $r->server->log_error("PerlSetVar '${auth_name}LoginScript' not set", $r->uri); return SERVER_ERROR; } my $status = $self->login_form_status($r); $status = HTTP_FORBIDDEN unless defined $status; $r->custom_response($status, $authen_script); return $status; } sub login_form_status { my ($self, $r) = @_; my $ua = $r->headers_in->get('User-Agent') or return HTTP_FORBIDDEN; if (Apache::AuthCookie::Util::understands_forbidden_response($ua)) { return HTTP_FORBIDDEN; } else { return HTTP_OK; } } sub satisfy_is_valid { my ($auth_type, $r, $satisfy) = @_; $satisfy = lc $satisfy; if ($satisfy eq 'any' or $satisfy eq 'all') { return 1; } else { my $auth_name = $r->auth_name; $r->server->log_error("PerlSetVar ${auth_name}Satisfy $satisfy invalid",$r->uri); return 0; } } sub get_satisfy { my ($auth_type, $r) = @_; my $auth_name = $r->auth_name; return lc $r->dir_config("${auth_name}Satisfy") || 'all'; } sub authorize { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error('authorize() for '.$r->uri()) if $debug >= 3; return OK unless $r->is_initial_req; #only the first internal request if ($r->auth_type ne $auth_type) { $r->server->log_error("auth type mismatch $auth_type != ".$r->auth_type) if $debug >= 3; return DECLINED; } my $reqs_arr = $r->requires or return DECLINED; my $user = $r->user; $r->server->log_error("authorize user=$user type=$auth_type") if $debug >=3; if ($user->is_blank) { # the authentication failed $r->server->log_error("No user authenticated", $r->uri); return HTTP_FORBIDDEN; } my $satisfy = $auth_type->get_satisfy($r); return SERVER_ERROR unless $auth_type->satisfy_is_valid($r,$satisfy); my $satisfy_all = $satisfy eq 'all'; my ($forbidden); foreach my $req (@$reqs_arr) { my ($requirement, $args) = split /\s+/, $req->{requirement}, 2; $args = '' unless defined $args; $r->server->log_error("requirement := $requirement, $args") if $debug >= 2; if (lc($requirement) eq 'valid-user') { if ($satisfy_all) { next; } else { return OK; } } if ($requirement eq 'user') { if ($args =~ m/\b$user\b/) { next if $satisfy_all; return OK; # satisfy any } $forbidden = 1; next; } # Call a custom method my $ret_val = $auth_type->$requirement($r, $args); $r->server->log_error("$auth_type->$requirement returned $ret_val") if $debug >= 3; if ($ret_val == OK) { next if $satisfy_all; return OK; # satisfy any } # Nothing succeeded, deny access to this user. $forbidden = 1; } return $forbidden ? HTTP_FORBIDDEN : OK; } sub send_cookie { my ($self, $r, $ses_key, $cookie_args) = @_; $cookie_args = {} unless defined $cookie_args; my $cookie_name = $self->cookie_name($r); my $cookie = $self->cookie_string( request => $r, key => $cookie_name, value => $ses_key, %$cookie_args ); $self->send_p3p($r); $r->err_headers_out->add("Set-Cookie" => $cookie); } # send P3P header if configured with ${auth_name}P3P sub send_p3p { my ($self, $r) = @_; my $auth_name = $r->auth_name; if (my $p3p = $r->dir_config("${auth_name}P3P")) { $r->err_headers_out->set(P3P => $p3p); } } # cookie_string takes named parameters: # request # key # value # expires # sub cookie_string { my $self = shift; my %p = @_; for (qw/request key/) { croak "missing required parameter $_" unless defined $p{$_}; } # its okay if value is undef here. my $r = $p{request}; $p{value} = '' unless defined $p{value}; my $string = sprintf '%s=%s', @p{'key','value'}; my $auth_name = $r->auth_name; if (my $expires = $p{expires} || $r->dir_config("${auth_name}Expires")) { $expires = Apache::AuthCookie::Util::expires($expires); $string .= "; expires=$expires"; } $string .= '; path=' . ( $self->get_cookie_path($r) || '/' ); if (my $domain = $r->dir_config("${auth_name}Domain")) { $string .= "; domain=$domain"; } if ($r->dir_config("${auth_name}Secure")) { $string .= '; secure'; } # HttpOnly is an MS extension. See # http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp if ($r->dir_config("${auth_name}HttpOnly")) { $string .= '; HttpOnly'; } return $string; } sub key { my ($self, $r) = @_; my $cookie_name = $self->cookie_name($r); my $allcook = ($r->headers_in->get("Cookie") || ""); return ($allcook =~ /(?:^|\s)$cookie_name=([^;]*)/)[0]; } sub get_cookie_path { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Path"); } 1; =pod =head1 NAME Apache2::AuthCookie - Perl Authentication and Authorization via cookies =head1 VERSION version 3.20 =head1 SYNOPSIS Make sure your mod_perl is at least 1.24, with StackedHandlers, MethodHandlers, Authen, and Authz compiled in. # In httpd.conf or .htaccess: PerlModule Sample::Apache2::AuthCookieHandler PerlSetVar WhatEverPath / PerlSetVar WhatEverLoginScript /login.pl # use to alter how "require" directives are matched. Can be "Any" or "All". # If its "Any", then you must only match Any of the "require" directives. If # its "All", then you must match All of the require directives. # # Default: All PerlSetVar WhatEverSatisfy Any # The following line is optional - it allows you to set the domain # scope of your cookie. Default is the current domain. PerlSetVar WhatEverDomain .yourdomain.com # Use this to only send over a secure connection PerlSetVar WhatEverSecure 1 # Use this if you want user session cookies to expire if the user # doesn't request a auth-required or recognize_user page for some # time period. If set, a new cookie (with updated expire time) # is set on every request. PerlSetVar WhatEverSessionTimeout +30m # to enable the HttpOnly cookie property, use HttpOnly. # this is an MS extension. See: # http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp PerlSetVar WhatEverHttpOnly 1 # Usually documents are uncached - turn off here PerlSetVar WhatEverCache 1 # Use this to make your cookies persistent (+2 hours here) PerlSetVar WhatEverExpires +2h # Use to make AuthCookie send a P3P header with the cookie # see http://www.w3.org/P3P/ for details about what the value # of this should be PerlSetVar WhatEverP3P "CP=\"...\"" # These documents require user to be logged in. AuthType Sample::Apache2::AuthCookieHandler AuthName WhatEver PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize require valid-user # These documents don't require logging in, but allow it. AuthType Sample::Apache2::AuthCookieHandler AuthName WhatEver PerlFixupHandler Sample::Apache2::AuthCookieHandler->recognize_user # This is the action of the login.pl script above. AuthType Sample::Apache2::AuthCookieHandler AuthName WhatEver SetHandler perl-script PerlResponseHandler Sample::Apache2::AuthCookieHandler->login =head1 DESCRIPTION This module is for mod_perl version 2. If you are running mod_perl version 1, you should be using B instead. B allows you to intercept a user's first unauthenticated access to a protected document. The user will be presented with a custom form where they can enter authentication credentials. The credentials are posted to the server where AuthCookie verifies them and returns a session key. The session key is returned to the user's browser as a cookie. As a cookie, the browser will pass the session key on every subsequent accesses. AuthCookie will verify the session key and re-authenticate the user. All you have to do is write a custom module that inherits from AuthCookie. Your module is a class which implements two methods: =over 4 =item C Verify the user-supplied credentials and return a session key. The session key can be any string - often you'll use some string containing username, timeout info, and any other information you need to determine access to documents, and append a one-way hash of those values together with some secret key. =item C Verify the session key (previously generated by C, possibly during a previous request) and return the user ID. This user ID will be fed to C<$r-Euser()> to set Apache's idea of who's logged in. =back By using AuthCookie versus Apache's built-in AuthBasic you can design your own authentication system. There are several benefits. =over 4 =item 1. The client doesn't *have* to pass the user credentials on every subsequent access. If you're using passwords, this means that the password can be sent on the first request only, and subsequent requests don't need to send this (potentially sensitive) information. This is known as "ticket-based" authentication. =item 2. When you determine that the client should stop using the credentials/session key, the server can tell the client to delete the cookie. Letting users "log out" is a notoriously impossible-to-solve problem of AuthBasic. =item 3. AuthBasic dialog boxes are ugly. You can design your own HTML login forms when you use AuthCookie. =item 4. You can specify the domain of a cookie using PerlSetVar commands. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverDomain .yourhost.com into your server setup file and your access cookies will span all hosts ending in C<.yourhost.com>. =item 5. You can optionally specify the name of your cookie using the C directive. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverCookieName MyCustomName into your server setup file and your cookies for this AuthCookie realm will be named MyCustomName. Default is AuthType_AuthName. =item 6. By default users must satisfy ALL of the C directives. If you want authentication to succeed if ANY C directives are met, use the C directive. For instance, if your AuthName is C, you can put the command PerlSetVar WhatEverSatisfy Any into your server startup file and authentication for this realm will succeed if ANY of the C directives are met. =back This is the flow of the authentication handler, less the details of the redirects. Two HTTP_MOVED_TEMPORARILY's are used to keep the client from displaying the user's credentials in the Location field. They don't really change AuthCookie's model, but they do add another round-trip request to the client. =for html

 (-----------------------)     +---------------------------------+
 ( Request a protected   )     | AuthCookie sets custom error    |
 ( page, but user hasn't )---->| document and returns            |
 ( authenticated (no     )     | HTTP_FORBIDDEN. Apache abandons |      
 ( session key cookie)   )     | current request and creates sub |      
 (-----------------------)     | request for the error document. |<-+
                               | Error document is a script that |  |
                               | generates a form where the user |  |
                 return        | enters authentication           |  |
          ^------------------->| credentials (login & password). |  |
         / \      False        +---------------------------------+  |
        /   \                                   |                   |
       /     \                                  |                   |
      /       \                                 V                   |
     /         \               +---------------------------------+  |
    /   Pass    \              | User's client submits this form |  |
   /   user's    \             | to the LOGIN URL, which calls   |  |
   | credentials |<------------| AuthCookie->login().            |  |
   \     to      /             +---------------------------------+  |
    \authen_cred/                                                   |
     \ function/                                                    |
      \       /                                                     |
       \     /                                                      |
        \   /            +------------------------------------+     |
         \ /   return    | Authen cred returns a session      |  +--+
          V------------->| key which is opaque to AuthCookie.*|  |
                True     +------------------------------------+  |
                                              |                  |
               +--------------------+         |      +---------------+
               |                    |         |      | If we had a   |
               V                    |         V      | cookie, add   |
  +----------------------------+  r |         ^      | a Set-Cookie  |
  | If we didn't have a session|  e |T       / \     | header to     |
  | key cookie, add a          |  t |r      /   \    | override the  |
  | Set-Cookie header with this|  u |u     /     \   | invalid cookie|
  | session key. Client then   |  r |e    /       \  +---------------+
  | returns session key with   |  n |    /  pass   \               ^    
  | successive requests        |    |   /  session  \              |
  +----------------------------+    |  /   key to    \    return   |
               |                    +-| authen_ses_key|------------+
               V                       \             /     False
  +-----------------------------------+ \           /
  | Tell Apache to set Expires header,|  \         /
  | set user to user ID returned by   |   \       /
  | authen_ses_key, set authentication|    \     /
  | to our type (e.g. AuthCookie).    |     \   /
  +-----------------------------------+      \ /
                                              V
         (---------------------)              ^
         ( Request a protected )              |
         ( page, user has a    )--------------+
         ( session key cookie  )
         (---------------------)


 *  The session key that the client gets can be anything you want.  For
    example, encrypted information about the user, a hash of the
    username and password (similar in function to Digest
    authentication), or the user name and password in plain text
    (similar in function to HTTP Basic authentication).

    The only requirement is that the authen_ses_key function that you
    create must be able to determine if this session_key is valid and
    map it back to the originally authenticated user ID.

=for html 
=head1 METHODS C has several methods you should know about. Here is the documentation for each. =) =over 4 =item * authenticate() This method is one you'll use in a server config file (httpd.conf, .htaccess, ...) as a PerlAuthenHandler. If the user provided a session key in a cookie, the C method will get called to check whether the key is valid. If not, or if there is no key provided, we redirect to the login form. =item * authorize() This will step through the C directives you've given for protected documents and make sure the user passes muster. The C and C directives are handled for you. You can implement custom directives, such as C, by defining a method called C in your subclass, which will then be called. The method will be called as C<$r-Especies($r, $args)>, where C<$args> is everything on your C line after the word C. The method should return OK on success and HTTP_FORBIDDEN on failure. =item * authen_cred() You must define this method yourself in your subclass of C. Its job is to create the session key that will be preserved in the user's cookie. The arguments passed to it are: sub authen_cred ($$\@) { my $self = shift; # Package name (same as AuthName directive) my $r = shift; # Apache request object my @cred = @_; # Credentials from login form ...blah blah blah, create a session key... return $session_key; } The only limitation on the session key is that you should be able to look at it later and determine the user's username. You are responsible for implementing your own session key format. A typical format is to make a string that contains the username, an expiration time, whatever else you need, and an MD5 hash of all that data together with a secret key. The hash will ensure that the user doesn't tamper with the session key. More info in the Eagle book. =item * authen_ses_key() You must define this method yourself in your subclass of Apache2::AuthCookie. Its job is to look at a session key and determine whether it is valid. If so, it returns the username of the authenticated user. sub authen_ses_key ($$$) { my ($self, $r, $session_key) = @_; ...blah blah blah, check whether $session_key is valid... return $ok ? $username : undef; } Optionally, return an array of 2 or more items that will be passed to method custom_errors. It is the responsibility of this method to return the correct response to the main Apache module. =item * custom_errors($r,@_) Note: this interface is experimental. This method handles the server response when you wish to access the Apache custom_response method. Any suitable response can be used. this is particularly useful when implementing 'by directory' access control using the user authentication information. i.e. /restricted /one user is allowed access here /two not here /three AND here The authen_ses_key method would return a normal response when the user attempts to access 'one' or 'three' but return (NOT_FOUND, 'File not found') if an attempt was made to access subdirectory 'two'. Or, in the case of expired credentials, (AUTH_REQUIRED,'Your session has timed out, you must login again'). example 'custom_errors' sub custom_errors { my ($self,$r,$CODE,$msg) = @_; # return custom message else use the server's standard message $r->custom_response($CODE, $msg) if $msg; return($CODE); } where CODE is a valid code from Apache2::Const =item * login() This method handles the submission of the login form. It will call the C method, passing it C<$r> and all the submitted data with names like C<"credential_#">, where # is a number. These will be passed in a simple array, so the prototype is C<$self-Eauthen_cred($r, @credentials)>. After calling C, we set the user's cookie and redirect to the URL contained in the C<"destination"> submitted form field. =item * login_form($r) This method is responsible for displaying the login form. The default implementation will make an internal redirect and display the URL you specified with the C configuration directive. You can overwrite this method to provide your own mechanism. =item * login_form_status($r) This method returns the HTTP status code that will be returned with the login form response. The default behaviour is to return HTTP_FORBIDDEN, except for some known browsers which ignore HTML content for HTTP_FORBIDDEN responses (e.g.: SymbianOS). You can override this method to return custom codes. Note that HTTP_FORBIDDEN is the most correct code to return as the given request was not authorized to view the requested page. You should only change this if HTTP_FORBIDDEN does not work. =item * logout() This is simply a convenience method that unsets the session key for you. You can call it in your logout scripts. Usually this looks like C<$r-Eauth_type-Elogout($r);>. =item * send_cookie($r, $session_key) By default this method simply sends out the session key you give it. If you need to change the default behavior (perhaps to update a timestamp in the key) you can override this method. =item * recognize_user() If the user has provided a valid session key but the document isn't protected, this method will set C<$r-Euser> anyway. Use it as a PerlFixupHandler, unless you have a better idea. =item * key($r) This method will return the current session key, if any. This can be handy inside a method that implements a C directive check (like the C method discussed above) if you put any extra information like clearances or whatever into the session key. =item * untaint_destination($self, $uri) This method returns a modified version of the destination parameter before embedding it into the response header. Per default it escapes CR, LF and TAB characters of the uri to avoid certain types of security attacks. You can override it to more limit the allowed destinations, e.g., only allow relative uris, only special hosts or only limited set of characters. =back =head1 EXAMPLE For an example of how to use Apache2::AuthCookie, you may want to check out the test suite, which runs AuthCookie through a few of its paces. The documents are located in t/eg/, and you may want to peruse t/real.t to see the generated httpd.conf file (at the bottom of real.t) and check out what requests it's making of the server (at the top of real.t). =head1 THE LOGIN SCRIPT You will need to create a login script (called login.pl above) that generates an HTML form for the user to fill out. You might generate the page using a ModPerl::Registry script, a HTML::Mason component, an Apache handler, or perhaps even using a static HTML page. It's usually useful to generate it dynamically so that you can define the 'destination' field correctly (see below). The following fields must be present in the form: =over 4 =item 1. The ACTION of the form must be /LOGIN (or whatever you defined in your server configuration as handled by the ->login() method - see example in the SYNOPSIS section). =item 2. The various user input fields (username, passwords, etc.) must be named 'credential_0', 'credential_1', etc. on the form. These will get passed to your authen_cred() method. =item 3. You must define a form field called 'destination' that tells AuthCookie where to redirect the request after successfully logging in. Typically this value is obtained from C<$r-Eprev-Euri>. See the login.pl script in t/eg/. =back In addition, you might want your login page to be able to tell why the user is being asked to log in. In other words, if the user sent bad credentials, then it might be useful to display an error message saying that the given username or password are invalid. Also, it might be useful to determine the difference between a user that sent an invalid auth cookie, and a user that sent no auth cookie at all. To cope with these situations, B will set C<$r-Esubprocess_env('AuthCookieReason')> to one of the following values. =over 4 =item I The user presented no cookie at all. Typically this means the user is trying to log in for the first time. =item I The cookie the user presented is invalid. Typically this means that the user is not allowed access to the given page. =item I The user tried to log in, but the credentials that were passed are invalid. =back You can examine this value in your login form by examining C<$r-Eprev-Esubprocess_env('AuthCookieReason')> (because it's a sub-request). Of course, if you want to give more specific information about why access failed when a cookie is present, your C method can set arbitrary entries in C<$r-Esubprocess_env>. =head1 THE LOGOUT SCRIPT If you want to let users log themselves out (something that can't be done using Basic Auth), you need to create a logout script. For an example, see t/htdocs/docs/logout.pl. Logout scripts may want to take advantage of AuthCookie's C method, which will set the proper cookie headers in order to clear the user's cookie. This usually looks like C<$r-Eauth_type-Elogout($r);>. Note that if you don't necessarily trust your users, you can't count on cookie deletion for logging out. You'll have to expire some server-side login information too. AuthCookie doesn't do this for you, you have to handle it yourself. =head1 ABOUT SESSION KEYS Unlike the sample AuthCookieHandler, you have you verify the user's login and password in C, then you do something like: my $date = localtime; my $ses_key = MD5->hexhash(join(';', $date, $PID, $PAC)); save C<$ses_key> along with the user's login, and return C<$ses_key>. Now C looks up the C<$ses_key> passed to it and returns the saved login. I use Oracle to store the session key and retrieve it later, see the ToDo section below for some other ideas. =head2 TO DO =over 4 =item * It might be nice if the logout method could accept some parameters that could make it easy to redirect the user to another URI, or whatever. I'd have to think about the options needed before I implement anything, though. =back =head1 HISTORY Originally written by Eric Bartley versions 2.x were written by Ken Williams =head1 COPYRIGHT Copyright (c) 2000 Ken Williams. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L, L, L. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut __END__ # vim: sw=4 ts=4 ai et echo-user.pl100755000766000024 55012251436463 21577 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsuse strict; use constant MODPERL2 => ($mod_perl::VERSION >= 1.99); if (MODPERL2) { require Apache2::Access; } my $r = MODPERL2 ? Apache2::RequestUtil->request : Apache->request; $r->content_type("text/html"); $r->status(200); $r->send_http_header unless MODPERL2; my $user = MODPERL2 ? $r->user : $r->connection->user; print "$user"; echo_cookie.pl100755000766000024 56612251436463 22163 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsuse strict; use constant MODPERL2 => ($mod_perl::VERSION >= 1.99); if (MODPERL2) { require Apache2::Access; } my $r = MODPERL2 ? Apache2::RequestUtil->request : Apache->request; my $auth_type = $r->auth_type; # Delete the cookie, etc. $r->content_type("text/html"); $r->status(200); $r->send_http_header unless MODPERL2; print $auth_type->key($r); AuthCookie000755000766000024 012251436463 20530 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/ApacheFAQ.pod100644000766000024 325112251436463 22004 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookie# make Dist::Zilla happy. package Apache::AuthCookie::FAQ; 1; __END__ =pod =head1 NAME Apache::AuthCookie::FAQ =head1 VERSION version 3.20 =head1 DESCRIPTION This document serves to answer the most frequently asked questions about L. =head2 How can I protect an entire site (/) with Apache::AuthCookie? You have to give an Apache C directive that applies to all requests for B for your login handler. The easiest way to do this is to override the auth handlers for your login script. For example, if your login handler is C, then you need to use something like the following: AuthType My::AuthCookieHandler AuthName Whatever PerlAuthenHandler My::AuthCookieHandler->authenticate PerlAuthzHandler My::AuthCookieHandler->authorize require valid-user PerlAuthenHandler Apache2::Const::OK PerlAuthzHandler Apache2::Const::OK ... =head1 NAME Apache::AuthCookie::FAQ - Frequently Asked Questions about Apache::AuthCookie. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Util.pm100644000766000024 666312251436463 22156 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookiepackage Apache::AuthCookie::Util; { $Apache::AuthCookie::Util::VERSION = '3.20'; } # ABSTRACT: Internal Utility Functions for AuthCookie use strict; # -- expires() shamelessly taken from CGI::Util sub expires { my($time,$format) = @_; $format ||= 'http'; my(@MON)=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; my(@WDAY) = qw/Sun Mon Tue Wed Thu Fri Sat/; # pass through preformatted dates for the sake of expire_calc() $time = expire_calc($time); return $time unless $time =~ /^\d+$/; # make HTTP/cookie date string from GMT'ed time # (cookies use '-' as date separator, HTTP uses ' ') my($sc) = ' '; $sc = '-' if $format eq "cookie"; my($sec,$min,$hour,$mday,$mon,$year,$wday) = gmtime($time); $year += 1900; return sprintf("%s, %02d$sc%s$sc%04d %02d:%02d:%02d GMT", $WDAY[$wday],$mday,$MON[$mon],$year,$hour,$min,$sec); } # -- expire_calc() shamelessly taken from CGI::Util # This internal routine creates an expires time exactly some number of # hours from the current time. It incorporates modifications from # Mark Fisher. sub expire_calc { my($time) = @_; my(%mult) = ('s'=>1, 'm'=>60, 'h'=>60*60, 'd'=>60*60*24, 'M'=>60*60*24*30, 'y'=>60*60*24*365); # format for time can be in any of the forms... # "now" -- expire immediately # "+180s" -- in 180 seconds # "+2m" -- in 2 minutes # "+12h" -- in 12 hours # "+1d" -- in 1 day # "+3M" -- in 3 months # "+2y" -- in 2 years # "-3m" -- 3 minutes ago(!) # If you don't supply one of these forms, we assume you are # specifying the date yourself my($offset); if (!$time || (lc($time) eq 'now')) { $offset = 0; } elsif ($time=~/^\d+/) { return $time; } elsif ($time=~/^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/) { $offset = ($mult{$2} || 1)*$1; } else { return $time; } return (time+$offset); } # escape embedded CR, LF, TAB's to prevent possible XSS attacks. # see http://www.securiteam.com/securityreviews/5WP0E2KFGK.html sub escape_destination { my $text = shift; $text =~ s/([\r\n\t\>\<"])/sprintf("%%%02X", ord $1)/ge; return $text; } # return true if the given user agent understands a HTTP_FORBIDDEN response # with custom content. Some agents (e.g.: Symbian OS browser), use their own # HTML and completely ignore the HTTP content. sub understands_forbidden_response { my $ua = shift; return 0 if $ua =~ qr{\AMozilla/5\.0 \(SymbianOS/} # Symbian phones or $ua =~ qr{\bIEMobile/10}; # Nokia Lumia 920, possibly others? return 1; } 1; __END__ =pod =head1 NAME Apache::AuthCookie::Util - Internal Utility Functions for AuthCookie =head1 VERSION version 3.20 =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Params.pm100644000766000024 360512251436463 22455 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookiepackage Apache::AuthCookie::Params; { $Apache::AuthCookie::Params::VERSION = '3.20'; } # ABSTRACT: AuthCookie Params Driver for mod_perl 1.x use strict; use warnings; use base 'Apache::AuthCookie::Params::Base'; use Class::Load qw(try_load_class load_class); sub _new_instance { my ($class, $r) = @_; my $debug = $r->dir_config('AuthCookieDebug') || 0; my $obj; if (try_load_class('Apache::Request')) { $r->server->log_error("params: using Apache::Request") if $debug >= 3; return Apache::Request->new($r); } else { load_class('CGI'); $r->server->log_error("params: using CGI") if $debug >= 3; # CGI->new($r) doesn't work for POST->GET conversion in MP1, so get # query string manually my $qstring = $r->method eq 'POST' ? scalar $r->content : scalar $r->args; return CGI->new($qstring); } return; } 1; __END__ =pod =head1 NAME Apache::AuthCookie::Params - AuthCookie Params Driver for mod_perl 1.x =head1 VERSION version 3.20 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This class handles CGI form data for L. It will try to use L (from libapreq) if it is available. If not, it will fall back to use L. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Skeleton000755000766000024 012251436463 16575 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/tAuthCookieHandler.pm100644000766000024 140212251436463 22621 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/Skeletonpackage Skeleton::AuthCookieHandler; use strict; use Apache; use Apache::Constants qw(:common); use Apache::AuthCookie; use vars qw($VERSION @ISA); $VERSION = substr(q$Revision$, 10); @ISA = qw(Apache::AuthCookie); sub authen_cred ($$\@) { my $self = shift; my $r = shift; my @creds = @_; # This would really authenticate the credentials # and return the session key. # Here I'm just using setting the session # key to the credentials and delaying authentication. # # Similar to HTTP Basic Authentication, only not base 64 encoded join(":", @creds); } sub authen_ses_key ($$$) { my $self = shift; my $r = shift; my($user, $password) = split(/:/, shift, 2); # Authenticate use here... return $user; } 1; Autobox.pm100644000766000024 256212251436463 22654 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookiepackage Apache::AuthCookie::Autobox; { $Apache::AuthCookie::Autobox::VERSION = '3.20'; } # ABSTRACT: Autobox Extensions for AuthCookie use strict; use base 'autobox'; sub import { my $class = shift; $class->SUPER::import( SCALAR => __PACKAGE__ . '::Scalar', UNDEF => __PACKAGE__ . '::Scalar'); } package Apache::AuthCookie::Autobox::Scalar; { $Apache::AuthCookie::Autobox::Scalar::VERSION = '3.20'; } sub is_blank { return defined $_[0] && ($_[0] =~ /\S/) ? 0 : 1; } 1; __END__ =pod =head1 NAME Apache::AuthCookie::Autobox - Autobox Extensions for AuthCookie =head1 VERSION version 3.20 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This module provides autobox extensions for AuthCookie =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut AuthCookie000755000766000024 012251436463 20612 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache2Params.pm100644000766000024 331112251436463 22531 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache2/AuthCookiepackage Apache2::AuthCookie::Params; { $Apache2::AuthCookie::Params::VERSION = '3.20'; } # ABSTRACT: AuthCookie Params Driver for mod_perl 2.x use strict; use warnings; use base 'Apache::AuthCookie::Params::Base'; use Class::Load qw(try_load_class load_class); sub _new_instance { my ($class, $r) = @_; my $debug = $r->dir_config('AuthCookieDebug') || 0; my $obj; if (try_load_class('Apache2::Request')) { $r->server->log_error("params: using Apache2::Request") if $debug >= 3; return Apache2::Request->new($r); } else { load_class('CGI'); $r->server->log_error("params: using CGI") if $debug >= 3; return CGI->new($r); } return; } 1; __END__ =pod =head1 NAME Apache2::AuthCookie::Params - AuthCookie Params Driver for mod_perl 2.x =head1 VERSION version 3.20 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This class handles CGI form data for L. It will try to use L (from libapreq2) if it is available. If not, it will fall back to use L. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut authall000755000766000024 012251436463 20657 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsget_me.html100644000766000024 25012251436463 23122 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/authall Congratulations

Congratulations, you got past AuthCookie

Log Out

authany000755000766000024 012251436463 20676 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsget_me.html100644000766000024 25012251436463 23141 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/authany Congratulations

Congratulations, you got past AuthCookie

Log Out

stimeout000755000766000024 012251436463 21076 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsget_me.html100644000766000024 25012251436463 23341 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/stimeout Congratulations

Congratulations, you got past AuthCookie

Log Out

protected000755000766000024 012251436463 21216 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsget_me.html100644000766000024 25012251436463 23461 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/protected Congratulations

Congratulations, you got past AuthCookie

Log Out

Params000755000766000024 012251436463 21753 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookieBase.pm100644000766000024 257012251436463 23327 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/lib/Apache/AuthCookie/Paramspackage Apache::AuthCookie::Params::Base; { $Apache::AuthCookie::Params::Base::VERSION = '3.20'; } # ABSTRACT: Interanal CGI AuthCookie Params Base Class use strict; use warnings; sub new { my ($class, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; # use existing params object if possible my $obj = $r->pnotes($class); if (defined $obj) { return $obj; } $obj = $class->_new_instance($r); $r->pnotes($class, $obj); return $obj; } 1; __END__ =pod =head1 NAME Apache::AuthCookie::Params::Base - Interanal CGI AuthCookie Params Base Class =head1 VERSION version 3.20 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This is the base class for AuthCookie Params drivers. =head1 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests to bug-apache-authcookie@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=Apache-AuthCookie =head1 AUTHOR Michael Schout =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Ken Williams. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut cookiename000755000766000024 012251436463 21337 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docsget_me.html100644000766000024 25012251436463 23602 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/cookiename Congratulations

Congratulations, you got past AuthCookie

Log Out

echo_user.pl100755000766000024 57612251436463 23662 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/htdocs/docs/protecteduse strict; use constant MODPERL2 => ($mod_perl::VERSION >= 1.99); if (MODPERL2) { require Apache2::Access; } my $r = MODPERL2 ? Apache2::RequestUtil->request : Apache->request; my $auth_type = $r->auth_type; # Delete the cookie, etc. $r->content_type("text/html"); $r->status(200); $r->send_http_header unless MODPERL2; print "User: $ENV{REMOTE_USER}\n"; Apache000755000766000024 012251436463 20161 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/lib/SampleAuthCookieHandler.pm100644000766000024 322212251436463 24207 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/lib/Sample/Apachepackage Sample::Apache::AuthCookieHandler; use strict; use Apache; use Apache::Constants qw(:common); use Apache::AuthCookie; use vars qw(@ISA); @ISA = qw(Apache::AuthCookie); sub authen_cred ($$\@) { my $self = shift; my $r = shift; my @creds = @_; return if $creds[0] eq 'fail'; # simulate bad_credentials # This would really authenticate the credentials # and return the session key. # Here I'm just using setting the session # key to the credentials and delaying authentication. # # Similar to HTTP Basic Authentication, only not base 64 encoded join(":", @creds); } sub authen_ses_key ($$$) { my $self = shift; my $r = shift; my($user, $password) = split(/:/, shift, 2); if ($user eq 'programmer' && $password eq 'Hero') { return $user; } elsif ($user eq 'some-user') { return $user; } elsif ($user eq '0') { return $user; } return; } sub dwarf { my $self = shift; my $r = shift; my $user = $r->connection->user; if ("bashful doc dopey grumpy happy sleepy sneezy programmer" =~ /\b$user\b/) { # You might be thinking to yourself that there were only 7 # dwarves, that's because the marketing folks left out # the often under appreciated "programmer" because: # # 10) He didn't hold 8 to 5 hours. # 9) Sometimes forgot to shave several days at a time. # 8) Was always buzzed on caffine. # 7) Wasn't into heavy labor. # 6) Prone to "swearing while he worked." # 5) Wasn't as easily controlled as the other dwarves. # # 1) He posted naked pictures of Snow White to the Internet. return OK; } return FORBIDDEN; } 1; Apache2000755000766000024 012251436463 20243 5ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/lib/SampleAuthCookieHandler.pm100644000766000024 610712251436463 24276 0ustar00mschoutstaff000000000000Apache-AuthCookie-3.20/t/lib/Sample/Apache2package Sample::Apache2::AuthCookieHandler; use strict; use Apache2::Const qw(:common HTTP_FORBIDDEN); use Apache2::AuthCookie; use Apache2::RequestRec; use Apache2::RequestIO; use vars qw(@ISA); @ISA = qw(Apache2::AuthCookie); sub authen_cred ($$\@) { my $self = shift; my $r = shift; my @creds = @_; return if $creds[0] eq 'fail'; # simulate bad_credentials # This would really authenticate the credentials # and return the session key. # Here I'm just using setting the session # key to the credentials and delaying authentication. # # Similar to HTTP Basic Authentication, only not base 64 encoded join(":", @creds); } sub authen_ses_key ($$$) { my ($self, $r, $cookie) = @_; my($user, $password) = split(/:/, $cookie); $r->server->log_error("user=$user pass=$password cookie=$cookie"); if ($user eq 'programmer' && $password eq 'Hero') { return $user; } elsif ($user eq 'some-user') { return $user; } elsif ($user eq '0') { return $user; } else { return; } } sub dwarf { my $self = shift; my $r = shift; my $user = $r->user; if ("bashful doc dopey grumpy happy sleepy sneezy programmer" =~ /\b$user\b/) { # You might be thinking to yourself that there were only 7 # dwarves, that's because the marketing folks left out # the often under appreciated "programmer" because: # # 10) He didn't hold 8 to 5 hours. # 9) Sometimes forgot to shave several days at a time. # 8) Was always buzzed on caffine. # 7) Wasn't into heavy labor. # 6) Prone to "swearing while he worked." # 5) Wasn't as easily controlled as the other dwarves. # # 1) He posted naked pictures of Snow White to the Internet. return OK; } return HTTP_FORBIDDEN; } sub login_form_handler { my ($self, $r) = @_; my $uri = $r->prev->uri; my $args = $r->prev->args; if ($args) { $uri .= "?$args"; } my $reason = $r->prev->subprocess_env('AuthCookieReason'); my $form = < Enter Login and Password

This is a secure document

Failure reason: '$reason'. Please enter your login and password to authenticate.

Login:
Password:
HERE $r->no_cache(1); $r->content_type('text/html'); my $len = length $form; $r->headers_out->set('Content-length', $len); $r->headers_out->set('Pragma', 'no-cache'); $r->print($form); return OK; } 1;