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

(-)a/Koha/Patron/Password/Recovery.pm (+12 lines)
Lines 107-112 sub GetValidLinkInfo { Link Here
107
 C<$update>     Update existing password recovery request
107
 C<$update>     Update existing password recovery request
108
 C<$params>     Additional parameters as follows:
108
 C<$params>     Additional parameters as follows:
109
                  url:       Custom password reset link (third party integrations)
109
                  url:       Custom password reset link (third party integrations)
110
                  skip_mail: If true, will not enqueue the letter, but return it
111
                             instead. Use this flag if you want a third party
112
                             service to handle emailing instead of Koha.
110
=cut
113
=cut
111
114
112
sub SendPasswordRecoveryEmail {
115
sub SendPasswordRecoveryEmail {
Lines 180-185 sub SendPasswordRecoveryEmail { Link Here
180
          { passwordreseturl => $uuidLink, user => $borrower->userid },
183
          { passwordreseturl => $uuidLink, user => $borrower->userid },
181
    );
184
    );
182
185
186
    if ($params->{skip_mail}) {
187
        return {
188
            to_address => $userEmail,
189
            uuid       => $uuid_str,
190
            uuidLink   => $uuidLink,
191
            letter     => $letter->{content},
192
        };
193
    }
194
183
    # define from emails
195
    # define from emails
184
    my $library = $borrower->library;
196
    my $library = $borrower->library;
185
    my $kohaEmail = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');  # send from patron's branch or Koha Admin
197
    my $kohaEmail = $library->branchemail || C4::Context->preference('KohaAdminEmailAddress');  # send from patron's branch or Koha Admin
(-)a/Koha/REST/V1/Patrons/Password.pm (-2 / +29 lines)
Lines 80-92 sub recovery { Link Here
80
        }
80
        }
81
81
82
        my $link = $body->{'custom_link'};
82
        my $link = $body->{'custom_link'};
83
        my $skip_mail =  0;
84
        if ($body->{'skip_mail'}) {
85
            Koha::Exceptions::Authentication::Required->throw(
86
                error => 'Authentication required while skip_mail parameter is on'
87
            ) unless ref($c->stash('koha.user')) eq 'Koha::Patron';
88
            if (my $user = $c->stash('koha.user')) {
89
                Koha::Exceptions::Authorization::Unauthorized->throw(
90
                    error => 'Permission get_password_reset_uuid required while '
91
                            .'skip_mail parameter is on'
92
                ) unless C4::Auth::haspermission($user->userid, {
93
                    borrowers => 'get_password_reset_uuid'
94
                });
95
                $skip_mail = 1;
96
            }
97
        }
83
98
84
        my $resend = ValidateBorrowernumber($patron->borrowernumber);
99
        my $resend = ValidateBorrowernumber($patron->borrowernumber);
85
100
86
        SendPasswordRecoveryEmail($patron, $patron->email, $resend, {
101
        my $ret = SendPasswordRecoveryEmail($patron, $patron->email, $resend, {
87
            url => $link
102
            url => $link,
103
            skip_mail => $skip_mail,
88
        });
104
        });
89
105
106
        if (ref($ret) eq 'HASH') {
107
            $ret->{status} = 2;
108
            return $c->render(status => 201, openapi => $ret);
109
        }
110
90
        return $c->render(status => 201, openapi => {
111
        return $c->render(status => 201, openapi => {
91
            status => 1,
112
            status => 1,
92
            to_address => $patron->email
113
            to_address => $patron->email
Lines 103-108 sub recovery { Link Here
103
        elsif ($_->isa('Koha::Exceptions::WrongParameter')) {
124
        elsif ($_->isa('Koha::Exceptions::WrongParameter')) {
104
            return $c->render(status => 400, openapi => { error => $_->error });
125
            return $c->render(status => 400, openapi => { error => $_->error });
105
        }
126
        }
127
        elsif ($_->isa('Koha::Exceptions::Authentication::Required')) {
128
            return $c->render(status => 401, openapi => { error => $_->error });
129
        }
130
        elsif ($_->isa('Koha::Exceptions::Authorization::Unauthorized')) {
131
            return $c->render(status => 403, openapi => { error => $_->error });
132
        }
106
        elsif ($_->isa('Koha::Exceptions::Patron::NotFound')) {
133
        elsif ($_->isa('Koha::Exceptions::Patron::NotFound')) {
107
            return $c->render(status => 404, openapi => { error => $_->error });
134
            return $c->render(status => 404, openapi => { error => $_->error });
108
        }
135
        }
(-)a/api/v1/swagger/definitions/patron_password_recovery.json (-1 / +13 lines)
Lines 1-12 Link Here
1
{
1
{
2
  "type": "object",
2
  "type": "object",
3
  "properties": {
3
  "properties": {
4
    "letter": {
5
      "description": "Content of the password recovery letter (returned only if Koha skips letter enqueueing)",
6
      "type": "string"
7
    },
4
    "to_address": {
8
    "to_address": {
5
      "description": "Patron email address",
9
      "description": "Patron email address",
6
      "type": ["string", "null"]
10
      "type": ["string", "null"]
7
    },
11
    },
12
    "uuid": {
13
      "description": "Uuid of password recovery request (the token that patron can reset their password with) (returned only if Koha skips letter enqueueing)",
14
      "type": "string"
15
    },
16
    "uuidLink": {
17
      "description": "The URL that password reset can be completed at (returned only if Koha skips letter enqueueing)",
18
      "type": "string"
19
    },
8
    "status": {
20
    "status": {
9
      "description": "Status code. 1 = email has been enqueued",
21
      "description": "Status code. 1 = email has been enqueued in Koha, 2 = recovery request stored and uuid returned (but not emailed - API user should decide how to deliver the recovery request to patron)",
10
      "type": "integer"
22
      "type": "integer"
11
    }
23
    }
12
  }
24
  }
(-)a/api/v1/swagger/paths/patrons.json (+8 lines)
Lines 733-738 Link Here
733
            "complete_url": {
733
            "complete_url": {
734
              "description": "The location where patron will complete their password recovery. This location will be added into the password recovery email. To avoid malicious use, whitelist certain hosts in OpacResetPasswordHostWhitelist system preference and forbid all other custom links. Use {uuid} for adding the password recovery uuid into your custom link. It will be replaced by the actual uuid in the email.",
734
              "description": "The location where patron will complete their password recovery. This location will be added into the password recovery email. To avoid malicious use, whitelist certain hosts in OpacResetPasswordHostWhitelist system preference and forbid all other custom links. Use {uuid} for adding the password recovery uuid into your custom link. It will be replaced by the actual uuid in the email.",
735
              "type": "string"
735
              "type": "string"
736
            },
737
            "skip_email": {
738
              "description": "If true, Koha will not enqueue password recovery email. Makes this endpoint return the letter, uuid and patron email. Requires permission borrowers.get_password_recovery_uuid. Useful for third party service integrations that wish to handle emailing instead of Koha.",
739
              "type": "boolean"
736
            }
740
            }
737
          },
741
          },
738
          "required": ["email"]
742
          "required": ["email"]
Lines 748-753 Link Here
748
          "description": "Bad parameter(s)",
752
          "description": "Bad parameter(s)",
749
          "schema": { "$ref": "../definitions.json#/error" }
753
          "schema": { "$ref": "../definitions.json#/error" }
750
        },
754
        },
755
        "401": {
756
          "description": "Authentication required",
757
          "schema": { "$ref": "../definitions.json#/error" }
758
        },
751
        "403": {
759
        "403": {
752
          "description": "Password recovery disabled, no access to this endpoint",
760
          "description": "Password recovery disabled, no access to this endpoint",
753
          "schema": {
761
          "schema": {
(-)a/installer/data/mysql/atomicupdate/Bug-19133-2_Add_permission_get_password_reset_uuid.perl (+8 lines)
Line 0 Link Here
1
$DBversion = 'XXX';  # will be replaced by the RM
2
if( CheckVersion( $DBversion ) ) {
3
4
    $dbh->do( "INSERT INTO permissions (module_bit, code, description) VALUES ( 4, 'get_password_reset_uuid', 'Allow the user to get password reset uuid when recovering passwords. Useful for third party service integrations that wish to do something with the uuid, such as handle emails themselves instead of Koha.')" );
5
6
    SetVersion( $DBversion );
7
    print "Upgrade to $DBversion done (Bug 19133 - Add permission get_password_reset_uuid.)\n";
8
}
(-)a/installer/data/mysql/mandatory/userpermissions.sql (+1 lines)
Lines 38-43 INSERT INTO permissions (module_bit, code, description) VALUES Link Here
38
   ( 3, 'manage_background_jobs', 'Manage background jobs'),
38
   ( 3, 'manage_background_jobs', 'Manage background jobs'),
39
   ( 4, 'edit_borrowers', 'Add, modify and view patron information'),
39
   ( 4, 'edit_borrowers', 'Add, modify and view patron information'),
40
   ( 4, 'view_borrower_infos_from_any_libraries', 'View patron infos from any libraries'),
40
   ( 4, 'view_borrower_infos_from_any_libraries', 'View patron infos from any libraries'),
41
   ( 4, 'get_password_reset_uuid', 'Allow the user to get password reset uuid when recovering passwords. Useful for third party service integrations that wish to do something with the uuid, such as handle emails themselves instead of Koha.'),
41
   ( 6, 'place_holds', 'Place holds for patrons'),
42
   ( 6, 'place_holds', 'Place holds for patrons'),
42
   ( 6, 'modify_holds_priority', 'Modify holds priority'),
43
   ( 6, 'modify_holds_priority', 'Modify holds priority'),
43
   ( 9, 'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'),
44
   ( 9, 'edit_catalogue', 'Edit catalog (Modify bibliographic/holdings data)'),
(-)a/t/db_dependent/api/v1/patrons_password_recovery.t (-2 / +73 lines)
Lines 43-49 my $remote_address = '127.0.0.1'; Link Here
43
my $t              = Test::Mojo->new('Koha::REST::V1');
43
my $t              = Test::Mojo->new('Koha::REST::V1');
44
44
45
subtest 'recovery() tests' => sub {
45
subtest 'recovery() tests' => sub {
46
    plan tests => 31;
46
    plan tests => 32;
47
47
48
    $schema->storage->txn_begin;
48
    $schema->storage->txn_begin;
49
49
Lines 245-250 subtest 'recovery() tests' => sub { Link Here
245
        );
245
        );
246
    };
246
    };
247
247
248
    subtest 'skip letter enqueueing' => sub {
249
        plan tests => 10;
250
251
        t::lib::Mocks::mock_preference(
252
            'OpacResetPasswordHostWhitelist', 'anotherallowed'
253
        );
254
        my ($service_borrowernumber, $service_session) = create_user_and_session({
255
            authorized => 1
256
        });
257
258
        $tx = $t->ua->build_tx(POST => $url => json => {
259
            email      => $patron->email,
260
            userid     => $patron->userid,
261
            custom_link => 'https://anotherallowed/reset-password.pl',
262
            skip_mail => Mojo::JSON->true
263
        });
264
        $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
265
        $t->request_ok($tx)
266
          ->status_is(401);
267
268
        $tx = $t->ua->build_tx(POST => $url => json => {
269
            email      => $patron->email,
270
            userid     => $patron->userid,
271
            custom_link => 'https://anotherallowed/reset-password.pl',
272
            skip_mail => Mojo::JSON->true
273
        });
274
        $tx->req->cookies({name => 'CGISESSID', value => $session->id});
275
        $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
276
        $t->request_ok($tx)
277
          ->status_is(403);
278
279
        Koha::Notice::Messages->search({
280
            borrowernumber => $patron->borrowernumber,
281
            letter_code => 'PASSWORD_RESET',
282
            message_transport_type => 'email'
283
        })->delete;
284
285
        $tx = $t->ua->build_tx(POST => $url => json => {
286
            email      => $patron->email,
287
            userid     => $patron->userid,
288
            custom_link => 'https://notallowed/reset-password.pl',
289
            skip_mail => Mojo::JSON->true,
290
        });
291
        $tx->req->cookies({name => 'CGISESSID', value => $service_session->id});
292
        $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
293
        $t->request_ok($tx)
294
          ->status_is(400);
295
296
        $tx = $t->ua->build_tx(POST => $url => json => {
297
            email      => $patron->email,
298
            userid     => $patron->userid,
299
            custom_link => 'https://anotherallowed/reset-password.pl',
300
            skip_mail => Mojo::JSON->true,
301
        });
302
        $tx->req->cookies({name => 'CGISESSID', value => $service_session->id});
303
        $tx->req->env({REMOTE_ADDR => '127.0.0.1'});
304
        $t->request_ok($tx)
305
          ->status_is(201);
306
        my $uuid = $rs->search({
307
            borrowernumber => $patron->borrowernumber
308
        }, {
309
            order_by => { '-desc' => 'valid_until' }
310
        })->next->uuid;
311
        $t->json_is('/uuid', $uuid);
312
313
       is(Koha::Notice::Messages->search({
314
            borrowernumber => $patron->borrowernumber,
315
            letter_code => 'PASSWORD_RESET',
316
            message_transport_type => 'email'
317
        })->count, 0, 'Email not enqueued');
318
    };
319
248
    $schema->storage->txn_rollback;
320
    $schema->storage->txn_rollback;
249
};
321
};
250
322
251
- 

Return to bug 19133