pax_global_header00006660000000000000000000000064135707670430014526gustar00rootroot0000000000000052 comment=224df2f2f628442b18eff18d05ddfa05f94e0817 libapache2-authcookie-perl-3.28/000077500000000000000000000000001357076704300165455ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/Changes000066400000000000000000000466401357076704300200520ustar00rootroot00000000000000Revision history for Apache::AuthCookie 3.28 2019-11-19 - Add support for SameSite cookie property (can be strict/lax). - Minor POD updates. 3.27 2017-07-28 - Fix POD spelling error [#118545]. 3.26 2016-09-30 - remove unused module Apache::AuthCookie::Autobox from dist - remove CGI.pm dependency. CGI.pm has been removed from perl core, which was the primary reason we used it in the first place. Replaced with dependency on lighter weight set of three modules: * HTTP::Body * WWW::Form::UrlEncoded * Hash::MultiValue Also recommended (but not required) is WWW::Form::UrlEncoded::XS - Add optional support for charset encoding. If you have something like PerlSetVar MyAuthNameEncoding UTF-8 Then AuthCookie with now automatically decode parameters using the given encoding now. AuthCookie params() data will be decoded automatically if this is on. See details in AuthCookie module documentation. In addition r->user will be encoded (using byte semantics) using this encoding. ***** IMPORTANT ***** If you turn this on, this could break your code. r->user() will now be byte encoded using the given encoding. If you use usernames that contain non-ascii characters you either need to use decoded_user(), or decode r->user() yourself in your subclasses. See the AuthCookie docs for more details. - add optional support for decoding httpd.conf requires directives. This is enabled with a RequiresEncoding setting: PerlSetVar MyAuthNameRequiresEncoding UTF-8 Then decoded_requires($r) will return the decoded value of $r->requires You only need this if you have non-ascii characters in your requires directives such as: Requires user programmør - add decoded_user($r) method to get the value of r->user decoded using character semantics instead of bytes. Due to the fact that r->user is a C API method we cannot get character semantics on r->user directly. If no Encoding directive is in effect, then this is the same as r->user. - add encoding($r): string which returns the value of the Encoding directive that is in effect for the current request. 3.25 2016-08-30 - 2.4: fix POD typo and add missing ABSTRACT - reorganize real.t tests into subtests - make sure signature test ignores generated files - remove autobox dependency - fix authenticate so that r->user is copied from r->main on subrequests. Previously this was only done for internal redirects (r->prev is defined). This fixes DirectoryIndexes on AuthCookie enabled directories under apache 2.4. 3.24 2016-01-13 - Update Apache 2.4 README, flesh out guts of Authz Provider notes. - Improve Apache 2.4 README's AuthzProvider documentation - Add POD to Apache2_4::AuthCookie - Add FAQ to Apache2_4::AuthCookie documenation - 2.4: document that PerlAddAuthzProvider is only needed for *custom* Requires directives. - 2.4: make authz_handler recognize multiple usernames in the directive like mod_authz_user does. - add test case for internal authz_handler - explicitly require Apache::Test 1.39 so that APACHE2_4 defines are set 3.23 2015-09-10 - Improve CGI mode param() handling to avoi CGI.pm's "param() called in list context" warning. - add support for Apache 2.4 via mod_perl 1.09. ***** IMPORTANT ***** Apache 2.4 has a *VERY* different API for authentication. You will need to port your subclass and configuration over to the Apache 2.4 API in order to use Apache 2.4! Please be sure to read README.apache-2.4.pod for porting instructions! 3.22 2014-05-07 3.21 2014-05-07 - Bad release - deleted 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. - fix abstract in FAQ pod. 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 libapache2-authcookie-perl-3.28/LICENSE000066400000000000000000000436551357076704300175670ustar00rootroot00000000000000This 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, Fifth Floor, Boston, MA 02110-1301 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 libapache2-authcookie-perl-3.28/MANIFEST000066400000000000000000000023101357076704300176720ustar00rootroot00000000000000# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. Changes LICENSE MANIFEST MANIFEST.SKIP META.json META.yml Makefile.PL README README.apache-2.4.pod README.modperl2 SIGNATURE lib/Apache/AuthCookie.pm lib/Apache/AuthCookie/FAQ.pod lib/Apache/AuthCookie/Params.pm lib/Apache/AuthCookie/Params/Base.pm lib/Apache/AuthCookie/Params/CGI.pm lib/Apache/AuthCookie/Util.pm lib/Apache2/AuthCookie.pm lib/Apache2/AuthCookie/Base.pm lib/Apache2/AuthCookie/Params.pm lib/Apache2_4/AuthCookie.pm scripts/perlbrew-smoke t/Skeleton/AuthCookieHandler.pm t/TEST.PL t/author-pod-syntax.t t/author-signature.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/myuser/get_me.html t/htdocs/docs/protected/echo_user.pl t/htdocs/docs/protected/get_me.html t/htdocs/docs/protected/index.html t/htdocs/docs/stimeout/get_me.html t/lib/Sample/Apache/AuthCookieHandler.pm t/lib/Sample/Apache2/AuthCookieHandler.pm t/lib/Sample/Apache2_4/AuthCookieHandler.pm t/real.t t/signature.t t/startup.pl t/util.t libapache2-authcookie-perl-3.28/MANIFEST.SKIP000066400000000000000000000005611357076704300204450ustar00rootroot00000000000000^blib/ ^Makefile$ ^Makefile\.old$ ^pm_to_blib$ ^\.git/ ^\.gitignore$ ^t/TEST$ ^t/db/ ~$ error_log ^dist\.ini ^weaver\.ini ^t/httpd.loc ^MYMETA # 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$ ^t/SMOKE$ libapache2-authcookie-perl-3.28/META.json000066400000000000000000000062201357076704300201660ustar00rootroot00000000000000{ "abstract" : "Perl Authentication and Authorization via cookies", "author" : [ "Michael Schout " ], "dynamic_config" : 1, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Apache-AuthCookie", "prereqs" : { "configure" : { "requires" : { "Apache::Test" : "1.39", "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Dist::Zilla" : "5", "Dist::Zilla::Plugin::Prereqs" : "0", "Dist::Zilla::PluginBundle::ApacheTest" : "0", "Dist::Zilla::PluginBundle::Filter" : "0", "Dist::Zilla::PluginBundle::MSCHOUT" : "0", "Software::License::Perl_5" : "0", "Test::Pod" : "1.41", "Test::Signature" : "0" } }, "runtime" : { "recommends" : { "WWW::Form::UrlEncoded::XS" : "0" }, "requires" : { "Carp" : "0", "Class::Load" : "0.03", "Encode" : "0", "HTTP::Body" : "0", "Hash::MultiValue" : "0", "Test::More" : "0.94", "WWW::Form::UrlEncoded" : "0" } }, "test" : { "requires" : { "URI::Escape" : "1.31" } } }, "provides" : { "Apache2::AuthCookie" : { "file" : "lib/Apache2/AuthCookie.pm", "version" : "3.28" }, "Apache2::AuthCookie::Base" : { "file" : "lib/Apache2/AuthCookie/Base.pm", "version" : "3.28" }, "Apache2::AuthCookie::Params" : { "file" : "lib/Apache2/AuthCookie/Params.pm", "version" : "3.28" }, "Apache2_4::AuthCookie" : { "file" : "lib/Apache2_4/AuthCookie.pm", "version" : "3.28" }, "Apache::AuthCookie" : { "file" : "lib/Apache/AuthCookie.pm", "version" : "3.28" }, "Apache::AuthCookie::Params" : { "file" : "lib/Apache/AuthCookie/Params.pm", "version" : "3.28" }, "Apache::AuthCookie::Params::Base" : { "file" : "lib/Apache/AuthCookie/Params/Base.pm", "version" : "3.28" }, "Apache::AuthCookie::Params::CGI" : { "file" : "lib/Apache/AuthCookie/Params/CGI.pm", "version" : "3.28" }, "Apache::AuthCookie::Util" : { "file" : "lib/Apache/AuthCookie/Util.pm", "version" : "3.28" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/mschout/apache-authcookie/issues" }, "homepage" : "https://github.com/mschout/apache-authcookie", "repository" : { "type" : "git", "url" : "https://github.com/mschout/apache-authcookie.git", "web" : "https://github.com/mschout/apache-authcookie" } }, "version" : "3.28", "x_generated_by_perl" : "v5.26.2", "x_serialization_backend" : "Cpanel::JSON::XS version 4.04" } libapache2-authcookie-perl-3.28/META.yml000066400000000000000000000033341357076704300200210ustar00rootroot00000000000000--- abstract: 'Perl Authentication and Authorization via cookies' author: - 'Michael Schout ' build_requires: URI::Escape: '1.31' configure_requires: Apache::Test: '1.39' ExtUtils::MakeMaker: '0' dynamic_config: 1 generated_by: 'Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Apache-AuthCookie provides: Apache2::AuthCookie: file: lib/Apache2/AuthCookie.pm version: '3.28' Apache2::AuthCookie::Base: file: lib/Apache2/AuthCookie/Base.pm version: '3.28' Apache2::AuthCookie::Params: file: lib/Apache2/AuthCookie/Params.pm version: '3.28' Apache2_4::AuthCookie: file: lib/Apache2_4/AuthCookie.pm version: '3.28' Apache::AuthCookie: file: lib/Apache/AuthCookie.pm version: '3.28' Apache::AuthCookie::Params: file: lib/Apache/AuthCookie/Params.pm version: '3.28' Apache::AuthCookie::Params::Base: file: lib/Apache/AuthCookie/Params/Base.pm version: '3.28' Apache::AuthCookie::Params::CGI: file: lib/Apache/AuthCookie/Params/CGI.pm version: '3.28' Apache::AuthCookie::Util: file: lib/Apache/AuthCookie/Util.pm version: '3.28' recommends: WWW::Form::UrlEncoded::XS: '0' requires: Carp: '0' Class::Load: '0.03' Encode: '0' HTTP::Body: '0' Hash::MultiValue: '0' Test::More: '0.94' WWW::Form::UrlEncoded: '0' resources: bugtracker: https://github.com/mschout/apache-authcookie/issues homepage: https://github.com/mschout/apache-authcookie repository: https://github.com/mschout/apache-authcookie.git version: '3.28' x_generated_by_perl: v5.26.2 x_serialization_backend: 'YAML::Tiny version 1.73' libapache2-authcookie-perl-3.28/Makefile.PL000066400000000000000000000070631357076704300205250ustar00rootroot00000000000000# This Makefile.PL for Apache-AuthCookie was generated by # Dist::Zilla::Plugin::MakeMaker::ApacheTest 0.04 # and Dist::Zilla::Plugin::MakeMaker::Awesome 0.47. # Don't edit it but the dist.ini and plugins used to construct it. use strict; use warnings; use ExtUtils::MakeMaker; # figure out if mod_perl v1 or v2 is installed. DynamicPrereqs in the # PluginBundle needs this to require the appropriate mod_perl module. my $mp_version = mod_perl_version(); # configure Apache::Test test_configure(); my %WriteMakefileArgs = ( "ABSTRACT" => "Perl Authentication and Authorization via cookies", "AUTHOR" => "Michael Schout ", "CONFIGURE_REQUIRES" => { "Apache::Test" => "1.39", "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Apache-AuthCookie", "LICENSE" => "perl", "NAME" => "Apache::AuthCookie", "PREREQ_PM" => { "Carp" => 0, "Class::Load" => "0.03", "Encode" => 0, "HTTP::Body" => 0, "Hash::MultiValue" => 0, "Test::More" => "0.94", "WWW::Form::UrlEncoded" => 0 }, "TEST_REQUIRES" => { "URI::Escape" => "1.31" }, "VERSION" => "3.28", "clean" => { "FILES" => [ "t/TEST" ] }, "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Carp" => 0, "Class::Load" => "0.03", "Encode" => 0, "HTTP::Body" => 0, "Hash::MultiValue" => 0, "Test::More" => "0.94", "URI::Escape" => "1.31", "WWW::Form::UrlEncoded" => 0 ); # inserted by Dist::Zilla::Plugin::DynamicPrereqs 0.035 if ($mp_version == 2) { requires('mod_perl2', '1.999022'); } elsif ($mp_version == 1) { requires('mod_perl', '1.27'); } unless ( eval { ExtUtils::MakeMaker->VERSION('6.63_03') } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); sub test_configure { require Apache::TestMM; # enable make test Apache::TestMM->import(qw(test clean)); Apache::TestMM::filter_args(); Apache::TestMM::generate_script('t/TEST'); } sub mod_perl_version { # try MP2 eval { require mod_perl2; }; unless ($@) { return 2; } # try MP1 eval { require mod_perl; }; unless ($@) { if ($mod_perl::VERSION >= 1.99) { # mod_perl 2, prior to the mod_perl2 rename (1.99_21, AKA 2.0.0 RC5) die "mod_perl 2.0 RC5 or later is required\n"; } return 1; } # assume mod_perl version 2 is wanted return 2; } # inserted by Dist::Zilla::Plugin::DynamicPrereqs 0.035 sub _add_prereq { my ($mm_key, $module, $version_or_range) = @_; $version_or_range ||= 0; warn "$module already exists in $mm_key (at version $WriteMakefileArgs{$mm_key}{$module}) -- need to do a sane metamerge!" if exists $WriteMakefileArgs{$mm_key}{$module} and $WriteMakefileArgs{$mm_key}{$module} ne '0' and $WriteMakefileArgs{$mm_key}{$module} ne $version_or_range; warn "$module already exists in FallbackPrereqs (at version $WriteMakefileArgs{$mm_key}{$module}) -- need to do a sane metamerge!" if exists $FallbackPrereqs{$module} and $FallbackPrereqs{$module} ne '0' and $FallbackPrereqs{$module} ne $version_or_range; $WriteMakefileArgs{$mm_key}{$module} = $FallbackPrereqs{$module} = $version_or_range; return; } sub requires { goto &runtime_requires } sub runtime_requires { my ($module, $version_or_range) = @_; _add_prereq(PREREQ_PM => $module, $version_or_range); } libapache2-authcookie-perl-3.28/README000066400000000000000000000015711357076704300174310ustar00rootroot00000000000000OVERVIEW 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 libapache2-authcookie-perl-3.28/README.apache-2.4.pod000066400000000000000000000200611357076704300217260ustar00rootroot00000000000000=head1 APACHE 2.4 PORTING NOTES =head2 VERY IMPORTANT!!! Apache 2.4 has a B different authentication API from previous versions. You will not be able to simply ugrade apache and upgrade AuthCookie in order to migrate to Apache 2.4. You will also need to port your AuthCookie subclass over to the Apache 2.4 API, and update your Apache configuration for Apache 2.4. This document attempts to help you understand the changes required and how to port your module over to Apache 2.4. If your subclass stopped working when you migrated to Apache 2.4, please make sure you have read and understand everything in this document before filing a bug report. =head2 Changes Required to Run Under Apache 2.4 =over 4 =item Mod Perl You need at least C version 2.0.9, which is the first official release to support Apache 2.4. =item Apache::Test You need Apache::Test version 1.39 or later. Previous versions do not define the constant C which is needed for the test suite. =item Your AuthCookie Subclass =over 4 =item * You must not call authcookie's authorize() method. Authorization is done using AuthzProvider's under Apache 2.4 and these work very different from previous apache versions. If you are simply doing simple things such as C or C in your C, then you likely do not need an authorization provider at all. Apache 2.4 handles these for you in C. =item * Related to previous item, you must change every method that was called as a C under previous versions to return one of the following values: =over 4 =item Apache2::Const::AUTHZ_DENIED_NO_USER return this constant if C<< $r->user >> is empty/undefined and you do not wish to allow anonymous access. =item Apache2::Const::AUTHZ_DENIED return this constant if C<< $r->user >> is not authorized for the current request. =item Apache2::Const::AUTHZ_GRANTED return this constant if C<< $r->user >> is authorized for the current request =item Apache2::Const::AUTHZ_GENERAL_ERROR return this constant to indicate an error processing authz requirements. =item Apache2::Const::AUTHZ_NEUTRAL return this constant to indicate a neutral response. It is assumed that another authz provider will be checked in a parent/sibling scope that will return granted or denied. =back =back =item httpd.conf =over 4 =item * Remove all C entries. C is not necessary in Apache 2.4. If you are doing custom authoriaztion, you need to convert these to C entries: =item * Depending on what your C directives say, you may need to add one or more top level C entires and implement a handler for each one. If your C directives are simply C or C then you do not need to do this. Apache already provides an authz provider that handles C and C requirements for you in C. =item * If you are C'ing anything other than C or C then you will need to write your own Authz Provider method and register it with Apache. Authz Providers are the Apache 2.4 equivalent of a C method. Each one implements a specific requirement. E.g.: PerlAddAuthzProvider species My::AuthCookieHandler->authz_species Will be called to handle a Require species klingon Directive. It is important to know that Authz Providers are called B for a request. First, the authz provider is called before authentication has been processed to check for anonymous access. In this method call, C<< $r->user >> is not set (to allow for your handler to allow annonymous access). You are expected to return one of: =over 4 =item AUTHZ_GRANTED Access is granted and no further authn/authz processing will occur for this request. =item AUTHZ_DENIED =item AUTHZ_NEUTRAL The response is C (unless neutral is overridden by another provider) =item AUTHZ_DENIED_NO_USER This should be returned if C<< $r->user >> is not set and you do not wish to allow anonymous access. Authentication will be processed, C<< $r->user >> will be set with the current username and your authz provider will be called again. =back The second time the authz provider is called, C<< $r->user >> is set and you are expected to return one of: =over 4 =item AUTHZ_GRANTED The request is allowed =item AUTHZ_DENIED The request is forbidden =item AUTHZ_NEUTRAL The request is forbidden, unless another authz provider returns C. Consult the apache documentation about authorization merging for more info. =back You could also return C from any of these to indicate an error processing authz directives and halt processing immediately. One way to think about these response codes what kind of Require satisfies is in effect: =over 4 =item RequireAll/RequireNone In this case the priority of responses is: =over 4 =item AUTHZ_GENERAL_ERROR Processing stops immediately =item AUTHZ_DENIED Processing stops immediately, no siblings are processed. Request is denied. =item AUTHZ_DENIED_NO_USER Process Authentication and try again =item AUTHZ_GRANTED Continue processing siblings. =item AUTZ_NEUTRAL Continue processing siblings. =back =item RequireAny In this case the priority of responses is: =over 4 =item AUTHZ_GENERAL_ERROR Processing stops immediately =item AUTHZ_GRANTED Processing stops immediately, no siblings are processed. Request is allowed. =item AUTHZ_DENIED_NO_USER Process Authentication and try again =item AUTHZ_DENIED Continue processing siblings. =item AUTZ_NEUTRAL Continue processing siblings. =back =back =back =back =head2 Important Internal API Changes for Apache 2.4 =over 4 =item authorize() has been removed You need to use a C and write an appropriate handler as described above instead. Note that you do not need a C for C or C requirements. Apache already handles those internally via C =item ${auth_name}Satisfy Satisfy support is removed as it is no longer needed with Apache 2.4. You are expected to use C or C instead. e.g.: PerlAddAuthzProvider species Your::AuthCookieHandler->authz_species_handler Require valid-user Require species klingon see: L =item Unauthorized User HTTP Response Code In Apache 2.4, in C, if no authz handlers return C, then C is returned. In previous versions of Apache, C was returned. You can get the old behaviour if you want it with: # in httpd.conf AuthzSendForbiddenOnFailure On =back =head1 FREQUENTLY ASKED QUESTIONS =over 4 =item * Why is my authz method called twice per request? This is normal behaviour under Apache 2.4. This is to accomodate for authorization of anonymous access. You are expected to return C IF C<< $r->user >> has not yet been set if you want authentication to proceed. Your authz handler will be called a second time after the user has been authenticated. =item * I get an error like C This is because you called C's C method, which is illegal under Apache 2.4. This could either be because your C subclass explicitly called C, or (more likely) because your C contains a line like: PerlAuthzHandler My::AuthCookie->authorize You should remove lines from C that call C, and your subclass should not be calling authorize(). If you need to do custom autorization, you need to write an authz provider instead. =item * My log shows an entry like: authorization result of Require ...: denied (no + # authenticated user yet) These are normal. This happens because the authz provider returned C and the authz provider will be called again after authentication happens. =back libapache2-authcookie-perl-3.28/README.modperl2000066400000000000000000000026421357076704300211540ustar00rootroot00000000000000**** 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$ libapache2-authcookie-perl-3.28/SIGNATURE000066400000000000000000000110431357076704300200300ustar00rootroot00000000000000This file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.81. 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: SHA256 SHA1 fe72ac1f2889686a810cd2de3e09c3aefa7f52e7 Changes SHA1 cb36dd242de6d18cd64c4b55444347ebf09e43e7 LICENSE SHA1 def2e0aaac81605fb2882534da78860f2f3a6d06 MANIFEST SHA1 0ff75e1a6186d7274e76387884eca541fdd5ca4a MANIFEST.SKIP SHA1 b3fb49bc74ad5ef515d5daaf288469a21eca0a1a META.json SHA1 e52c3b60a1c1a46c197bbfbaab54d1cd1152e409 META.yml SHA1 7e82961d4f23d5d33785c700d036b8c3b11f87ba Makefile.PL SHA1 b9945378262a25db34dcdba06da956a52876188b README SHA1 f9d2e5d4bcafb4b3d73b0a1f390aa25579cafbd2 README.apache-2.4.pod SHA1 ccbc46a0385aabadd1e6f4a22f8d4ebb11b44901 README.modperl2 SHA1 e5b590c4028e31fc4e2e05d0d790c951505ca76c lib/Apache/AuthCookie.pm SHA1 a5a155c7c1114bd1521b166fe2289b6d8defcc8f lib/Apache/AuthCookie/FAQ.pod SHA1 5591f3a83dcfd292b018f8dcfd75d32378627468 lib/Apache/AuthCookie/Params.pm SHA1 8b96d310e565562e1c131cdb2e9e97741aa82579 lib/Apache/AuthCookie/Params/Base.pm SHA1 61d90732b1757078c17ac45199ca5e6797ab79eb lib/Apache/AuthCookie/Params/CGI.pm SHA1 2e4d31e8fffef85465c6567ea7311dc515a058dc lib/Apache/AuthCookie/Util.pm SHA1 4c9362a58bea52476dffda3a48d9d628ee61166a lib/Apache2/AuthCookie.pm SHA1 6c515c233a85b35b208b6a0b77a6ebf2b694a871 lib/Apache2/AuthCookie/Base.pm SHA1 026f7f258bb85168c4a8fcde905e58508728f397 lib/Apache2/AuthCookie/Params.pm SHA1 33026bb4f30abe1d5a2afd8827dd6f4d6d1bcef7 lib/Apache2_4/AuthCookie.pm SHA1 fee15f1cd6c3fe17e8370838aafe6d54bfd72611 scripts/perlbrew-smoke SHA1 3ac8de46e7bba83f6969caec3c9c14cbd99881cb t/Skeleton/AuthCookieHandler.pm SHA1 b1f854e6edecbdd44fc7b8db719e0fe21d9340d1 t/TEST.PL SHA1 8efad25309730a4d501fb40fc03eda4697303372 t/author-pod-syntax.t SHA1 19cc343f8a85c6805bbeb02580487483a6283887 t/author-signature.t SHA1 fa77f587d29613a40b627846db67d69442c11ac0 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 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/myuser/get_me.html SHA1 b37a85d16cbb2342b407f2ba70b8a61aa1ca67bb t/htdocs/docs/protected/echo_user.pl SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/protected/get_me.html SHA1 077d964c9f67b5dfe4f5f6a73c71ccbd60bd03af t/htdocs/docs/protected/index.html SHA1 2156ea84b69ca7fef7b73d72a06c07cb145da7a9 t/htdocs/docs/stimeout/get_me.html SHA1 b17cdb6e4dfb752901c2a9df5ce822a2d54b92b5 t/lib/Sample/Apache/AuthCookieHandler.pm SHA1 9ba342e14a302d89f38ab063c56e5ae000a7595c t/lib/Sample/Apache2/AuthCookieHandler.pm SHA1 b19593e0dc51baa6a4d84bc27da2e53632ab8592 t/lib/Sample/Apache2_4/AuthCookieHandler.pm SHA1 28df698c77347acf6a6957bef1483d0c01ad282a t/real.t SHA1 f0c37746e0277de1ddb62c9227628a5ebe5a777a t/signature.t SHA1 e91bf0ef7d63322eaf15ca7d9907c6db47ce90ca t/startup.pl SHA1 da33f704880ddd2596521c39be5b7b6a22913882 t/util.t -----BEGIN PGP SIGNATURE----- iQJFBAEBCAAvFiEE2EtuRfhGgngE8PsARAzvLrlUzY4FAl3UDA8RHG1zY2hvdXRA Y3Bhbi5vcmcACgkQRAzvLrlUzY4vYxAAgBF2GxUwOVraAgI7NOxMVMS0VTWMy0Je ro/AsnjK2OwdA7RCcWzG3YT7QwElduROYLu5DkRrblibPjfiYGDs9J8Th5m7MLHp jmDYjAFJEviEHn0sRl0YfLdmcC7BXxR6ypstJg5DRugjJ+ekBdWBSPH5KQ04iDa+ xZD/kWwglSn3QL3Q9ySJ0NsTYKQTlCMmxW2hVZubMQfz8TWNmMJ+ChxHO48yjXhu EHxVliK5ZC1eFSfuVD/UVjMIe/KSrYE5rr5Q9Bz8Nrf2BkDhCtIyvYs7is5v0Pki VQqiXG4tbYfjBUP73F3kswCMtHu3o4LSGBUnsPZvc2suhm+YbLcvPJF53KFDXWe1 9ebVC8D7wx5zmAsSm2yUnirxWls4IzBTwaYCPYfy1cllAhvLhoDVkE09qgpsuSdx P8Tki1rZFcK1Zw8OsdALJIB0r/HZQHPRCPdmbR604+W2i/5ZTfyjNxE86ZrbQNVz xrmlxN8BLsH7/ezSw1X2ZDoMQLPjANm0l60ElljgkkMK5reqvbnGatFzBgj7fNec Oan7QAsf0ZXsh908apgBmNTMNG7KX0e2KHipeFN7E9g/wVq10MzAgKJq+PekZTty FkonaTfafOzKNr2LtTadR0/hvepP7hc1e5KH0Gy7bHizZAom5D5l/jmtiZkjGGno 7wUilSo5T6s= =Pnp+ -----END PGP SIGNATURE----- libapache2-authcookie-perl-3.28/lib/000077500000000000000000000000001357076704300173135ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache/000077500000000000000000000000001357076704300204745ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie.pm000066400000000000000000001224721357076704300230750ustar00rootroot00000000000000package Apache::AuthCookie; $Apache::AuthCookie::VERSION = '3.28'; # 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 qw(is_blank); use Apache::Util qw(escape_uri); use Encode (); sub recognize_user ($$) { my ($self, $r) = @_; # only check if user is not already set return DECLINED unless is_blank($r->connection->user); my $debug = $r->dir_config("AuthCookieDebug") || 0; my ($auth_type, $auth_name) = ($r->auth_type, $r->auth_name); return DECLINED if is_blank($auth_type) or is_blank($auth_name); return DECLINED if is_blank($r->header_in('Cookie')); 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 (!is_blank($user) 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( $self->_encode($r, $user) ); } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $user, @args); } return is_blank($user) ? 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 encoding { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Encoding"); } sub requires_encoding { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}RequiresEncoding"); } sub decoded_user { my ($self, $r) = @_; my $user = $r->connection->user; if (is_blank($user)) { return $user; } my $encoding = $self->encoding($r); if (!is_blank($encoding)) { $user = Encode::decode($encoding, $user); } return $user; } sub decoded_requires { my ($self, $r) = @_; my $reqs = $r->requires or return; my $encoding = $self->requires_encoding($r); unless (is_blank($encoding)) { for my $req (@$reqs) { $$req{requirement} = Encode::decode($encoding, $$req{requirement}); } } return $reqs; } 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. # encoding would have been handled in prev req, so do not encode here. $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 (!is_blank($auth_user) 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_type->_encode($r, $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 = $auth_type->decoded_requires($r) or return DECLINED; my $user = $auth_type->decoded_user($r); if (is_blank($user)) { # 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); } 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'; } # SameSite is an anti-CSRF cookie property. See # https://www.owasp.org/index.php/SameSite if (my $samesite = $r->dir_config("${auth_name}SameSite")) { if ($samesite =~ /\A(strict|lax)\z/i) { $samesite = lc($1); $string .= "; SameSite=$samesite"; } } 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"); } sub _encode { my ($self, $r, $value) = @_; my $encoding = $self->encoding($r); if (is_blank($encoding)) { return $value; } else { return Encode::encode($encoding, $value); } } 1; =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie - Perl Authentication and Authorization via cookies =head1 VERSION version 3.28 =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. (-----------------------) +---------------------------------+ ( 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. =head1 METHODS =head2 authen_cred($r, @credentials) 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. =head2 authen_ses_key($r, $session_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. =head2 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 =head2 recognize_user($r) 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. =head2 encoding($r): string Return the ${auth_name}Encoding setting that is in effect for this request. =head2 requires_encoding($r): string Return the ${auth_name}RequiresEncoding setting that is in effect for this request. =head2 decoded_user($r): string If you have set ${auth_name}Encoding, then this will return the decoded value of C<< $r->connection->user >>. =head2 decoded_requires($r): arrayref This method returns the C<< $r->requires >> array, with the C values decoded if C<${auth_name}RequiresEncoding> is in effect for this request. =head2 handle_cache(): void If C<${auth_name}Cache> is defined, this sets up the response so that the client will not cache the result. This sents C in the apache request object and sends the appropriate headers so that the client will not cache the response. =head2 remove_cookie(): void Adds a C header that instructs the client to delete the cookie immediately. =head2 params($r): Apache::AuthCookie::Params Get the params object for this request. =head2 login($r) 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. =head2 untaint_destination($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. =head2 logout($r) 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);>. =head2 authenticate($r) 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. =head2 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. =head2 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. =head2 get_satisfy(): string Get the C value for the current request, or C if it is not configured. =head2 authorize($r) 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. =head2 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. =head2 send_p3p(): void Set a P3P response header if C<${auth_name}P3P> is configured. The value of the header is whatever is in the C<${auth_name}P3P> setting. =head2 cookie_string(%args): string Generate a cookie string. C<%args> are: =over 4 =item * request The Apache request object =item * key The Cookie name =item * value the Cookie value =item * expires (optional) When the cookie expires. See L. Uses C<${auth_name}Expires> if not given. =back All other cookie settings come from C settings. =head2 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. =head2 get_cookie_path(): string Returns the value of C. =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 ENCODING AND CHARACTER SETS =head2 Encoding AuthCookie provides support for decoding POST/GET data if you tell it what the client encoding is. You do this by setting the C<< ${auth_name}Encoding >> setting in C. E.g.: PerlSetVar WhateEverEncoding UTF-8 # and you also need to arrange for charset=UTF-8 at the end of the # Content-Type header with something like: AddDefaultCharset UTF-8 Note that you B use charsets other than C, however, you need to arrange for the browser to send the right encoding back to the server. If you have turned on Encoding support by setting C<< ${auth_name}Encoding >>, this has the following effects: =over 4 =item * The internal pure-perl params processing subclass will be used, even if libapreq is installed. libapreq does not handle encoding. =item * POST/GET data intercepted by AuthCookie will be decoded to perl's internal format using L. =item * The value stored in C<< $r-Econnection-Euser >> will be encoded as B, not characters using the configured encoding name. This is because the value stored by mod_perl is a C API string, and not a perl string. You can use L to get user string encoded using B semantics. =back This does has some caveats: =over 4 =item * your L and L function is expected to return a decoded username, either by passing it through L, or, by turning on the UTF8 flag if appropriate. =item * Due to the way HTTP works, cookies cannot contain non-ASCII characters. Because of this, if you are including the username in your generated session key, you will need to escape any non-ascii characters in the session key returned by L. =item * Similarly, you must reverse this escaping process in L and return a L decoded username. If your L function already only generates ASCII-only session keys then you do not need to worry about any of this. =item * The value stored in C<< $r-Econnection-Euser >> will be encoded using bytes semantics using the configured B. If you want the decoded user value, use L instead. =back =head2 Requires You can also specify what the charset is of the Apache C<< $r-Erequires >> data is by setting C<< ${auth_name}RequiresEncoding >> in httpd.conf. E.g.: PerlSetVar WhatEverRequiresEncoding UTF-8 This will make it so that AuthCookie will decode your C directives using the configured character set. You really only need to do this if you have used non-ascii characters in any of your C directives in httpd.conf. e.g.: requires user programmør =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/000077500000000000000000000000001357076704300225275ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/FAQ.pod000066400000000000000000000034751357076704300236530ustar00rootroot00000000000000# make Dist::Zilla happy. package Apache::AuthCookie::FAQ; # ABSTRACT: Frequently Asked Questions about Apache::AuthCookie. 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie::FAQ - Frequently Asked Questions about Apache::AuthCookie. =head1 VERSION version 3.28 =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 SOURCE The development version is on github at L and may be cloned from L =head1 BUGS Please report any bugs or feature requests on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/Params.pm000066400000000000000000000034361357076704300243160ustar00rootroot00000000000000package Apache::AuthCookie::Params; $Apache::AuthCookie::Params::VERSION = '3.28'; # 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); 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 { $r->server->log_error("params: using CGI") if $debug >= 3; return $class->SUPER::_new_instance($r); } return; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie::Params - AuthCookie Params Driver for mod_perl 1.x =head1 VERSION version 3.28 =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/Params/000077500000000000000000000000001357076704300237525ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/Params/Base.pm000066400000000000000000000044231357076704300251650ustar00rootroot00000000000000package Apache::AuthCookie::Params::Base; $Apache::AuthCookie::Params::Base::VERSION = '3.28'; # ABSTRACT: Internal CGI AuthCookie Params Base Class use strict; use warnings; use Class::Load qw(load_class); use Apache::AuthCookie::Util qw(is_blank); 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; } # if an encoding is in effect, then always use the ::CGI interface because # libapreq has no support for UTF-8 my $auth_name = $r->auth_name; if (!is_blank($r->dir_config("${auth_name}Encoding"))) { $obj = __PACKAGE__->_new_instance($r); } else { $obj = $class->_new_instance($r); } $r->pnotes($class, $obj); return $obj; } sub _new_instance { my ($self, $r) = @_; load_class('Apache::AuthCookie::Params::CGI'); return Apache::AuthCookie::Params::CGI->new($r); } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie::Params::Base - Internal CGI AuthCookie Params Base Class =head1 VERSION version 3.28 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This is the base class for AuthCookie Params drivers. =head1 METHODS =head2 new($r) Constructor. This will generate either an internal L object, or, if available, use libapreq2. Note that libapreq2 will not be used if you turned on C support because libapreq2 does not have any support for unicode. =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/Params/CGI.pm000066400000000000000000000130561357076704300247170ustar00rootroot00000000000000package Apache::AuthCookie::Params::CGI; $Apache::AuthCookie::Params::CGI::VERSION = '3.28'; # ABSTRACT: Internal CGI Params Subclass use strict; use warnings; use Carp (); use WWW::Form::UrlEncoded qw(parse_urlencoded); use Hash::MultiValue; use HTTP::Body; use Encode (); sub new { my ($class, $r) = @_; $class = ref $class || $class; return bless { _request => $r }, $class; } sub request { my $self = shift; return $self->{_request}; } sub param { my ($self, $name, @vals) = @_; # no param name given, return list of all parameter names. unless (defined $name) { return $self->params->keys; } # set values if (@vals) { $self->params->remove($name); $self->params->add($name => @vals); } # return values return $self->params->get_all($name); } sub params { my $self = shift; $self->{_params} ||= Hash::MultiValue->new( map $_->flatten, $self->query_params, $self->body_params ); } sub query_params { my $self = shift; $self->{_query_params} ||= $self->_compute_pnote('request.query-params', sub { my $query = $self->request->args || ''; $self->_decode( Hash::MultiValue->new(parse_urlencoded($query)) ); }); } sub body_params { my $self = shift; $self->{_body_params} ||= $self->_compute_pnote('request.body-params', sub { $self->_decode( Hash::MultiValue->from_mixed($self->_http_body->param) ); }); } sub content_length { my $self = shift; $self->{_content_length} ||= $self->request->headers_in->get('Content-Length') || 0; } sub content_type { my $self = shift; $self->{_content_type} ||= $self->request->headers_in->get('Content-Type') || ''; } sub _http_body { my $self = shift; $self->{_http_body} ||= $self->_compute_pnote('request.body', sub { $self->_read_body; }); } sub _read_body { my $self = shift; my $length = $self->content_length; my $body = HTTP::Body->new($self->content_type, $length); # HTTP::Body creates temp files for uploads. we need to tell it to clean up # those files when the body goes out of scope. $body->cleanup(1); my $r = $self->request; my $spin = 0; while ($length) { $r->read(my $buffer, ($length < 8192) ? $length : 8192); my $bytes_read = length $buffer; $length -= $bytes_read; $body->add($buffer); # guard against a signal interrupting read() if ($bytes_read == 0 && $spin++ > 2000) { Carp::croak "Bad Content-Length: maybe client disconnect? ($length bytes remaining)"; } } return $body; } # utility method to fetch a pnote, or set it to a computed value if it has not # already been set. sub _compute_pnote { my ($self, $key, $code) = @_; my $r = $self->request; unless (defined $r->pnotes($key)) { $r->pnotes($key, $code->()); } return $r->pnotes($key); } sub _decode { my ($self, $hash) = @_; my $r = $self->request; my $auth_name = $r->auth_name; if (my $encoding = $r->dir_config("${auth_name}Encoding")) { my $decoded = Hash::MultiValue->new; $hash->each(sub { my @dec = map { Encode::decode($encoding, $_) } @_; $decoded->add(@dec); }); return $decoded; } else { return $hash; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie::Params::CGI - Internal CGI Params Subclass =head1 VERSION version 3.28 =head1 SYNOPSIS Internal Use Only! =head1 DESCRIPTION This is a pure perl implementation of HTTP/CGI parameter processing for Apache::AuthCookie. =head1 METHODS =head2 new($r) Constructor =head2 request(): scalar Get the apache request object =head2 param() Get or set parameters. This manipulates the enderlying L object. When called with no parameters returns the list of CGI parameter names. Return value depends on the arguments passed: =over 4 =item * param() Return the list of CGI parameter names =item * param($field) Return the value of the given CGI field. If the field has multiple values they will all be returned as a list. =item * param($field, @values) Set the given CGI field value to the given values. Existing values will be replaced. =end =back =head2 params(): Hash::MultiValue Get the underlying CGI parameters. This is a merged version of L and L. =head2 query_params(): Hash::MultiValue Get the request query parameters. =head2 body_params(): Hash::MultiValue Get the request body parameters. =head2 content_length(): int Get the values of the C header. Returns C<0> if the header is not present or empty. =head2 content_type(): string Get the value of the C header. Returns an empty string if the header is not present. =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache/AuthCookie/Util.pm000066400000000000000000000073641357076704300240140ustar00rootroot00000000000000package Apache::AuthCookie::Util; $Apache::AuthCookie::Util::VERSION = '3.28'; # ABSTRACT: Internal Utility Functions for AuthCookie use strict; use base 'Exporter'; our @EXPORT_OK = qw(is_blank); 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; } # return true if the given value is blank or not defined. sub is_blank { return defined $_[0] && ($_[0] =~ /\S/) ? 0 : 1; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache::AuthCookie::Util - Internal Utility Functions for AuthCookie =head1 VERSION version 3.28 =head1 DESCRIPTION Internal Use Only! =for Pod::Coverage *EVERYTHING* =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache2/000077500000000000000000000000001357076704300205565ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache2/AuthCookie.pm000066400000000000000000000603631357076704300231570ustar00rootroot00000000000000package Apache2::AuthCookie; $Apache2::AuthCookie::VERSION = '3.28'; # ABSTRACT: Perl Authentication and Authorization via cookies use strict; use Carp; use base 'Apache2::AuthCookie::Base'; use Apache2::Const qw(OK DECLINED SERVER_ERROR HTTP_FORBIDDEN); use Apache::AuthCookie::Util qw(is_blank); 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 = $auth_type->decoded_requires($r) or return DECLINED; my $user = $auth_type->decoded_user($r); $r->server->log_error("authorize user=$user type=$auth_type") if $debug >=3; if (is_blank($user)) { # 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 get_satisfy { my ($auth_type, $r) = @_; my $auth_name = $r->auth_name; return lc $r->dir_config("${auth_name}Satisfy") || 'all'; } 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; } } 1; =pod =encoding UTF-8 =head1 NAME Apache2::AuthCookie - Perl Authentication and Authorization via cookies =head1 VERSION version 3.28 =head1 SYNOPSIS Make sure your mod_perl is at least 2.0.0-RC5, 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 # to enable the SameSite cookie property, set SameSite to "lax" or "strict". # See: https://www.owasp.org/index.php/SameSite PerlSetVar WhatEverSameSite strict # 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=\"...\"" # optional: enable decoding of intercepted GET/POST params: PerlSetVar WhatEverEncoding UTF-8 # optional: enable decoding of httpd.conf "Requires" directives PerlSetVar WhatEverRequiresEncoding UTF-8 # 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. (-----------------------) +---------------------------------+ ( 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. =head1 METHODS =head2 authorize(): int 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. =head2 get_satisfy(): string Get the value of C<${auth_name}Satisfy>, or C if it is not set. =head2 satisfy_is_valid(): bool return true if the configured C<${auth_name}Satisfy> is valid, false otherwise. =head2 authen_cred(): string 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. =head2 authen_ses_key($r, $session_key): string 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. =head2 custom_errors($r,@_): int 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 =head1 ENCODING AND CHARACTER SETS =head2 Encoding AuthCookie provides support for decoding POST/GET data if you tell it what the client encoding is. You do this by setting the C<< ${auth_name}Encoding >> setting in C. E.g.: PerlSetVar WhateEverEncoding UTF-8 # and you also need to arrange for charset=UTF-8 at the end of the # Content-Type header with something like: AddDefaultCharset UTF-8 Note that you B use charsets other than C, however, you need to arrange for the browser to send the right encoding back to the server. If you have turned on Encoding support by setting C<< ${auth_name}Encoding >>, this has the following effects: =over 4 =item * The internal pure-perl params processing subclass will be used, even if libapreq2 is installed. libapreq2 does not have any support for encoding or unicode. =item * POST/GET data intercepted by AuthCookie will be decoded to perl's internal format using L. =item * The value stored in C<< $r-Euser >> will be encoded as B, not characters using the configured encoding name. This is because the value stored by mod_perl is a C API string, and not a perl string. You can use L to get user string encoded using B semantics. =back This does has some caveats: =over 4 =item * your L and L function is expected to return a decoded username, either by passing it through L, or, by turning on the UTF8 flag if appropriate. =item * Due to the way HTTP works, cookies cannot contain non-ASCII characters. Because of this, if you are including the username in your generated session key, you will need to escape any non-ascii characters in the session key returned by L. =item * Similarly, you must reverse this escaping process in L and return a L decoded username. If your L function already only generates ASCII-only session keys then you do not need to worry about any of this. =item * The value stored in C<< $r-Euser >> will be encoded using bytes semantics using the configured B. If you want the decoded user value, use L instead. =back =head2 Requires You can also specify what the charset is of the Apache C<< $r-Erequires >> data is by setting C<< ${auth_name}RequiresEncoding >> in httpd.conf. E.g.: PerlSetVar WhatEverRequiresEncoding UTF-8 This will make it so that AuthCookie will decode your C directives using the configured character set. You really only need to do this if you have used non-ascii characters in any of your C directives in httpd.conf. e.g.: requires user programmør =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 =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache2/AuthCookie/000077500000000000000000000000001357076704300226115ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache2/AuthCookie/Base.pm000066400000000000000000000464511357076704300240330ustar00rootroot00000000000000package Apache2::AuthCookie::Base; $Apache2::AuthCookie::Base::VERSION = '3.28'; # ABSTRACT: Common Methods Shared by Apache2 and Apache2_4 AuthCookie Subclasses. use strict; use mod_perl2 '1.99022'; use Carp; use Apache::AuthCookie::Util qw(is_blank); use Apache2::AuthCookie::Params; use Apache2::RequestRec; use Apache2::RequestUtil; use Apache2::Log; use Apache2::Access; use Apache2::Response; use Apache2::Util; use APR::Table; use Apache2::Const qw(OK DECLINED SERVER_ERROR M_GET HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY HTTP_OK); use Encode (); sub authenticate { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("authenticate() entry") if ($debug >= 3); $r->server->log_error("auth_type " . $auth_type) if ($debug >= 3); if (my $prev = ($r->prev || $r->main)) { # we are in a subrequest or internal redirect. Just copy user from the # previous or main request if its is present if (defined $prev->user) { $r->server->log_error('authenticate() is in a subrequest or internal redirect.') if $debug >= 3; # encoding would have been handled in prev req, so do not encode here. $r->user( $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 (!is_blank($auth_user) 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_type->_encode($r, $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 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 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'; } # SameSite is an anti-CSRF cookie property. See # https://www.owasp.org/index.php/SameSite if (my $samesite = $r->dir_config("${auth_name}SameSite")) { if ($samesite =~ /\A(strict|lax)\z/i) { $samesite = lc($1); $string .= "; SameSite=$samesite"; } } return $string; } sub decoded_requires { my ($self, $r) = @_; my $reqs = $r->requires or return; my $encoding = $self->requires_encoding($r); unless (is_blank($encoding)) { for my $req (@$reqs) { $$req{requirement} = Encode::decode($encoding, $$req{requirement}); } } return $reqs; } sub decoded_user { my ($self, $r) = @_; my $user = $r->user; if (is_blank($user)) { return $user; } my $encoding = $self->encoding($r); if (!is_blank($encoding)) { $user = Encode::decode($encoding, $user); } return $user; } sub encoding { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Encoding"); } sub escape_uri { my ($r, $string) = @_; return Apache2::Util::escape_path($string, $r->pool); } sub get_cookie_path { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Path"); } 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 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 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 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 logout { my ($self,$r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $self->remove_cookie($r); $self->handle_cache($r); } sub params { my ($self, $r) = @_; return Apache2::AuthCookie::Params->new($r); } sub recognize_user { my ($self, $r) = @_; # only check if user is not already set return DECLINED unless is_blank($r->user); my $debug = $r->dir_config("AuthCookieDebug") || 0; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; return DECLINED if is_blank($auth_type) or is_blank($auth_name); return DECLINED if is_blank($r->headers_in->get('Cookie')); 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 is_blank($cookie); my ($user,@args) = $auth_type->authen_ses_key($r, $cookie); if (!is_blank($user) 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( $self->_encode($r, $user) ); } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $user, @args); } return is_blank($user) ? DECLINED : OK; } 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; } sub requires_encoding { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}RequiresEncoding"); } 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); } 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); } } sub untaint_destination { my ($self, $dest) = @_; return Apache::AuthCookie::Util::escape_destination($dest); } # 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 _encode { my ($self, $r, $value) = @_; my $encoding = $self->encoding($r); if (is_blank($encoding)) { return $value; } else { return Encode::encode($encoding, $value); } } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache2::AuthCookie::Base - Common Methods Shared by Apache2 and Apache2_4 AuthCookie Subclasses. =head1 VERSION version 3.28 =head1 DESCRIPTION This module contains common code shared by AuthCookie for Apache 2.x and Apache 2.4. =head1 METHODS =head2 authenticate($r): int 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. =head2 cookie_name($r): string Return the name of the auth cookie for this request. This is either C<${auth_name}CookieName>, or AuthCookie's self generated name. =head2 cookie_string(%args): string Generate a cookie string. C<%args> are: =over 4 =item * request The Apache request object =item * key The Cookie name =item * value the Cookie value =item * expires (optional) When the cookie expires. See L. Uses C<${auth_name}Expires> if not giv =back All other cookie settings come from C settings. =head2 decoded_requires($r): arrayref This method returns the C<< $r->requires >> array, with the C values decoded if C<${auth_name}RequiresEncoding> is in effect for this request. =head2 decoded_user($r): string If you have set ${auth_name}Encoding, then this will return the decoded value of C<< $r-Euser >>. =head2 encoding($r): string Return the ${auth_name}Encoding setting that is in effect for this request. =head2 escape_uri($r, $value): string Escape the given string so it is suitable to be used in a URL. =head2 get_cookie_path($r): string Returns the value of C. =head2 handle_cache($r): void If C<${auth_name}Cache> is defined, this sets up the response so that the client will not cache the result. This sents C in the apache request object and sends the appropriate headers so that the client will not cache the response. =head2 key($r): string 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. =head2 login($r): int 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, 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 submitted form field. =head2 login_form($r): int 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. =head2 login_form_status($r): int 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. =head2 logout($r): void 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)>. =head2 params($r): Apache2::AuthCookie::Params Get the GET/POST params object for this request. =head2 recognize_user($r): int 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. =head2 remove_cookie($r): void Adds a C header that instructs the client to delete the cookie immediately. =head2 requires_encoding($r): string Return the ${auth_name}RequiresEncoding setting that is in effect for this request. =head2 send_cookie($r, $ses_key, $args): void 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. =head2 send_p3p($r): void Set a P3P response header if C<${auth_name}P3P> is configured. The value of the header is whatever is in the C<${auth_name}P3P> setting. =head2 untaint_destination($destination): string 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. =for Pod::Coverage OK DECLINED SERVER_ERROR M_GET HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY HTTP_OK =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache2/AuthCookie/Params.pm000066400000000000000000000034471357076704300244020ustar00rootroot00000000000000package Apache2::AuthCookie::Params; $Apache2::AuthCookie::Params::VERSION = '3.28'; # 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); 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 { $r->server->log_error("params: using CGI") if $debug >= 3; return $class->SUPER::_new_instance($r); } return; } 1; __END__ =pod =encoding UTF-8 =head1 NAME Apache2::AuthCookie::Params - AuthCookie Params Driver for mod_perl 2.x =head1 VERSION version 3.28 =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/lib/Apache2_4/000077500000000000000000000000001357076704300210015ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/lib/Apache2_4/AuthCookie.pm000066400000000000000000000572071357076704300234050ustar00rootroot00000000000000package Apache2_4::AuthCookie; $Apache2_4::AuthCookie::VERSION = '3.28'; # ABSTRACT: Perl Authentication and Authorization via cookies for Apache 2.4 use strict; use base 'Apache2::AuthCookie::Base'; use Apache2::Log; use Apache2::Const -compile => qw(AUTHZ_GRANTED AUTHZ_DENIED AUTHZ_DENIED_NO_USER); use Apache::AuthCookie::Util qw(is_blank); # You really do not need this provider at all. This provides an implementation # for "Require user ..." directives, that is compatible with mod_authz_core # (with the exception that expressions are not supported). You should really # just let mod_authz_core be your "user" authz provider. Nevertheless, due to # the fact that AuthCookie was released for Apache 2.4 with documentation that # shows this is needed, we leave this implementation for backwards # compatibility. sub authz_handler { my ($auth_type, $r, $requires) = @_; my $user = $r->user; if (is_blank($user)) { # user is not yet authenticated return Apache2::Const::AUTHZ_DENIED_NO_USER; } if (is_blank($requires)) { $r->server->log_error(q[Your 'Require user ...' config does not specify any users]); return Apache2::Const::AUTHZ_DENIED; } my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("authz user=$user type=$auth_type req=$requires") if $debug >=3; for my $valid_user (split /\s+/, $requires) { if ($user eq $valid_user) { return Apache2::Const::AUTHZ_GRANTED; } } # log a message similar to mod_authz_user $r->log->debug(sprintf q[access to %s failed, reason: user '%s' does not meet 'require'ments for a ]. q[user to be allowed access], $r->uri, $r->user); return Apache2::Const::AUTHZ_DENIED; } 1; =pod =encoding UTF-8 =head1 NAME Apache2_4::AuthCookie - Perl Authentication and Authorization via cookies for Apache 2.4 =head1 VERSION version 3.28 =head1 SYNOPSIS Make sure your mod_perl is at least 2.0.9, with StackedHandlers, MethodHandlers, Authen, and Authz compiled in. # In httpd.conf or .htaccess: PerlModule Sample::Apache2::AuthCookieHandler PerlSetVar WhatEverPath / PerlSetVar WhatEverLoginScript /login.pl # 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 # to enable the SameSite cookie property, set SameSite to "lax" or "strict". # See: https://www.owasp.org/index.php/SameSite PerlSetVar WhatEverSameSite strict # 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=\"...\"" # optional: enable decoding of intercepted GET/POST params: PerlSetVar WhatEverEncoding UTF-8 # optional: enable decoding of httpd.conf "Requires" directives PerlSetVar WhatEverRequiresEncoding UTF-8 # These documents require user to be logged in. AuthType Sample::Apache2::AuthCookieHandler AuthName WhatEver PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate Require valid-user # How to handle a custom requirement (non-user). PerlAddAuthzProvider species Sample::Apache2::AuthCookieHandler->authz_species Require species klingon # 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 C version 2 for C version 2.4.x. If you are running mod_perl version 1, you need B instead. If you are running C 2.0.0-2.2.x, you need 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 C 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. =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. (-----------------------) +---------------------------------+ ( 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. =head1 METHODS =head2 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. =head2 authen_ses_key() You must define this method yourself in your subclass of C. 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. =head2 custom_errors($r,@_) 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 =head1 EXAMPLE For an example of how to use C, 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 ENCODING AND CHARACTER SETS =head2 Encoding AuthCookie provides support for decoding POST/GET data if you tell it what the client encoding is. You do this by setting the C<< ${auth_name}Encoding >> setting in C. E.g.: PerlSetVar WhateEverEncoding UTF-8 # and you also need to arrange for charset=UTF-8 at the end of the # Content-Type header with something like: AddDefaultCharset UTF-8 Note that you B use charsets other than C, however, you need to arrange for the browser to send the right encoding back to the server. If you have turned on Encoding support by setting C<< ${auth_name}Encoding >>, this has the following effects: =over 4 =item * The internal pure-perl params processing subclass will be used, even if libapreq2 is installed. libapreq2 does not have any support for encoding or unicode. =item * POST/GET data intercepted by AuthCookie will be decoded to perl's internal format using L. =item * The value stored in C<< $r-Euser >> will be encoded as B, not characters using the configured encoding name. This is because the value stored by mod_perl is a C API string, and not a perl string. You can use L to get user string encoded using B semantics. =back This does has some caveats: =over 4 =item * your L and L function is expected to return a decoded username, either by passing it through L, or, by turning on the UTF8 flag if appropriate. =item * Due to the way HTTP works, cookies cannot contain non-ASCII characters. Because of this, if you are including the username in your generated session key, you will need to escape any non-ascii characters in the session key returned by L. =item * Similarly, you must reverse this escaping process in L and return a L decoded username. If your L function already only generates ASCII-only session keys then you do not need to worry about any of this. =item * The value stored in C<< $r-Euser >> will be encoded using bytes semantics using the configured B. If you want the decoded user value, use L instead. =back =head2 Requires You can also specify what the charset is of the Apache C<< $r-Erequires >> data is by setting C<< ${auth_name}RequiresEncoding >> in httpd.conf. E.g.: PerlSetVar WhatEverRequiresEncoding UTF-8 This will make it so that AuthCookie will decode your C directives using the configured character set. You really only need to do this if you have used non-ascii characters in any of your C directives in httpd.conf. e.g.: requires user programmør =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 C<-Elogin()> 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 C 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 = Digest::SHA::sha256_hex(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 a database to store the session key and retrieve it later. =head1 FREQUENTLY ASKED QUESTIONS =over 4 =item * I upgraded to Apache 2.4 and now AuthCookie doesn't work! Apache 2.4 radically changed the authenciation and authorization API. You will need to port your AuthCookie subclass over to the Apache 2.4 API. See the POD documentation in L for more information, but the quick rundown is you need to: =over 4 =item * Inherit from C =item * Remove all C configuration entries. =item * Write Authz Provider methods for any C directives that you are using that apache does not provide for already (e.g. apache already handles C and C) and register them with something like. PerlAddAuthzProvier species Sample::AuthCookieHandler->authz_species =item * Replace instances of C<${AuthName}Satistfy> with either C or C blocks. =back =item * Why is my authz method called twice per request? This is normal behaviour under Apache 2.4. This is to accommodate for authorization of anonymous access. You are expected to return C IF C<< $r->user >> has not yet been set if you want authentication to proceed. Your authz handler will be called a second time after the user has been authenticated. =item * AuthCookie authenticates, but the authorization handler is returning C instead of C! In Apache 2.4, in C, if no authz handlers return C, then C is returned. In previous versions of Apache, C was returned. You can get the old behaviour if you want it with: AuthzSendForbiddenOnFailure On =item * My log shows an entry like: authorization result of Require ...: denied (no authenticated user yet) These are normal. This happens because the authz provider returned C and the authz provider will be called again after authentication happens. =back =head1 HISTORY Originally written by Eric Bartley versions 2.x were written by Ken Williams =head1 COPYRIGHT Copyright (c) 2015 Michael Schout. 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 =for Pod::Coverage AUTHZ_GRANTED AUTHZ_DENIED AUTHZ_DENIED_NO_USER =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 on the bugtracker website L When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature. =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 libapache2-authcookie-perl-3.28/scripts/000077500000000000000000000000001357076704300202345ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/scripts/perlbrew-smoke000077500000000000000000000044031357076704300231210ustar00rootroot00000000000000#!/usr/bin/env bash # # This is an internal use only script for smoke testing this dist against # various perlbrew installations. # # fail fast and hard set -eo pipefail # make sure perlbrew is initialized . $HOME/perl5/perlbrew/etc/bashrc die() { echo $@ exit 1 } check_perlbrew() { if [ -z "$PERLBREW_ROOT" ] || [ -z "$PERLBREW_PERL" ]; then die "perlbrew does not seem to be initialized" fi if [ ! -d $PERLBREW_ROOT/perls/$PERLBREW_PERL ]; then die "$PERLBREW_PERL does not seem to be a valid perl installation" fi if [ ! -d $PERLBREW_ROOT/perls/$PERLBREW_PERL/.git ]; then die "perlbrew installation $PERLBREW_PERL is not a git repository" fi } perlbrew_reset() { ( cd $PERLBREW_ROOT/perls/$PERLBREW_PERL git clean -fdx git checkout . ) } install_cpan_deps() { PERL_VERSION=$(perl -e 'print $]') if [ ! -z "$CPAN_MINVERSION" ]; then cpanm -q --notest \ Test::More@0.94 \ URI@1.30 \ LWP::UserAgent@2.033 \ Apache::Test@1.39 \ Class::Load@0.03 else cpanm -q --notest --skip-satisfied --installdeps . # install optional modules so that tests run cpanm -q --notest --skip-satisfied URI~1.30 LWP::UserAgent~2.033 # modern Test::More+ApacheTest and 5.8.9 do not play nicely together. # force downgrade to 0.94 if necessary if [ $PERL_VERSION = "5.008009" ] || [ $PERL_VERSION = "5.010001" ]; then cpanm -q --notest Test::More@0.94 fi fi # if author testing is set, install author testing deps if [ ! -z "$AUTHOR_TESTING" ]; then cpanm -q --notest Test::Pod fi # if signature testing is on, install Module::Signature if [ ! -z "$TEST_SIGNATURE" ]; then cpanm -q --notest Module::Signature fi } smoke_perl() { local variant=$1 perlbrew use $variant check_perlbrew if [ -z "$NO_RESET" ]; then perlbrew_reset fi install_cpan_deps [ -f Makefile ] && make clean perl Makefile.PL && make test } perlbrew list | grep apache | sed -e 's/*//' | while read variant do echo "=====> TESTING AGAINST PERLBREW INSTALL: $variant <=====" smoke_perl $variant echo "" done libapache2-authcookie-perl-3.28/t/000077500000000000000000000000001357076704300170105ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/Skeleton/000077500000000000000000000000001357076704300205745ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/Skeleton/AuthCookieHandler.pm000066400000000000000000000014021357076704300244600ustar00rootroot00000000000000package 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; libapache2-authcookie-perl-3.28/t/TEST.PL000066400000000000000000000006171357076704300200300ustar00rootroot00000000000000#!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); libapache2-authcookie-perl-3.28/t/author-pod-syntax.t000066400000000000000000000004541357076704300226060ustar00rootroot00000000000000#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); libapache2-authcookie-perl-3.28/t/author-signature.t000066400000000000000000000006571357076704300225060ustar00rootroot00000000000000#!perl -w BEGIN { unless ($ENV{AUTHOR_TESTING}) { print qq{1..0 # SKIP these tests are for testing by the author\n}; exit } } # This file was automatically generated by Dist::Zilla::Plugin::AuthorSignatureTest use strict; use warnings; use Test::More; unless (eval { require Test::Signature; 1 }) { plan skip_all => 'Test::Signature is required for this test'; } Test::Signature::signature_ok(); done_testing; libapache2-authcookie-perl-3.28/t/conf/000077500000000000000000000000001357076704300177355ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/conf/extra.conf.in000066400000000000000000000221151357076704300223350ustar00rootroot00000000000000PerlRequire @ServerRoot@/startup.pl AddDefaultCharset UTF-8 PerlModule Sample::Apache::AuthCookieHandler PerlModule Apache::Registry PerlSwitches -I@ServerRoot@/lib PerlModule Sample::Apache2::AuthCookieHandler PerlModule ModPerl::Registry PerlModule Sample::Apache2_4::AuthCookieHandler PerlAddAuthzProvider dwarf Sample::Apache2_4::AuthCookieHandler->dwarf PerlAddAuthzProvider myuser Sample::Apache2_4::AuthCookieHandler->authz_handler PerlSetVar WhatEverPath / PerlSetVar WhatEverLoginScript /docs/login.pl PerlSetVar AuthCookieDebug 3 PerlSetVar WhatEverCookieName Sample::AuthCookieHandler_WhatEver PerlSetVar WhatEverEncoding UTF-8 PerlSetVar WhatEverRequiresEncoding UTF-8 AllowOverride All AuthzSendForbiddenOnFailure On # 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 AuthType Sample::Apache2_4::AuthCookieHandler PerlAuthenHandler Sample::Apache2_4::AuthCookieHandler->authenticate Require user programmer DirectoryIndex index.html # 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 AuthType Sample::Apache2_4::AuthCookieHandler PerlAuthenHandler Sample::Apache2_4::AuthCookieHandler->authenticate Require user some-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 PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate AuthType Sample::Apache2::AuthCookieHandler PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize PerlAuthenHandler Sample::Apache2_4::AuthCookieHandler->authenticate AuthType Sample::Apache2_4::AuthCookieHandler # apache 1.x, apache 2.0, apache 2.2 Require dwarf Require user programmer # apache 2.4 Require user programmer Require dwarf # test our internal authz_handler for apache 2.4 AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler PerlAuthenHandler Sample::Apache::AuthCookieHandler->authenticate PerlAuthzHandler Sample::Apache::AuthCookieHandler->authorize PerlAuthenHandler Sample::Apache2::AuthCookieHandler->authenticate AuthType Sample::Apache2::AuthCookieHandler PerlAuthzHandler Sample::Apache2::AuthCookieHandler->authorize PerlAuthenHandler Sample::Apache2_4::AuthCookieHandler->authenticate AuthType Sample::Apache2_4::AuthCookieHandler # apache 1.x, apache 2.0, apache 2.2 Require user programmer # apache 2.4 Require myuser dopey 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 AuthType Sample::Apache2_4::AuthCookieHandler PerlAuthenHandler Sample::Apache2_4::AuthCookieHandler->authenticate 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 PerlFixupHandler Sample::Apache2::AuthCookieHandler->recognize_user AuthType Sample::Apache2::AuthCookieHandler PerlFixupHandler Sample::Apache2_4::AuthCookieHandler->recognize_user AuthType Sample::Apache2_4::AuthCookieHandler Require all granted SetHandler perl-script Options +ExecCGI AuthName WhatEver PerlHandler Apache::Registry AuthType Sample::Apache::AuthCookieHandler PerlFixupHandler Sample::Apache::AuthCookieHandler->recognize_user PerlResponseHandler ModPerl::Registry PerlFixupHandler Sample::Apache2::AuthCookieHandler->recognize_user AuthType Sample::Apache2::AuthCookieHandler PerlFixupHandler Sample::Apache2_4::AuthCookieHandler->recognize_user AuthType Sample::Apache2_4::AuthCookieHandler Require all granted # 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 AuthType Sample::Apache2_4::AuthCookieHandler Require all granted 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 AuthType Sample::Apache2_4::AuthCookieHandler PerlResponseHandler Sample::Apache2_4::AuthCookieHandler->login Require all granted # login action that sets SameSite PerlSetVar WhatEverSameSite strict AuthName WhatEver SetHandler perl-script AuthType Sample::Apache::AuthCookieHandler PerlHandler Sample::Apache::AuthCookieHandler->login AuthType Sample::Apache2::AuthCookieHandler PerlResponseHandler Sample::Apache2::AuthCookieHandler->login AuthType Sample::Apache2_4::AuthCookieHandler PerlResponseHandler Sample::Apache2_4::AuthCookieHandler->login Require all granted AuthName WhatEver AuthType Sample::Apache::AuthCookieHandler AuthType Sample::Apache2::AuthCookieHandler AuthType Sample::Apache2_4::AuthCookieHandler SetHandler perl-script PerlHandler Apache::Status # vim: filetype=apache sw=2 ts=2 ai et libapache2-authcookie-perl-3.28/t/htdocs/000077500000000000000000000000001357076704300202745ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/000077500000000000000000000000001357076704300212245ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/authall/000077500000000000000000000000001357076704300226565ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/authall/get_me.html000066400000000000000000000002501357076704300250010ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/authany/000077500000000000000000000000001357076704300226755ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/authany/get_me.html000066400000000000000000000002501357076704300250200ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/cookiename/000077500000000000000000000000001357076704300233365ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/cookiename/get_me.html000066400000000000000000000002501357076704300254610ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/echo-user.pl000077500000000000000000000005501357076704300234560ustar00rootroot00000000000000use 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"; libapache2-authcookie-perl-3.28/t/htdocs/docs/echo_cookie.pl000077500000000000000000000005661357076704300240420ustar00rootroot00000000000000use 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); libapache2-authcookie-perl-3.28/t/htdocs/docs/index.html000066400000000000000000000001611357076704300232170ustar00rootroot00000000000000 Get the protected document libapache2-authcookie-perl-3.28/t/htdocs/docs/login.pl000077500000000000000000000034361357076704300227020ustar00rootroot00000000000000#!/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); libapache2-authcookie-perl-3.28/t/htdocs/docs/logout.pl000077500000000000000000000011431357076704300230740ustar00rootroot00000000000000use 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 libapache2-authcookie-perl-3.28/t/htdocs/docs/myuser/000077500000000000000000000000001357076704300225505ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/myuser/get_me.html000066400000000000000000000002501357076704300246730ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/protected/000077500000000000000000000000001357076704300232155ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/protected/echo_user.pl000077500000000000000000000005761357076704300255410ustar00rootroot00000000000000use 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"; libapache2-authcookie-perl-3.28/t/htdocs/docs/protected/get_me.html000066400000000000000000000002501357076704300253400ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/protected/index.html000066400000000000000000000002431357076704300252110ustar00rootroot00000000000000 Congratulations

Congratulations, you got index.html

Log Out

libapache2-authcookie-perl-3.28/t/htdocs/docs/stimeout/000077500000000000000000000000001357076704300230755ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/htdocs/docs/stimeout/get_me.html000066400000000000000000000002501357076704300252200ustar00rootroot00000000000000 Congratulations

Congratulations, you got past AuthCookie

Log Out

libapache2-authcookie-perl-3.28/t/lib/000077500000000000000000000000001357076704300175565ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/lib/Sample/000077500000000000000000000000001357076704300207775ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/lib/Sample/Apache/000077500000000000000000000000001357076704300221605ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/lib/Sample/Apache/AuthCookieHandler.pm000066400000000000000000000041721357076704300260530ustar00rootroot00000000000000package Sample::Apache::AuthCookieHandler; use strict; use utf8; use base 'Apache::AuthCookie'; use Apache; use Apache::Constants qw(:common); use Apache::AuthCookie; use Apache::Util; use URI::Escape qw(uri_escape_utf8 uri_unescape); use Encode; 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 escaped credentials and delaying authentication. return join ':', map { uri_escape_utf8($_) } @creds; } sub authen_ses_key ($$$) { my ($self, $r, $ses_key) = @_; # NOTE: uri_escape_utf8() was used to encode this so we have to decode # using UTF-8. We don't rely on $self->encoding($r) here because if an # encoding other than UTF-8 is configured in t/conf/extra.conf.in, then the # wrong encoding gets used here. my($user, $password) = map { decode('UTF-8', uri_unescape($_)) } split /:/, $ses_key, 2; if ($user eq 'programmer' && $password eq 'Hero') { return $user; } elsif ($user eq 'some-user') { return $user; } elsif ($user eq '0') { return $user; } elsif ($user eq '程序员') { # programmer in chinese, at least according to google translate 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; libapache2-authcookie-perl-3.28/t/lib/Sample/Apache2/000077500000000000000000000000001357076704300222425ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/lib/Sample/Apache2/AuthCookieHandler.pm000066400000000000000000000075231357076704300261400ustar00rootroot00000000000000package Sample::Apache2::AuthCookieHandler; use strict; use utf8; use Class::Load 'load_class'; use Apache2::Const qw(:common HTTP_FORBIDDEN); use Apache2::AuthCookie; use Apache2::RequestRec; use Apache2::RequestIO; use Apache2::Util; use URI::Escape qw(uri_escape_utf8 uri_unescape); use Encode qw(decode); use vars qw(@ISA); use Apache::Test; use Apache::TestUtil; if (have_min_apache_version('2.4.0')) { load_class('Apache2_4::AuthCookie'); @ISA = qw(Apache2_4::AuthCookie); } else { load_class('Apache2::AuthCookie'); @ISA = qw(Apache2::AuthCookie); } sub authen_cred ($$\@) { my $self = shift; my $r = shift; my @creds = @_; $r->server->log_error("authen_cred entry"); 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 escaped credentials and delaying authentication. return join ':', map { uri_escape_utf8($_) } @creds; } sub authen_ses_key ($$$) { my ($self, $r, $cookie) = @_; my ($user, $password) = map { decode('UTF-8', uri_unescape($_)) } split /:/, $cookie, 2; $r->server->log_error("authen_ses_key entry"); $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; } elsif ($user eq '程序员') { # programmer in chinese, at least according to google translate return $user; } else { return; } } sub dwarf { my $self = shift; my $r = shift; $r->server->log_error("dwarf entry"); my $user = $r->user; $r->server->log_error("USER=$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; libapache2-authcookie-perl-3.28/t/lib/Sample/Apache2_4/000077500000000000000000000000001357076704300224655ustar00rootroot00000000000000libapache2-authcookie-perl-3.28/t/lib/Sample/Apache2_4/AuthCookieHandler.pm000066400000000000000000000016771357076704300263670ustar00rootroot00000000000000package Sample::Apache2_4::AuthCookieHandler; use strict; use Sample::Apache2::AuthCookieHandler; use Apache2::Const qw(AUTHZ_DENIED_NO_USER); use Apache2::RequestRec; use Apache::AuthCookie::Util qw(is_blank); use vars qw(@ISA); @ISA = qw(Sample::Apache2::AuthCookieHandler); my %Dwarves = map { $_ => 1 } qw(bashful doc dopey grumpy happy sleepy sneezy programmer); # authz under apache 2.4 is very different from previous versions sub dwarf { my ($self, $r) = @_; $r->server->log_error("dwarf entry"); my $user = $r->user; if (is_blank($user)) { $r->server->log_error("No user authenticted yet"); return Apache2::Const::AUTHZ_DENIED_NO_USER; } elsif (defined $Dwarves{$user}) { $r->server->log_error("$user is a dwarf"); return Apache2::Const::AUTHZ_GRANTED; } else { $r->server->log_error("$user is not a dwarf"); return Apache2::Const::AUTHZ_DENIED; } } 1; libapache2-authcookie-perl-3.28/t/real.t000077500000000000000000000371731357076704300201360ustar00rootroot00000000000000# 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 utf8; use Apache::Test '-withtestmore'; use Apache::TestUtil; use Apache::TestRequest qw(GET POST GET_BODY); use Encode qw(encode); Apache::TestRequest::user_agent( reset => 1, requests_redirectable => 0 ); plan tests => 34, need_lwp; ok 1, 'Test initialized'; # 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. subtest 'get index.html' => sub { plan tests => 1; 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 subtest 'no cookie' => sub { plan tests => 1; 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. subtest 'login redirects' => sub { plan tests => 2; 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'); }; subtest 'redirect with bad session key' => sub { plan tests => 3; 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'); }; # get protected document with valid cookie. Should succeed. subtest 'redirect wit valid cookie' => sub { plan tests => 2; 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'); }; subtest 'directory index' => sub { plan tests => 2; my $uri = '/docs/protected/'; my $r = GET( $uri, Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero;' ); is($r->code, '200', 'get protected document'); like($r->content, qr/Congratulations, you got index\.html/s, 'check protected index.html document content'); }; # should have a Set-Cookie header that expired at epoch. subtest 'logout deletes cookie' => sub { plan tests => 1; 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 subtest 'session key data' => sub { plan tests => 1; 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' subtest 'invalid user' => sub { plan tests => 1; 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 get the login form back (bad_cookie). subtest 'invalid cookie' => sub { plan tests => 1; 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) subtest 'bad credentials' => sub { plan tests => 1; 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'); }; subtest 'AuthAny' => sub { plan tests => 3; 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 subtest 'AuthAll' => sub { plan tests => 3; 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'); # should pass, ALL requirements are met $r = GET( '/docs/authall/get_me.html', 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'); }; subtest 'POST to GET conversion' => sub { plan tests => 1; my $r = POST('/docs/protected/get_me.html', [ utf8 => 'programmør' ]); like($r->content, qr#"/docs/protected/get_me\.html\?utf8=programm%c3%b8r"#, 'POST -> GET conversion works'); }; subtest 'QUERY_STRING is preserved' => sub { plan tests => 1; 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) subtest 'AuthAny' => sub { plan tests => 3; 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. $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'); # no AuthAny requirements met $r = GET( '/docs/authany/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=nouser:mypassword' ); is($r->code, 403, 'AuthAny forbidden'); }; # local authz provider test for 2.4 (works same as authany on older versions) subtest 'Authz Provider' => sub { plan tests => 1; my $r = GET( '/docs/myuser/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=programmer:Hero' ); like($r->content, qr/Congratulations, you got past AuthCookie/, 'myuser=programmer access allowed'); }; # login with username=0 works subtest 'login with username=0' => sub { plan tests => 2; 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'); }; subtest 'parameter encoding' => sub { plan tests => 5; my $r = POST('/LOGIN', [ destination => '/docs/authany/get_me.html', credential_0 => '程序员', credential_1 => 'Hero' ]); is($r->code, 302, 'UTF-8 username works'); is($r->header('Location'), '/docs/authany/get_me.html', 'redirect header exists, and contains expected url'); like $r->header('Set-Cookie'), qr#Sample::AuthCookieHandler_WhatEver=%E7%A8%8B%E5%BA%8F%E5%91%98:Hero;#, 'response contains the session key cookie'; $r = GET('/docs/authany/get_me.html', Cookie => 'Sample::AuthCookieHandler_WhatEver=%E7%A8%8B%E5%BA%8F%E5%91%98:Hero;' ); is $r->code, 200; like($r->content, qr/Congratulations, you got past AuthCookie/s, 'check protected document content'); }; # Should succeed and cookie should have HttpOnly attribute subtest 'HttpOnly cookie attribute' => sub { plan tests => 3; 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'); }; # Should succeed and cookie should have SameSite attribute subtest 'SameSite cookie attribute' => sub { plan tests => 3; my $r = POST('/LOGIN-SAMESITE', [ destination => '/docs/protected/get_me.html', credential_0 => 'programmer', credential_1 => 'Heroo' ]); is($r->header('Location'), '/docs/protected/get_me.html', 'SameSite location header'); is($r->header('Set-Cookie'), 'Sample::AuthCookieHandler_WhatEver=programmer:Heroo; path=/; SameSite=strict', 'cookie contains SameSite attribute'); is($r->code, 302, 'check redirect response code'); }; # test SessionTimeout subtest 'session timeout' => sub { plan tests => 1; 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. subtest 'creds are in pnotes' => sub { plan tests => 1; 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. subtest 'unescape URL with spaces' => sub { plan tests => 1; 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 # '+' subtest 'do not remove encoded +' => sub { plan tests => 1; 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. subtest 'XSS: no newlines in destination' => sub { plan tests => 4; 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 subtest 'XSS: no embedded HTML in destination' => sub { plan tests => 1; 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 subtest 'XSS: no embedded script' => sub { plan tests => 1; my $r = POST('/LOGIN', [ destination => q{">} ]); ok index($r->content, q{}) == -1; }; subtest 'preserve / in password' => sub { plan tests => 1; 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. subtest 'multi-valued form data is preserved' => sub { plan tests => 2; 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 subtest 'setup $ENV{REMOTE_USER}' => sub { plan tests => 1; 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 subtest 'SymbianOS login form response code' => sub { plan tests => 4; 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); }; subtest 'recognize user' => sub { plan tests => 1; # 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 libapache2-authcookie-perl-3.28/t/signature.t000077500000000000000000000017661357076704300212130ustar00rootroot00000000000000#!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 => 1); SKIP: { skip "Module::Signature cannot verify", 1 if $ret eq Module::Signature::CANNOT_VERIFY(); cmp_ok $ret, '==', Module::Signature::SIGNATURE_OK(), "Valid signature"; } libapache2-authcookie-perl-3.28/t/startup.pl000066400000000000000000000000601357076704300210430ustar00rootroot00000000000000use lib qw(../blib/lib blib/lib lib t/lib); 1; libapache2-authcookie-perl-3.28/t/util.t000077500000000000000000000007441357076704300201620ustar00rootroot00000000000000#!/usr/bin/env perl # # tests for Apache::AuthCookie::Util # use strict; use Test::More tests => 2; # don't use_ok, this needs to load at compile time. use_ok 'Apache::AuthCookie::Util' or exit 1; subtest is_blank => sub { plan tests => 8; Apache::AuthCookie::Util->import('is_blank'); ok is_blank(' '); ok is_blank(''); ok is_blank("\t"); ok is_blank("\n"); ok is_blank("\r\n"); ok is_blank(undef); ok !is_blank(0); ok !is_blank('a'); };