Lines 20-26
use Modern::Perl;
Link Here
|
20 |
use Test::More tests => 2; |
20 |
use Test::More tests => 2; |
21 |
|
21 |
|
22 |
use Test::Mojo; |
22 |
use Test::Mojo; |
23 |
use Test::Warn; |
|
|
24 |
|
23 |
|
25 |
use t::lib::TestBuilder; |
24 |
use t::lib::TestBuilder; |
26 |
use t::lib::Mocks; |
25 |
use t::lib::Mocks; |
Lines 30-41
use Koha::Patrons;
Link Here
|
30 |
my $schema = Koha::Database->new->schema; |
29 |
my $schema = Koha::Database->new->schema; |
31 |
my $builder = t::lib::TestBuilder->new; |
30 |
my $builder = t::lib::TestBuilder->new; |
32 |
|
31 |
|
33 |
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling |
32 |
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); |
34 |
# this affects the other REST api tests |
|
|
35 |
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); |
36 |
|
33 |
|
37 |
my $remote_address = '127.0.0.1'; |
34 |
my $t = Test::Mojo->new('Koha::REST::V1'); |
38 |
my $t = Test::Mojo->new('Koha::REST::V1'); |
|
|
39 |
|
35 |
|
40 |
subtest 'set() (authorized user tests)' => sub { |
36 |
subtest 'set() (authorized user tests)' => sub { |
41 |
|
37 |
|
Lines 43-122
subtest 'set() (authorized user tests)' => sub {
Link Here
|
43 |
|
39 |
|
44 |
$schema->storage->txn_begin; |
40 |
$schema->storage->txn_begin; |
45 |
|
41 |
|
46 |
my ( $patron, $session ) = create_user_and_session({ authorized => 1 }); |
42 |
my $privileged_patron = $builder->build_object( |
|
|
43 |
{ |
44 |
class => 'Koha::Patrons', |
45 |
value => { flags => 1 } |
46 |
} |
47 |
); |
48 |
my $password = 'thePassword123'; |
49 |
$privileged_patron->set_password( |
50 |
{ password => $password, skip_validation => 1 } ); |
51 |
my $userid = $privileged_patron->userid; |
52 |
|
53 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
47 |
|
54 |
|
48 |
t::lib::Mocks::mock_preference( 'minPasswordLength', 3 ); |
55 |
t::lib::Mocks::mock_preference( 'minPasswordLength', 3 ); |
49 |
t::lib::Mocks::mock_preference( 'RequireStrongPassword', 0 ); |
56 |
t::lib::Mocks::mock_preference( 'RequireStrongPassword', 0 ); |
50 |
|
57 |
|
51 |
my $new_password = 'abc'; |
58 |
my $new_password = 'abc'; |
52 |
|
59 |
|
53 |
my $tx |
60 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
54 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
61 |
. $patron->id |
55 |
. $patron->id |
62 |
. "/password" => json => |
56 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
63 |
{ password => $new_password, password_2 => $new_password } ) |
|
|
64 |
->status_is(200)->json_is(''); |
57 |
|
65 |
|
58 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
66 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
59 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
67 |
. $patron->id |
60 |
$t->request_ok($tx)->status_is(200)->json_is( '' ); |
68 |
. "/password" => json => |
61 |
|
69 |
{ password => $new_password, password_2 => 'cde' } )->status_is(400) |
62 |
$tx |
70 |
->json_is( { error => 'Passwords don\'t match' } ); |
63 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
|
|
64 |
. $patron->id |
65 |
. "/password" => json => { password => $new_password, password_2 => 'cde' } ); |
66 |
|
67 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
68 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
69 |
$t->request_ok($tx)->status_is(400)->json_is({ error => 'Passwords don\'t match' }); |
70 |
|
71 |
|
71 |
t::lib::Mocks::mock_preference( 'minPasswordLength', 5 ); |
72 |
t::lib::Mocks::mock_preference( 'minPasswordLength', 5 ); |
72 |
$tx |
|
|
73 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
74 |
. $patron->id |
75 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
76 |
|
73 |
|
77 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
74 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
78 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
75 |
. $patron->id |
79 |
$t->request_ok($tx)->status_is(400)->json_is({ error => 'Password length (3) is shorter than required (5)' }); |
76 |
. "/password" => json => |
|
|
77 |
{ password => $new_password, password_2 => $new_password } ) |
78 |
->status_is(400) |
79 |
->json_is( |
80 |
{ error => 'Password length (3) is shorter than required (5)' } ); |
80 |
|
81 |
|
81 |
$new_password = 'abc '; |
82 |
$new_password = 'abc '; |
82 |
$tx |
83 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
83 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
84 |
. $patron->id |
84 |
. $patron->id |
85 |
. "/password" => json => |
85 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
86 |
{ password => $new_password, password_2 => $new_password } ) |
86 |
|
87 |
->status_is(400)->json_is( |
87 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
88 |
{ |
88 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
89 |
error => |
89 |
$t->request_ok($tx)->status_is(400)->json_is({ error => '[Password contains leading/trailing whitespace character(s)]' }); |
90 |
'[Password contains leading/trailing whitespace character(s)]' |
|
|
91 |
} |
92 |
); |
90 |
|
93 |
|
91 |
$new_password = 'abcdefg'; |
94 |
$new_password = 'abcdefg'; |
92 |
$tx |
95 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
93 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
96 |
. $patron->id |
94 |
. $patron->id |
97 |
. "/password" => json => |
95 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
98 |
{ password => $new_password, password_2 => $new_password } ) |
96 |
|
99 |
->status_is(200)->json_is(''); |
97 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
100 |
|
98 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
101 |
t::lib::Mocks::mock_preference( 'RequireStrongPassword', 1 ); |
99 |
$t->request_ok($tx)->status_is(200)->json_is(''); |
102 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
100 |
|
103 |
. $patron->id |
101 |
t::lib::Mocks::mock_preference( 'RequireStrongPassword', 1); |
104 |
. "/password" => json => |
102 |
$tx |
105 |
{ password => $new_password, password_2 => $new_password } ) |
103 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
106 |
->status_is(400)->json_is( { error => '[Password is too weak]' } ); |
104 |
. $patron->id |
|
|
105 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
106 |
|
107 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
108 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
109 |
$t->request_ok($tx)->status_is(400)->json_is({ error => '[Password is too weak]' }); |
110 |
|
107 |
|
111 |
$new_password = 'ABcde123%&'; |
108 |
$new_password = 'ABcde123%&'; |
112 |
$tx |
109 |
$t->post_ok( "//$userid:$password@/api/v1/patrons/" |
113 |
= $t->ua->build_tx( POST => "/api/v1/patrons/" |
110 |
. $patron->id |
114 |
. $patron->id |
111 |
. "/password" => json => |
115 |
. "/password" => json => { password => $new_password, password_2 => $new_password } ); |
112 |
{ password => $new_password, password_2 => $new_password } ) |
116 |
|
113 |
->status_is(200)->json_is(''); |
117 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
|
|
118 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
119 |
$t->request_ok($tx)->status_is(200)->json_is(''); |
120 |
|
114 |
|
121 |
$schema->storage->txn_rollback; |
115 |
$schema->storage->txn_rollback; |
122 |
}; |
116 |
}; |
Lines 127-134
subtest 'set_public() (unprivileged user tests)' => sub {
Link Here
|
127 |
|
121 |
|
128 |
$schema->storage->txn_begin; |
122 |
$schema->storage->txn_begin; |
129 |
|
123 |
|
130 |
my ( $patron, $session ) = create_user_and_session({ authorized => 0 }); |
124 |
my $patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
131 |
my $other_patron = $builder->build_object({ class => 'Koha::Patrons' }); |
125 |
my $password = 'thePassword123'; |
|
|
126 |
$patron->set_password( { password => $password, skip_validation => 1 } ); |
127 |
my $userid = $patron->userid; |
128 |
my $other_patron = $builder->build_object( { class => 'Koha::Patrons' } ); |
132 |
|
129 |
|
133 |
# Enable the public API |
130 |
# Enable the public API |
134 |
t::lib::Mocks::mock_preference( 'RESTPublicAPI', 1 ); |
131 |
t::lib::Mocks::mock_preference( 'RESTPublicAPI', 1 ); |
Lines 139-256
subtest 'set_public() (unprivileged user tests)' => sub {
Link Here
|
139 |
|
136 |
|
140 |
my $new_password = 'abc'; |
137 |
my $new_password = 'abc'; |
141 |
|
138 |
|
142 |
my $tx = $t->ua->build_tx( |
139 |
$t->post_ok( |
143 |
POST => "/api/v1/public/patrons/" |
140 |
"//$userid:$password@/api/v1/public/patrons/" |
144 |
. $patron->id |
141 |
. $patron->id |
145 |
. "/password" => json => { |
142 |
. "/password" => json => { |
146 |
password => $new_password, |
143 |
password => $new_password, |
147 |
password_repeated => $new_password, |
144 |
password_repeated => $new_password, |
148 |
old_password => 'blah' |
145 |
old_password => 'blah' |
149 |
} |
146 |
} |
|
|
147 |
)->status_is(403)->json_is( |
148 |
{ |
149 |
error => |
150 |
'Configuration prevents password changes by unprivileged users' |
151 |
} |
150 |
); |
152 |
); |
151 |
|
153 |
|
152 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
|
|
153 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
154 |
$t->request_ok($tx) |
155 |
->status_is(403) |
156 |
->json_is({ |
157 |
error => 'Configuration prevents password changes by unprivileged users' |
158 |
}); |
159 |
|
160 |
t::lib::Mocks::mock_preference( 'OpacPasswordChange', 1 ); |
154 |
t::lib::Mocks::mock_preference( 'OpacPasswordChange', 1 ); |
161 |
|
155 |
|
162 |
my $password = 'holapassword'; |
156 |
$t->post_ok( |
163 |
$patron->set_password({ password => $password }); |
157 |
"//$userid:$password@/api/v1/public/patrons/" |
164 |
$tx = $t->ua->build_tx( |
158 |
. $other_patron->id |
165 |
POST => "/api/v1/public/patrons/" |
159 |
. "/password" => json => { |
166 |
. $other_patron->id |
|
|
167 |
. "/password" => json => { |
168 |
password => $new_password, |
160 |
password => $new_password, |
169 |
password_repeated => $new_password, |
161 |
password_repeated => $new_password, |
170 |
old_password => $password |
162 |
old_password => $password |
171 |
} |
163 |
} |
172 |
); |
164 |
)->status_is(403) |
173 |
|
165 |
->json_is( '/error', |
174 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
166 |
"Authorization failure. Missing required permission(s)." ); |
175 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
167 |
|
176 |
$t->request_ok($tx) |
168 |
$t->post_ok( |
177 |
->status_is(403) |
169 |
"//$userid:$password@/api/v1/public/patrons/" |
178 |
->json_is('/error', "Authorization failure. Missing required permission(s)."); |
170 |
. $patron->id |
179 |
|
171 |
. "/password" => json => { |
180 |
$tx = $t->ua->build_tx( |
|
|
181 |
POST => "/api/v1/public/patrons/" |
182 |
. $patron->id |
183 |
. "/password" => json => { |
184 |
password => $new_password, |
172 |
password => $new_password, |
185 |
password_repeated => 'wrong_password', |
173 |
password_repeated => 'wrong_password', |
186 |
old_password => $password |
174 |
old_password => $password |
187 |
} |
175 |
} |
|
|
176 |
)->status_is(400)->json_is( |
177 |
{ |
178 |
error => "Passwords don't match" |
179 |
} |
188 |
); |
180 |
); |
189 |
|
181 |
|
190 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
182 |
$t->post_ok( |
191 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
183 |
"//$userid:$password@/api/v1/public/patrons/" |
192 |
$t->request_ok($tx) |
184 |
. $patron->id |
193 |
->status_is(400) |
185 |
. "/password" => json => { |
194 |
->json_is({ |
|
|
195 |
error => "Passwords don't match" |
196 |
}); |
197 |
|
198 |
$tx = $t->ua->build_tx( |
199 |
POST => "/api/v1/public/patrons/" |
200 |
. $patron->id |
201 |
. "/password" => json => { |
202 |
password => $new_password, |
186 |
password => $new_password, |
203 |
password_repeated => $new_password, |
187 |
password_repeated => $new_password, |
204 |
old_password => 'badpassword' |
188 |
old_password => 'badpassword' |
205 |
} |
189 |
} |
|
|
190 |
)->status_is(400)->json_is( |
191 |
{ |
192 |
error => "Invalid password" |
193 |
} |
206 |
); |
194 |
); |
207 |
|
195 |
|
208 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
196 |
$t->post_ok( |
209 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
197 |
"//$userid:$password@/api/v1/public/patrons/" |
210 |
$t->request_ok($tx) |
198 |
. $patron->id |
211 |
->status_is(400) |
199 |
. "/password" => json => { |
212 |
->json_is({ |
|
|
213 |
error => "Invalid password" |
214 |
}); |
215 |
|
216 |
$tx = $t->ua->build_tx( |
217 |
POST => "/api/v1/public/patrons/" |
218 |
. $patron->id |
219 |
. "/password" => json => { |
220 |
password => $new_password, |
200 |
password => $new_password, |
221 |
password_repeated => $new_password, |
201 |
password_repeated => $new_password, |
222 |
old_password => $password |
202 |
old_password => $password |
223 |
} |
203 |
} |
224 |
); |
204 |
)->status_is(200)->json_is(''); |
225 |
|
|
|
226 |
$tx->req->cookies( { name => 'CGISESSID', value => $session->id } ); |
227 |
$tx->req->env( { REMOTE_ADDR => '127.0.0.1' } ); |
228 |
$t->request_ok($tx) |
229 |
->status_is(200) |
230 |
->json_is(''); |
231 |
|
205 |
|
232 |
$schema->storage->txn_rollback; |
206 |
$schema->storage->txn_rollback; |
233 |
}; |
207 |
}; |
234 |
|
|
|
235 |
sub create_user_and_session { |
236 |
|
237 |
my ( $args ) = @_; |
238 |
my $flags = ( $args->{authorized} ) ? 16 : 0; |
239 |
|
240 |
my $user = $builder->build_object( |
241 |
{ |
242 |
class => 'Koha::Patrons', |
243 |
value => { flags => $flags } |
244 |
} |
245 |
); |
246 |
|
247 |
# Create a session for the authorized user |
248 |
my $session = C4::Auth::get_session(''); |
249 |
$session->param( 'number', $user->borrowernumber ); |
250 |
$session->param( 'id', $user->userid ); |
251 |
$session->param( 'ip', '127.0.0.1' ); |
252 |
$session->param( 'lasttime', time() ); |
253 |
$session->flush; |
254 |
|
255 |
return ( $user, $session ); |
256 |
} |
257 |
- |