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

(-)a/Koha/Patron.pm (+27 lines)
Lines 24-29 use Carp; Link Here
24
24
25
use C4::Context;
25
use C4::Context;
26
use C4::Log;
26
use C4::Log;
27
use Koha::AuthUtils;
27
use Koha::Database;
28
use Koha::Database;
28
use Koha::DateUtils;
29
use Koha::DateUtils;
29
use Koha::Issues;
30
use Koha::Issues;
Lines 208-213 sub update_password { Link Here
208
    return 1;
209
    return 1;
209
}
210
}
210
211
212
=head2 change_password_to
213
214
my $changed = $patron->change_password_to($cleartext_password);
215
216
Changes patron's password to C<$cleartext_password>. This subroutine
217
also makes validations for new password, but does not check the old
218
one.
219
220
=cut
221
222
sub change_password_to {
223
    my ($self, $cleartext_password) = @_;
224
225
    my $min_length = C4::Context->preference("minPasswordLength");
226
    if ($min_length > length($cleartext_password)) {
227
        return (undef, "Password is too short. Minimum length: $min_length.");
228
    }
229
    if ($cleartext_password =~ m|^\s+| or $cleartext_password =~ m|\s+$|) {
230
        return (undef, "Password cannot contain trailing whitespaces.");
231
    }
232
    my $hashed_password = Koha::AuthUtils::hash_password($cleartext_password);
233
    $self->set({ password => $hashed_password })->store;
234
    logaction( "MEMBERS", "CHANGE PASS", $self->borrowernumber, "" ) if C4::Context->preference("BorrowersLog");
235
    return 1;
236
}
237
211
=head3 type
238
=head3 type
212
239
213
=cut
240
=cut
(-)a/Koha/REST/V1/Patron.pm (-1 / +27 lines)
Lines 19-25 use Modern::Perl; Link Here
19
19
20
use Mojo::Base 'Mojolicious::Controller';
20
use Mojo::Base 'Mojolicious::Controller';
21
21
22
use C4::Auth qw( haspermission );
22
use C4::Auth qw( haspermission checkpw_internal );
23
use Koha::Patrons;
23
use Koha::Patrons;
24
24
25
sub list {
25
sub list {
Lines 55-58 sub get { Link Here
55
    return $c->$cb($patron->unblessed, 200);
55
    return $c->$cb($patron->unblessed, 200);
56
}
56
}
57
57
58
sub changepassword {
59
    my ($c, $args, $cb) = @_;
60
61
    my $user = $c->stash('koha.user');
62
    my $patron = Koha::Patrons->find($args->{borrowernumber});
63
    unless ( $user
64
        && ( $user->borrowernumber == $args->{borrowernumber}
65
            || haspermission($user->userid, {borrowers => 1}) ) )
66
    {
67
        return $c->$cb({ error => "You don't have the required permission" }, 403);
68
    }
69
    return $c->$cb({ error => "Patron not found." }, 404) unless $patron;
70
71
    my $pw = $args->{'body'};
72
    my $dbh = C4::Context->dbh;
73
    unless (checkpw_internal($dbh, $user->userid, $pw->{'current_password'})) {
74
        return $c->$cb({ error => "Wrong current password." }, 400);
75
    }
76
77
    my ($success, $errmsg) = $user->change_password_to($pw->{'new_password'});
78
    if ($errmsg) {
79
        return $c->$cb({ error => $errmsg }, 400);
80
    }
81
    return $c->$cb({}, 200);
82
}
83
58
1;
84
1;
(-)a/api/v1/swagger.json (+54 lines)
Lines 74-79 Link Here
74
        }
74
        }
75
      }
75
      }
76
    },
76
    },
77
    "/patrons/{borrowernumber}/password": {
78
      "patch": {
79
        "operationId": "changepasswordPatron",
80
        "tags": ["patrons"],
81
        "parameters": [
82
          { "$ref": "#/parameters/borrowernumberPathParam" },
83
          {
84
            "name": "body",
85
            "in": "body",
86
            "description": "A JSON object containing informations about passwords",
87
            "required": true,
88
            "schema": {
89
              "type": "object",
90
              "properties": {
91
                "current_password": {
92
                  "description": "Current password",
93
                  "type": "string"
94
                },
95
                "new_password": {
96
                  "description": "New password",
97
                  "type": "string"
98
                }
99
              }
100
            }
101
          }
102
        ],
103
        "produces": [
104
          "application/json"
105
        ],
106
        "responses": {
107
          "200": {
108
            "description": "Password changed"
109
          },
110
          "400": {
111
            "description": "Bad request",
112
            "schema": {
113
              "$ref": "#/definitions/error"
114
            }
115
          },
116
          "403": {
117
            "description": "Access forbidden",
118
            "schema": {
119
              "$ref": "#/definitions/error"
120
            }
121
          },
122
          "404": {
123
            "description": "Patron not found",
124
            "schema": {
125
              "$ref": "#/definitions/error"
126
            }
127
          }
128
        }
129
      }
130
    },
