Dancer2-Session-Cookie-0.009/0000775000175000017500000000000013270176530015154 5ustar yanickyanickDancer2-Session-Cookie-0.009/xt/0000775000175000017500000000000013270176530015607 5ustar yanickyanickDancer2-Session-Cookie-0.009/xt/release/0000775000175000017500000000000013270176530017227 5ustar yanickyanickDancer2-Session-Cookie-0.009/xt/release/unused-vars.t0000644000175000017500000000036213270176530021667 0ustar yanickyanick#!perl use Test::More 0.96 tests => 1; eval { require Test::Vars }; SKIP: { skip 1 => 'Test::Vars required for testing for unused vars' if $@; Test::Vars->import; subtest 'unused vars' => sub { all_vars_ok(); }; }; Dancer2-Session-Cookie-0.009/lib/0000775000175000017500000000000013270176530015722 5ustar yanickyanickDancer2-Session-Cookie-0.009/lib/Dancer2/0000775000175000017500000000000013270176530017200 5ustar yanickyanickDancer2-Session-Cookie-0.009/lib/Dancer2/Session/0000775000175000017500000000000013270176530020623 5ustar yanickyanickDancer2-Session-Cookie-0.009/lib/Dancer2/Session/Cookie.pm0000644000175000017500000001300413270176530022366 0ustar yanickyanickuse 5.008001; use strict; use warnings; package Dancer2::Session::Cookie; our $AUTHORITY = 'cpan:YANICK'; # ABSTRACT: Dancer 2 session storage in secure cookies # VERSION $Dancer2::Session::Cookie::VERSION = '0.009'; use Session::Storage::Secure 0.011 (); use Moo; use Dancer2::Core::Types; #--------------------------------------------------------------------------# # Attributes #--------------------------------------------------------------------------# has secret_key => ( is => 'ro', isa => Str, required => 1, ); has default_duration => ( is => 'ro', isa => Int, predicate => 1, ); has with_request_address => ( is => 'ro', isa => Bool, ); has _store => ( is => 'lazy', isa => InstanceOf ['Session::Storage::Secure'], handles => { '_freeze' => 'encode', '_retrieve' => 'decode', }, ); before [qw/ _freeze _retrieve /] => sub { my $self = shift; return unless $self->with_request_address; $self->_store->{secret_key} = join '-', $self->secret_key, $self->request->address; }; sub _build__store { my ($self) = @_; my %args = ( secret_key => $self->secret_key, sereal_encoder_options => { snappy => 1, stringify_unknown => 1 }, sereal_decoder_options => { validate_utf8 => 1 }, ); $args{default_duration} = $self->default_duration if $self->has_default_duration; return Session::Storage::Secure->new(%args); } sub _change_id { # This is a noop with session cookies. return; } with 'Dancer2::Core::Role::SessionFactory'; #--------------------------------------------------------------------------# # Modified SessionFactory methods #--------------------------------------------------------------------------# # We don't need to generate an ID. We'll set it during cookie generation sub generate_id { '' } # Cookie generation: serialize the session data into the session ID # right before the cookie is generated before 'cookie' => sub { my ( $self, %params ) = @_; my $session = $params{session}; return unless ref $session && $session->isa("Dancer2::Core::Session"); $session->id( $self->_freeze( $session->data, $session->expires ) ); }; #--------------------------------------------------------------------------# # SessionFactory implementation methods #--------------------------------------------------------------------------# # _retrieve handled by _store # We don't actually flush data; instead we modify cookie generation sub _flush { return } # We have nothing to destroy, either; cookie expiration is all that matters sub _destroy { return } # There is no way to know about existing sessions when cookies # are used as the store, so we lie and return an empty list. sub _sessions { return [] } 1; # vim: ts=4 sts=4 sw=4 et: __END__ =pod =encoding UTF-8 =head1 NAME Dancer2::Session::Cookie - Dancer 2 session storage in secure cookies =head1 VERSION version 0.009 =head1 SYNOPSIS # In Dancer 2 config.yml file session: Cookie engines: session: Cookie: secret_key: your secret passphrase default_duration: 604800 with_request_address: 0 =head1 DESCRIPTION This module implements a session factory for Dancer 2 that stores session state within a browser cookie. Features include: =over =item * Data serialization and compression using L =item * Data encryption using AES with a unique derived key per cookie =item * Enforced expiration timestamp (independent of cookie expiration) =item * Cookie integrity protected with a message authentication code (MAC) =back See L for implementation details and important security caveats. =head1 ATTRIBUTES =head2 secret_key (required) This is used to secure the cookies. Encryption keys and message authentication keys are derived from this using one-way functions. Changing it will invalidate all sessions. =head2 default_duration Number of seconds for which the session may be considered valid. If C is not set as part of the session configuration, this is used instead to expire the session after a period of time, regardless of the length of the browser session. It is unset by default, meaning that sessions expiration is not capped. =head2 with_request_address If set to C, the secret key will have the request address (as provided by C<<$request->address>>) appended to it. This can help defeat some replay attacks (e.g. if the channel is not secure). But it will also cause session interruption for people on dynamic addresses. =for Pod::Coverage method_names_here generate_id =head1 SEE ALSO CPAN modules providing cookie session storage (possibly for other frameworks): =over =item * L -- Dancer 1 equivalent to this module =item * L -- encryption only =item * L -- encryption only =item * L -- MAC only =item * L -- MAC only =item * L -- really just a framework and you provide the guts with callbacks =item * L -- documentation of the base package, some more attributes to configure the cookie =back =head1 AUTHOR David Golden =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2018, 2016, 2014 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 =cut Dancer2-Session-Cookie-0.009/Makefile.PL0000644000175000017500000000370113270176530017125 0ustar yanickyanick# This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v6.011. use strict; use warnings; use 5.008001; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Dancer 2 session storage in secure cookies", "AUTHOR" => "David Golden ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Dancer2-Session-Cookie", "LICENSE" => "apache", "MIN_PERL_VERSION" => "5.008001", "NAME" => "Dancer2::Session::Cookie", "PREREQ_PM" => { "Dancer2" => "0.165", "Dancer2::Core::Role::SessionFactory" => 0, "Dancer2::Core::Types" => 0, "Moo" => 0, "Session::Storage::Secure" => "0.011", "strict" => 0, "warnings" => 0 }, "TEST_REQUIRES" => { "Dancer2" => "0.165", "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "HTTP::Cookies" => 0, "HTTP::Request::Common" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Plack" => "1.0029", "Plack::Test" => 0, "Test::MockObject" => 0, "Test::More" => "0.96", "YAML" => 0, "lib" => 0 }, "VERSION" => "0.009", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "Dancer2" => "0.165", "Dancer2::Core::Role::SessionFactory" => 0, "Dancer2::Core::Types" => 0, "ExtUtils::MakeMaker" => 0, "File::Spec" => 0, "HTTP::Cookies" => 0, "HTTP::Request::Common" => 0, "IO::Handle" => 0, "IPC::Open3" => 0, "Moo" => 0, "Plack" => "1.0029", "Plack::Test" => 0, "Session::Storage::Secure" => "0.011", "Test::MockObject" => 0, "Test::More" => "0.96", "YAML" => 0, "lib" => 0, "strict" => 0, "warnings" => 0 ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Dancer2-Session-Cookie-0.009/MANIFEST0000644000175000017500000000050313270176530016301 0ustar yanickyanickCONTRIBUTING CONTRIBUTORS Changes INSTALL LICENSE MANIFEST MANIFEST.SKIP META.json META.yml Makefile.PL README.mkdn SIGNATURE cpanfile doap.xml lib/Dancer2/Session/Cookie.pm t/00-compile.t t/00-report-prereqs.dd t/00-report-prereqs.t t/lib/TestApp.pm t/session_lifecycle.t t/with_request_address.t xt/release/unused-vars.t Dancer2-Session-Cookie-0.009/Changes0000644000175000017500000000334713270176530016454 0ustar yanickyanickRevision history for Dancer2-Session-Cookie 0.009 2018-04-25 - Dependency upped for Session::Storage::Secure. (GH#19, manwar) [ STATISTICS ] - code churn: 6 files changed, 40 insertions(+), 21 deletions(-) 0.008 2016-11-04 - Up Dancer2 dependency to 0.165, as 0.164 was a broken one. (GH#13) [ DOCUMENTATION ] - Clarify where 'cookie_duration' is set. (GH#14) [ ENHANCEMENTS ] - Add 'with_request_address' option to configuration. (GH#1) 0.007 2016-08-15 [ BUG FIXES ] - Remove context usage (GH#9, Sawyer X) [ CHANGED ] - Link to base package added in POD (GH#11, Moritz Grosch) - Simplify dzil config (Peter Mottram) - Rewrite tests to use Plack::Test (Peter Mottram) - Add noop _change_id method (Peter Mottram) [ ENHANCEMENTS ] [ NEW FEATURES ] [ STATISTICS ] 0.006 2014-07-28 [ CHANGED ] - Requires Session::Storage::Secure 0.010 to allow storing objects, which is specially relevant to JSON::bool data. [ STATISTICS ] - code churn: 12 files changed, 423 insertions(+), 477 deletions(-) 0.005 2013-06-05T22:56:23Z America/New_York [ FIXED ] - Bumped Test::TCP dependency to 1.30 to avoid parallel testing bugs 0.004 2013-05-31T23:40:09Z America/New_York [ CHANGED ] - Requires Session::Storage::Secure 0.007 for improved resistance to timing attacks 0.003 2013-05-28T13:42:39Z America/New_York [ FIXED ] - Fixed Dancer2 dependency version - Fixed tests for new, lazy session creation behavior in Dancer2 0.002 2013-02-22T22:26:31Z America/New_York [ CHANGED ] - Renamed from Dancer-Session-Factory-Cookie to Dancer2-Session-Cookie - Internals updated for new Dancer2 release 0.001 2013-01-24T17:33:57Z America/New_York - First release Dancer2-Session-Cookie-0.009/LICENSE0000644000175000017500000002637013270176530016167 0ustar yanickyanickThis software is Copyright (c) 2018, 2016, 2014 by David Golden. This is free software, licensed under: The Apache License, Version 2.0, January 2004 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Dancer2-Session-Cookie-0.009/README.mkdn0000644000175000017500000000563013270176530016766 0ustar yanickyanick# NAME Dancer2::Session::Cookie - Dancer 2 session storage in secure cookies # VERSION version 0.009 # SYNOPSIS ``` # In Dancer 2 config.yml file session: Cookie engines: session: Cookie: secret_key: your secret passphrase default_duration: 604800 with_request_address: 0 ``` # DESCRIPTION This module implements a session factory for Dancer 2 that stores session state within a browser cookie. Features include: - Data serialization and compression using [Sereal](https://metacpan.org/pod/Sereal) - Data encryption using AES with a unique derived key per cookie - Enforced expiration timestamp (independent of cookie expiration) - Cookie integrity protected with a message authentication code (MAC) See [Session::Storage::Secure](https://metacpan.org/pod/Session::Storage::Secure) for implementation details and important security caveats. # ATTRIBUTES ## secret\_key (required) This is used to secure the cookies. Encryption keys and message authentication keys are derived from this using one-way functions. Changing it will invalidate all sessions. ## default\_duration Number of seconds for which the session may be considered valid. If `cookie_duration` is not set as part of the session configuration, this is used instead to expire the session after a period of time, regardless of the length of the browser session. It is unset by default, meaning that sessions expiration is not capped. ## with\_request\_address If set to `true`, the secret key will have the request address (as provided by `<$request-`address>>) appended to it. This can help defeat some replay attacks (e.g. if the channel is not secure). But it will also cause session interruption for people on dynamic addresses. # SEE ALSO CPAN modules providing cookie session storage (possibly for other frameworks): - [Dancer::Session::Cookie](https://metacpan.org/pod/Dancer::Session::Cookie) -- Dancer 1 equivalent to this module - [Catalyst::Plugin::CookiedSession](https://metacpan.org/pod/Catalyst::Plugin::CookiedSession) -- encryption only - [HTTP::CryptoCookie](https://metacpan.org/pod/HTTP::CryptoCookie) -- encryption only - [Mojolicious::Sessions](https://metacpan.org/pod/Mojolicious::Sessions) -- MAC only - [Plack::Middleware::Session::Cookie](https://metacpan.org/pod/Plack::Middleware::Session::Cookie) -- MAC only - [Plack::Middleware::Session::SerializedCookie](https://metacpan.org/pod/Plack::Middleware::Session::SerializedCookie) -- really just a framework and you provide the guts with callbacks - [Dancer2::Core::Role::SessionFactory](https://metacpan.org/pod/Dancer2::Core::Role::SessionFactory) -- documentation of the base package, some more attributes to configure the cookie # AUTHOR David Golden # COPYRIGHT AND LICENSE This software is Copyright (c) 2018, 2016, 2014 by David Golden. This is free software, licensed under: ``` The Apache License, Version 2.0, January 2004 ``` Dancer2-Session-Cookie-0.009/SIGNATURE0000644000175000017500000000367013270176530016444 0ustar yanickyanickThis file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.79. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA1 3cb10e57dddd7f59c157436adc26d60dc208e2e6 CONTRIBUTING SHA1 f4aad48b029af4c43eaf7acc12f37fec9019b20e CONTRIBUTORS SHA1 6e7037522889a3e90a148b13ae57966270e6c218 Changes SHA1 4145903097b6e71fba164636f59885f3b6896ed7 INSTALL SHA1 251687276540674b07f86be02350e8c5a734d934 LICENSE SHA1 399d735ee054726bc19c0631f0af681cf2619226 MANIFEST SHA1 815e3372725b6a1bf778a4022b73ff326781de41 MANIFEST.SKIP SHA1 fd1138f85d02bf34399766796b604ddfb8274e0a META.json SHA1 898f8bf18eb0cee192e2b8ae84a1193e599d6e9d META.yml SHA1 1c16e8606745df75fa6160db7610ce324aefed56 Makefile.PL SHA1 61f1bd3180d6f344a116d2ef9f69901a61048aad README.mkdn SHA1 f6c5b1309ad5f0b7ff2bc7051d43f37fd11ee1d4 cpanfile SHA1 a0f62916d9512fb28570d8c8b5de3cfd5e41e7fa doap.xml SHA1 e2dd70d8ec9320d0d3fe7d9992bab5fa7f3b8662 lib/Dancer2/Session/Cookie.pm SHA1 3dfe2947d80c0234b65b05956af341b89a9abf00 t/00-compile.t SHA1 1d9e6814a987265fd535b5df34d065759d3372f0 t/00-report-prereqs.dd SHA1 504a672015f8761f5bad3863d844954c9e803c3f t/00-report-prereqs.t SHA1 e94732854d806f11998e6df89717ccad4e31c9ef t/lib/TestApp.pm SHA1 f3f7690e99a302aa80f7134fd77cba051c9425bc t/session_lifecycle.t SHA1 45b3b0056253ba186a32eac2f2b1649f886672a2 t/with_request_address.t SHA1 d1fe7d94b3edc7847eb187d4ee41f66e19cf8907 xt/release/unused-vars.t -----BEGIN PGP SIGNATURE----- iEYEARECAAYFAlrg/VgACgkQ34Hwf+GwC4wgqQCgpSp9m4G50DFW3hV+3eqD7SJr vuIAn2FM8h5O8l0IPrtwwh3Vd0PqufyA =wJET -----END PGP SIGNATURE----- Dancer2-Session-Cookie-0.009/INSTALL0000644000175000017500000000200313270176530016176 0ustar yanickyanickThis is the Perl distribution Dancer2-Session-Cookie. Installing Dancer2-Session-Cookie is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Dancer2::Session::Cookie If you are installing into a system-wide directory, you may need to pass the "-S" flag to cpanm, which uses sudo to install the module: % cpanm -S Dancer2::Session::Cookie ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Dancer2::Session::Cookie ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, then build it: % perl Makefile.PL % make && make test Then install it: % make install If you are installing into a system-wide directory, you may need to run: % sudo make install ## Documentation Dancer2-Session-Cookie documentation is available as POD. You can run perldoc from a shell to read the documentation: % perldoc Dancer2::Session::Cookie Dancer2-Session-Cookie-0.009/CONTRIBUTORS0000644000175000017500000000065113270176530017034 0ustar yanickyanick # DANCER2-SESSION-COOKIE CONTRIBUTORS # This is the (likely incomplete) list of people who have helped make this distribution what it is, either via code contributions, patches, bug reports, help with troubleshooting, etc. A huge 'thank you' to all of them. * Breno G. de Oliveira * Mohammad S Anwar * Moritz Grosch (LittleFox) * Peter Mottram * Russell Jenkins * Sawyer X * Yanick Champoux Dancer2-Session-Cookie-0.009/META.json0000644000175000017500000000521413270176530016575 0ustar yanickyanick{ "abstract" : "Dancer 2 session storage in secure cookies", "author" : [ "David Golden " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.011, CPAN::Meta::Converter version 2.150010", "license" : [ "apache_2_0" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Dancer2-Session-Cookie", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Test::More" : "0.96", "Test::Vars" : "0" } }, "runtime" : { "requires" : { "Dancer2" : "0.165", "Dancer2::Core::Role::SessionFactory" : "0", "Dancer2::Core::Types" : "0", "Moo" : "0", "Session::Storage::Secure" : "0.011", "perl" : "5.008001", "strict" : "0", "warnings" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "Dancer2" : "0.165", "ExtUtils::MakeMaker" : "0", "File::Spec" : "0", "HTTP::Cookies" : "0", "HTTP::Request::Common" : "0", "IO::Handle" : "0", "IPC::Open3" : "0", "Plack" : "1.0029", "Plack::Test" : "0", "Test::MockObject" : "0", "Test::More" : "0.96", "YAML" : "0", "lib" : "0" } } }, "provides" : { "Dancer2::Session::Cookie" : { "file" : "lib/Dancer2/Session/Cookie.pm", "version" : "0.009" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/PerlDancer/Dancer2-Session-Cookie/issues" }, "homepage" : "https://github.com/PerlDancer/Dancer2-Session-Cookie", "repository" : { "type" : "git", "url" : "https://github.com/PerlDancer/Dancer2-Session-Cookie.git", "web" : "https://github.com/PerlDancer/Dancer2-Session-Cookie" } }, "version" : "0.009", "x_authority" : "cpan:YANICK", "x_contributors" : [ "Breno G. de Oliveira ", "Mohammad S Anwar ", "Moritz Grosch (LittleFox) ", "Peter Mottram ", "Russell Jenkins ", "Sawyer X ", "Yanick Champoux " ], "x_serialization_backend" : "JSON::XS version 3.01" } Dancer2-Session-Cookie-0.009/CONTRIBUTING0000644000175000017500000000423413270176530017007 0ustar yanickyanick## HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with Dist::Zilla. This means than many of the usual files you might expect are not in the repository, but are generated at release time (e.g. Makefile.PL). ### Getting dependencies See the included `cpanfile` file for a list of dependencies. If you have App::cpanminus 1.6 or later installed, you can use `cpanm` to satisfy dependencies like this: $ cpanm --installdeps . Otherwise, you can install Module::CPANfile 1.0002 or later and then satisfy dependencies with the regular `cpan` client and `cpanfile-dump`: $ cpan `cpanfile-dump` ### Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most distributions, `prove` is entirely sufficent for you to test any patches you have. ### Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. If there is a `tidyall.ini` file, you can also install Code::TidyAll and run `tidyall` on a file or `tidyall -a` to tidy all files. ### Patching documentation Much of the documentation Pod is generated at release time. Depending on the distribution, some documentation may be written in a Pod dialect called WikiDoc. (See Pod::WikiDoc on CPAN.) If you would like to submit a documentation edit, please limit yourself to the documentation you see. If you see typos or documentation issues in the generated docs, please email or open a bug ticket instead of patching. ### Learning Dist::Zilla Dist::Zilla is a very powerful authoring tool, but requires a number of author-specific plugins. If you would like to use it for contributing, install it from CPAN, then run one of the following commands, depending on your CPAN client: $ cpan `dzil authordeps` $ dzil authordeps | cpanm Once installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil xtest You can learn more about Dist::Zilla at http://dzil.org/ Dancer2-Session-Cookie-0.009/META.yml0000644000175000017500000000307513270176530016430 0ustar yanickyanick--- abstract: 'Dancer 2 session storage in secure cookies' author: - 'David Golden ' build_requires: Dancer2: '0.165' ExtUtils::MakeMaker: '0' File::Spec: '0' HTTP::Cookies: '0' HTTP::Request::Common: '0' IO::Handle: '0' IPC::Open3: '0' Plack: '1.0029' Plack::Test: '0' Test::MockObject: '0' Test::More: '0.96' YAML: '0' lib: '0' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.011, CPAN::Meta::Converter version 2.150010' license: apache meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Dancer2-Session-Cookie provides: Dancer2::Session::Cookie: file: lib/Dancer2/Session/Cookie.pm version: '0.009' requires: Dancer2: '0.165' Dancer2::Core::Role::SessionFactory: '0' Dancer2::Core::Types: '0' Moo: '0' Session::Storage::Secure: '0.011' perl: '5.008001' strict: '0' warnings: '0' resources: bugtracker: https://github.com/PerlDancer/Dancer2-Session-Cookie/issues homepage: https://github.com/PerlDancer/Dancer2-Session-Cookie repository: https://github.com/PerlDancer/Dancer2-Session-Cookie.git version: '0.009' x_authority: cpan:YANICK x_contributors: - 'Breno G. de Oliveira ' - 'Mohammad S Anwar ' - 'Moritz Grosch (LittleFox) ' - 'Peter Mottram ' - 'Russell Jenkins ' - 'Sawyer X ' - 'Yanick Champoux ' x_serialization_backend: 'YAML::Tiny version 1.67' Dancer2-Session-Cookie-0.009/cpanfile0000644000175000017500000000171013270176530016655 0ustar yanickyanickrequires "Dancer2" => "0.165"; requires "Dancer2::Core::Role::SessionFactory" => "0"; requires "Dancer2::Core::Types" => "0"; requires "Moo" => "0"; requires "Session::Storage::Secure" => "0.011"; requires "perl" => "5.008001"; requires "strict" => "0"; requires "warnings" => "0"; on 'test' => sub { requires "Dancer2" => "0.165"; requires "ExtUtils::MakeMaker" => "0"; requires "File::Spec" => "0"; requires "HTTP::Cookies" => "0"; requires "HTTP::Request::Common" => "0"; requires "IO::Handle" => "0"; requires "IPC::Open3" => "0"; requires "Plack" => "1.0029"; requires "Plack::Test" => "0"; requires "Test::MockObject" => "0"; requires "Test::More" => "0.96"; requires "YAML" => "0"; requires "lib" => "0"; }; on 'test' => sub { recommends "CPAN::Meta" => "2.120900"; }; on 'configure' => sub { requires "ExtUtils::MakeMaker" => "0"; }; on 'develop' => sub { requires "Test::More" => "0.96"; requires "Test::Vars" => "0"; }; Dancer2-Session-Cookie-0.009/t/0000775000175000017500000000000013270176530015417 5ustar yanickyanickDancer2-Session-Cookie-0.009/t/lib/0000775000175000017500000000000013270176530016165 5ustar yanickyanickDancer2-Session-Cookie-0.009/t/lib/TestApp.pm0000644000175000017500000000174113270176530020104 0ustar yanickyanickpackage TestApp; use Dancer2; get '/no_session_data' => sub { return "session not modified"; }; get '/set_session/*' => sub { my ($name) = splat; session name => $name; }; get '/read_session' => sub { my $name = session('name') || ''; "name='$name'"; }; get '/change_session_id' => sub { if ( app->can('change_session_id') ) { app->change_session_id; return "change_session_id supported by Dancer2"; } else { return "change_session_id not supported by Dancer2"; } }; get '/destroy_session' => sub { my $name = session('name') || ''; app->destroy_session; return "destroyed='$name'"; }; get '/churn_session' => sub { app->destroy_session; session name => 'damian'; return "churned"; }; #setting appdir => $tempdir; #setting( engines => { session => { $engine => $config } } ); #setting( session => $engine ); set( show_errors => 1, startup_info => 0, environment => 'production', ); 1; Dancer2-Session-Cookie-0.009/t/with_request_address.t0000644000175000017500000000117413270176530022035 0ustar yanickyanickuse Test::More; use Test::MockObject; use Dancer2::Session::Cookie; sub session_of { my $request = Test::MockObject->new->set_always( address => shift || '127.0.0.1' ); $request->set_isa( 'Dancer2::Core::Request' ); Dancer2::Session::Cookie->new( with_request_address => 1, secret_key => 'hush', request => $request ); } my $session = session_of('127.0.0.1'); my $data = $session->_freeze( 'banana' ); isnt $data => 'banana', 'encrypted'; is session_of('127.0.0.1')->_retrieve($data) => 'banana', 'decrypted'; is session_of('127.0.0.2')->_retrieve($data) => undef, 'different address, not decrypted'; done_testing; Dancer2-Session-Cookie-0.009/t/00-report-prereqs.t0000644000175000017500000001273113270176530021015 0ustar yanickyanick#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.021 use Test::More tests => 1; use ExtUtils::MakeMaker; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do 't/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; if ( $source && $HAS_CPAN_META ) { if ( my $meta = eval { CPAN::Meta->load_file($source) } ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } } else { $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = MM->parse_version( File::Spec->catfile($prefix, $file) ); $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( @dep_errors ) { diag join("\n", "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n", "The following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: Dancer2-Session-Cookie-0.009/t/00-report-prereqs.dd0000644000175000017500000000406313270176530021140 0ustar yanickyanickdo { my $x = { 'configure' => { 'requires' => { 'ExtUtils::MakeMaker' => '0' } }, 'develop' => { 'requires' => { 'Test::More' => '0.96', 'Test::Vars' => '0' } }, 'runtime' => { 'requires' => { 'Dancer2' => '0.165', 'Dancer2::Core::Role::SessionFactory' => '0', 'Dancer2::Core::Types' => '0', 'Moo' => '0', 'Session::Storage::Secure' => '0.011', 'perl' => '5.008001', 'strict' => '0', 'warnings' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'Dancer2' => '0.165', 'ExtUtils::MakeMaker' => '0', 'File::Spec' => '0', 'HTTP::Cookies' => '0', 'HTTP::Request::Common' => '0', 'IO::Handle' => '0', 'IPC::Open3' => '0', 'Plack' => '1.0029', 'Plack::Test' => '0', 'Test::MockObject' => '0', 'Test::More' => '0.96', 'YAML' => '0', 'lib' => '0' } } }; $x; }Dancer2-Session-Cookie-0.009/t/00-compile.t0000644000175000017500000000213213270176530017445 0ustar yanickyanickuse 5.006; use strict; use warnings; # this test was generated with Dist::Zilla::Plugin::Test::Compile 2.052 use Test::More; plan tests => 1 + ($ENV{AUTHOR_TESTING} ? 1 : 0); my @module_files = ( 'Dancer2/Session/Cookie.pm' ); # no fake home requested my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib'; use File::Spec; use IPC::Open3; use IO::Handle; open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!"; my @warnings; for my $lib (@module_files) { # see L my $stderr = IO::Handle->new; my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]"); binmode $stderr, ':crlf' if $^O eq 'MSWin32'; my @_warnings = <$stderr>; waitpid($pid, 0); is($?, 0, "$lib loaded ok"); if (@_warnings) { warn @_warnings; push @warnings, @_warnings; } } is(scalar(@warnings), 0, 'no warnings found') or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING}; Dancer2-Session-Cookie-0.009/t/session_lifecycle.t0000644000175000017500000001535413270176530021314 0ustar yanickyanickuse strict; use warnings; use Test::More 0.96 import => ['!pass']; # subtests use YAML; use Plack::Test; use HTTP::Request::Common; use HTTP::Cookies; use lib 't/lib'; use TestApp; my $secret_key = "handbag imitation doublet sickout"; # Crypt::Diceware :-) my $engine = "Cookie"; my @configs = ( { label => "default", config => { secret_key => $secret_key, }, }, { label => "with default_duration", config => { secret_key => $secret_key, default_duration => 86400 * 7, }, }, { label => "with cookie_duration", config => { secret_key => $secret_key, cookie_duration => 3600, }, }, { label => "forced_expire", config => { secret_key => $secret_key, default_duration => -100, }, }, ); for my $c (@configs) { my ( $label, $config ) = @{$c}{qw/label config/}; { package TestAppConfig; use Dancer2 appname => 'TestApp'; setting( engines => { session => { $engine => $config } } ); setting( session => $engine ); } subtest $label => sub { my $url = 'http://localhost'; my $jar = HTTP::Cookies->new(); my $test = Plack::Test->create( TestApp->to_app ); my ( $req, $res ); # no session cookie set if session not referenced $res = $test->request( GET "$url/no_session_data" ); ok $res->is_success, "/no_session_data" or diag explain $res; $jar->extract_cookies($res); is $jar->as_string, "", "no cookie set" or diag explain $jar; # recent Dancer: no session created until session is written $res = $test->request( GET "$url/read_session" ); ok $res->is_success, "/read_session"; $jar->extract_cookies($res); is $jar->as_string, "", "no cookie set" or diag explain $jar; # set value into session $res = $test->request( GET "$url/set_session/larry" ); ok $res->is_success, "/set_session/larry"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; my $sid1; $jar->scan( sub { $sid1 = $_[2] } ); ok( $sid1, "Got SID from cookie: $sid1" ); # read value back $req = GET "$url/read_session"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/read_session"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; if ( $c->{label} eq 'forced_expire' ) { like $res->content, qr/name=''/, "session value reset"; } else { like $res->content, qr/name='larry'/, "session value looks good"; } # session cookie should persist even if we don't touch sessions $req = GET "$url/no_session_data"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/no_session_data"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; # change_session_id is a noop with this session engine but test # all the same { my ( $sid1, $sid2 ); $req = GET "$url/no_session_data"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/no_session_data"; $jar->extract_cookies($res); $jar->scan( sub { $sid1 = $_[2] } ); ok( $sid1, "Got SID from cookie: $sid1" ); $req = GET "$url/change_session_id"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/change_session_id"; $jar->extract_cookies($res); $jar->scan( sub { $sid2 = $_[2] } ); ok( $sid2, "Got SID from cookie: $sid2" ); isnt $sid1, $sid2, "session id has changed"; diag $res->content; } # destroy session and check that cookies expiration is set $req = GET "$url/destroy_session"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/destroy_session"; $jar->extract_cookies($res); is $jar->as_string, "", "session cookie is expired" or diag explain $jar; # shouldn't be sent session cookie after session destruction $req = GET "$url/no_session_data"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/no_session_data"; $jar->extract_cookies($res); is $jar->as_string, "", "no cookie set" or diag explain $jar; # set value into session again $req = GET "$url/set_session/curly"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/set_session/curly"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; my $sid2; $jar->scan( sub { $sid2 = $_[2] } ); ok( $sid2, "Got SID from cookie: $sid2" ); isnt( $sid2, $sid1, "changing data changes session ID" ) or diag explain $jar; # destroy and create a session in one request $req = GET "$url/churn_session"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/churn_session"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; # read value back $req = GET "$url/read_session"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/read_session"; $jar->extract_cookies($res); isnt $jar->as_string, "", "session cookie set" or diag explain $jar; if ( $c->{label} eq 'forced_expire' ) { like $res->content, qr/name=''/, "session value reset"; } else { like $res->content, qr/name='damian'/, "session value looks good"; } # try to manipulate cookie my @cookie; $jar->scan( sub { @cookie = @_ } ); $cookie[2] =~ s/~\d*~/"~" . (time + 100) . "~"/e; ok($jar->set_cookie(@cookie), "Set bad cookie value"); $req = GET "$url/read_session"; $jar->add_cookie_header($req); $res = $test->request($req); ok $res->is_success, "/read_session"; isnt $jar->as_string, "", "session cookie set" or diag explain $jar; like $res->content, qr/name=''/, "session reset after bad MAC"; }; } done_testing; Dancer2-Session-Cookie-0.009/MANIFEST.SKIP0000644000175000017500000000004213270176530017044 0ustar yanickyanickdist.ini perlcritic.rc Dancer2-.* Dancer2-Session-Cookie-0.009/doap.xml0000644000175000017500000001026113270176530016617 0ustar yanickyanick Dancer2-Session-Cookie Dancer 2 session storage in secure cookies David Golden Breno G. de Oliveira Mohammad S Anwar Moritz Grosch LittleFox Peter Mottram Russell Jenkins Sawyer X Yanick Champoux 0.001 2013-01-24T17:33:57Z 0.002 2013-02-22T22:26:31Z 0.003 2013-05-28T13:42:39Z 0.004 2013-05-31T23:40:09Z 0.005 2013-06-05T22:56:23Z 0.006 2014-07-28 0.007 2016-08-15 0.008 2016-11-04 Perl