Catalyst-Plugin-Session-Store-Cookie

 view release on metacpan or  search on metacpan

lib/Catalyst/Plugin/Session/Store/Cookie.pm  view on Meta::CPAN

package Catalyst::Plugin::Session::Store::Cookie;

use Moose;
use Session::Storage::Secure;
use MRO::Compat;
use Catalyst::Utils;

extends 'Catalyst::Plugin::Session::Store';
with 'Catalyst::ClassData';

our $VERSION = '0.005';

__PACKAGE__->mk_classdata($_)
  for qw/_secure_store _store_cookie_name _store_cookie_expires
    _store_cookie_secure _store_cookie_httponly _store_cookie_samesite/;

sub get_session_data {
  my ($self, $key) = @_;
  $self->_needs_early_session_finalization(1);

  # Don't decode if we've decoded this context already.
  return $self->{__cookie_session_store_cache__}->{$key} if
    exists($self->{__cookie_session_store_cache__}) &&
      exists($self->{__cookie_session_store_cache__}->{$key});

  my $cookie = $self->req->cookie($self->_store_cookie_name);
  $self->{__cookie_session_store_cache__} = defined($cookie) ?
    $self->_decode_secure_store($cookie, $key) : +{};

  return $self->{__cookie_session_store_cache__}->{$key};
}

sub _decode_secure_store {
  my ($self, $cookie, $key) = @_;
  my $decoded = eval {
    $self->_secure_store->decode($cookie->value);
  } || do {
    $self->log->error("Issue decoding cookie for key '$key': $@");
    return +{};
  };
  return $decoded;
}

sub store_session_data {
  my ($self, $key, $data) = @_;

  $self->{__cookie_session_store_cache__} = +{
    %{$self->{__cookie_session_store_cache__}},
    $key => $data};

  my $cookie = {
    value => $self->_secure_store->encode($self->{__cookie_session_store_cache__}),
    expires => $self->_store_cookie_expires,
  };

  # copied from Catalyst::Plugin::Session::State::Cookie
  my $sec = $self->_store_cookie_secure;
  $cookie->{secure} = 1 unless ( ($sec==0) || ($sec==2) );
  $cookie->{secure} = 1 if ( ($sec==2) && $self->req->secure );
  $cookie->{httponly} = $self->_store_cookie_httponly;
  $cookie->{samesite} = $self->_store_cookie_samesite;

  return $self->res->cookies->{$self->_store_cookie_name} = $cookie;
}

sub delete_session_data {
  my ($self, $key) = @_;
  delete $self->{__cookie_session_store_cache__}->{$key};
}

# Docs say 'this may be used in the future', like 10 years ago...
sub delete_expired_sessions { }

sub setup_session {
  my $class = shift;
  my $cfg = $class->_session_plugin_config;
  $class->_store_cookie_name($cfg->{storage_cookie_name} || Catalyst::Utils::appprefix($class) . '_store');
  $class->_store_cookie_expires($cfg->{storage_cookie_expires} || '+1d');
  $class->_secure_store(
    Session::Storage::Secure->new(
      secret_key => ($cfg->{storage_secret_key} ||
        die "storage_secret_key' configuration param for 'Catalyst::Plugin::Session::Store::Cookie' is missing!"),
      sereal_encoder_options => ($cfg->{sereal_encoder_options} || +{ snappy => 1, stringify_unknown => 1 }),
      sereal_decoder_options => ($cfg->{sereal_decoder_options} || +{ validate_utf8 => 1 })
    )
  );
  $class->_store_cookie_secure($cfg->{storage_cookie_secure} || 0);
  $class->_store_cookie_httponly($cfg->{storage_cookie_httponly} || 1);
  $class->_store_cookie_samesite($cfg->{storage_cookie_samesite} || 'Lax');

  return $class->maybe::next::method(@_);
}

__PACKAGE__->meta->make_immutable;

=head1 NAME

Catalyst::Plugin::Session::Store::Cookie - Store session data in the cookie

=head1 SYNOPSIS



( run in 1.848 second using v1.01-cache-2.11-cpan-39bf76dae61 )