pax_global_header00006660000000000000000000000064135753050640014522gustar00rootroot0000000000000052 comment=d828860a9e8237fe043285383827bba3310e50a8 libplack-middleware-session-perl-0.33/000077500000000000000000000000001357530506400177645ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/Build.PL000066400000000000000000000002731357530506400212620ustar00rootroot00000000000000# This Build.PL for Plack-Middleware-Session was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015. use strict; use warnings; use 5.006; use Module::Build::Tiny 0.034; Build_PL(); libplack-middleware-session-perl-0.33/Changes000066400000000000000000000102351357530506400212600ustar00rootroot00000000000000Revision history for Perl extension Plack::Middleware::Session 0.33 2019-03-09 15:18:15 PST - Removed dependency to Digest::SHA1 #45 - Added explicit dep to HTTP::Request::Common #44 - Add t/lib to INC for Perl 5.26+ - Fix POD link 0.32 2019-02-26 14:36:19 MST - Fix dependency for Cookie::Baker 0.31 2019-02-26 12:01:00 MST - Documentation fix - Support samesite cookie attributes #42 0.30 2015-03-02 10:24:38 PST - Fix VERSION 0.29 2015-02-17 15:56:25 PST - Moved repo to the plack organization on github 0.28 2015-02-16 08:30:08 PST - Same as 0.27. Make it non-trial 0.27 2015-02-13 16:52:11 PST - Added late_store in psgix.session.options to update the session after the streaming and reverts the default behavior to pre-0.26 (reported by darkkar, fixed by alexmv) #29, #30 0.26 2015-02-03 09:17:38 CET - Improved documentation (oalders, basiliscos, Mohammad Anwar, alexmv) - Session storage is now updated in the cleanup phase, after the streaming is complete (alexmv) #28 0.25 2014-09-28 20:07:42 PDT - Make tests safer for parallel execution. #21 0.24 2014-09-05 04:49:59 PDT - No changes since 0.23 0.23 2014-08-11 10:22:40 PDT - Changed the warning to error, when secret is not set. 0.22 2014-08-11 10:16:51 PDT - Document the vunlerability of using this middleware without secret, and warn when secret is not set on the runtime. In the next release the default will be changed to require the secret. (mala) 0.21 2013-10-12 11:41:37 PDT - use Cookie::Baker (kazeburo) 0.20 2013-06-24 16:09:21 PDT - Fix packaging (name casing) 0.19 2013-06-24 15:09:55 PDT - Use Milla - Add prereqs to LWP/HTTP::Cookies 0.18 Tue Feb 12 02:56:23 PST 2013 - Repackaging 0.17 Mon Feb 11 15:40:50 PST 2013 - Use constant time comparison of HMAC signature to be precautious for timing attacks 0.16 Sun Feb 10 11:41:14 PST 2013 - Fix minimum version requirement for Test::Fatal 0.15 Tue Sep 4 14:15:13 PDT 2012 - Fixed CPAN dependencies 0.14 Tue Mar 29 13:48:42 PDT 2011 - Support get_dbh callback in Store::DBI (kazeburo) 0.13 Wed Dec 22 08:56:52 PST 2010 - Added WARNINGS to deprecate request parameter based session state from the default state - Added 'change_id' option for paranoids against session fixation (s-aska, nihen) 0.12 Wed Jul 7 15:54:05 PDT 2010 - Improved documents (markstos, haarg) - Support httponly option (haarg) 0.11 Sat Feb 27 02:40:29 PST 2010 - Added Session::Store::DBI by lestrrat 0.10 Mon Feb 22 19:03:17 PST 2010 - Make this a non-dev release now Plack 0.9910 is out 0.09_03 Tue Feb 2 20:42:56 PST 2010 - Fixed so the default Cookie path is now correctly set to '/' You can override that by setting path = undef in psgix.session.options. (Reported by tomyhero) 0.09_02 Sat Jan 30 23:13:50 PST 2010 - Fixed a bug in Cookie serialization where it breaks the response headers generated by applications (tomyhero) 0.09_01 Sat Jan 30 13:39:21 PST 2010 - Reworked the internal code and API a lot, so Session persistence and retrieval are handled in a more stateless way - INCOMPATIBLE: psgix.session is now a hash reference rather than an object. If you need an object like before, do: use Plack::Session; $session = Plack::Session->new($env); - Added Plack::Middleware::Session::Cookie which uses CookieStore - Updated Cookie handling code to work with Plack 0.99 and later 0.03 Thurs. Jan. 7, 2009 * Plack::Middleware::Session - change plack.session to psgix.session (plack.session is retained for back-compat, but is deprecated and will be removed in future versions) * Plack::Session::Store::File - changed to lock_* versions of the Storaable functions (thanks to Miyagawa) 0.02 Sat. Dec. 19, 2009 - fixed dependency list (RT #52891) (Thanks to Andreas Koenig) - fixed some POD misspellings (Thanks to franckcuny) - fixed streaming interface (Thanks to clkao and miyagawa) 0.01 Tues. Dec. 15, 2009 - Hello CPAN World!libplack-middleware-session-perl-0.33/LICENSE000066400000000000000000000436771357530506400210120ustar00rootroot00000000000000This software is copyright (c) 2009 by Tatsuhiko Miyagawa. 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) 2009 by Tatsuhiko Miyagawa. 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) 2009 by Tatsuhiko Miyagawa. 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 libplack-middleware-session-perl-0.33/MANIFEST000066400000000000000000000020101357530506400211060ustar00rootroot00000000000000# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. Build.PL Changes LICENSE MANIFEST META.json META.yml README cpanfile dist.ini examples/counter-raw.psgi lib/Plack/Middleware/Session.pm lib/Plack/Middleware/Session/Cookie.pm lib/Plack/Session.pm lib/Plack/Session/Cleanup.pm lib/Plack/Session/State.pm lib/Plack/Session/State/Cookie.pm lib/Plack/Session/Store.pm lib/Plack/Session/Store/Cache.pm lib/Plack/Session/Store/DBI.pm lib/Plack/Session/Store/File.pm lib/Plack/Session/Store/Null.pm t/000_load.t t/001_basic.t t/001a_basic.t t/002_basic_w_cookie.t t/002a_basic_w_cookie.t t/003_basic_w_file_store.t t/003a_basic_w_file_store.t t/004_basic_file_w_customs.t t/004a_basic_file_w_customs.t t/005_basic_w_cache_store.t t/005a_basic_w_cache_store.t t/006_basic_w_dbi_store.t t/010_middleware.t t/010a_middleware.t t/012_streaming.t t/013_cookiestore.t t/014_cookie_options.t t/015_cookie_options_mw.t t/016_cookiestore_w_customs.t t/author-pod-syntax.t t/lib/TestSession.pm t/lib/TestSessionHash.pm libplack-middleware-session-perl-0.33/META.json000066400000000000000000000055531357530506400214150ustar00rootroot00000000000000{ "abstract" : "Middleware for session management", "author" : [ "Tatsuhiko Miyagawa" ], "dynamic_config" : 0, "generated_by" : "Dist::Milla version v1.0.20, Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.143240", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Plack-Middleware-Session", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034" } }, "develop" : { "requires" : { "Dist::Milla" : "v1.0.20", "Test::Pod" : "1.41" } }, "runtime" : { "requires" : { "Cookie::Baker" : "0.10", "Digest::HMAC_SHA1" : "1.03", "Digest::SHA" : "0", "Plack" : "0.9910" } }, "test" : { "requires" : { "HTTP::Cookies" : "0", "HTTP::Request::Common" : "0", "LWP::UserAgent" : "0", "Test::Fatal" : "0.006", "Test::More" : "0.88", "Test::Requires" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/plack/Plack-Middleware-Session/issues" }, "homepage" : "https://github.com/plack/Plack-Middleware-Session", "repository" : { "type" : "git", "url" : "https://github.com/plack/Plack-Middleware-Session.git", "web" : "https://github.com/plack/Plack-Middleware-Session" } }, "version" : "0.33", "x_contributors" : [ "Alex Vandiver ", "Florian Schlichting ", "franck cuny ", "Graham Knop ", "Graham Knop ", "Ivan Baidakou ", "James E Keenan ", "John Lifsey ", "Lee Aylward ", "lestrrat ", "Mark Stosberg ", "Masahiro Chiba ", "Masahiro Nagano ", "Mohammad S Anwar ", "Olaf Alders ", "s-aska ", "Stevan Little ", "Tatsuhiko Miyagawa ", "Tatsuhiko Miyagawa ", "Tokuhiro Matsuno ", "vividsnow " ], "x_generated_by_perl" : "v5.20.1", "x_serialization_backend" : "Cpanel::JSON::XS version 4.09", "x_static_install" : 1 } libplack-middleware-session-perl-0.33/META.yml000066400000000000000000000036721357530506400212450ustar00rootroot00000000000000--- abstract: 'Middleware for session management' author: - 'Tatsuhiko Miyagawa' build_requires: HTTP::Cookies: '0' HTTP::Request::Common: '0' LWP::UserAgent: '0' Test::Fatal: '0.006' Test::More: '0.88' Test::Requires: '0' configure_requires: Module::Build::Tiny: '0.034' dynamic_config: 0 generated_by: 'Dist::Milla version v1.0.20, Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.143240' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Plack-Middleware-Session no_index: directory: - eg - examples - inc - share - t - xt requires: Cookie::Baker: '0.10' Digest::HMAC_SHA1: '1.03' Digest::SHA: '0' Plack: '0.9910' resources: bugtracker: https://github.com/plack/Plack-Middleware-Session/issues homepage: https://github.com/plack/Plack-Middleware-Session repository: https://github.com/plack/Plack-Middleware-Session.git version: '0.33' x_contributors: - 'Alex Vandiver ' - 'Florian Schlichting ' - 'franck cuny ' - 'Graham Knop ' - 'Graham Knop ' - 'Ivan Baidakou ' - 'James E Keenan ' - 'John Lifsey ' - 'Lee Aylward ' - 'lestrrat ' - 'Mark Stosberg ' - 'Masahiro Chiba ' - 'Masahiro Nagano ' - 'Mohammad S Anwar ' - 'Olaf Alders ' - 's-aska ' - 'Stevan Little ' - 'Tatsuhiko Miyagawa ' - 'Tatsuhiko Miyagawa ' - 'Tokuhiro Matsuno ' - 'vividsnow ' x_generated_by_perl: v5.20.1 x_serialization_backend: 'YAML::Tiny version 1.73' x_static_install: 1 libplack-middleware-session-perl-0.33/README000066400000000000000000000122531357530506400206470ustar00rootroot00000000000000NAME Plack::Middleware::Session - Middleware for session management SYNOPSIS use Plack::Builder; my $app = sub { my $env = shift; my $session = $env->{'psgix.session'}; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello, you've been here for ", $session->{counter}++, "th time!" ], ]; }; builder { enable 'Session'; $app; }; # Or, use the File store backend (great if you use multiprocess server) # For more options, see perldoc Plack::Session::Store::File builder { enable 'Session', store => 'File'; $app; }; DESCRIPTION This is a Plack Middleware component for session management. By default it will use cookies to keep session state and store data in memory. This distribution also comes with other state and store solutions. See perldoc for these backends how to use them. It should be noted that we store the current session as a hash reference in the psgix.session key inside the $env where you can access it as needed. NOTE: As of version 0.04 the session is stored in psgix.session instead of plack.session. State Plack::Session::State This will maintain session state by passing the session through the request params. It does not do this automatically though, you are responsible for passing the session param. Plack::Session::State::Cookie This will maintain session state using browser cookies. Store Plack::Session::Store This is your basic in-memory session data store. It is volatile storage and not recommended for multiprocessing environments. However it is very useful for development and testing. Plack::Session::Store::File This will persist session data in a file. By default it uses Storable but it can be configured to have a custom serializer and deserializer. Plack::Session::Store::Cache This will persist session data using the Cache interface. Plack::Session::Store::Null Sometimes you don't care about storing session data, in that case you can use this noop module. OPTIONS The following are options that can be passed to this module. state This is expected to be an instance of Plack::Session::State or an object that implements the same interface. If no option is provided the default Plack::Session::State::Cookie will be used. store This is expected to be an instance of Plack::Session::Store or an object that implements the same interface. If no option is provided the default Plack::Session::Store will be used. It should be noted that this default is an in-memory volatile store is only suitable for development (or single process servers). For a more robust solution see Plack::Session::Store::File or Plack::Session::Store::Cache. PLACK REQUEST OPTIONS In addition to providing a psgix.session key in $env for persistent session information, this module also provides a psgix.session.options key which can be used to control the behavior of the module per-request. The following sub-keys exist: change_id If set to a true value, forces the session identifier to change (rotate). This should always be done after logging in, to prevent session fixation attacks from subdomains; see http://en.wikipedia.org/wiki/Session_fixation#Attacks_using_cross-sub domain_cooking expire If set to a true value, expunges the session from the store, and clears the state in the client. no_store If set to a true value, no changes made to the session in this request will be saved to the store. Either "expire" and "change_id" take precedence over this, as both need to update the session store. late_store If set to a true value, the session will be saved at the end of the request, after all data has been sent to the client -- this may be required if streaming responses attempt to alter the session after the header has already been sent to the client. Note, however, that it introduces a possible race condition, where the server attempts to store the updated session before the client makes the next request. For redirects, or other responses on which the client needs do minimal processing before making a second request, this race is quite possible to win -- causing the second request to obtain stale session data. id This key contains the session identifier of the session. It should be considered read-only; to generate a new identifier, use "change_id". BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. AUTHOR Tatsuhiko Miyagawa Stevan Little COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. http://www.iinteractive.com This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. libplack-middleware-session-perl-0.33/cpanfile000066400000000000000000000006641357530506400214760ustar00rootroot00000000000000requires 'Plack' => '0.9910'; requires 'Cookie::Baker' => '0.10'; # for session ID gen requires 'Digest::SHA' => '0'; requires 'Digest::HMAC_SHA1' => '1.03'; # things the tests need on test => sub { requires 'Test::More' => '0.88'; requires 'Test::Requires' => '0'; requires 'Test::Fatal', '0.006'; requires 'LWP::UserAgent'; requires 'HTTP::Cookies'; requires 'HTTP::Request::Common'; }; libplack-middleware-session-perl-0.33/dist.ini000066400000000000000000000000521357530506400214250ustar00rootroot00000000000000name = Plack-Middleware-Session [@Milla] libplack-middleware-session-perl-0.33/examples/000077500000000000000000000000001357530506400216025ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/examples/counter-raw.psgi000077500000000000000000000037251357530506400247460ustar00rootroot00000000000000#!/usr/bin/perl # Simple counter web application # NOTE: This example uses Plack::Request to illustrate how # Plack::Middleware::Session interface ($env->{'psgix.session'}) could # be wrapped and integrated as part of the request API. See Tatsumaki # (integrated via subclassing Plack::Request) and Dancer::Session::PSGI # how to adapt Plack::Middleware::Session to web frameworks' APIs. # You're not recommended to write a new web application using this style. use strict; use Plack::Session; use Plack::Session::State; use Plack::Session::State::Cookie; use Plack::Session::Store; use Plack::Middleware::Session; my $app = Plack::Middleware::Session->wrap( sub { my $env = shift; my $r = Plack::Request->new( $env ); return [ 404, [], [] ] if $r->path_info =~ /favicon.ico/; my $session = $r->session; my $id = $session->id; my $counter = $session->get('counter') || 0; $session->set( 'counter' => $counter + 1 ); my $resp; if ( $r->param( 'logout' ) ) { $session->expire; $resp = $r->new_response; $resp->redirect( $r->path_info ); } else { $resp = $r->new_response( 200, [ 'Content-Type' => 'text/html' ], [ qq{ Plack::Middleware::Session Example

