| Line 0
          
      
      
        Link Here | 
            
              |  |  | 1 | package Koha::Token; | 
            
              | 2 |  | 
            
              | 3 | # Created as wrapper for CSRF tokens, but designed for more general use | 
            
              | 4 |  | 
            
              | 5 | # Copyright 2016 Rijksmuseum | 
            
              | 6 | # | 
            
              | 7 | # This file is part of Koha. | 
            
              | 8 | # | 
            
              | 9 | # Koha is free software; you can redistribute it and/or modify it under the | 
            
              | 10 | # terms of the GNU General Public License as published by the Free Software | 
            
              | 11 | # Foundation; either version 3 of the License, or (at your option) any later | 
            
              | 12 | # version. | 
            
              | 13 | # | 
            
              | 14 | # Koha is distributed in the hope that it will be useful, but WITHOUT ANY | 
            
              | 15 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | 
            
              | 16 | # A PARTICULAR PURPOSE.  See the GNU General Public License for more details. | 
            
              | 17 | # | 
            
              | 18 | # You should have received a copy of the GNU General Public License along | 
            
              | 19 | # with Koha; if not, write to the Free Software Foundation, Inc., | 
            
              | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 
            
              | 21 |  | 
            
              | 22 | =head1 NAME | 
            
              | 23 |  | 
            
              | 24 | Koha::Token - Tokenizer | 
            
              | 25 |  | 
            
              | 26 | =head1 SYNOPSIS | 
            
              | 27 |  | 
            
              | 28 |     use Koha::Token; | 
            
              | 29 |     my $tokenizer = Koha::Token->new; | 
            
              | 30 |     my $token = $tokenizer->generate({ length => 20 }); | 
            
              | 31 |  | 
            
              | 32 |     # safely generate a CSRF token (nonblocking) | 
            
              | 33 |     my $csrf_token = $tokenizer->generate({ | 
            
              | 34 |         CSRF => 1, id => $id, secret => $secret, | 
            
              | 35 |     }); | 
            
              | 36 |  | 
            
              | 37 |     # or check a CSRF token | 
            
              | 38 |     my $result = $tokenizer->check({ | 
            
              | 39 |         CSRF => 1, id => $id, secret => $secret, token => $token, | 
            
              | 40 |     }); | 
            
              | 41 |  | 
            
              | 42 | =head1 DESCRIPTION | 
            
              | 43 |  | 
            
              | 44 |     Designed for providing general tokens. | 
            
              | 45 |     Created due to the need for a nonblocking call to Bytes::Random::Secure | 
            
              | 46 |     when generating a CSRF token. | 
            
              | 47 |  | 
            
              | 48 | =cut | 
            
              | 49 |  | 
            
              | 50 | use Modern::Perl; | 
            
              | 51 | use base qw(Class::Accessor); | 
            
              | 52 | use constant HMAC_SHA1_LENGTH => 20; | 
            
              | 53 |  | 
            
              | 54 | =head1 METHODS | 
            
              | 55 |  | 
            
              | 56 | =head2 new | 
            
              | 57 |  | 
            
              | 58 |     Create object (via Class::Accessor). | 
            
              | 59 |  | 
            
              | 60 | =cut | 
            
              | 61 |  | 
            
              | 62 | sub new { | 
            
              | 63 |     my ( $class ) = @_; | 
            
              | 64 |     return $class->SUPER::new(); | 
            
              | 65 | } | 
            
              | 66 |  | 
            
              | 67 | =head2 generate | 
            
              | 68 |  | 
            
              | 69 |     my $token = $tokenizer->generate({ length => 20 }); | 
            
              | 70 |     my $csrf_token = $tokenizer->generate({ | 
            
              | 71 |         CSRF => 1, id => $id, secret => $secret, | 
            
              | 72 |     }); | 
            
              | 73 |  | 
            
              | 74 |     Generate several types of tokens. Now includes CSRF. | 
            
              | 75 |     Room for future extension. | 
            
              | 76 |  | 
            
              | 77 | =cut | 
            
              | 78 |  | 
            
              | 79 | sub generate { | 
            
              | 80 |     my ( $self, $params ) = @_; | 
            
              | 81 |     if( $params->{CSRF} ) { | 
            
              | 82 |         $self->{lasttoken} = _gen_csrf( $params ); | 
            
              | 83 |     } else { | 
            
              | 84 |         $self->{lasttoken} = _gen_rand( $params ); | 
            
              | 85 |     } | 
            
              | 86 |     return $self->{lasttoken}; | 
            
              | 87 | } | 
            
              | 88 |  | 
            
              | 89 | =head2 check | 
            
              | 90 |  | 
            
              | 91 |     my $result = $tokenizer->check({ | 
            
              | 92 |         CSRF => 1, id => $id, secret => $secret, token => $token, | 
            
              | 93 |     }); | 
            
              | 94 |  | 
            
              | 95 |     Check several types of tokens. Now includes CSRF. | 
            
              | 96 |     Room for future extension. | 
            
              | 97 |  | 
            
              | 98 | =cut | 
            
              | 99 |  | 
            
              | 100 | sub check { | 
            
              | 101 |     my ( $self, $params ) = @_; | 
            
              | 102 |     if( $params->{CSRF} ) { | 
            
              | 103 |         return _chk_csrf( $params ); | 
            
              | 104 |     } | 
            
              | 105 |     return; | 
            
              | 106 | } | 
            
              | 107 |  | 
            
              | 108 | # --- Internal routines --- | 
            
              | 109 |  | 
            
              | 110 | sub _gen_csrf { | 
            
              | 111 |  | 
            
              | 112 | # Since WWW::CSRF::generate_csrf_token does not use the NonBlocking | 
            
              | 113 | # parameter of Bytes::Random::Secure, we are passing random bytes from | 
            
              | 114 | # a non blocking source to WWW::CSRF via its Random parameter. | 
            
              | 115 |  | 
            
              | 116 |     my ( $params ) = @_; | 
            
              | 117 |     return if !$params->{id} || !$params->{secret}; | 
            
              | 118 |  | 
            
              | 119 |     require Bytes::Random::Secure; | 
            
              | 120 |     require WWW::CSRF; | 
            
              | 121 |  | 
            
              | 122 |     my $randomizer = Bytes::Random::Secure->new( NonBlocking => 1 ); | 
            
              | 123 |         # this is most fundamental: do not use /dev/random since it is | 
            
              | 124 |         # blocking, but use /dev/urandom ! | 
            
              | 125 |     my $random = $randomizer->bytes( HMAC_SHA1_LENGTH ); | 
            
              | 126 |     my $token = WWW::CSRF::generate_csrf_token( | 
            
              | 127 |         $params->{id}, $params->{secret}, { Random => $random }, | 
            
              | 128 |     ); | 
            
              | 129 |  | 
            
              | 130 |     return $token; | 
            
              | 131 | } | 
            
              | 132 |  | 
            
              | 133 | sub _chk_csrf { | 
            
              | 134 |     my ( $params ) = @_; | 
            
              | 135 |     return if !$params->{id} || !$params->{secret} || !$params->{token}; | 
            
              | 136 |  | 
            
              | 137 |     require WWW::CSRF; | 
            
              | 138 |     my $csrf_status = WWW::CSRF::check_csrf_token( | 
            
              | 139 |         $params->{id}, | 
            
              | 140 |         $params->{secret}, | 
            
              | 141 |         $params->{token}, | 
            
              | 142 |     ); | 
            
              | 143 |     return $csrf_status == WWW::CSRF::CSRF_OK(); | 
            
              | 144 | } | 
            
              | 145 |  | 
            
              | 146 | sub _gen_rand { | 
            
              | 147 |     my ( $params ) = @_; | 
            
              | 148 |     my $length = $params->{length} || 1; | 
            
              | 149 |     $length = 1 unless $length > 0; | 
            
              | 150 |  | 
            
              | 151 |     require String::Random; | 
            
              | 152 |     return String::Random::random_string( '.' x $length ); | 
            
              | 153 | } | 
            
              | 154 |  | 
            
              | 155 | =head1 AUTHOR | 
            
              | 156 |  | 
            
              | 157 |     Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands | 
            
              | 158 |  | 
            
              | 159 | =cut | 
            
              | 160 |  | 
            
              | 161 | 1; |