77
    "/holds": {
131
    "/holds": {
78
      "get": {
132
      "get": {
79
        "operationId": "listHolds",
133
        "operationId": "listHolds",
(-)a/t/db_dependent/api/v1/patrons.t (-9 / +52 lines)
Lines 17-48 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 10;
20
use Test::More tests => 26;
21
use Test::Mojo;
21
use Test::Mojo;
22
use t::lib::TestBuilder;
22
use t::lib::TestBuilder;
23
use t::lib::Mocks;
23
24
24
use C4::Auth;
25
use C4::Auth;
25
use C4::Context;
26
use C4::Context;
26
27
28
use Koha::AuthUtils;
27
use Koha::Database;
29
use Koha::Database;
28
use Koha::Patron;
30
use Koha::Patron;
29
31
30
my $builder = t::lib::TestBuilder->new();
32
my $builder = t::lib::TestBuilder->new();
31
33
32
my $dbh = C4::Context->dbh;
34
my $schema = Koha::Database->new->schema;
33
$dbh->{AutoCommit} = 0;
35
$schema->storage->txn_begin;
34
$dbh->{RaiseError} = 1;
35
36
36
$ENV{REMOTE_ADDR} = '127.0.0.1';
37
$ENV{REMOTE_ADDR} = '127.0.0.1';
37
my $t = Test::Mojo->new('Koha::REST::V1');
38
my $t = Test::Mojo->new('Koha::REST::V1');
38
39
39
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
40
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
40
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
41
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
42
my $password = "secret";
41
my $borrower = $builder->build({
43
my $borrower = $builder->build({
42
    source => 'Borrower',
44
    source => 'Borrower',
43
    value => {
45
    value => {
44
        branchcode   => $branchcode,
46
        branchcode   => $branchcode,
45
        categorycode => $categorycode
47
        categorycode => $categorycode,
46
    }
48
    }
47
});
49
});
48
50
Lines 52-63 $t->get_ok('/api/v1/patrons') Link Here
52
$t->get_ok("/api/v1/patrons/" . $borrower->{ borrowernumber })
54
$t->get_ok("/api/v1/patrons/" . $borrower->{ borrowernumber })
53
  ->status_is(403);
55
  ->status_is(403);
54
56
57
$t->patch_ok("/api/v1/patrons/-100/password")
58
  ->status_is(400);
59
60
my $password_obj = {
61
    current_password    => $password,
62
    new_password        => "new password",
63
};
64
65
my $tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/-100/password' => json => $password_obj);
66
$t->request_ok($tx)
67
  ->status_is(403);
68
55
my $loggedinuser = $builder->build({
69
my $loggedinuser = $builder->build({
56
    source => 'Borrower',
70
    source => 'Borrower',
57
    value => {
71
    value => {
58
        branchcode   => $branchcode,
72
        branchcode   => $branchcode,
59
        categorycode => $categorycode,
73
        categorycode => $categorycode,
60
        flags        => 16 # borrowers flag
74
        flags        => 16, # borrowers flag
75
        password     => Koha::AuthUtils::hash_password($password),
61
    }
76
    }
62
});
77
});
63
78
Lines 68-74 $session->param('ip', '127.0.0.1'); Link Here
68
$session->param('lasttime', time());
83
$session->param('lasttime', time());
69
$session->flush;
84
$session->flush;
70
85
71
my $tx = $t->ua->build_tx(GET => '/api/v1/patrons');
86
$tx = $t->ua->build_tx(GET => '/api/v1/patrons');
72
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
87
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
73
$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
88
$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
74
$t->request_ok($tx)
89
$t->request_ok($tx)
Lines 81-84 $t->request_ok($tx) Link Here
81
  ->json_is('/borrowernumber' => $borrower->{ borrowernumber })
96
  ->json_is('/borrowernumber' => $borrower->{ borrowernumber })
82
  ->json_is('/surname' => $borrower->{ surname });
97
  ->json_is('/surname' => $borrower->{ surname });
83
98
84
$dbh->rollback;
99
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/-100/password' => json => $password_obj);
100
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
101
$t->request_ok($tx)
102
  ->status_is(404);
103
  
104
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
105
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
106
$t->request_ok($tx)
107
  ->status_is(200);
108
109
ok(C4::Auth::checkpw_hash($password_obj->{'new_password'}, Koha::Patrons->find($loggedinuser->{borrowernumber})->password), "New password in database.");
110
is(C4::Auth::checkpw_hash($password_obj->{'current_password'}, Koha::Patrons->find($loggedinuser->{borrowernumber})->password), "", "Old password is gone.");
111
112
$password_obj->{'current_password'} = $password_obj->{'new_password'};
113
$password_obj->{'new_password'} = "a";
114
t::lib::Mocks::mock_preference("minPasswordLength", 5);
115
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
116
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
117
$t->request_ok($tx)
118
  ->status_is(400)
119
  ->json_like('/error', qr/Password is too short/, "Password too short");
120
121
$password_obj->{'new_password'} = " abcdef ";
122
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
123
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
124
$t->request_ok($tx)
125
  ->status_is(400)
126
  ->json_is('/error', "Password cannot contain trailing whitespaces.");
127
128
$schema->storage->txn_rollback;
85
- 

Return to bug 17006