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::Checkouts;
28
use Koha::Checkouts;
28
use Koha::Database;
29
use Koha::Database;
29
use Koha::DateUtils;
30
use Koha::DateUtils;
Lines 654-659 sub account_locked { Link Here
654
          and $self->login_attempts >= $FailedLoginAttempts )? 1 : 0;
655
          and $self->login_attempts >= $FailedLoginAttempts )? 1 : 0;
655
}
656
}
656
657
658
=head3 change_password_to
659
660
my $changed = $patron->change_password_to($cleartext_password);
661
662
Changes patron's password to C<$cleartext_password>. This subroutine
663
also makes validations for new password, but does not check the old
664
one.
665
666
=cut
667
668
sub change_password_to {
669
    my ($self, $cleartext_password) = @_;
670
671
    my $min_length = C4::Context->preference("minPasswordLength");
672
    if ($min_length > length($cleartext_password)) {
673
        return (undef, "Password is too short. Minimum length: $min_length.");
674
    }
675
    if ($cleartext_password =~ m|^\s+| or $cleartext_password =~ m|\s+$|) {
676
        return (undef, "Password cannot contain trailing whitespaces.");
677
    }
678
    my $hashed_password = Koha::AuthUtils::hash_password($cleartext_password);
679
    $self->set({ password => $hashed_password })->store;
680
    logaction( "MEMBERS", "CHANGE PASS", $self->borrowernumber, "" ) if C4::Context->preference("BorrowersLog");
681
    return 1;
682
}
683
657
=head3 type
684
=head3 type
658
685
659
=cut
686
=cut
(-)a/Koha/REST/V1/Patron.pm (+22 lines)
Lines 19-24 use Modern::Perl; Link Here
19
19
20
use Mojo::Base 'Mojolicious::Controller';
20
use Mojo::Base 'Mojolicious::Controller';
21
21
22
23
use C4::Auth qw( haspermission checkpw_internal );
22
use Koha::Patrons;
24
use Koha::Patrons;
23
25
24
sub list {
26
sub list {
Lines 44-47 sub get { Link Here
44
    return $c->$cb($patron, 200);
46
    return $c->$cb($patron, 200);
45
}
47
}
46
48
49
sub changepassword {
50
    my ($c, $args, $cb) = @_;
51
52
    my $user = $c->stash('koha.user');
53
    my $patron = Koha::Patrons->find($args->{borrowernumber});
54
    return $c->$cb({ error => "Patron not found." }, 404) unless $patron;
55
56
    my $pw = $args->{'body'};
57
    my $dbh = C4::Context->dbh;
58
    unless (checkpw_internal($dbh, $user->userid, $pw->{'current_password'})) {
59
        return $c->$cb({ error => "Wrong current password." }, 400);
60
    }
61
62
    my ($success, $errmsg) = $user->change_password_to($pw->{'new_password'});
63
    if ($errmsg) {
64
        return $c->$cb({ error => $errmsg }, 400);
65
    }
66
    return $c->$cb({}, 200);
67
}
68
47
1;
69
1;
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 16-20 Link Here
16
  },
16
  },
17
  "/patrons/{borrowernumber}": {
17
  "/patrons/{borrowernumber}": {
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
19
  },
20
  "/patrons/{borrowernumber}/password": {
21
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}~1password"
19
  }
22
  }
20
}
23
}
(-)a/api/v1/swagger/paths/patrons.json (+60 lines)
Lines 69-73 Link Here
69
        }
69
        }
70
      }
70
      }
71
    }
71
    }
72
  },
73
  "/patrons/{borrowernumber}/password": {
74
    "patch": {
75
      "operationId": "changepasswordPatron",
76
      "tags": ["patrons"],
77
      "parameters": [{
78
          "$ref": "../parameters.json#/borrowernumberPathParam"
79
        }, {
80
          "name": "body",
81
          "in": "body",
82
          "description": "A JSON object containing informations about passwords",
83
          "required": true,
84
          "schema": {
85
            "type": "object",
86
            "properties": {
87
              "current_password": {
88
                "description": "Current password",
89
                "type": "string"
90
              },
91
              "new_password": {
92
                "description": "New password",
93
                "type": "string"
94
              }
95
            }
96
          }
97
        }
98
      ],
99
      "produces": [
100
          "application/json"
101
      ],
102
      "responses": {
103
        "200": {
104
          "description": "Password changed"
105
        },
106
        "400": {
107
          "description": "Bad request",
108
          "schema": {
109
            "$ref": "../definitions.json#/error"
110
          }
111
        },
112
        "403": {
113
          "description": "Access forbidden",
114
          "schema": {
115
            "$ref": "../definitions.json#/error"
116
          }
117
        },
118
        "404": {
119
          "description": "Patron not found",
120
          "schema": {
121
            "$ref": "../definitions.json#/error"
122
          }
123
        }
124
      },
125
      "x-koha-authorization": {
126
        "allow-owner": true,
127
        "permissions": {
128
          "borrowers": "1"
129
        }
130
      }
131
    }
72
  }
132
  }