Session Id: ${id}

Counter: ${counter}


Logout } ] ); } $resp->finalize; }, state => Plack::Session::State::Cookie->new, store => Plack::Session::Store->new, ); libplack-middleware-session-perl-0.33/lib/000077500000000000000000000000001357530506400205325ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/000077500000000000000000000000001357530506400215645ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Middleware/000077500000000000000000000000001357530506400236415ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Middleware/Session.pm000066400000000000000000000176101357530506400256270ustar00rootroot00000000000000package Plack::Middleware::Session; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Plack::Util; use Scalar::Util; use Plack::Session::Cleanup; use parent 'Plack::Middleware'; use Plack::Util::Accessor qw( state store ); sub prepare_app { my $self = shift; $self->state( 'Cookie' ) unless $self->state; $self->state( $self->inflate_backend('Plack::Session::State', $self->state) ); $self->store( $self->inflate_backend('Plack::Session::Store', $self->store) ); } sub inflate_backend { my($self, $prefix, $backend) = @_; return $backend if defined $backend && Scalar::Util::blessed $backend; my @class; push @class, $backend if defined $backend; # undef means the root class push @class, $prefix; Plack::Util::load_class(@class)->new(); } sub call { my $self = shift; my $env = shift; my($id, $session) = $self->get_session($env); if ($id && $session) { $env->{'psgix.session'} = $session; } else { $id = $self->generate_id($env); $env->{'psgix.session'} = {}; } $env->{'psgix.session.options'} = { id => $id }; my $res = $self->app->($env); $self->response_cb($res, sub { $self->finalize($env, $_[0]) }); } sub get_session { my($self, $env) = @_; my $id = $self->state->extract($env) or return; my $session = $self->store->fetch($id) or return; return ($id, $session); } sub generate_id { my($self, $env) = @_; $self->state->generate($env); } sub commit { my($self, $env) = @_; my $session = $env->{'psgix.session'}; my $options = $env->{'psgix.session.options'}; my $end = sub { return if $options->{no_store}; $self->store->store($options->{id}, $session); }; if (not $options->{late_store}) { $end->(); } elsif ($env->{'psgix.cleanup'}) { push @{$env->{'psgix.cleanup.handlers'}}, $end; } else { $env->{'psgix.session.cleanup'} = Plack::Session::Cleanup->new($end); } } sub finalize { my($self, $env, $res) = @_; my $session = $env->{'psgix.session'}; my $options = $env->{'psgix.session.options'}; if ($options->{expire}) { $self->expire_session($options->{id}, $res, $env); } else { $self->change_id($env) if $options->{change_id}; $self->commit($env); $self->save_state($options->{id}, $res, $env); } } sub change_id { my($self, $env) = @_; my $options = $env->{'psgix.session.options'}; $self->store->remove($options->{id}); $options->{id} = $self->generate_id($env); } sub expire_session { my($self, $id, $res, $env) = @_; $self->store->remove($id); $self->state->expire_session_id($id, $res, $env->{'psgix.session.options'}); } sub save_state { my($self, $id, $res, $env) = @_; $self->state->finalize($id, $res, $env->{'psgix.session.options'}); } 1; __END__ =pod =head1 NAME Plack::Middleware::Session - Middleware for session management =head1 SYNOPSIS use Plack::Builder; my $app = sub { my $env = shift; my $session = $env->{'psgix.session'}; return [ 200, [ 'Content-Type' => 'text/plain' ], [ "Hello, you've been here for ", $session->{counter}++, "th time!" ], ]; }; builder { enable 'Session'; $app; }; # Or, use the File store backend (great if you use multiprocess server) # For more options, see perldoc Plack::Session::Store::File builder { enable 'Session', store => 'File'; $app; }; =head1 DESCRIPTION This is a Plack Middleware component for session management. By default it will use cookies to keep session state and store data in memory. This distribution also comes with other state and store solutions. See perldoc for these backends how to use them. It should be noted that we store the current session as a hash reference in the C key inside the C<$env> where you can access it as needed. B As of version 0.04 the session is stored in C instead of C. =head2 State =over 4 =item L This will maintain session state by passing the session through the request params. It does not do this automatically though, you are responsible for passing the session param. =item L This will maintain session state using browser cookies. =back =head2 Store =over 4 =item L This is your basic in-memory session data store. It is volatile storage and not recommended for multiprocessing environments. However it is very useful for development and testing. =item L This will persist session data in a file. By default it uses L but it can be configured to have a custom serializer and deserializer. =item L This will persist session data using the L interface. =item L Sometimes you don't care about storing session data, in that case you can use this noop module. =back =head1 OPTIONS The following are options that can be passed to this module. =over 4 =item I This is expected to be an instance of L or an object that implements the same interface. If no option is provided the default L will be used. =item I This is expected to be an instance of L or an object that implements the same interface. If no option is provided the default L will be used. It should be noted that this default is an in-memory volatile store is only suitable for development (or single process servers). For a more robust solution see L or L. =back =head1 PLACK REQUEST OPTIONS In addition to providing a C key in C<$env> for persistent session information, this module also provides a C key which can be used to control the behavior of the module per-request. The following sub-keys exist: =over =item I If set to a true value, forces the session identifier to change (rotate). This should always be done after logging in, to prevent session fixation attacks from subdomains; see L =item I If set to a true value, expunges the session from the store, and clears the state in the client. =item I If set to a true value, no changes made to the session in this request will be saved to the store. Either L and L take precedence over this, as both need to update the session store. =item I If set to a true value, the session will be saved at the I of the request, after all data has been sent to the client -- this may be required if streaming responses attempt to alter the session after the header has already been sent to the client. Note, however, that it introduces a possible race condition, where the server attempts to store the updated session before the client makes the next request. For redirects, or other responses on which the client needs do minimal processing before making a second request, this race is quite possible to win -- causing the second request to obtain stale session data. =item I This key contains the session identifier of the session. It should be considered read-only; to generate a new identifier, use L. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Tatsuhiko Miyagawa Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Middleware/Session/000077500000000000000000000000001357530506400252645ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Middleware/Session/Cookie.pm000066400000000000000000000074571357530506400270500ustar00rootroot00000000000000package Plack::Middleware::Session::Cookie; use strict; use parent qw(Plack::Middleware::Session); use Plack::Util::Accessor qw(secret session_key domain expires path secure httponly serializer deserializer); use Digest::HMAC_SHA1; use MIME::Base64 (); use Storable (); use Time::HiRes; use Plack::Util; use Plack::Session::State::Cookie; sub prepare_app { my $self = shift; die "Plack::Session::Middleware::Cookie requires setting 'secret' option." unless $self->secret; $self->session_key("plack_session") unless $self->session_key; $self->serializer(sub {MIME::Base64::encode(Storable::nfreeze($_[0]), '' )}) unless $self->serializer; $self->deserializer(sub {Storable::thaw(MIME::Base64::decode($_[0]))}) unless $self->deserializer; $self->state( Plack::Session::State::Cookie->new ); for my $attr (qw(session_key path domain expires secure httponly)) { $self->state->$attr($self->$attr); } } sub _compare { my($s1, $s2) = @_; return if length $s1 != length $s2; my $r = 0; for my $i (0..length($s1) - 1) { $r |= ord(substr $s1, $i) ^ ord(substr $s2, $i); } return $r == 0; } sub get_session { my($self, $request) = @_; my $cookie = $self->state->get_session_id($request) or return; my($time, $b64, $sig) = split /:/, $cookie, 3; _compare($self->sig($b64), $sig) or return; # NOTE: do something with $time? my $session = $self->deserializer->($b64); return ($self->generate_id, $session); } sub generate_id { my $self = shift; return scalar Time::HiRes::gettimeofday; } sub commit { } sub change_id { my($self, $env) = @_; my $options = $env->{'psgix.session.options'}; $options->{id} = $self->generate_id($env); } sub expire_session { my($self, $id, $res, $env) = @_; $self->state->expire_session_id($id, $res, $env->{'psgix.session.options'}); } sub save_state { my($self, $id, $res, $env) = @_; my $cookie = $self->_serialize($id, $env->{'psgix.session'}); $self->state->finalize($cookie, $res, $env->{'psgix.session.options'}); } sub _serialize { my($self, $id, $session) = @_; my $b64 = $self->serializer->($session); join ":", $id, $b64, $self->sig($b64); } sub sig { my($self, $b64) = @_; return '.' unless $self->secret; Digest::HMAC_SHA1::hmac_sha1_hex($b64, $self->secret); } 1; __END__ =head1 NAME Plack::Middleware::Session::Cookie - Session middleware that saves session data in the cookie =head1 SYNOPSIS enable 'Session::Cookie', session_key => 'my_session', expires => 3600, # 1 hour secret => 'top-secret' ; =head1 DESCRIPTION This middleware component allows you to use the cookie as a sole cookie state and store, without any server side storage to do the session management. This middleware utilizes its own state and store automatically for you, so you can't override the objects. =head1 CONFIGURATIONS This middleware is a subclass of L and accepts most configuration of the parent class. In addition, following options are accepted. =over 4 =item secret Server side secret to sign the session data using HMAC SHA1. Defaults to nothing (i.e. do not sign) but B to set your own secret string. Unless you use your own serializer/deserializer, running this middleware without setting a secret is vulnerable to arbitrary code execution. B. =item session_key, domain, expires, path, secure, httponly Accessors for the cookie attributes. See L for these options. =back =head1 AUTHOR Tatsuhiko Miyagawa =head1 SEE ALSO L L =cut libplack-middleware-session-perl-0.33/lib/Plack/Session.pm000066400000000000000000000051301357530506400235440ustar00rootroot00000000000000package Plack::Session; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Plack::Util::Accessor qw( session options ); sub new { my ($class, $env) = @_; bless { session => $env->{'psgix.session'}, options => $env->{'psgix.session.options'}, }, $class; } sub id { my $self = shift; $self->options->{id}; } ## Data Managment sub dump { my $self = shift; $self->session; } sub get { my ($self, $key) = @_; $self->session->{$key}; } sub set { my ($self, $key, $value) = @_; delete $self->options->{no_store}; $self->session->{$key} = $value; } sub remove { my ($self, $key) = @_; delete $self->options->{no_store}; delete $self->session->{$key}; } sub keys { my $self = shift; keys %{$self->session}; } ## Lifecycle Management sub expire { my $self = shift; for my $key ($self->keys) { delete $self->session->{$key}; } $self->options->{expire} = 1; } 1; __END__ =pod =head1 NAME Plack::Session - Middleware for session management =head1 SYNOPSIS # Use with Middleware::Session enable "Session"; # later in your app use Plack::Session; my $app = sub { my $env = shift; my $session = Plack::Session->new($env); $session->id; $session->get($key); $session->set($key, $value); $session->remove($key); $session->keys; $session->expire; }; =head1 DESCRIPTION This is the core session object, you probably want to look at L, unless you are writing your own session middleware component. =head1 METHODS =over 4 =item B The constructor takes a PSGI request env hash reference. =item B This is the accessor for the session id. =back =head2 Session Data Management These methods allows you to read and write the session data like Perl's normal hash. =over 4 =item B =item B =item B =item B =item B, B =back =head2 Session Lifecycle Management =over 4 =item B This method can be called to expire the current session id. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/000077500000000000000000000000001357530506400232075ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Session/Cleanup.pm000066400000000000000000000016171357530506400251410ustar00rootroot00000000000000package Plack::Session::Cleanup; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; sub new { my $class = shift; my $subref = shift; my $self = bless $subref, $class; return $self; } sub DESTROY { my $self = shift; $self->(); } 1; __END__ =pod =head1 NAME Plack::Session::Cleanup - Run code when the environment is destroyed =head1 SYNOPSIS $env->{'run_at_cleanup'} = Plack::Session::Cleanup->new( sub { # ... } ); =head1 DESCRIPTION This provides a way for L to run code when the environment is cleaned up. =head1 METHODS =over 4 =item B Executes the given code reference when the object is C'd. Care should be taken that the given code reference does not close over C<$env>, creating a cycle and preventing the C<$env> from being destroyed. =back =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/State.pm000066400000000000000000000115261357530506400246320ustar00rootroot00000000000000package Plack::Session::State; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Digest::SHA (); use Plack::Request; use Plack::Util::Accessor qw[ session_key sid_generator sid_validator ]; sub new { my ($class, %params) = @_; $params{'session_key'} ||= 'plack_session'; $params{'sid_generator'} ||= sub { Digest::SHA::sha1_hex(rand() . $$ . {} . time) }; $params{'sid_validator'} ||= qr/\A[0-9a-f]{40}\Z/; bless { %params } => $class; } sub expire_session_id { my ($self, $id, $res) = @_; } sub validate_session_id { my ($self, $id) = @_; $id =~ $self->sid_validator; } sub get_session_id { my ($self, $env) = @_; return Plack::Request->new($env)->param( $self->session_key ); } sub extract { my ($self, $env) = @_; my $id = $self->get_session_id( $env ); return unless defined $id; return $id if $self->validate_session_id( $id ); return; } sub generate { my $self = shift; $self->sid_generator->( @_ ); } sub finalize { my ($self, $id, $res, $options) = @_; (); } 1; __END__ =pod =head1 NAME Plack::Session::State - Basic parameter-based session state =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; use Plack::Session::State; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session', state => Plack::Session::State->new; $app; }; =head1 DESCRIPTION This will maintain session state by passing the session through the request params. It does not do this automatically though, you are responsible for passing the session param. This should be considered the state "base" class (although subclassing is not a requirement) and defines the spec for all B modules. You will only need to override a couple methods if you do subclass. See L for an example of this. B: parameter based session ID management makes session fixation really easy, and that makes your website vulnerable. You should really avoid using this state in the production environment except when you have to deal with legacy HTTP clients that do not support cookies. In the future this parameter based state handling will be removed from this base class and will be moved to its own State class. =head1 METHODS =over 4 =item B The C<%params> can include I, I and I however in both cases a default will be provided for you. =item B This is the name of the session key, it defaults to 'plack_session'. =item B This is a CODE ref used to generate unique session ids, by default it will generate a SHA1 using fairly sufficient entropy. If you are concerned or interested, just read the source. =item B This is a regex used to validate requested session id. =back =head2 Session ID Managment =over 4 =item B This is the method used to extract the session id from a C<$env>. Subclasses will often only need to override this method and the C method. =item B This will use the C regex and confirm that the C<$session_id> is valid. =item B This will attempt to extract the session from a C<$env> by looking for the C in the request params. It will then check to see if the session is valid and that it has not expired. It will return the session id if everything is good or undef otherwise. =item B This will generate a new session id using the C callback. The C<$request> argument is not used by this method but is there for use by subclasses. The C<$request> is expected to be a L instance or an object with an equivalent interface. =item B Given a C<$session_id> and a C<$response> this will perform any finalization necessary to preserve state. This method is called by the L C method. The C<$response> is expected to be a L instance or an object with an equivalent interface. =back =head2 Session Expiration Handling =over 4 =item B This will mark the session for C<$id> as expired. This method is called by the L C method. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/State/000077500000000000000000000000001357530506400242675ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Session/State/Cookie.pm000066400000000000000000000066241357530506400260460ustar00rootroot00000000000000package Plack::Session::State::Cookie; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use parent 'Plack::Session::State'; use Cookie::Baker; use Plack::Util; use Plack::Util::Accessor qw[ path domain expires secure httponly samesite ]; sub get_session_id { my ($self, $env) = @_; crush_cookie($env->{HTTP_COOKIE})->{$self->session_key}; } sub merge_options { my($self, %options) = @_; delete $options{id}; $options{path} = $self->path || '/' if !exists $options{path}; $options{domain} = $self->domain if !exists $options{domain} && defined $self->domain; $options{secure} = $self->secure if !exists $options{secure} && defined $self->secure; $options{httponly} = $self->httponly if !exists $options{httponly} && defined $self->httponly; $options{samesite} = $self->samesite if !exists $options{samesite} && defined $self->samesite; if (!exists $options{expires} && defined $self->expires) { $options{expires} = time + $self->expires; } return %options; } sub expire_session_id { my ($self, $id, $res, $options) = @_; my %opts = $self->merge_options(%$options, expires => time); $self->_set_cookie($id, $res, %opts); } sub finalize { my ($self, $id, $res, $options) = @_; my %opts = $self->merge_options(%$options); $self->_set_cookie($id, $res, %opts); } sub _set_cookie { my($self, $id, $res, %options) = @_; my $cookie = bake_cookie( $self->session_key, { value => $id, %options, } ); Plack::Util::header_push($res->[1], 'Set-Cookie', $cookie); } 1; __END__ =pod =head1 NAME Plack::Session::State::Cookie - Basic cookie-based session state =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session'; # Cookie is the default state $app; }; =head1 DESCRIPTION This is a subclass of L and implements its full interface. This is the default state used in L. =head1 METHODS =over 4 =item B The C<%params> can include I, I, I, I, and I options, as well as all the options accepted by L. =item B Path of the cookie, this defaults to "/"; =item B Domain of the cookie, if nothing is supplied then it will not be included in the cookie. =item B Expiration time of the cookie in seconds, if nothing is supplied then it will not be included in the cookie, which means the session expires per browser session. =item B Secure flag for the cookie, if nothing is supplied then it will not be included in the cookie. =item B HttpOnly flag for the cookie, if nothing is supplied then it will not be included in the cookie. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/Store.pm000066400000000000000000000045321357530506400246450ustar00rootroot00000000000000package Plack::Session::Store; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Plack::Util::Accessor qw[ _stash ]; sub new { my ($class, %params) = @_; $params{'_stash'} ||= +{}; bless { %params } => $class; } sub fetch { my ($self, $session_id) = @_; $self->_stash->{ $session_id }; } sub store { my ($self, $session_id, $session) = @_; $self->_stash->{ $session_id } = $session; } sub remove { my ($self, $session_id) = @_; delete $self->_stash->{ $session_id } } 1; __END__ =pod =head1 NAME Plack::Session::Store - Basic in-memory session store =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; use Plack::Session::Store; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session'; # this is the default store $app; }; =head1 DESCRIPTION This is a very basic in-memory session data store. It is volatile storage and not recommended for multiprocessing environments. However it is very useful for development and testing. This should be considered the store "base" class (although subclassing is not a requirement) and defines the spec for all B modules. You will only need to override a couple methods if you do subclass. See the other B for examples of this. =head1 METHODS =over 4 =item B No parameters are expected to this constructor. =back =head2 Session Data Management These methods fetch data from the session storage. It's designed to store or delete multiple keys at a time. =over 4 =item B =item B =back =head2 Storage Management =over 4 =item B This method is called by the L C method and is used to remove any session data. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/Store/000077500000000000000000000000001357530506400243035ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/lib/Plack/Session/Store/Cache.pm000066400000000000000000000040061357530506400256440ustar00rootroot00000000000000package Plack::Session::Store::Cache; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Scalar::Util qw[ blessed ]; use parent 'Plack::Session::Store'; use Plack::Util::Accessor qw[ cache ]; sub new { my ($class, %params) = @_; die('cache require get, set and remove method.') unless blessed $params{cache} && $params{cache}->can('get') && $params{cache}->can('set') && $params{cache}->can('remove'); bless { %params } => $class; } sub fetch { my ($self, $session_id ) = @_; $self->cache->get($session_id); } sub store { my ($self, $session_id, $session) = @_; $self->cache->set($session_id => $session); } sub remove { my ($self, $session_id) = @_; $self->cache->remove($session_id); } 1; __END__ =pod =head1 NAME Plack::Session::Store::Cache - Cache session store =head1 SYNOPSIS use Plack::Builder; use Plack::Session::Store::Cache; use CHI; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session', store => Plack::Session::Store::Cache->new( cache => CHI->new(driver => 'FastMmap') ); $app; }; =head1 DESCRIPTION This will persist session data using any module which implements the L interface. This offers a lot of flexibility due to the many excellent L, L and L drivers available. This is a subclass of L and implements its full interface. =head1 METHODS =over 4 =item B The constructor expects the I param to be an object instance which has the I, I, and I methods, it will throw an exception if that is not the case. =item B A simple accessor for the cache handle. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Masahiro Chiba =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/Store/DBI.pm000066400000000000000000000111761357530506400252450ustar00rootroot00000000000000package Plack::Session::Store::DBI; use strict; use warnings; # XXX Is there a notion of auto-expiry? our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use MIME::Base64 (); use Storable (); use parent 'Plack::Session::Store'; use Plack::Util::Accessor qw[ dbh get_dbh table_name serializer deserializer ]; sub new { my ($class, %params) = @_; if (! $params{dbh} && ! $params{get_dbh}) { die "DBI instance or a callback was not available in the argument list"; } $params{table_name} ||= 'sessions'; $params{serializer} ||= sub { MIME::Base64::encode_base64( Storable::nfreeze( $_[0] ) ) }; $params{deserializer} ||= sub { Storable::thaw( MIME::Base64::decode_base64( $_[0] ) ) }; my $self = bless { %params }, $class; return $self; } sub _dbh { my $self =shift; ( exists $self->{get_dbh} ) ? $self->{get_dbh}->() : $self->{dbh}; } sub fetch { my ($self, $session_id) = @_; my $table_name = $self->{table_name}; my $dbh = $self->_dbh; my $sth = $dbh->prepare_cached("SELECT session_data FROM $table_name WHERE id = ?"); $sth->execute( $session_id ); my ($data) = $sth->fetchrow_array(); $sth->finish; return $data ? $self->deserializer->( $data ) : (); } sub store { my ($self, $session_id, $session) = @_; my $table_name = $self->{table_name}; # XXX To be honest, I feel like there should be a transaction # call here.... but Catalyst didn't have it, so I'm not so sure my $sth = $self->_dbh->prepare_cached("SELECT 1 FROM $table_name WHERE id = ?"); $sth->execute($session_id); # need to fetch. on some DBD's execute()'s return status and # rows() is not reliable my ($exists) = $sth->fetchrow_array(); $sth->finish; if ($exists) { my $sth = $self->_dbh->prepare_cached("UPDATE $table_name SET session_data = ? WHERE id = ?"); $sth->execute( $self->serializer->($session), $session_id ); } else { my $sth = $self->_dbh->prepare_cached("INSERT INTO $table_name (id, session_data) VALUES (?, ?)"); $sth->execute( $session_id , $self->serializer->($session) ); } } sub remove { my ($self, $session_id) = @_; my $table_name = $self->{table_name}; my $sth = $self->_dbh->prepare_cached("DELETE FROM $table_name WHERE id = ?"); $sth->execute( $session_id ); $sth->finish; } 1; __END__ =head1 NAME Plack::Session::Store::DBI - DBI-based session store =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; use Plack::Session::Store::DBI; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session', store => Plack::Session::Store::DBI->new( dbh => DBI->connect( @connect_args ) ); $app; }; # set get_dbh callback for ondemand builder { enable 'Session', store => Plack::Session::Store::DBI->new( get_dbh => sub { DBI->connect( @connect_args ) } ); $app; }; # with custom serializer/deserializer builder { enable 'Session', store => Plack::Session::Store::DBI->new( dbh => DBI->connect( @connect_args ) # YAML takes its args in the opposite order serializer => sub { YAML::DumpFile( reverse @_ ) }, deserializer => sub { YAML::LoadFile( @_ ) }, ); $app; }; # use custom session table name builder { enable 'Session', store => Plack::Session::Store::DBI->new( dbh => DBI->connect( @connect_args ), table_name => 'my_session_table', ); $app; }; =head1 DESCRIPTION This implements a DBI based storage for session data. By default it will use L and L to serialize and deserialize the data, but this can be configured easily. This is a subclass of L and implements its full interface. =head1 SESSION TABLE SCHEMA Your session table must have at least the following schema structure: CREATE TABLE sessions ( id CHAR(72) PRIMARY KEY, session_data TEXT ); Note that MySQL TEXT fields only store 64KB, so if your session data will exceed that size you'll want to move to MEDIUMTEXT, MEDIUMBLOB, or larger. =head1 AUTHORS Many aspects of this module were partially based upon L Daisuke Maki =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Daisuke Maki C<< >> This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/Store/File.pm000066400000000000000000000067631357530506400255340ustar00rootroot00000000000000package Plack::Session::Store::File; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; use Storable (); use parent 'Plack::Session::Store'; use Plack::Util::Accessor qw[ dir serializer deserializer ]; sub new { my ($class, %params) = @_; $params{'dir'} ||= $ENV{TMPDIR} || '/tmp'; die "Storage directory (" . $params{'dir'} . ") is not writeable" unless -w $params{'dir'}; $params{'serializer'} ||= sub { Storable::lock_nstore( @_ ) }; $params{'deserializer'} ||= sub { Storable::lock_retrieve( @_ ) }; bless { %params } => $class; } sub fetch { my ($self, $session_id) = @_; my $file_path = $self->_get_session_file_path( $session_id ); return unless -f $file_path; $self->deserializer->( $file_path ); } sub store { my ($self, $session_id, $session) = @_; my $file_path = $self->_get_session_file_path( $session_id ); $self->serializer->( $session, $file_path ); } sub remove { my ($self, $session_id) = @_; unlink $self->_get_session_file_path( $session_id ); } sub _get_session_file_path { my ($self, $session_id) = @_; $self->dir . '/' . $session_id; } 1; __END__ =pod =head1 NAME Plack::Session::Store::File - Basic file-based session store =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; use Plack::Session::Store::File; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session', store => Plack::Session::Store::File->new( dir => '/path/to/sessions' ); $app; }; # with custom serializer/deserializer builder { enable 'Session', store => Plack::Session::Store::File->new( dir => '/path/to/sessions', # YAML takes it's args the opposite order serializer => sub { YAML::DumpFile( reverse @_ ) }, deserializer => sub { YAML::LoadFile( @_ ) }, ); $app; }; =head1 DESCRIPTION This implements a basic file based storage for session data. By default it will use L to serialize and deserialize the data, but this can be configured easily. This is a subclass of L and implements its full interface. =head1 METHODS =over 4 =item B The C<%params> can include I, I and I options. It will check to be sure that the I is writeable for you. =item B This is the directory to store the session data files in, if nothing is provided then "/tmp" is used. =item B This is a CODE reference that implements the serialization logic. The CODE ref gets two arguments, the C<$value>, which is a HASH reference to be serialized, and the C<$file_path> to save it to. It is not expected to return anything. =item B This is a CODE reference that implements the deserialization logic. The CODE ref gets one argument, the C<$file_path> to load the data from. It is expected to return a HASH reference. =back =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/lib/Plack/Session/Store/Null.pm000066400000000000000000000024551357530506400255610ustar00rootroot00000000000000package Plack::Session::Store::Null; use strict; use warnings; our $VERSION = '0.33'; our $AUTHORITY = 'cpan:STEVAN'; sub new { bless {} => shift } sub fetch {} sub store {} sub remove {} 1; __END__ =pod =head1 NAME Plack::Session::Store::Null - Null store =head1 SYNOPSIS use Plack::Builder; use Plack::Middleware::Session; use Plack::Session::Store::Null; my $app = sub { return [ 200, [ 'Content-Type' => 'text/plain' ], [ 'Hello Foo' ] ]; }; builder { enable 'Session', store => Plack::Session::Store::Null->new; $app; }; =head1 DESCRIPTION Sometimes you don't want to store anything in your sessions, but L requires a C instance, so you can use this one and all methods will return null. This is a subclass of L and implements its full interface. =head1 BUGS All complex software has bugs lurking in it, and this module is no exception. If you find a bug please either email me, or add the bug to cpan-RT. =head1 AUTHOR Stevan Little Estevan.little@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2009, 2010 Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut libplack-middleware-session-perl-0.33/t/000077500000000000000000000000001357530506400202275ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/t/000_load.t000066400000000000000000000004751357530506400217200ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use_ok( $_ ) || BAIL_OUT foreach qw[ Plack::Middleware::Session Plack::Session Plack::Session::Store Plack::Session::Store::Cache Plack::Session::Store::File Plack::Session::State Plack::Session::State::Cookie ]; done_testing; libplack-middleware-session-perl-0.33/t/001_basic.t000077500000000000000000000014051357530506400220600ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State; use Plack::Session::Store; use lib "."; use t::lib::TestSession; t::lib::TestSession::run_all_tests( store => Plack::Session::Store->new, state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/001a_basic.t000066400000000000000000000013711357530506400222200ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session::State; use Plack::Session::Store; use lib "."; use t::lib::TestSessionHash; t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store->new, state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/002_basic_w_cookie.t000077500000000000000000000024151357530506400237420ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State::Cookie; use Plack::Session::Store; use Plack::Util; use lib "."; use t::lib::TestSession; t::lib::TestSession::run_all_tests( store => Plack::Session::Store->new, state => Plack::Session::State::Cookie->new, env_cb => sub { my $cookies = shift; open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', HTTP_COOKIE => join "; " => map { $_ . "=" . $cookies->{ $_ } } keys %$cookies, }; }, response_test => sub { my ($res_cb, $session_id, $check_expired) = @_; my $cookie; $res_cb->(sub { my $res = shift; $cookie = Plack::Util::header_get($res->[1], 'Set-Cookie'); }); like($cookie, qr/plack_session=$session_id/, '... cookie value is as suspected'); if ($check_expired) { like($cookie, qr/expires=/, '... cookie is expriring as suspected'); } } ); done_testing; libplack-middleware-session-perl-0.33/t/002a_basic_w_cookie.t000066400000000000000000000024011357530506400240730ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session::State::Cookie; use Plack::Session::Store; use Plack::Util; use lib "."; use t::lib::TestSessionHash; t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store->new, state => Plack::Session::State::Cookie->new, env_cb => sub { my $cookies = shift; open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', HTTP_COOKIE => join "; " => map { $_ . "=" . $cookies->{ $_ } } keys %$cookies, }; }, response_test => sub { my ($res_cb, $session_id, $check_expired) = @_; my $cookie; $res_cb->(sub { my $res = shift; $cookie = Plack::Util::header_get($res->[1], 'Set-Cookie'); }); like($cookie, qr/plack_session=$session_id/, '... cookie value is as suspected'); if ($check_expired) { like($cookie, qr/expires=/, '... cookie is expriring as suspected'); } } ); done_testing; libplack-middleware-session-perl-0.33/t/003_basic_w_file_store.t000077500000000000000000000015661357530506400246330ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Temp qw(tempdir); use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State::Cookie; use Plack::Session::Store::File; use lib "."; use t::lib::TestSession; my $tmp = tempdir(CLEANUP => 1); t::lib::TestSession::run_all_tests( store => Plack::Session::Store::File->new( dir => $tmp ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/003a_basic_w_file_store.t000077500000000000000000000015521357530506400247670ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Temp qw(tempdir); use Test::More; use Plack::Request; use Plack::Session::State::Cookie; use Plack::Session::Store::File; use lib "."; use t::lib::TestSessionHash; my $tmp = tempdir(CLEANUP => 1); t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store::File->new( dir => $tmp ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/004_basic_file_w_customs.t000077500000000000000000000021131357530506400251620ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Temp qw(tempdir); use Test::Requires 'YAML'; use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State::Cookie; use Plack::Session::Store::File; use lib "."; use t::lib::TestSession; my $tmp = tempdir(CLEANUP => 1); t::lib::TestSession::run_all_tests( store => Plack::Session::Store::File->new( dir => $tmp, serializer => sub { YAML::DumpFile( reverse @_ ) }, # YAML takes it's args the opposite of Storable deserializer => sub { YAML::LoadFile( @_ ) }, ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/004a_basic_file_w_customs.t000077500000000000000000000020771357530506400253340ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Temp qw(tempdir); use Test::Requires 'YAML'; use Test::More; use Plack::Request; use Plack::Session::State::Cookie; use Plack::Session::Store::File; use lib "."; use t::lib::TestSessionHash; my $tmp = tempdir(CLEANUP => 1); t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store::File->new( dir => $tmp, serializer => sub { YAML::DumpFile( reverse @_ ) }, # YAML takes it's args the opposite of Storable deserializer => sub { YAML::LoadFile( @_ ) }, ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/005_basic_w_cache_store.t000077500000000000000000000021711357530506400247520ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State; use Plack::Session::Store::Cache; use lib "."; use t::lib::TestSession; { package TestCache; sub new { bless {} => shift; } sub set { my ($self, $key, $val ) = @_; $self->{$key} = $val; } sub get { my ($self, $key ) = @_; $self->{$key}; } sub remove { my ($self, $key ) = @_; delete $self->{$key}; } } t::lib::TestSession::run_all_tests( store => Plack::Session::Store::Cache->new( cache => TestCache->new ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/005a_basic_w_cache_store.t000066400000000000000000000021551357530506400251120ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use Test::More; use Plack::Request; use Plack::Session::State; use Plack::Session::Store::Cache; use lib "."; use t::lib::TestSessionHash; { package TestCache; sub new { bless {} => shift; } sub set { my ($self, $key, $val ) = @_; $self->{$key} = $val; } sub get { my ($self, $key ) = @_; $self->{$key}; } sub remove { my ($self, $key ) = @_; delete $self->{$key}; } } t::lib::TestSessionHash::run_all_tests( store => Plack::Session::Store::Cache->new( cache => TestCache->new ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); done_testing; libplack-middleware-session-perl-0.33/t/006_basic_w_dbi_store.t000066400000000000000000000034461357530506400244510ustar00rootroot00000000000000#!/usr/bin/perl use strict; use warnings; use File::Spec; use File::Temp qw(tempdir); use Test::Requires qw(DBI DBD::SQLite MIME::Base64 Storable); use Test::More; use Plack::Request; use Plack::Session; use Plack::Session::State::Cookie; use Plack::Session::Store::DBI; use lib "."; use t::lib::TestSession; my $tmp = tempdir(CLEANUP => 1); my $file = File::Spec->catfile($tmp, "006_basic_w_dbi_store.db"); my $dbh = DBI->connect( "dbi:SQLite:$file", undef, undef, {RaiseError => 1, AutoCommit => 1} ); $dbh->do(< Plack::Session::Store::DBI->new( dbh => $dbh ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); t::lib::TestSession::run_all_tests( store => Plack::Session::Store::DBI->new( get_dbh => sub { $dbh } ), state => Plack::Session::State->new, env_cb => sub { open my $in, '<', \do { my $d }; my $env = { 'psgi.version' => [ 1, 0 ], 'psgi.input' => $in, 'psgi.errors' => *STDERR, 'psgi.url_scheme' => 'http', SERVER_PORT => 80, REQUEST_METHOD => 'GET', QUERY_STRING => join "&" => map { $_ . "=" . $_[0]->{ $_ } } keys %{$_[0] || +{}}, }; }, ); $dbh->disconnect; done_testing; libplack-middleware-session-perl-0.33/t/010_middleware.t000066400000000000000000000014411357530506400231110ustar00rootroot00000000000000use Plack::Test; use Plack::Middleware::Session; use Test::More; use HTTP::Request::Common; use HTTP::Cookies; my $app = sub { my $env = shift; my $counter = $env->{'psgix.session'}->{counter} || 0; my $body = "Counter=$counter"; $env->{'psgix.session'}->{counter} = $counter + 1; return [ 200, [ 'Content-Type', 'text/html' ], [ $body ] ]; }; $app = Plack::Middleware::Session->wrap($app); test_psgi $app, sub { my $cb = shift; my $jar = HTTP::Cookies->new; my $res = $cb->(GET "http://localhost/"); is $res->content_type, 'text/html'; is $res->content, "Counter=0"; $jar->extract_cookies($res); my $req = GET "http://localhost/"; $jar->add_cookie_header($req); $res = $cb->($req); is $res->content, "Counter=1"; }; done_testing; libplack-middleware-session-perl-0.33/t/010a_middleware.t000066400000000000000000000013521357530506400232530ustar00rootroot00000000000000use Plack::Test; use Plack::Middleware::Session; use Test::More; use HTTP::Request::Common; use HTTP::Cookies; my $app = sub { my $env = shift; my $counter = $env->{'psgix.session'}->{'counter'} || 0; my $body = "Counter=$counter"; $counter++; $env->{'psgix.session'}->{counter} = $counter; return [ 200, [], [ $body ] ]; }; $app = Plack::Middleware::Session->wrap($app); test_psgi $app, sub { my $cb = shift; my $jar = HTTP::Cookies->new; my $res = $cb->(GET "http://localhost/"); is $res->content, "Counter=0"; $jar->extract_cookies($res); my $req = GET "http://localhost/"; $jar->add_cookie_header($req); $res = $cb->($req); is $res->content, "Counter=1"; }; done_testing; libplack-middleware-session-perl-0.33/t/012_streaming.t000066400000000000000000000034071357530506400227730ustar00rootroot00000000000000use strict; use File::Temp qw(tempdir); use Test::More; use Plack::Test; use Plack::Middleware::Session; use Plack::Session::Store::File; use HTTP::Request::Common; use LWP::UserAgent; use HTTP::Cookies; $Plack::Test::Impl = 'Server'; my $base_app = sub { my $env = shift; return sub { my $respond = shift; # Enable late storage on the second request $env->{'psgix.session.options'}->{late_store} = 1 if $env->{'psgix.session'}->{early}; $env->{'psgix.session'}->{early}++; my $w = $respond->([ 200, [ 'Content-Type' => 'text/html' ] ]); $w->write("Hello"); $env->{'psgix.session'}->{late}++; $w->close; }; }; my $tmp = tempdir(CLEANUP => 1); my $store = Plack::Session::Store::File->new( dir => $tmp ); my $app = Plack::Middleware::Session->wrap( $base_app, store => $store); my $ua = LWP::UserAgent->new; $ua->cookie_jar( HTTP::Cookies->new ); test_psgi ua => $ua, app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "Hello"; like $res->header('Set-Cookie'), qr/plack_session/; my ($session_id) = $res->header('Set-Cookie') =~ /plack_session=([a-f0-9]+)/; ok $session_id, "Found session"; my $session = $store->fetch($session_id); ok $session, "Fetched session $session_id"; is $session->{early}, 1, "Early data is set"; is $session->{late}, undef, "Late data was lost, as late_store was not set"; $res = $cb->(GET "/"); is $res->content, "Hello"; like $res->header('Set-Cookie'), qr/plack_session/; $session = $store->fetch($session_id); ok $session, "Fetched session $session_id"; is $session->{early}, 2, "Early data is set"; is $session->{late}, 1, "Late data was stored"; }; done_testing; libplack-middleware-session-perl-0.33/t/013_cookiestore.t000066400000000000000000000022111357530506400233210ustar00rootroot00000000000000use strict; use Test::More; use Test::Requires qw(Digest::HMAC_SHA1); use Plack::Test; use Plack::Middleware::Session::Cookie; use HTTP::Request::Common; use LWP::UserAgent; use HTTP::Cookies; $Plack::Test::Impl = 'Server'; my $app = sub { my $env = shift; my $session = $env->{'psgix.session'}; my $counter = $session->{counter} || 0; if ($session->{counter}++ >= 2) { $env->{'psgix.session.options'}->{expire} = 1; } return [ 200, [], [ "counter=$counter" ] ]; }; $app = Plack::Middleware::Session::Cookie->wrap($app, secret => "foobar", expires => 3600); my $ua = LWP::UserAgent->new; $ua->cookie_jar( HTTP::Cookies->new ); test_psgi ua => $ua, app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "counter=0"; like $res->header('Set-Cookie'), qr/expires=/; like $res->header('Set-Cookie'), qr/path=\//; $res = $cb->(GET "/"); is $res->content, "counter=1"; like $res->header('Set-Cookie'), qr/expires=/; $res = $cb->(GET "/"); is $res->content, "counter=2"; $res = $cb->(GET "/"); is $res->content, "counter=0"; }; done_testing; libplack-middleware-session-perl-0.33/t/014_cookie_options.t000066400000000000000000000013671357530506400240330ustar00rootroot00000000000000use strict; use Test::More; my $time = 1264843167; BEGIN { *CORE::GLOBAL::time = sub() { $time } } use Plack::Session::State::Cookie; my $st = Plack::Session::State::Cookie->new; $st->domain('.example.com'); $st->secure(1); $st->expires(3600); $st->path('/cgi-bin'); is_deeply +{ $st->merge_options(id => 123) }, { domain => '.example.com', secure => 1, expires => $time + 3600, path => '/cgi-bin' }; is_deeply +{ $st->merge_options(id => 123, path => '/', domain => '.perl.org') }, { domain => '.perl.org', secure => 1, expires => $time + 3600, path => '/' }; is_deeply +{ $st->merge_options(id => 123, expires => $time + 1, secure => 0) }, { domain => '.example.com', secure => 0, expires => $time + 1, path => '/cgi-bin' }; done_testing; libplack-middleware-session-perl-0.33/t/015_cookie_options_mw.t000066400000000000000000000017101357530506400245270ustar00rootroot00000000000000use strict; use Plack::Test; use Plack::Middleware::Session; use Test::More; use HTTP::Request::Common; use HTTP::Cookies; my $app = sub { my $env = shift; $env->{'psgix.session'}->{counter} = 1; my $path = $env->{PATH_INFO} =~ /with_path/ ? "/foo" : undef; $env->{'psgix.session.options'}{path} = $path; $env->{'psgix.session.options'}{domain} = '.example.com'; $env->{'psgix.session.options'}{httponly} = 1; $env->{'psgix.session.options'}{samesite} = 'Lax'; return [ 200, [], [ "Hi" ] ]; }; $app = Plack::Middleware::Session->wrap($app); test_psgi $app, sub { my $cb = shift; my $res = $cb->(GET "http://localhost/"); like $res->header('Set-Cookie'), qr/plack_session=\w+; domain=.example.com; SameSite=Lax; HttpOnly/; $res = $cb->(GET "http://localhost/with_path"); like $res->header('Set-Cookie'), qr/plack_session=\w+; domain=.example.com; path=\/foo; SameSite=Lax; HttpOnly/; }; done_testing; libplack-middleware-session-perl-0.33/t/016_cookiestore_w_customs.t000066400000000000000000000024501357530506400254340ustar00rootroot00000000000000use strict; use Test::More; use Test::Requires qw(Digest::HMAC_SHA1 YAML); use Plack::Test; use Plack::Middleware::Session::Cookie; use HTTP::Request::Common; use LWP::UserAgent; use HTTP::Cookies; $Plack::Test::Impl = 'Server'; my $app = sub { my $env = shift; my $session = $env->{'psgix.session'}; my $counter = $session->{counter} || 0; if ($session->{counter}++ >= 2) { $env->{'psgix.session.options'}->{expire} = 1; } return [ 200, [], [ "counter=$counter" ] ]; }; $app = Plack::Middleware::Session::Cookie->wrap( $app, secret => "foobar", expires => 3600, serializer => sub { MIME::Base64::encode(YAML::Dump($_[0])) }, deserializer => sub { YAML::Load(MIME::Base64::decode($_[0])) }, ); my $ua = LWP::UserAgent->new; $ua->cookie_jar( HTTP::Cookies->new ); test_psgi ua => $ua, app => $app, client => sub { my $cb = shift; my $res = $cb->(GET "/"); is $res->content, "counter=0"; like $res->header('Set-Cookie'), qr/expires=/; like $res->header('Set-Cookie'), qr/path=\//; $res = $cb->(GET "/"); is $res->content, "counter=1"; like $res->header('Set-Cookie'), qr/expires=/; $res = $cb->(GET "/"); is $res->content, "counter=2"; $res = $cb->(GET "/"); is $res->content, "counter=0"; }; done_testing; libplack-middleware-session-perl-0.33/t/author-pod-syntax.t000066400000000000000000000004541357530506400240250ustar00rootroot00000000000000#!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(); libplack-middleware-session-perl-0.33/t/lib/000077500000000000000000000000001357530506400207755ustar00rootroot00000000000000libplack-middleware-session-perl-0.33/t/lib/TestSession.pm000066400000000000000000000126371357530506400236270ustar00rootroot00000000000000package t::lib::TestSession; use strict; use warnings; use Test::More; use Test::Fatal qw(lives_ok); use Plack::Middleware::Session; use Plack::Session; sub create_session { my($mw, $env) = @_; my $session; my $app = sub { my $env = shift; $session = Plack::Session->new($env); return sub { my $responder = shift; $responder->([ 200, [], [] ]); }; }; my $res = $mw->($app)->($env); return ($session, $res); } sub run_all_tests { my %params = @_; my ( $env_cb, $state, $storage, $response_test ) = @params{qw[ env_cb state store response_test ]}; my $m = sub { Plack::Middleware::Session->wrap($_[0], state => $state, store => $storage) }; $response_test ||= sub { my($res_cb, $session_id, $check_expired) = @_; $res_cb->(sub { my $res = shift }); }; my @sids; { my($s, $res) = create_session($m, $env_cb->()); push @sids, $s->id; ok(!$s->get('foo'), '... no value stored in foo for session'); lives_ok { $s->set( foo => 'bar' ); } '... set the value successfully in session'; is($s->get('foo'), 'bar', '... got the foo value back successfully from session'); ok(!$s->get('bar'), '... no value stored in foo for session'); lives_ok { $s->set( bar => 'baz' ); } '... set the value successfully in session'; is($s->get('bar'), 'baz', '... got the foo value back successfully from session'); is_deeply( $s->dump, { foo => 'bar', bar => 'baz' }, '... got the session dump we expected'); $response_test->($res, $sids[0]); } { my($s, $res) = create_session($m, $env_cb->()); push @sids, $s->id; isnt($sids[0], $sids[1], "no same Session ID"); ok(!$s->get('foo'), '... no value stored for foo in session'); lives_ok { $s->set( foo => 'baz' ); } '... set the value successfully'; is($s->get('foo'), 'baz', '... got the foo value back successfully from session'); is_deeply( $s->dump, { foo => 'baz' }, '... got the session dump we expected'); $response_test->($res, $sids[1]); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($s->id, $sids[0], '... got a basic session id'); is($s->get('foo'), 'bar', '... got the value for foo back successfully from session'); lives_ok { $s->remove( 'foo' ); } '... removed the foo value successfully from session'; ok(!$s->get('foo'), '... no value stored for foo in session'); is_deeply( $s->dump, { bar => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[0] ); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[1] })); is($s->id, $sids[1], '... got a basic session id'); is($s->get('foo'), 'baz', '... got the foo value back successfully from session'); is_deeply( $s->dump, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[1] ); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($s->id, $sids[0], '... got a basic session id'); ok(!$s->get('foo'), '... no value stored for foo in session'); lives_ok { $s->set( baz => 'gorch' ); } '... set the bar value successfully in session'; is_deeply( $s->dump, { bar => 'baz', baz => 'gorch' }, '... got the session dump we expected'); $response_test->( $res, $sids[0] ); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($s->get('bar'), 'baz', '... got the bar value back successfully from session'); lives_ok { $s->expire; } '... expired session successfully'; $response_test->( $res, $sids[0], 1 ); is_deeply( $s->dump, {}, '... got the session dump we expected'); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); push @sids, $s->id; isnt($s->id, $sids[0], 'expired ... got a new session id'); ok(!$s->get('bar'), '... no bar value stored'); is_deeply( $s->dump, {}, '... got the session dump we expected'); $response_test->( $res, $sids[2] ); } { my($s, $res) = create_session($m, $env_cb->({ plack_session => $sids[1] })); is($s->id, $sids[1], '... got a basic session id'); is($s->get('foo'), 'baz', '... got the foo value back successfully from session'); is_deeply( $s->dump, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[1] ); } { # wrong format session_id my($s, $res) = create_session($m, $env_cb->({ plack_session => "../wrong" })); isnt('../wrong' => $s->id, '... regenerate session id'); ok(!$s->get('foo'), '... no value stored for foo in session'); lives_ok { $s->set( foo => 'baz' ); } '... set the value successfully'; is($s->get('foo'), 'baz', '... got the foo value back successfully from session'); is_deeply( $s->dump, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $s->id ); } } 1; libplack-middleware-session-perl-0.33/t/lib/TestSessionHash.pm000066400000000000000000000133471357530506400244320ustar00rootroot00000000000000package t::lib::TestSessionHash; use strict; use warnings; use Test::More; use Test::Fatal qw(lives_ok); use Plack::Middleware::Session; sub create_session { my($mw, $env) = @_; my ($session, $session_options); my $app = sub { my $env = shift; $session = $env->{'psgix.session'}; $session_options = $env->{'psgix.session.options'}; return sub { my $responder = shift; $responder->([ 200, [], [] ]); }; }; my $res = $mw->($app)->($env); return ($session, $session_options, $res); } sub run_all_tests { my %params = @_; my ( $env_cb, $state, $storage, $response_test, $middleware_create_cb ) = @params{qw[ env_cb state store response_test middleware_create_cb ]}; my $m = $middleware_create_cb || sub { Plack::Middleware::Session->wrap($_[0], state => $state, store => $storage) }; $response_test ||= sub { my($res_cb, $session_id, $check_expired) = @_; $res_cb->(sub { my $res = shift }); }; my @sids; { my($s, $opts, $res) = create_session($m, $env_cb->()); push @sids, $opts->{id}; ok(!$s->{'foo'}, '... no value stored in foo for session'); lives_ok { $s->{foo} = 'bar'; } '... set the value successfully in session'; is($s->{'foo'}, 'bar', '... got the foo value back successfully from session'); ok(!$s->{'bar'}, '... no value stored in foo for session'); lives_ok { $s->{bar} = 'baz'; } '... set the value successfully in session'; is($s->{'bar'}, 'baz', '... got the foo value back successfully from session'); is_deeply( $s, { foo => 'bar', bar => 'baz' }, '... got the session dump we expected'); $response_test->($res, $sids[0]); } { my($s, $opts, $res) = create_session($m, $env_cb->()); push @sids, $opts->{id}; isnt($sids[0], $sids[1], "no same Session ID"); ok(!$s->{'foo'}, '... no value stored for foo in session'); lives_ok { $s->{foo} = 'baz'; } '... set the value successfully'; is($s->{'foo'}, 'baz', '... got the foo value back successfully from session'); is_deeply( $s, { foo => 'baz' }, '... got the session dump we expected'); $response_test->($res, $sids[1]); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($opts->{id}, $sids[0], '... got a basic session id'); is($s->{'foo'}, 'bar', '... got the value for foo back successfully from session'); lives_ok { delete $s->{'foo'}; } '... removed the foo value successfully from session'; ok(!$s->{'foo'}, '... no value stored for foo in session'); is_deeply( $s, { bar => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[0] ); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[1] })); is($opts->{id}, $sids[1], '... got a basic session id'); is($s->{'foo'}, 'baz', '... got the foo value back successfully from session'); is_deeply( $s, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[1] ); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($opts->{id}, $sids[0], '... got a basic session id'); ok(!$s->{'foo'}, '... no value stored for foo in session'); lives_ok { $s->{baz} = 'gorch'; } '... set the bar value successfully in session'; is_deeply( $s, { bar => 'baz', baz => 'gorch' }, '... got the session dump we expected'); $response_test->( $res, $sids[0] ); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); is($s->{'bar'}, 'baz', '... got the bar value back successfully from session'); lives_ok { $opts->{expire} = 1; } '... expired session successfully'; $response_test->( $res, $sids[0], 1 ); # XXX # this will not pass, because # it is just a hash ref and we are # not clearing it. Should we be? # - SL # is_deeply( $s, {}, '... got the session dump we expected'); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[0] })); push @sids, $opts->{id}; isnt($opts->{id}, $sids[0], 'expired ... got a new session id'); ok(!$s->{'bar'}, '... no bar value stored'); is_deeply( $s, {}, '... got the session dump we expected'); $response_test->( $res, $sids[2] ); } { my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => $sids[1] })); is($opts->{id}, $sids[1], '... got a basic session id'); is($s->{'foo'}, 'baz', '... got the foo value back successfully from session'); is_deeply( $s, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $sids[1] ); } { # wrong format session_id my($s, $opts, $res) = create_session($m, $env_cb->({ plack_session => "../wrong" })); isnt('../wrong' => $opts->{id}, '... regenerate session id'); ok(!$s->{'foo'}, '... no value stored for foo in session'); lives_ok { $s->{foo} = 'baz'; } '... set the value successfully'; is($s->{'foo'}, 'baz', '... got the foo value back successfully from session'); is_deeply( $s, { foo => 'baz' }, '... got the session dump we expected'); $response_test->( $res, $opts->{id} ); } } 1;