View | Details | Raw Unified | Return to bug 29543
Collapse All | Expand All

(-)a/C4/Installer/PerlDependencies.pm (+5 lines)
Lines 767-772 our $PERL_DEPS = { Link Here
767
        'required' => '1',
767
        'required' => '1',
768
        'min_ver'  => '0.05',
768
        'min_ver'  => '0.05',
769
    },
769
    },
770
    'Mojo::JWT' => {
771
        'usage'    => 'Core',
772
        'required' => '1',
773
        'min_ver'  => '0.08',
774
    },
770
    'Mojolicious' => {
775
    'Mojolicious' => {
771
        'usage'    => 'REST API',
776
        'usage'    => 'REST API',
772
        'required' => '1',
777
        'required' => '1',
(-)a/Koha/Token.pm (-1 / +87 lines)
Lines 1-6 Link Here
1
package Koha::Token;
1
package Koha::Token;
2
2
3
# Created as wrapper for CSRF tokens, but designed for more general use
3
# Created as wrapper for CSRF and JWT tokens, but designed for more general use
4
4
5
# Copyright 2016 Rijksmuseum
5
# Copyright 2016 Rijksmuseum
6
#
6
#
Lines 52-57 use Modern::Perl; Link Here
52
use Bytes::Random::Secure ();
52
use Bytes::Random::Secure ();
53
use String::Random ();
53
use String::Random ();
54
use WWW::CSRF ();
54
use WWW::CSRF ();
55
use Mojo::JWT;
55
use Digest::MD5 qw(md5_base64);
56
use Digest::MD5 qw(md5_base64);
56
use Encode qw( encode );
57
use Encode qw( encode );
57
use Koha::Exceptions::Token;
58
use Koha::Exceptions::Token;
Lines 78-83 sub new { Link Here
78
    my $csrf_token = $tokenizer->generate({
79
    my $csrf_token = $tokenizer->generate({
79
        type => 'CSRF', id => $id, secret => $secret,
80
        type => 'CSRF', id => $id, secret => $secret,
80
    });
81
    });
82
    my $jwt = $tokenizer->generate({
83
        type => 'JWT, id => $id, secret => $secret,
84
    });
81
85
82
    Generate several types of tokens. Now includes CSRF.
86
    Generate several types of tokens. Now includes CSRF.
83
    For non-CSRF tokens an optional pattern parameter overrides length.
87
    For non-CSRF tokens an optional pattern parameter overrides length.
Lines 101-106 sub generate { Link Here
101
    my ( $self, $params ) = @_;
105
    my ( $self, $params ) = @_;
102
    if( $params->{type} && $params->{type} eq 'CSRF' ) {
106
    if( $params->{type} && $params->{type} eq 'CSRF' ) {
103
        $self->{lasttoken} = _gen_csrf( $params );
107
        $self->{lasttoken} = _gen_csrf( $params );
108
    } elsif( $params->{type} && $params->{type} eq 'JWT' ) {
109
        $self->{lasttoken} = _gen_jwt( $params );
104
    } else {
110
    } else {
105
        $self->{lasttoken} = _gen_rand( $params );
111
        $self->{lasttoken} = _gen_rand( $params );
106
    }
112
    }
Lines 122-127 sub generate_csrf { Link Here
122
    return $self->generate({ %$params, type => 'CSRF' });
128
    return $self->generate({ %$params, type => 'CSRF' });
123
}
129
}
124
130
131
=head2 generate_jwt
132
133
    Like: generate({ type => 'JWT', ... })
134
    Note that JWT is designed to encode a structure but here we are actually only allowing a value
135
    that will be store in the key 'id'.
136
137
=cut
138
139
sub generate_jwt {
140
    my ( $self, $params ) = @_;
141
    return if !$params->{id};
142
    $params = _add_default_jwt_params( $params );
143
    return $self->generate({ %$params, type => 'JWT' });
144
}
145
125
=head2 check
146
=head2 check
126
147
127
    my $result = $tokenizer->check({
148
    my $result = $tokenizer->check({
Lines 138-143 sub check { Link Here
138
    if( $params->{type} && $params->{type} eq 'CSRF' ) {
159
    if( $params->{type} && $params->{type} eq 'CSRF' ) {
139
        return _chk_csrf( $params );
160
        return _chk_csrf( $params );
140
    }
161
    }
162
    elsif( $params->{type} && $params->{type} eq 'JWT' ) {
163
        return _chk_jwt( $params );
164
    }
141
    return;
165
    return;
142
}
166
}
143
167
Lines 156-161 sub check_csrf { Link Here
156
    return $self->check({ %$params, type => 'CSRF' });
180
    return $self->check({ %$params, type => 'CSRF' });
157
}
181
}
158
182
183
=head2 check_jwt
184
185
    Like: check({ type => 'JWT', id => $id, token => $token })
186
187
    Will return true if the token contains the passed id
188
189
=cut
190
191
sub check_jwt {
192
    my ( $self, $params ) = @_;
193
    $params = _add_default_jwt_params( $params );
194
    return $self->check({ %$params, type => 'JWT' });
195
}
196
197
=head2 decode_jwt
198
199
    $tokenizer->decode_jwt({ type => 'JWT', token => $token })
200
201
    Will return the value of the id stored in the token.
202
203
=cut
204
sub decode_jwt {
205
    my ( $self, $params ) = @_;
206
    $params = _add_default_jwt_params( $params );
207
    return _decode_jwt( $params );
208
}
209
159
# --- Internal routines ---
210
# --- Internal routines ---
160
211
161
sub _add_default_csrf_params {
212
sub _add_default_csrf_params {
Lines 220-225 sub _gen_rand { Link Here
220
    return $token;
271
    return $token;
221
}
272
}
222
273
274
sub _add_default_jwt_params {
275
    my ( $params ) = @_;
276
    my $pw = C4::Context->config('pass');
277
    $params->{secret} //= md5_base64( Encode::encode( 'UTF-8', $pw ) ),
278
    return $params;
279
}
280
281
sub _gen_jwt {
282
    my ( $params ) = @_;
283
    return if !$params->{id} || !$params->{secret};
284
285
    return Mojo::JWT->new(
286
        claims => { id => $params->{id} },
287
        secret => $params->{secret}
288
    )->encode;
289
}
290
291
sub _chk_jwt {
292
    my ( $params ) = @_;
293
    return if !$params->{id} || !$params->{secret} || !$params->{token};
294
295
    my $claims = Mojo::JWT->new(secret => $params->{secret})->decode($params->{token});
296
297
    return 1 if exists $claims->{id} && $claims->{id} == $params->{id};
298
}
299
300
sub _decode_jwt {
301
    my ( $params ) = @_;
302
    return if !$params->{token} || !$params->{secret};
303
304
    my $claims = Mojo::JWT->new(secret => $params->{secret})->decode($params->{token});
305
306
    return $claims->{id};
307
}
308
223
=head1 AUTHOR
309
=head1 AUTHOR
224
310
225
    Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands
311
    Marcel de Rooy, Rijksmuseum Amsterdam, The Netherlands
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/circulation.pref (+1 lines)
Lines 986-991 Circulation: Link Here
986
              choices:
986
              choices:
987
                  yes: Username and Password
987
                  yes: Username and Password
988
                  no: Cardnumber
988
                  no: Cardnumber
989
            - ".</br>NOTE: If using 'cardnumber' and AutoSelfCheckAllowed you should set SelfCheckAllowByIPRanges to prevent brute force attacks to gain patron information outside the library."
989
        -
990
        -
990
            - "Time out the current patron's web-based self checkout system login after"
991
            - "Time out the current patron's web-based self checkout system login after"
991
            - pref: SelfCheckTimeout
992
            - pref: SelfCheckTimeout
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/sco/sco-main.tt (-2 / +7 lines)
Lines 190-195 Link Here
190
                    <div class="alert alert-info">
190
                    <div class="alert alert-info">
191
                        <p>Item renewed</p>
191
                        <p>Item renewed</p>
192
                    </div>
192
                    </div>
193
                    [% ELSIF ( renewed == 0) %]
194
                    <span class="sco-alert-warning renew"></span>
195
                    <div class="alert alert-info">
196
                        <p>Item not renewed</p>
197
                    </div>
193
                    [% ELSIF ( returned == 0 ) %]
198
                    [% ELSIF ( returned == 0 ) %]
194
                    <span class="sco-alert-warning return"></span>
199
                    <span class="sco-alert-warning return"></span>
195
                    <div class="alert alert-info">
200
                    <div class="alert alert-info">
Lines 372-380 Link Here
372
                                        [% IF ( Koha.Preference('SelfCheckoutByLogin') ) %]
377
                                        [% IF ( Koha.Preference('SelfCheckoutByLogin') ) %]
373
                                            <legend>Log in to your account</legend>
378
                                            <legend>Log in to your account</legend>
374
                                            <label for="patronlogin">Login:</label>
379
                                            <label for="patronlogin">Login:</label>
375
                                            <input type="text" id="patronlogin" class="focus" size="20" name="patronlogin" />
380
                                            <input type="text" id="patronlogin" class="focus" size="20" name="patronlogin" autocomplete="off"/>
376
                                            <label for="patronpw">Password:</label>
381
                                            <label for="patronpw">Password:</label>
377
                                            <input type="password" id="patronpw" size="20" name="patronpw" />
382
                                            <input type="password" id="patronpw" size="20" name="patronpw" autocomplete="off"/>
378
                                            <fieldset class="action">
383
                                            <fieldset class="action">
379
                                                <button type="submit" class="btn">Log in</button>
384
                                                <button type="submit" class="btn">Log in</button>
380
                                            </fieldset>
385
                                            </fieldset>
(-)a/opac/sco/sco-main.pl (-45 / +62 lines)
Lines 21-35 Link Here
21
21
22
# We're going to authenticate a self-check user.  we'll add a flag to borrowers 'selfcheck'
22
# We're going to authenticate a self-check user.  we'll add a flag to borrowers 'selfcheck'
23
#
23
#
24
# We're in a controlled environment; we trust the user.
24
# We're not in a controlled environment; we never trust the user.
25
# So the selfcheck station will accept a patronid and issue items to that borrower.
26
# FIXME: NOT really a controlled environment...  We're on the internet!
27
#
25
#
28
# The checkout permission comes form the CGI cookie/session of a staff user.
26
# The checkout permission comes form the CGI cookie/session of a staff user.
29
# The patron is not really logging in here in the same way as they do on the
27
# The patron is not really logging in here in the same way as they do on the
30
# rest of the OPAC.  So don't confuse loggedinuser with the patron user.
28
# rest of the OPAC.  So don't confuse loggedinuser with the patron user.
31
#
29
# The patron id/cardnumber is retrieved from the JWT
32
# FIXME: inputfocus not really used in TMPL
33
30
34
use Modern::Perl;
31
use Modern::Perl;
35
32
Lines 99-107 if (defined C4::Context->preference('AllowSelfCheckReturns')) { Link Here
99
}
96
}
100
97
101
my $issuerid = $loggedinuser;
98
my $issuerid = $loggedinuser;
102
my ($op, $patronid, $patronlogin, $patronpw, $barcode, $confirmed, $newissues) = (
99
my ($op, $patronlogin, $patronpw, $barcode, $confirmed, $newissues) = (
103
    $query->param("op")         || '',
100
    $query->param("op")         || '',
104
    $query->param("patronid")   || '',
105
    $query->param("patronlogin")|| '',
101
    $query->param("patronlogin")|| '',
106
    $query->param("patronpw")   || '',
102
    $query->param("patronpw")   || '',
107
    $query->param("barcode")    || '',
103
    $query->param("barcode")    || '',
Lines 109-144 my ($op, $patronid, $patronlogin, $patronpw, $barcode, $confirmed, $newissues) = Link Here
109
    $query->param("newissues")  || '',
105
    $query->param("newissues")  || '',
110
);
106
);
111
107
108
my $jwt = $query->cookie('JWT');
109
if ($op eq "logout") {
110
    $template->param( loggedout => 1 );
111
    $query->param( patronlogin => undef, patronpw => undef );
112
    undef $jwt;
113
}
114
112
my @newissueslist = split /,/, $newissues;
115
my @newissueslist = split /,/, $newissues;
113
my $issuenoconfirm = 1; #don't need to confirm on issue.
116
my $issuenoconfirm = 1; #don't need to confirm on issue.
114
my $issuer   = Koha::Patrons->find( $issuerid )->unblessed;
117
my $issuer   = Koha::Patrons->find( $issuerid )->unblessed;
115
my $item     = Koha::Items->find({ barcode => $barcode });
118
116
if (C4::Context->preference('SelfCheckoutByLogin') && !$patronid) {
119
my $patronid = $jwt ? Koha::Token->new->decode_jwt({ token => $jwt }) : undef;
117
    my $dbh = C4::Context->dbh;
120
unless ( $patronid ) {
118
    my $resval;
121
    if ( C4::Context->preference('SelfCheckoutByLogin') ) {
119
    ($resval, $patronid) = checkpw($dbh, $patronlogin, $patronpw);
122
        my $dbh = C4::Context->dbh;
123
        ( undef, $patronid ) = checkpw( $dbh, $patronlogin, $patronpw );
124
    }
125
    else {    # People should not do that unless they know what they are doing!
126
              # SelfCheckAllowByIPRanges MUST be configured
127
        $patronid = $query->param('patronid');
128
    }
129
    $jwt = Koha::Token->new->generate_jwt({ id => $patronid }) if $patronid;
120
}
130
}
121
131
122
my ( $borrower, $patron );
132
my $patron;
123
if ( $patronid ) {
133
if ( $patronid ) {
124
    $patron = Koha::Patrons->find( { cardnumber => $patronid } );
134
    $patron = Koha::Patrons->find( { cardnumber => $patronid } );
125
    $borrower = $patron->unblessed if $patron;
126
}
135
}
127
136
128
my $branch = $issuer->{branchcode};
137
my $branch = $issuer->{branchcode};
129
my $confirm_required = 0;
138
my $confirm_required = 0;
130
my $return_only = 0;
139
my $return_only = 0;
131
#warn "issuer cardnumber: " .   $issuer->{cardnumber};
132
#warn "patron cardnumber: " . $borrower->{cardnumber};
133
if ($op eq "logout") {
140
if ($op eq "logout") {
134
    $template->param( loggedout => 1 );
141
    $template->param( loggedout => 1 );
135
    $query->param( patronid => undef, patronlogin => undef, patronpw => undef );
142
    $query->param( patronid => undef, patronlogin => undef, patronpw => undef );
136
}
143
}
137
elsif ( $op eq "returnbook" && $allowselfcheckreturns ) {
144
138
    my ($doreturn) = AddReturn( $barcode, $branch );
145
if ( $op eq "returnbook" && $allowselfcheckreturns ) {
146
147
    # Patron cannot checkin an item they don't own
148
    my $item = Koha::Items->find( { barcode => $barcode } );
149
    my $doreturn = $patron->checkouts->find( { itemnumber => $item->itemnumber} ) ? 1 : 0;
150
151
    if ( $doreturn ) {
152
        ($doreturn) = AddReturn( $barcode, $branch );
153
    }
139
    $template->param( returned => $doreturn );
154
    $template->param( returned => $doreturn );
140
}
155
}
141
elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) {
156
elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) {
157
    my $item = Koha::Items->find( { barcode => $barcode } );
142
    my $impossible  = {};
158
    my $impossible  = {};
143
    my $needconfirm = {};
159
    my $needconfirm = {};
144
    ( $impossible, $needconfirm ) = CanBookBeIssued(
160
    ( $impossible, $needconfirm ) = CanBookBeIssued(
Lines 159-165 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
159
        }
175
        }
160
    }
176
    }
161
177
162
    #warn "confirm_required: " . $confirm_required ;
163
    if (scalar keys %$impossible) {
178
    if (scalar keys %$impossible) {
164
179
165
        my $issue_error = (keys %$impossible)[0]; # FIXME This is wrong, we assume only one error and keys are not ordered
180
        my $issue_error = (keys %$impossible)[0]; # FIXME This is wrong, we assume only one error and keys are not ordered
Lines 174-180 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
174
        if ($issue_error eq 'DEBT') {
189
        if ($issue_error eq 'DEBT') {
175
            $template->param(DEBT => $impossible->{DEBT});
190
            $template->param(DEBT => $impossible->{DEBT});
176
        }
191
        }
177
        #warn "issue_error: " . $issue_error ;
178
        if ( $issue_error eq "NO_MORE_RENEWALS" ) {
192
        if ( $issue_error eq "NO_MORE_RENEWALS" ) {
179
            $return_only = 1;
193
            $return_only = 1;
180
            $template->param(
194
            $template->param(
Lines 184-193 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
184
        }
198
        }
185
    } elsif ( $needconfirm->{RENEW_ISSUE} || $op eq 'renew' ) {
199
    } elsif ( $needconfirm->{RENEW_ISSUE} || $op eq 'renew' ) {
186
        if ($confirmed) {
200
        if ($confirmed) {
187
            #warn "renewing";
201
            if ( $patron->checkouts->find( { itemnumber => $item->itemnumber } ) ) {
188
            AddRenewal( $borrower->{borrowernumber}, $item->itemnumber );
202
                AddRenewal( $patron->borrowernumber, $item->itemnumber );
189
            push @newissueslist, $barcode;
203
                push @newissueslist, $barcode;
190
            $template->param( renewed => 1 );
204
                $template->param( renewed => 1 );
205
            } else {
206
                $template->param( renewed => 0 );
207
            }
191
        } else {
208
        } else {
192
            #warn "renew confirmation";
209
            #warn "renew confirmation";
193
            $template->param(
210
            $template->param(
Lines 199-205 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
199
            );
216
            );
200
        }
217
        }
201
    } elsif ( $confirm_required && !$confirmed ) {
218
    } elsif ( $confirm_required && !$confirmed ) {
202
        #warn "failed confirmation";
203
        $template->param(
219
        $template->param(
204
            impossible                => 1,
220
            impossible                => 1,
205
            "circ_error_$issue_error" => 1,
221
            "circ_error_$issue_error" => 1,
Lines 218-224 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
218
                $hold_existed = Koha::Holds->search(
234
                $hold_existed = Koha::Holds->search(
219
                    {
235
                    {
220
                        -and => {
236
                        -and => {
221
                            borrowernumber => $borrower->{borrowernumber},
237
                            borrowernumber => $patron->borrowernumber,
222
                            -or            => {
238
                            -or            => {
223
                                biblionumber => $item->biblionumber,
239
                                biblionumber => $item->biblionumber,
224
                                itemnumber   => $item->itemnumber
240
                                itemnumber   => $item->itemnumber
Lines 228-234 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
228
                )->count;
244
                )->count;
229
            }
245
            }
230
246
231
            AddIssue( $borrower, $barcode );
247
            AddIssue( $patron->unblessed, $barcode );
232
            $template->param( issued => 1 );
248
            $template->param( issued => 1 );
233
            push @newissueslist, $barcode;
249
            push @newissueslist, $barcode;
234
250
Lines 239-245 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
239
                    # Note that this should not be needed but since we do not have proper exception handling here we do it this way
255
                    # Note that this should not be needed but since we do not have proper exception handling here we do it this way
240
                    patron_has_hold_fee => Koha::Account::Lines->search(
256
                    patron_has_hold_fee => Koha::Account::Lines->search(
241
                        {
257
                        {
242
                            borrowernumber  => $borrower->{borrowernumber},
258
                            borrowernumber  => $patron->borrowernumber,
243
                            debit_type_code => 'RESERVE',
259
                            debit_type_code => 'RESERVE',
244
                            description     => $item->biblio->title,
260
                            description     => $item->biblio->title,
245
                            date            => $dtf->format_date(dt_from_string)
261
                            date            => $dtf->format_date(dt_from_string)
Lines 249-275 elsif ( $patron && ( $op eq 'checkout' || $op eq 'renew' ) ) { Link Here
249
            }
265
            }
250
        } else {
266
        } else {
251
            $confirm_required = 1;
267
            $confirm_required = 1;
252
            #warn "issue confirmation";
253
            $template->param(
268
            $template->param(
254
                confirm    => "Issuing title: " . $item->biblio->title,
269
                confirm    => "Issuing title: " . $item->biblio->title,
255
                barcode    => $barcode,
270
                barcode    => $barcode,
256
                hide_main  => 1,
271
                hide_main  => 1,
257
                inputfocus => 'confirm',
258
            );
272
            );
259
        }
273
        }
260
    }
274
    }
261
} # $op
275
} # $op
262
276
263
if ($borrower) {
277
if ($patron) {
264
#   warn "issuer's  branchcode: " .   $issuer->{branchcode};
278
    my $borrowername = sprintf "%s %s", ($patron->firstname || ''), ($patron->surname || '');
265
#   warn   "user's  branchcode: " . $borrower->{branchcode};
266
    my $borrowername = sprintf "%s %s", ($borrower->{firstname} || ''), ($borrower->{surname} || '');
267
    my $pending_checkouts = $patron->pending_checkouts;
279
    my $pending_checkouts = $patron->pending_checkouts;
268
    my @checkouts;
280
    my @checkouts;
269
    while ( my $c = $pending_checkouts->next ) {
281
    while ( my $c = $pending_checkouts->next ) {
270
        my $checkout = $c->unblessed_all_relateds;
282
        my $checkout = $c->unblessed_all_relateds;
271
        my ($can_be_renewed, $renew_error) = CanBookBeRenewed(
283
        my ($can_be_renewed, $renew_error) = CanBookBeRenewed(
272
            $borrower->{borrowernumber},
284
            $patron->borrowernumber,
273
            $checkout->{itemnumber},
285
            $checkout->{itemnumber},
274
        );
286
        );
275
        $checkout->{can_be_renewed} = $can_be_renewed; # In the future this will be $checkout->can_be_renewed
287
        $checkout->{can_be_renewed} = $can_be_renewed; # In the future this will be $checkout->can_be_renewed
Lines 301-312 if ($borrower) { Link Here
301
        ISSUES => \@checkouts,
313
        ISSUES => \@checkouts,
302
        HOLDS => $holds,
314
        HOLDS => $holds,
303
        newissues => join(',',@newissueslist),
315
        newissues => join(',',@newissueslist),
304
        patronid => $patronid,
305
        patronlogin => $patronlogin,
316
        patronlogin => $patronlogin,
306
        patronpw => $patronpw,
317
        patronpw => $patronpw,
307
        waiting_holds_count => $waiting_holds_count,
318
        waiting_holds_count => $waiting_holds_count,
308
        noitemlinks => 1 ,
319
        noitemlinks => 1 ,
309
        borrowernumber => $borrower->{'borrowernumber'},
320
        borrowernumber => $patron->borrowernumber,
310
        SuspendHoldsOpac => C4::Context->preference('SuspendHoldsOpac'),
321
        SuspendHoldsOpac => C4::Context->preference('SuspendHoldsOpac'),
311
        AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
322
        AutoResumeSuspendedHolds => C4::Context->preference('AutoResumeSuspendedHolds'),
312
        howpriority   => $show_priority,
323
        howpriority   => $show_priority,
Lines 316-349 if ($borrower) { Link Here
316
327
317
    my $patron_messages = Koha::Patron::Messages->search(
328
    my $patron_messages = Koha::Patron::Messages->search(
318
        {
329
        {
319
            borrowernumber => $borrower->{'borrowernumber'},
330
            borrowernumber => $patron->borrowernumber,
320
            message_type => 'B',
331
            message_type => 'B',
321
        }
332
        }
322
    );
333
    );
323
    $template->param(
334
    $template->param(
324
        patron_messages => $patron_messages,
335
        patron_messages => $patron_messages,
325
        opacnote => $borrower->{opacnote},
336
        opacnote => $patron->opacnote,
326
    );
337
    );
327
338
328
    my $inputfocus = ($return_only      == 1) ? 'returnbook' :
329
                     ($confirm_required == 1) ? 'confirm'    : 'barcode' ;
330
    $template->param(
339
    $template->param(
331
        inputfocus => $inputfocus,
332
        nofines => 1,
340
        nofines => 1,
333
341
334
    );
342
    );
335
    if (C4::Context->preference('ShowPatronImageInWebBasedSelfCheck')) {
343
    if (C4::Context->preference('ShowPatronImageInWebBasedSelfCheck')) {
336
        my $patron_image = Koha::Patron::Images->find($borrower->{borrowernumber});
344
        my $patron_image = $patron->image;
337
        $template->param(
345
        $template->param(
338
            display_patron_image => 1,
346
            display_patron_image => 1,
339
            csrf_token           => Koha::Token->new->generate_csrf( { session_id => scalar $query->cookie('CGISESSID') . $borrower->{cardnumber}, id => $borrower->{userid}} ),
347
            csrf_token           => Koha::Token->new->generate_csrf( { session_id => scalar $query->cookie('CGISESSID') . $patron->cardnumber, id => $patron->userid } ),
340
        ) if $patron_image;
348
        ) if $patron_image;
341
    }
349
    }
342
} else {
350
} else {
343
    $template->param(
351
    $template->param(
344
        patronid   => $patronid,
345
        nouser     => $patronid,
352
        nouser     => $patronid,
346
    );
353
    );
347
}
354
}
348
355
356
$cookie = $query->cookie(
357
    -name => 'JWT',
358
    -value => $jwt // '',
359
    -expires => $jwt ? '+1d' : '',
360
    -HttpOnly => 1,
361
    -secure => ( C4::Context->https_enabled() ? 1 : 0 ),
362
);
363
364
$template->param(patronid => $patronid);
365
349
output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 };
366
output_html_with_http_headers $query, $cookie, $template->output, undef, { force_no_caching => 1 };
(-)a/t/Token.t (-2 / +17 lines)
Lines 20-26 Link Here
20
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
21
22
use Modern::Perl;
22
use Modern::Perl;
23
use Test::More tests => 11;
23
use Test::More tests => 12;
24
use Test::Exception;
24
use Test::Exception;
25
use Time::HiRes qw|usleep|;
25
use Time::HiRes qw|usleep|;
26
use C4::Context;
26
use C4::Context;
Lines 101-103 subtest 'Pattern parameter' => sub { Link Here
101
    ok( $id !~ /[^A-Z]/, 'Only uppercase letters' );
101
    ok( $id !~ /[^A-Z]/, 'Only uppercase letters' );
102
    throws_ok( sub { $tokenizer->generate({ pattern => 'abc{d,e}', }) }, 'Koha::Exceptions::Token::BadPattern', 'Exception should be thrown when wrong pattern is used');
102
    throws_ok( sub { $tokenizer->generate({ pattern => 'abc{d,e}', }) }, 'Koha::Exceptions::Token::BadPattern', 'Exception should be thrown when wrong pattern is used');
103
};
103
};
104
- 
104
105
subtest 'JWT' => sub {
106
    plan tests => 3;
107
108
    my $id = 42;
109
    my $jwt = $tokenizer->generate_jwt({ id => $id });
110
111
    my $is_valid = $tokenizer->check_jwt({ id => $id, token => $jwt });
112
    is( $is_valid, 1, 'valid token should return 1' );
113
114
    $is_valid = $tokenizer->check_jwt({ id => 24, token => $jwt });
115
    isnt( $is_valid, 1, 'invalid token should not return 1' );
116
117
    my $retrieved_id = $tokenizer->decode_jwt({ token => $jwt });
118
    is( $retrieved_id, $id, 'id stored in jwt should be correct' );
119
};

Return to bug 29543