73
}
133
}
(-)a/t/db_dependent/api/v1/patrons.t (-3 / +56 lines)
Lines 17-23 Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::More tests => 21;
20
use Test::More tests => 39;
21
use Test::Mojo;
21
use Test::Mojo;
22
use t::lib::TestBuilder;
22
use t::lib::TestBuilder;
23
use t::lib::Mocks;
23
use t::lib::Mocks;
Lines 25-30 use t::lib::Mocks; Link Here
25
use C4::Auth;
25
use C4::Auth;
26
use C4::Context;
26
use C4::Context;
27
27
28
use Koha::AuthUtils;
28
use Koha::Database;
29
use Koha::Database;
29
use Koha::Patron;
30
use Koha::Patron;
30
31
Lines 42-47 my $t = Test::Mojo->new('Koha::REST::V1'); Link Here
42
43
43
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
44
my $categorycode = $builder->build({ source => 'Category' })->{ categorycode };
44
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
45
my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode };
46
45
my $guarantor = $builder->build({
47
my $guarantor = $builder->build({
46
    source => 'Borrower',
48
    source => 'Borrower',
47
    value => {
49
    value => {
Lines 50-55 my $guarantor = $builder->build({ Link Here
50
        flags        => 0,
52
        flags        => 0,
51
    }
53
    }
52
});
54
});
55
56
my $password = "secret";
53
my $borrower = $builder->build({
57
my $borrower = $builder->build({
54
    source => 'Borrower',
58
    source => 'Borrower',
55
    value => {
59
    value => {
Lines 86-91 $tx->req->cookies({name => 'CGISESSID', value => $session->id}); Link Here
86
$t->request_ok($tx)
90
$t->request_ok($tx)
87
  ->status_is(403);
91
  ->status_is(403);
88
92
93
89
$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . ($borrower->{ borrowernumber }-1));
94
$tx = $t->ua->build_tx(GET => "/api/v1/patrons/" . ($borrower->{ borrowernumber }-1));
90
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
95
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
91
$t->request_ok($tx)
96
$t->request_ok($tx)
Lines 105-116 $t->request_ok($tx) Link Here
105
  ->status_is(200)
110
  ->status_is(200)
106
  ->json_is('/guarantorid', $guarantor->{borrowernumber});
111
  ->json_is('/guarantorid', $guarantor->{borrowernumber});
107
112
113
my $password_obj = {
114
    current_password    => $password,
115
    new_password        => "new password",
116
};
117
118
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/-100/password' => json => $password_obj);
119
$t->request_ok($tx)
120
  ->status_is(401);
121
122
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$borrower->{borrowernumber}.'/password');
123
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
124
$t->request_ok($tx)
125
  ->status_is(400);
126
127
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$guarantor->{borrowernumber}.'/password' => json => $password_obj);
128
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
129
$t->request_ok($tx)
130
  ->status_is(403);
131
108
my $loggedinuser = $builder->build({
132
my $loggedinuser = $builder->build({
109
    source => 'Borrower',
133
    source => 'Borrower',
110
    value => {
134
    value => {
111
        branchcode   => $branchcode,
135
        branchcode   => $branchcode,
112
        categorycode => $categorycode,
136
        categorycode => $categorycode,
113
        flags        => 16 # borrowers flag
137
        flags        => 16, # borrowers flag
138
        password     => Koha::AuthUtils::hash_password($password),
114
    }
139
    }
115
});
140
});
116
141
Lines 135-138 $t->request_ok($tx) Link Here
135
  ->json_is('/surname' => $borrower->{ surname })
160
  ->json_is('/surname' => $borrower->{ surname })
136
  ->json_is('/lost' => 1 );
161
  ->json_is('/lost' => 1 );
137
162
163
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/-100/password' => json => $password_obj);
164
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
165
$t->request_ok($tx)
166
  ->status_is(404);
167
168
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
169
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
170
$t->request_ok($tx)
171
  ->status_is(200);
172
173
ok(C4::Auth::checkpw_hash($password_obj->{'new_password'}, Koha::Patrons->find($loggedinuser->{borrowernumber})->password), "New password in database.");
174
is(C4::Auth::checkpw_hash($password_obj->{'current_password'}, Koha::Patrons->find($loggedinuser->{borrowernumber})->password), "", "Old password is gone.");
175
176
$password_obj->{'current_password'} = $password_obj->{'new_password'};
177
$password_obj->{'new_password'} = "a";
178
t::lib::Mocks::mock_preference("minPasswordLength", 5);
179
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
180
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
181
$t->request_ok($tx)
182
  ->status_is(400)
183
  ->json_like('/error', qr/Password is too short/, "Password too short");
184
185
$password_obj->{'new_password'} = " abcdef ";
186
$tx = $t->ua->build_tx(PATCH => '/api/v1/patrons/'.$loggedinuser->{borrowernumber}.'/password' => json => $password_obj);
187
$tx->req->cookies({name => 'CGISESSID', value => $session->id});
188
$t->request_ok($tx)
189
  ->status_is(400)
190
  ->json_is('/error', "Password cannot contain trailing whitespaces.");
191
138
$schema->storage->txn_rollback;
192
$schema->storage->txn_rollback;
139
- 

Return to bug 17006