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

(-)a/Koha/REST/V1/Config/SFTP/Servers.pm (+184 lines)
Line 0 Link Here
1
package Koha::REST::V1::Config::SFTP::Servers;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::SFTP::Servers;
23
24
use Try::Tiny qw( catch try );
25
26
=head1 API
27
28
=head2 Methods
29
30
=head3 list
31
32
Controller method that handles listing Koha::SFTP::Server objects
33
34
=cut
35
36
sub list {
37
    my $c = shift->openapi->valid_input or return;
38
39
    return try {
40
        my $sftp_servers_set = Koha::SFTP::Servers->new;
41
        my $sftp_servers     = $c->objects->search($sftp_servers_set);
42
        return $c->render(
43
            status  => 200,
44
            openapi => $sftp_servers
45
        );
46
    } catch {
47
        $c->unhandled_exception($_);
48
    };
49
}
50
51
=head3 get
52
53
Controller method that handles retrieving a single Koha::SFTP::Server object
54
55
=cut
56
57
sub get {
58
    my $c = shift->openapi->valid_input or return;
59
60
    return try {
61
        my $sftp_server = Koha::SFTP::Servers->find( $c->param('sftp_server_id') );
62
63
        return $c->render_resource_not_found("FTP/SFTP server")
64
            unless $sftp_server;
65
66
        return $c->render(
67
            status  => 200,
68
            openapi => $c->objects->to_api($sftp_server),
69
        );
70
    } catch {
71
        $c->unhandled_exception($_);
72
    }
73
}
74
75
=head3 add
76
77
Controller method that handles adding a new Koha::SFTP::Server object
78
79
=cut
80
81
sub add {
82
    my $c = shift->openapi->valid_input or return;
83
84
    return try {
85
86
        my $sftp_server = Koha::SFTP::Server->new_from_api( $c->req->json );
87
        $sftp_server->store->discard_changes;
88
89
        $c->res->headers->location( $c->req->url->to_string . '/' . $sftp_server->id );
90
91
        return $c->render(
92
            status  => 201,
93
            openapi => $c->objects->to_api($sftp_server),
94
        );
95
    } catch {
96
        if ( blessed $_ and $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
97
            return $c->render(
98
                status  => 409,
99
                openapi => {
100
                    error    => $_->error,
101
                    conflict => $_->duplicate_id
102
                }
103
            );
104
        }
105
106
        $c->unhandled_exception($_);
107
    };
108
}
109
110
=head3 update
111
112
Controller method that handles updating a Koha::SFTP::Server object
113
114
=cut
115
116
sub update {
117
    my $c = shift->openapi->valid_input or return;
118
119
    my $sftp_server = Koha::SFTP::Servers->find( $c->param('sftp_server_id') );
120
121
    return $c->render_resource_not_found("FTP/SFTP server")
122
        unless $sftp_server;
123
124
    return try {
125
        $sftp_server->set_from_api( $c->req->json );
126
        $sftp_server->store->discard_changes;
127
128
        return $c->render(
129
            status  => 200,
130
            openapi => $c->objects->to_api($sftp_server),
131
        );
132
    } catch {
133
        if ( blessed $_ and $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
134
            return $c->render(
135
                status  => 409,
136
                openapi => {
137
                    error    => $_->error,
138
                    conflict => $_->duplicate_id
139
                }
140
            );
141
        }
142
143
        $c->unhandled_exception($_);
144
    };
145
}
146
147
=head3 delete
148
149
Controller method that handles deleting a Koha::SFTP::Server object
150
151
=cut
152
153
sub delete {
154
    my $c = shift->openapi->valid_input or return;
155
156
    my $sftp_server = Koha::SFTP::Servers->find( $c->param('sftp_server_id') );
157
158
    return $c->render_resource_not_found("FTP/SFTP server")
159
        unless $sftp_server;
160
161
    return try {
162
        $sftp_server->delete;
163
        return $c->render_resource_deleted;
164
    } catch {
165
        $c->unhandled_exception($_);
166
    };
167
}
168
169
=head3 test
170
171
Controller method that invokes Koha::SFTP::Server->test_conn
172
173
=cut
174
175
sub test {
176
    my $c = shift->openapi->valid_input or return;
177
178
    my $sftp_server = Koha::SFTP::Servers->find( $c->param('sftp_server_id') );
179
180
    return $c->render_resource_not_found("FTP/SFTP server")
181
        unless $sftp_server;
182
}
183
184
1;
(-)a/Koha/REST/V1/SFTPServer.pm (+55 lines)
Line 0 Link Here
1
package Koha::REST::V1::SFTPServer;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::SFTP::Servers;
23
24
use Try::Tiny qw( catch try );
25
26
=head1 API
27
28
=head2 Methods
29
30
=head3 test
31
32
Controller method that invokes Koha::SFTP::Server->test_conn
33
34
=cut
35
36
sub test {
37
    my $c = shift->openapi->valid_input or return;
38
39
    return try {
40
        my $sftp_server = Koha::SFTP::Servers->find( $c->param('sftp_server_id') );
41
        return $c->render_resource_not_found("FTP/SFTP Server")
42
            unless $sftp_server;
43
44
        my $sftp_server_test_conn = $sftp_server->test_conn;
45
46
        return $c->render(
47
            status  => 200,
48
            openapi => $sftp_server_test_conn,
49
        );
50
    } catch {
51
        $c->unhandled_exception($_);
52
    };
53
}
54
55
1;
(-)a/Koha/SFTP/Server.pm (+432 lines)
Line 0 Link Here
1
package Koha::SFTP::Server;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Koha::Database;
21
use Koha::Exceptions::Object;
22
use Koha::Encryption;
23
use Koha::SFTP::Servers;
24
25
use Try::Tiny qw( catch try );
26
use Mojo::JSON;
27
use Net::SFTP::Foreign;
28
use Net::FTP;
29
30
use base qw(Koha::Object);
31
32
=head1 NAME
33
34
Koha::SFTP::Server - Koha SFTP Server Object class
35
36
=head1 API
37
38
=head2 Class methods
39
40
=head3 store
41
42
    $server->store;
43
44
Overloaded store method.
45
46
=cut
47
48
sub store {
49
    my ($self) = @_;
50
51
    $self->download_directory( $self->download_directory . '/' )
52
        if ( ( substr( $self->download_directory, -1 ) ne '/' ) && ( $self->download_directory ne '' ) );
53
    $self->upload_directory( $self->upload_directory . '/' )
54
        if ( ( substr( $self->upload_directory, -1 ) ne '/' ) && ( $self->upload_directory ne '' ) );
55
56
    return $self->SUPER::store;
57
}
58
59
=head3 to_api
60
61
    my $json = $sftp_server->to_api;
62
63
Overloaded method that returns a JSON representation of the Koha::SFTP::Server object,
64
suitable for API output.
65
66
=cut
67
68
sub to_api {
69
    my ( $self, $params ) = @_;
70
71
    my $json_sftp = $self->SUPER::to_api($params);
72
    return unless $json_sftp;
73
    delete $json_sftp->{password};
74
    delete $json_sftp->{key_file};
75
76
    return $json_sftp;
77
}
78
79
=head3 to_api_mapping
80
81
This method returns the mapping for representing a Koha::SFTP::Server object
82
on the API.
83
84
=cut
85
86
sub to_api_mapping {
87
    return { id => 'sftp_server_id' };
88
}
89
90
=head3 plain_text_password
91
92
    $server->plain_text_password;
93
Fetches the plaintext password, from the object
94
95
=cut
96
97
sub plain_text_password {
98
    my ($self) = @_;
99
100
    return Koha::Encryption->new->decrypt_hex( $self->password )
101
        if $self->password;
102
}
103
104
=head3 plain_text_key
105
106
    $server->plain_text_key;
107
Fetches the plaintext key file, from the object
108
109
=cut
110
111
sub plain_text_key {
112
    my ($self) = @_;
113
114
    return Koha::Encryption->new->decrypt_hex( $self->key_file ) . "\n"
115
        if $self->key_file;
116
}
117
118
=head3 write_key_file
119
120
    $server->write_key_file;
121
Writes the keyfile from the db into a file
122
123
=cut
124
125
sub write_key_file {
126
    my ($self)      = @_;
127
    my $upload_path = C4::Context->config('upload_path') or return;
128
    my $key_path    = $upload_path . '/ssh_keys';
129
    my $key_file    = $key_path . '/id_ssh_' . $self->id;
130
131
    mkdir $key_path if ( !-d $key_path );
132
133
    unlink $key_file if ( -f $key_file );
134
    my $fh;
135
    $fh = IO::File->new( $key_file, 'w' ) or return;
136
    chmod 0600, $key_file if ( -f $key_file );
137
138
    print $fh $self->plain_text_key;
139
140
    undef $fh;
141
142
    return 1;
143
}
144
145
=head3 locate_key_file
146
147
    $server->locate_key_file;
148
Returns the keyfile's expected path
149
150
=cut
151
152
sub locate_key_file {
153
    my ($self) = @_;
154
    my $upload_path = C4::Context->config('upload_path');
155
    return if not defined $upload_path;
156
157
    my $keyf_path = $upload_path . '/ssh_keys/id_ssh_' . $self->id;
158
159
    return ( -f $keyf_path ) ? $keyf_path : undef;
160
}
161
162
=head3 update_password
163
    $server->update_password;
164
Update the password of this FTP/SFTP server
165
166
=cut
167
168
sub update_password {
169
    my ( $self, $value ) = @_;
170
171
    return
172
        if not defined $value;
173
174
    $self->password( Koha::Encryption->new->encrypt_hex( $value ) );
175
176
    return $self->SUPER::store;
177
}
178
179
=head3 update_key_file
180
    $server->update_password;
181
Update the password of this FTP/SFTP server
182
183
=cut
184
185
sub update_key_file {
186
    my ( $self, $value ) = @_;
187
188
    return
189
        if not defined $value;
190
191
    $self->key_file( Koha::Encryption->new->encrypt_hex( _dos2unix( $value ) ) );
192
193
    return $self->SUPER::store;
194
}
195
196
=head3 update_status
197
198
    $server->update_status;
199
Update the status of this FTP/SFTP server
200
201
=cut
202
203
sub update_status {
204
    my ( $self, $value ) = @_;
205
206
    return
207
        if not defined $value;
208
209
    return $self->set( { status => $value } )->store;
210
}
211
212
=head3 test_conn
213
214
    $server->test_conn;
215
Tests a connection to a given sftp server
216
217
=cut
218
219
sub test_conn {
220
    my ($self) = @_;
221
    my $default_result = {
222
        passed => Mojo::JSON->false,
223
        err    => undef,
224
        msg    => undef,
225
    };
226
    my $sftp_test_results;
227
228
    if ( $self->transport eq 'sftp' ) {
229
        $sftp_test_results->{'1_sftp_conn'} = {%$default_result};
230
        my $sftp = Net::SFTP::Foreign->new(
231
            host     => $self->host,
232
            user     => $self->user_name,
233
            password => $self->plain_text_password,
234
            key_path => $self->locate_key_file,
235
            port     => $self->port,
236
            timeout  => 10,
237
            more     => [
238
                qw(-vv),
239
                qw(-o StrictHostKeyChecking=no),
240
            ],
241
        );
242
        unless ( $sftp->error ) {
243
            $sftp_test_results->{'1_sftp_conn'}->{'passed'} = Mojo::JSON->true;
244
            $sftp_test_results->{'1_sftp_conn'}->{'msg'}    = $sftp->status;
245
        } else {
246
            $sftp_test_results->{'1_sftp_conn'}->{'err'} = $sftp->error;
247
        }
248
249
        unless ( $sftp->error ) {
250
            unless ( $self->download_directory eq '' ) {
251
                $sftp_test_results->{'2a_sftp_cwd_dl'} = {%$default_result};
252
                $sftp->setcwd( $self->download_directory );
253
                unless ( $sftp->error ) {
254
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'passed'} = Mojo::JSON->true;
255
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'msg'}    = $sftp->status;
256
                } else {
257
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'err'} = $sftp->error;
258
                }
259
            }
260
261
            $sftp_test_results->{'2b_sftp_ls_dl'} = {%$default_result};
262
            $sftp->ls();
263
            unless ( $sftp->error ) {
264
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'passed'} = Mojo::JSON->true;
265
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'msg'}    = $sftp->status;
266
            } else {
267
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'err'} = $sftp->error;
268
            }
269
270
            unless ( $self->upload_directory eq '' ) {
271
                $sftp_test_results->{'2c_sftp_cwd_ul'} = {%$default_result};
272
                $sftp->setcwd( $self->upload_directory );
273
                unless ( $sftp->error ) {
274
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'passed'} = Mojo::JSON->true;
275
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'msg'}    = $sftp->status;
276
                } else {
277
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'err'} = $sftp->error;
278
                }
279
            }
280
281
            $sftp_test_results->{'2d_sftp_ls_ul'} = {%$default_result};
282
            $sftp->ls( $self->upload_directory );
283
            unless ( $sftp->error ) {
284
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'passed'} = Mojo::JSON->true;
285
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'msg'}    = $sftp->status;
286
            } else {
287
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'err'} = $sftp->error;
288
            }
289
290
            $sftp_test_results->{'3_sftp_write'} = {%$default_result};
291
            open my $fh, '<', \"Hello, world!\n";
292
            close $fh if ( $sftp->put( $fh, $self->upload_directory . '.koha_test_file' ) );
293
            unless ( $sftp->error ) {
294
                $sftp_test_results->{'3_sftp_write'}->{'passed'} = Mojo::JSON->true;
295
                $sftp_test_results->{'3_sftp_write'}->{'msg'}    = $sftp->status;
296
            } else {
297
                $sftp_test_results->{'3_sftp_write'}->{'err'} = $sftp->error;
298
            }
299
300
            $sftp_test_results->{'4_sftp_del'} = {%$default_result};
301
            $sftp->remove( $self->upload_directory . '.koha_test_file' );
302
            unless ( $sftp->error ) {
303
                $sftp_test_results->{'4_sftp_del'}->{'passed'} = Mojo::JSON->true;
304
                $sftp_test_results->{'4_sftp_del'}->{'msg'}    = $sftp->status;
305
            } else {
306
                $sftp_test_results->{'4_sftp_del'}->{'err'} = $sftp->error;
307
            }
308
        }
309
    } elsif ( $self->transport eq 'ftp' ) {
310
        $sftp_test_results->{'1_ftp_conn'} = {%$default_result};
311
        my $ftp = Net::FTP->new(
312
            $self->host,
313
            Port    => $self->port,
314
            Timeout => 10,
315
            Passive => ( scalar $self->passiv ) ? 1 : 0,
316
        );
317
        if ($ftp) {
318
            $sftp_test_results->{'1_ftp_conn'}->{'passed'} = Mojo::JSON->true;
319
            $sftp_test_results->{'1_ftp_conn'}->{'msg'}    = $ftp->message;
320
        } else {
321
            $sftp_test_results->{'1_ftp_conn'}->{'err'} = 'cannot connect to ' . $self->host . ': ' . $@;
322
        }
323
324
        if ($ftp) {
325
            $sftp_test_results->{'2_ftp_login'} = {%$default_result};
326
            my $login = $ftp->login(
327
                $self->user_name,
328
                $self->plain_text_password,
329
            );
330
            if ($login) {
331
                $sftp_test_results->{'2_ftp_login'}->{'passed'} = Mojo::JSON->true;
332
                $sftp_test_results->{'2_ftp_login'}->{'msg'}    = $ftp->message;
333
            } else {
334
                $sftp_test_results->{'2_ftp_login'}->{'err'} = $ftp->message;
335
            }
336
337
            unless ( $self->download_directory eq '' ) {
338
                $sftp_test_results->{'3a_ftp_cwd_dl'} = {%$default_result};
339
                my $cwd_dl = $ftp->cwd( $self->download_directory );
340
                if ($cwd_dl) {
341
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'passed'} = Mojo::JSON->true;
342
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'msg'}    = $ftp->message;
343
                } else {
344
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'err'} = $ftp->message;
345
                }
346
            }
347
348
            $sftp_test_results->{'3b_ftp_ls_dl'} = {%$default_result};
349
            my $ls_dl = $ftp->ls();
350
            if ($ls_dl) {
351
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'passed'} = Mojo::JSON->true;
352
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'msg'}    = $ftp->message;
353
            } else {
354
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'err'} = $ftp->message;
355
            }
356
357
            unless ( $self->upload_directory eq '' ) {
358
                $sftp_test_results->{'3c_ftp_cwd_ul'} = {%$default_result};
359
                my $cwd_ul = $ftp->cwd( $self->upload_directory );
360
                if ($cwd_ul) {
361
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'passed'} = Mojo::JSON->true;
362
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'msg'}    = $ftp->message;
363
                } else {
364
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'err'} = $ftp->message;
365
                }
366
            }
367
368
            $sftp_test_results->{'3d_ftp_ls_ul'} = {%$default_result};
369
            my $ls_ul = $ftp->ls();
370
            if ($ls_ul) {
371
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'passed'} = Mojo::JSON->true;
372
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'msg'}    = $ftp->message;
373
            } else {
374
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'err'} = $ftp->message;
375
            }
376
377
            $sftp_test_results->{'4_ftp_write'} = {%$default_result};
378
            open my $fh, '<', \"Hello, world!\n";
379
            close $fh if ( my $put = $ftp->put( $fh, $self->upload_directory . '.koha_test_file' ) );
380
            if ($put) {
381
                $sftp_test_results->{'4_ftp_write'}->{'passed'} = Mojo::JSON->true;
382
                $sftp_test_results->{'4_ftp_write'}->{'msg'}    = $ftp->message;
383
            } else {
384
                $sftp_test_results->{'4_ftp_write'}->{'err'} = $ftp->message;
385
            }
386
387
            $sftp_test_results->{'5_ftp_del'} = {%$default_result};
388
            my $delete = $ftp->delete( $self->upload_directory . '.koha_test_file' );
389
            if ($delete) {
390
                $sftp_test_results->{'5_ftp_del'}->{'passed'} = Mojo::JSON->true;
391
                $sftp_test_results->{'5_ftp_del'}->{'msg'}    = $ftp->message;
392
            } else {
393
                $sftp_test_results->{'5_ftp_del'}->{'err'} = $ftp->message;
394
            }
395
        }
396
    }
397
398
    $self->update_status('tests_ok');
399
    foreach my $val ( values %$sftp_test_results ) {
400
        if ( defined $val->{'err'} ) {
401
            $self->update_status('tests_failed');
402
        }
403
    }
404
405
    return ( 1, $sftp_test_results );
406
}
407
408
=head2 Internal methods
409
410
=head3 _dos2unix
411
412
Return a CR-free string from an input
413
414
=cut
415
416
sub _dos2unix {
417
    my $dosStr = shift;
418
419
    return $dosStr =~ s/\015\012/\012/gr;
420
}
421
422
=head3 _type
423
424
Return type of Object relating to Schema ResultSet
425
426
=cut
427
428
sub _type {
429
    return 'SftpServer';
430
}
431
432
1;
(-)a/Koha/SFTP/Servers.pm (+55 lines)
Line 0 Link Here
1
package Koha::SFTP::Servers;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Koha::Database;
21
use Koha::Exceptions;
22
23
use Koha::SFTP::Server;
24
25
use base qw(Koha::Objects);
26
27
=head1 NAME
28
29
Koha::SFTP::Servers - Koha SFTP Server Object set class
30
31
=head1 API
32
33
=head2 Internal methods
34
35
=head3 _type
36
37
Return type of object, relating to Schema ResultSet
38
39
=cut
40
41
sub _type {
42
    return 'SftpServer';
43
}
44
45
=head3 object_class
46
47
Return object class
48
49
=cut
50
51
sub object_class {
52
    return 'Koha::SFTP::Server';
53
}
54
55
1;
(-)a/Koha/Schema/Result/SftpServer.pm (+186 lines)
Line 0 Link Here
1
use utf8;
2
package Koha::Schema::Result::SftpServer;
3
4
# Created by DBIx::Class::Schema::Loader
5
# DO NOT MODIFY THE FIRST PART OF THIS FILE
6
7
=head1 NAME
8
9
Koha::Schema::Result::SftpServer
10
11
=cut
12
13
use strict;
14
use warnings;
15
16
use base 'DBIx::Class::Core';
17
18
=head1 TABLE: C<sftp_servers>
19
20
=cut
21
22
__PACKAGE__->table("sftp_servers");
23
24
=head1 ACCESSORS
25
26
=head2 id
27
28
  data_type: 'integer'
29
  is_auto_increment: 1
30
  is_nullable: 0
31
32
=head2 name
33
34
  data_type: 'varchar'
35
  is_nullable: 0
36
  size: 80
37
38
=head2 host
39
40
  data_type: 'varchar'
41
  default_value: 'localhost'
42
  is_nullable: 0
43
  size: 80
44
45
=head2 port
46
47
  data_type: 'integer'
48
  default_value: 22
49
  is_nullable: 0
50
51
=head2 transport
52
53
  data_type: 'enum'
54
  default_value: 'sftp'
55
  extra: {list => ["ftp","sftp"]}
56
  is_nullable: 0
57
58
=head2 passiv
59
60
  data_type: 'tinyint'
61
  default_value: 1
62
  is_nullable: 0
63
64
=head2 user_name
65
66
  data_type: 'varchar'
67
  is_nullable: 1
68
  size: 80
69
70
=head2 password
71
72
  data_type: 'mediumtext'
73
  is_nullable: 1
74
75
=head2 key_file
76
77
  data_type: 'mediumtext'
78
  is_nullable: 1
79
80
=head2 auth_mode
81
82
  data_type: 'enum'
83
  default_value: 'password'
84
  extra: {list => ["password","key_file","noauth"]}
85
  is_nullable: 0
86
87
=head2 download_directory
88
89
  data_type: 'mediumtext'
90
  is_nullable: 1
91
92
=head2 upload_directory
93
94
  data_type: 'mediumtext'
95
  is_nullable: 1
96
97
=head2 status
98
99
  data_type: 'varchar'
100
  is_nullable: 1
101
  size: 32
102
103
=head2 debug
104
105
  data_type: 'tinyint'
106
  default_value: 0
107
  is_nullable: 0
108
109
=cut
110
111
__PACKAGE__->add_columns(
112
  "id",
113
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
114
  "name",
115
  { data_type => "varchar", is_nullable => 0, size => 80 },
116
  "host",
117
  {
118
    data_type => "varchar",
119
    default_value => "localhost",
120
    is_nullable => 0,
121
    size => 80,
122
  },
123
  "port",
124
  { data_type => "integer", default_value => 22, is_nullable => 0 },
125
  "transport",
126
  {
127
    data_type => "enum",
128
    default_value => "sftp",
129
    extra => { list => ["ftp", "sftp"] },
130
    is_nullable => 0,
131
  },
132
  "passiv",
133
  { data_type => "tinyint", default_value => 1, is_nullable => 0 },
134
  "user_name",
135
  { data_type => "varchar", is_nullable => 1, size => 80 },
136
  "password",
137
  { data_type => "mediumtext", is_nullable => 1 },
138
  "key_file",
139
  { data_type => "mediumtext", is_nullable => 1 },
140
  "auth_mode",
141
  {
142
    data_type => "enum",
143
    default_value => "password",
144
    extra => { list => ["password", "key_file", "noauth"] },
145
    is_nullable => 0,
146
  },
147
  "download_directory",
148
  { data_type => "mediumtext", is_nullable => 1 },
149
  "upload_directory",
150
  { data_type => "mediumtext", is_nullable => 1 },
151
  "status",
152
  { data_type => "varchar", is_nullable => 1, size => 32 },
153
  "debug",
154
  { data_type => "tinyint", default_value => 0, is_nullable => 0 },
155
);
156
157
=head1 PRIMARY KEY
158
159
=over 4
160
161
=item * L</id>
162
163
=back
164
165
=cut
166
167
__PACKAGE__->set_primary_key("id");
168
169
170
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-12-17 10:30:17
171
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Dnz0prvPxRoopZCdRaUtnA
172
173
__PACKAGE__->add_columns(
174
    '+passiv'     => { is_boolean => 1 },
175
    '+debug'      => { is_boolean => 1 },
176
);
177
178
sub koha_objects_class {
179
    'Koha::SFTP::Servers';
180
}
181
182
sub koha_object_class {
183
    'Koha::SFTP::Server';
184
}
185
186
1;
(-)a/admin/sftp_servers.pl (+234 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2024 PTFS Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI          qw ( -utf8 );
23
use Scalar::Util qw( blessed );
24
use Try::Tiny    qw( catch try );
25
26
use C4::Auth   qw( get_template_and_user );
27
use C4::Output qw( output_html_with_http_headers );
28
29
use Koha::SFTP::Servers;
30
31
my $input = CGI->new;
32
my $op    = $input->param('op') || 'list';
33
34
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
35
    {
36
        template_name => "admin/sftp_servers.tt",
37
        query         => $input,
38
        type          => "intranet",
39
        flagsrequired => { parameters => 'manage_sftp_servers' },
40
    }
41
);
42
43
my @messages;
44
45
my $sftp_servers = Koha::SFTP::Servers->search;
46
47
if ( $op eq 'cud-add' ) {
48
    my $name               = $input->param('sftp_name');
49
    my $host               = $input->param('sftp_host')      || 'localhost';
50
    my $port               = $input->param('sftp_port')      || 22;
51
    my $transport          = $input->param('sftp_transport') || 'sftp';
52
    my $passiv             = ( scalar $input->param('sftp_passiv') ) ? 1 : 0;
53
    my $auth_mode          = $input->param('sftp_auth_mode')          || 'password';
54
    my $user_name          = $input->param('sftp_user_name')          || undef;
55
    my $password           = $input->param('sftp_password')           || undef;
56
    my $key_file           = $input->param('sftp_key_file')           || undef;
57
    my $download_directory = $input->param('sftp_download_directory') || undef;
58
    my $upload_directory   = $input->param('sftp_upload_directory')   || undef;
59
    my $status             = $input->param('sftp_status')             || '';
60
    my $debug              = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
61
62
    try {
63
        my $sftp_server = Koha::SFTP::Server->new(
64
            {
65
                name               => $name,
66
                host               => $host,
67
                port               => $port,
68
                transport          => $transport,
69
                passiv             => $passiv,
70
                auth_mode          => $auth_mode,
71
                user_name          => $user_name,
72
                download_directory => $download_directory,
73
                upload_directory   => $upload_directory,
74
                status             => $status,
75
                debug              => $debug,
76
            }
77
        )->store;
78
79
        $sftp_server->update_password($password)
80
            if ($password);
81
82
        $sftp_server->update_key_file($key_file)
83
            if ($key_file);
84
85
        push @messages, {
86
            type => 'message',
87
            code => 'success_on_insert'
88
        };
89
    } catch {
90
        if ( blessed $_ and $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
91
            push @messages, {
92
                type   => 'alert',
93
                code   => 'error_on_insert',
94
                reason => 'duplicate_id'
95
            };
96
        }
97
    };
98
99
    # list servers after adding
100
    $op = 'list';
101
102
} elsif ( $op eq 'edit_form' ) {
103
    my $sftp_server_id = $input->param('sftp_server_id');
104
    my $sftp_server;
105
    my $sftp_server_plain_text_password;
106
    my $sftp_server_plain_text_key;
107
108
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
109
        unless !$sftp_server_id;
110
111
    unless ( !$sftp_server ) {
112
        $sftp_server_plain_text_password = $sftp_server->plain_text_password || '';
113
        $sftp_server_plain_text_key      = $sftp_server->plain_text_key      || '';
114
    }
115
116
    if ($sftp_server) {
117
        $template->param(
118
            sftp_server                     => $sftp_server,
119
            sftp_server_plain_text_password => $sftp_server_plain_text_password,
120
            sftp_server_plain_text_key      => $sftp_server_plain_text_key,
121
        );
122
    } else {
123
        push @messages, {
124
            type   => 'alert',
125
            code   => 'error_on_edit',
126
            reason => 'invalid_id'
127
        };
128
    }
129
130
} elsif ( $op eq 'cud-edit_save' ) {
131
    my $sftp_server_id = $input->param('sftp_server_id');
132
    my $sftp_server_plain_text_password;
133
    my $sftp_server;
134
135
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
136
        unless !$sftp_server_id;
137
138
    $sftp_server_plain_text_password = $sftp_server->plain_text_password
139
        unless !$sftp_server_id;
140
141
    if ($sftp_server) {
142
        my $name               = $input->param('sftp_name');
143
        my $host               = $input->param('sftp_host')      || 'localhost';
144
        my $port               = $input->param('sftp_port')      || 22;
145
        my $transport          = $input->param('sftp_transport') || 'sftp';
146
        my $passiv             = ( scalar $input->param('sftp_passiv') ) ? 1 : 0;
147
        my $auth_mode          = $input->param('sftp_auth_mode')          || 'password';
148
        my $user_name          = $input->param('sftp_user_name')          || undef;
149
        my $password           = $input->param('sftp_password')           || undef;
150
        my $key_file           = $input->param('sftp_key_file')           || undef;
151
        my $download_directory = $input->param('sftp_download_directory') || undef;
152
        my $upload_directory   = $input->param('sftp_upload_directory')   || undef;
153
        my $status             = $input->param('sftp_status')             || '';
154
        my $debug              = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
155
156
        try {
157
            $sftp_server->set(
158
                {
159
                    name               => $name,
160
                    host               => $host,
161
                    port               => $port,
162
                    transport          => $transport,
163
                    passiv             => $passiv,
164
                    auth_mode          => $auth_mode,
165
                    user_name          => $user_name,
166
                    download_directory => $download_directory,
167
                    upload_directory   => $upload_directory,
168
                    status             => $status,
169
                    debug              => $debug,
170
                }
171
            )->store;
172
173
            $sftp_server->update_password($password)
174
                if ($password);
175
176
            $sftp_server->update_key_file($key_file)
177
                if ($key_file);
178
179
            push @messages, {
180
                type => 'message',
181
                code => 'success_on_update'
182
            };
183
184
        } catch {
185
186
            push @messages, {
187
                type => 'alert',
188
                code => 'error_on_update'
189
            };
190
191
        };
192
193
        # list servers after adding
194
        $op = 'list';
195
    } else {
196
        push @messages, {
197
            type   => 'alert',
198
            code   => 'error_on_update',
199
            reason => 'invalid_id'
200
        };
201
    }
202
203
} elsif ( $op eq 'test_form' ) {
204
    my $sftp_server_id = $input->param('sftp_server_id');
205
    my $sftp_server;
206
207
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
208
        unless !$sftp_server_id;
209
210
    if ($sftp_server) {
211
        $template->param(
212
            sftp_server => $sftp_server,
213
        );
214
    } else {
215
        push @messages, {
216
            type   => 'alert',
217
            code   => 'error_on_test',
218
            reason => 'invalid_id',
219
        };
220
    }
221
}
222
223
if ( $op eq 'list' ) {
224
    $template->param(
225
        servers_count => $sftp_servers->count,
226
    );
227
}
228
229
$template->param(
230
    op       => $op,
231
    messages => \@messages,
232
);
233
234
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/api/v1/swagger/definitions/sftp_server.yaml (+69 lines)
Line 0 Link Here
1
---
2
type: object
3
properties:
4
  sftp_server_id:
5
    type: integer
6
    description: Internal FTP/SFTP server identifier
7
    readOnly: true
8
  name:
9
    type: string
10
    description: Name of the FTP/SFTP server
11
  host:
12
    type: string
13
    description: FTP/SFTP host name (optional)
14
  port:
15
    type: integer
16
    description: TCP port number (optional)
17
  transport:
18
    type: string
19
    enum:
20
      - ftp
21
      - sftp
22
      - file
23
    description: Transport of FTP/SFTP server (optional)
24
  auth_mode:
25
    type: string
26
    enum:
27
      - password
28
      - key_file
29
      - noauth
30
    description: Authentication mode to use (optional)
31
  passiv:
32
    type: boolean
33
    description: Whether or not to use passive mode (optional)
34
  user_name:
35
    type:
36
      - string
37
      - "null"
38
    description: The user name to use for authentication (optional)
39
  password:
40
    type:
41
      - string
42
      - "null"
43
    description: The password to use for authentication (optional)
44
  key_file:
45
    type:
46
      - string
47
      - "null"
48
    description: The key file to use for authentication (optional)
49
  download_directory:
50
    type:
51
      - string
52
      - "null"
53
    description: Path on the remote server we download from (optional)
54
  upload_directory:
55
    type:
56
      - string
57
      - "null"
58
    description: Path on the remote server we upload to (optional)
59
  status:
60
    type:
61
      - string
62
      - "null"
63
    description: Most recent status of the FTP/SFTP server (optional)
64
  debug:
65
    type: boolean
66
    description: If the SMTP connection is set to debug mode (optional)
67
additionalProperties: false
68
required:
69
  - name
(-)a/api/v1/swagger/paths/config_sftp_servers.yaml (+236 lines)
Line 0 Link Here
1
---
2
/config/sftp_servers:
3
  get:
4
    x-mojo-to: Config::SFTP::Servers#list
5
    operationId: listSFTPServers
6
    tags:
7
      - sftp_servers
8
    summary: List FTP/SFTP servers
9
    produces:
10
      - application/json
11
    parameters:
12
      - $ref: "../swagger.yaml#/parameters/match"
13
      - $ref: "../swagger.yaml#/parameters/order_by"
14
      - $ref: "../swagger.yaml#/parameters/page"
15
      - $ref: "../swagger.yaml#/parameters/per_page"
16
      - $ref: "../swagger.yaml#/parameters/q_param"
17
      - $ref: "../swagger.yaml#/parameters/q_body"
18
      - $ref: "../swagger.yaml#/parameters/request_id_header"
19
    responses:
20
      "200":
21
        description: A list of FTP/SFTP servers
22
        schema:
23
          type: array
24
          items:
25
            $ref: "../swagger.yaml#/definitions/sftp_server"
26
      "400":
27
        description: |
28
          Bad request. Possible `error_code` attribute values:
29
30
            * `invalid_query`
31
        schema:
32
          $ref: "../swagger.yaml#/definitions/error"
33
      "403":
34
        description: Access forbidden
35
        schema:
36
          $ref: "../swagger.yaml#/definitions/error"
37
      "500":
38
        description: |
39
          Internal server error. Possible `error_code` attribute values:
40
41
          * `internal_server_error`
42
        schema:
43
          $ref: "../swagger.yaml#/definitions/error"
44
      "503":
45
        description: Under maintenance
46
        schema:
47
          $ref: "../swagger.yaml#/definitions/error"
48
    x-koha-authorization:
49
      permissions:
50
        parameters: manage_sftp_servers
51
  post:
52
    x-mojo-to: Config::SFTP::Servers#add
53
    operationId: addSFTPServer
54
    tags:
55
      - sftp_servers
56
    summary: Add FTP/SFTP server
57
    parameters:
58
      - name: body
59
        in: body
60
        description: A JSON object representing a new FTP/SFTP server configuration
61
        required: true
62
        schema:
63
          $ref: "../swagger.yaml#/definitions/sftp_server"
64
    produces:
65
      - application/json
66
    responses:
67
      "201":
68
        description: An FTP/SFTP server object
69
        schema:
70
          $ref: "../swagger.yaml#/definitions/sftp_server"
71
      "400":
72
        description: Bad request
73
        schema:
74
          $ref: "../swagger.yaml#/definitions/error"
75
      "401":
76
        description: Authentication required
77
        schema:
78
          $ref: "../swagger.yaml#/definitions/error"
79
      "403":
80
        description: Access forbidden
81
        schema:
82
          $ref: "../swagger.yaml#/definitions/error"
83
      "409":
84
        description: Conflict in creating resource
85
        schema:
86
          $ref: "../swagger.yaml#/definitions/error"
87
      "500":
88
        description: |
89
          Internal server error. Possible `error_code` attribute values:
90
91
          * `internal_server_error`
92
        schema:
93
          $ref: "../swagger.yaml#/definitions/error"
94
      "503":
95
        description: Under maintenance
96
        schema:
97
          $ref: "../swagger.yaml#/definitions/error"
98
    x-koha-authorization:
99
      permissions:
100
        parameters: manage_sftp_servers
101
"/config/sftp_servers/{sftp_server_id}":
102
  get:
103
    x-mojo-to: Config::SFTP::Servers#get
104
    operationId: getSFTPServer
105
    tags:
106
      - sftp_servers
107
    summary: Get FTP/SFTP server
108
    parameters:
109
      - $ref: "../swagger.yaml#/parameters/sftp_server_id_pp"
110
    produces:
111
      - application/json
112
    responses:
113
      "200":
114
        description: An FTP/SFTP server object
115
        schema:
116
          $ref: "../swagger.yaml#/definitions/sftp_server"
117
      "400":
118
        description: Bad request
119
        schema:
120
          $ref: "../swagger.yaml#/definitions/error"
121
      "404":
122
        description: Object not found
123
        schema:
124
          $ref: "../swagger.yaml#/definitions/error"
125
      "409":
126
        description: Conflict updating resource
127
        schema:
128
          $ref: "../swagger.yaml#/definitions/error"
129
      "500":
130
        description: |
131
          Internal server error. Possible `error_code` attribute values:
132
133
          * `internal_server_error`
134
        schema:
135
          $ref: "../swagger.yaml#/definitions/error"
136
      "503":
137
        description: Under maintenance
138
        schema:
139
          $ref: "../swagger.yaml#/definitions/error"
140
    x-koha-authorization:
141
      permissions:
142
        parameters: manage_sftp_servers
143
  put:
144
    x-mojo-to: Config::SFTP::Servers#update
145
    operationId: updateSFTPServer
146
    tags:
147
      - sftp_servers
148
    summary: Update FTP/SFTP server
149
    parameters:
150
      - $ref: "../swagger.yaml#/parameters/sftp_server_id_pp"
151
      - name: body
152
        in: body
153
        description: An FTP/SFTP server object
154
        required: true
155
        schema:
156
          $ref: "../swagger.yaml#/definitions/sftp_server"
157
    produces:
158
      - application/json
159
    responses:
160
      "200":
161
        description: An FTP/SFTP server object
162
        schema:
163
          $ref: "../swagger.yaml#/definitions/sftp_server"
164
      "400":
165
        description: Bad request
166
        schema:
167
          $ref: "../swagger.yaml#/definitions/error"
168
      "401":
169
        description: Authentication required
170
        schema:
171
          $ref: "../swagger.yaml#/definitions/error"
172
      "403":
173
        description: Access forbidden
174
        schema:
175
          $ref: "../swagger.yaml#/definitions/error"
176
      "404":
177
        description: Object not found
178
        schema:
179
          $ref: "../swagger.yaml#/definitions/error"
180
      "500":
181
        description: |
182
          Internal server error. Possible `error_code` attribute values:
183
184
          * `internal_server_error`
185
        schema:
186
          $ref: "../swagger.yaml#/definitions/error"
187
      "503":
188
        description: Under maintenance
189
        schema:
190
          $ref: "../swagger.yaml#/definitions/error"
191
    x-koha-authorization:
192
      permissions:
193
        parameters: manage_sftp_servers
194
  delete:
195
    x-mojo-to: Config::SFTP::Servers#delete
196
    operationId: deleteSFTPServer
197
    tags:
198
      - sftp_servers
199
    summary: Delete FTP/SFTP server
200
    parameters:
201
      - $ref: "../swagger.yaml#/parameters/sftp_server_id_pp"
202
    produces:
203
      - application/json
204
    responses:
205
      "204":
206
        description: FTP/SFTP server deleted
207
      "400":
208
        description: Bad request
209
        schema:
210
          $ref: "../swagger.yaml#/definitions/error"
211
      "401":
212
        description: Authentication required
213
        schema:
214
          $ref: "../swagger.yaml#/definitions/error"
215
      "403":
216
        description: Access forbidden
217
        schema:
218
          $ref: "../swagger.yaml#/definitions/error"
219
      "404":
220
        description: Object not found
221
        schema:
222
          $ref: "../swagger.yaml#/definitions/error"
223
      "500":
224
        description: |
225
          Internal server error. Possible `error_code` attribute values:
226
227
          * `internal_server_error`
228
        schema:
229
          $ref: "../swagger.yaml#/definitions/error"
230
      "503":
231
        description: Under maintenance
232
        schema:
233
          $ref: "../swagger.yaml#/definitions/error"
234
    x-koha-authorization:
235
      permissions:
236
        parameters: manage_sftp_servers
(-)a/api/v1/swagger/paths/test_sftp_servers.yaml (+48 lines)
Line 0 Link Here
1
---
2
"/sftp_server/{sftp_server_id}/test_connection":
3
  get:
4
    x-mojo-to: SFTPServer#test
5
    operationId: testSFTPServer
6
    tags:
7
      - sftp_servers
8
    summary: Test FTP/SFTP server
9
    produces:
10
      - application/json
11
    parameters:
12
      - $ref: "../swagger.yaml#/parameters/sftp_server_id_pp"
13
    responses:
14
      "200":
15
        description: Results of FTP/SFTP server test
16
        schema:
17
          type: object
18
          items:
19
            $ref: "../swagger.yaml#/definitions/sftp_server"
20
      "400":
21
        description: |
22
          Bad request. Possible `error_code` attribute values:
23
24
            * `invalid_query`
25
        schema:
26
          $ref: "../swagger.yaml#/definitions/error"
27
      "404":
28
        description: Not Found
29
        schema:
30
          $ref: "../swagger.yaml#/definitions/error"
31
      "403":
32
        description: Access forbidden
33
        schema:
34
          $ref: "../swagger.yaml#/definitions/error"
35
      "500":
36
        description: |
37
          Internal server error. Possible `error_code` attribute values:
38
39
          * `internal_server_error`
40
        schema:
41
          $ref: "../swagger.yaml#/definitions/error"
42
      "503":
43
        description: Under maintenance
44
        schema:
45
          $ref: "../swagger.yaml#/definitions/error"
46
    x-koha-authorization:
47
      permissions:
48
        parameters: manage_sftp_servers
(-)a/api/v1/swagger/swagger.yaml (+17 lines)
Lines 174-179 definitions: Link Here
174
    $ref: ./definitions/search_filter.yaml
174
    $ref: ./definitions/search_filter.yaml
175
  smtp_server:
175
  smtp_server:
176
    $ref: ./definitions/smtp_server.yaml
176
    $ref: ./definitions/smtp_server.yaml
177
  sftp_server:
178
    $ref: ./definitions/sftp_server.yaml
177
  suggestion:
179
  suggestion:
178
    $ref: ./definitions/suggestion.yaml
180
    $ref: ./definitions/suggestion.yaml
179
  ticket:
181
  ticket:
Lines 305-310 paths: Link Here
305
    $ref: ./paths/config_smtp_servers.yaml#/~1config~1smtp_servers
307
    $ref: ./paths/config_smtp_servers.yaml#/~1config~1smtp_servers
306
  "/config/smtp_servers/{smtp_server_id}":
308
  "/config/smtp_servers/{smtp_server_id}":
307
    $ref: "./paths/config_smtp_servers.yaml#/~1config~1smtp_servers~1{smtp_server_id}"
309
    $ref: "./paths/config_smtp_servers.yaml#/~1config~1smtp_servers~1{smtp_server_id}"
310
  /config/sftp_servers:
311
    $ref: ./paths/config_sftp_servers.yaml#/~1config~1sftp_servers
312
  "/config/sftp_servers/{sftp_server_id}":
313
    $ref: "./paths/config_sftp_servers.yaml#/~1config~1sftp_servers~1{sftp_server_id}"
308
  "/deleted/biblios":
314
  "/deleted/biblios":
309
    $ref: "./paths/deleted_biblios.yaml#/~1deleted~1biblios"
315
    $ref: "./paths/deleted_biblios.yaml#/~1deleted~1biblios"
310
  "/deleted/biblios/{biblio_id}":
316
  "/deleted/biblios/{biblio_id}":
Lines 551-556 paths: Link Here
551
    $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}"
557
    $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}"
552
  "/rotas/{rota_id}/stages/{stage_id}/position":
558
  "/rotas/{rota_id}/stages/{stage_id}/position":
553
    $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}~1stages~1{stage_id}~1position"
559
    $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}~1stages~1{stage_id}~1position"
560
  "/sftp_server/{sftp_server_id}/test_connection":
561
    $ref: "./paths/test_sftp_servers.yaml#/~1sftp_server~1{sftp_server_id}~1test_connection"
554
  /suggestions:
562
  /suggestions:
555
    $ref: ./paths/suggestions.yaml#/~1suggestions
563
    $ref: ./paths/suggestions.yaml#/~1suggestions
556
  "/suggestions/{suggestion_id}":
564
  "/suggestions/{suggestion_id}":
Lines 931-936 parameters: Link Here
931
    name: smtp_server_id
939
    name: smtp_server_id
932
    required: true
940
    required: true
933
    type: integer
941
    type: integer
942
  sftp_server_id_pp:
943
    description: FTP/SFTP server internal identifier
944
    in: path
945
    name: sftp_server_id
946
    required: true
947
    type: integer
934
  suggestion_id_pp:
948
  suggestion_id_pp:
935
    description: Internal suggestion identifier
949
    description: Internal suggestion identifier
936
    in: path
950
    in: path
Lines 1278-1283 tags: Link Here
1278
  - description: "Manage SMTP servers configurations\n"
1292
  - description: "Manage SMTP servers configurations\n"
1279
    name: smtp_servers
1293
    name: smtp_servers
1280
    x-displayName: SMTP servers
1294
    x-displayName: SMTP servers
1295
  - description: "Manage FTP/SFTP servers configurations\n"
1296
    name: sftp_servers
1297
    x-displayName: FTP/SFTP servers
1281
  - description: "Manage tickets\n"
1298
  - description: "Manage tickets\n"
1282
    name: tickets
1299
    name: tickets
1283
    x-displayName: Tickets
1300
    x-displayName: Tickets
(-)a/installer/data/mysql/atomicupdate/bug_35761-add_SFTP_tables_and_perm.pl (+44 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
return {
4
    bug_number  => "35761",
5
    description => "Add new table and permission for generalised SFTP",
6
    up          => sub {
7
        my ($args) = @_;
8
        my ( $dbh, $out ) = @$args{qw(dbh out)};
9
10
        $dbh->do(
11
            q{
12
                INSERT IGNORE INTO permissions (module_bit, code, description)
13
                VALUES (3, 'manage_sftp_servers', 'Manage FTP/SFTP servers configuration');
14
            }
15
        );
16
        say $out "Added new manage_sftp_servers permission";
17
18
        unless ( TableExists('sftp_servers') ) {
19
            $dbh->do(
20
                q {
21
                    CREATE TABLE `sftp_servers` (
22
                    `id` int(11) NOT NULL AUTO_INCREMENT,
23
                    `name` varchar(80) NOT NULL,
24
                    `host` varchar(80) NOT NULL DEFAULT 'localhost',
25
                    `port` int(11) NOT NULL DEFAULT 22,
26
                    `transport` enum('ftp','sftp') NOT NULL DEFAULT 'sftp',
27
                    `passiv` tinyint(1) NOT NULL DEFAULT 1,
28
                    `user_name` varchar(80) DEFAULT NULL,
29
                    `password` mediumtext DEFAULT NULL,
30
                    `key_file` mediumtext DEFAULT NULL,
31
                    `auth_mode` enum('password','key_file','noauth') NOT NULL DEFAULT 'password',
32
                    `download_directory` mediumtext DEFAULT NULL,
33
                    `upload_directory` mediumtext DEFAULT NULL,
34
                    `status` varchar(32) DEFAULT NULL,
35
                    `debug` tinyint(1) NOT NULL DEFAULT 0,
36
                    PRIMARY KEY (`id`),
37
                    KEY `host_idx` (`host`)
38
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
39
                }
40
            );
41
            say $out "Added new sftp_servers table";
42
        }
43
    },
44
};
(-)a/installer/data/mysql/kohastructure.sql (+27 lines)
Lines 5923-5928 CREATE TABLE `sessions` ( Link Here
5923
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5923
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5924
/*!40101 SET character_set_client = @saved_cs_client */;
5924
/*!40101 SET character_set_client = @saved_cs_client */;
5925
5925
5926
--
5927
-- Table structure for table `sftp_servers`
5928
--
5929
5930
DROP TABLE IF EXISTS `sftp_servers`;
5931
/*!40101 SET @saved_cs_client     = @@character_set_client */;
5932
/*!40101 SET character_set_client = utf8 */;
5933
CREATE TABLE `sftp_servers` (
5934
  `id` int(11) NOT NULL AUTO_INCREMENT,
5935
  `name` varchar(80) NOT NULL,
5936
  `host` varchar(80) NOT NULL DEFAULT 'localhost',
5937
  `port` int(11) NOT NULL DEFAULT 22,
5938
  `transport` enum('ftp','sftp') NOT NULL DEFAULT 'sftp',
5939
  `passiv` tinyint(1) NOT NULL DEFAULT 1,
5940
  `user_name` varchar(80) DEFAULT NULL,
5941
  `password` mediumtext DEFAULT NULL,
5942
  `key_file` mediumtext DEFAULT NULL,
5943
  `auth_mode` enum('password','key_file','noauth') NOT NULL DEFAULT 'password',
5944
  `download_directory` mediumtext DEFAULT NULL,
5945
  `upload_directory` mediumtext DEFAULT NULL,
5946
  `status` varchar(32) DEFAULT NULL,
5947
  `debug` tinyint(1) NOT NULL DEFAULT 0,
5948
  PRIMARY KEY (`id`),
5949
  KEY `host_idx` (`host`)
5950
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5951
/*!40101 SET character_set_client = @saved_cs_client */;
5952
5926
--
5953
--
5927
-- Table structure for table `sms_providers`
5954
-- Table structure for table `sms_providers`
5928
--
5955
--
(-)a/installer/data/mysql/mandatory/userpermissions.sql (+1 lines)
Lines 39-44 INSERT INTO permissions (module_bit, code, description) VALUES Link Here
39
   ( 3, 'manage_additional_fields', 'Add, edit, or delete additional custom fields for baskets or subscriptions (also requires order_manage or edit_subscription permissions)'),
39
   ( 3, 'manage_additional_fields', 'Add, edit, or delete additional custom fields for baskets or subscriptions (also requires order_manage or edit_subscription permissions)'),
40
   ( 3, 'manage_keyboard_shortcuts', 'Manage keyboard shortcuts for the advanced cataloging editor'),
40
   ( 3, 'manage_keyboard_shortcuts', 'Manage keyboard shortcuts for the advanced cataloging editor'),
41
   ( 3, 'manage_smtp_servers', 'Manage SMTP servers configuration'),
41
   ( 3, 'manage_smtp_servers', 'Manage SMTP servers configuration'),
42
   ( 3, 'manage_sftp_servers', 'Manage FTP/SFTP servers configuration'),
42
   ( 3, 'manage_background_jobs', 'Manage background jobs'),
43
   ( 3, 'manage_background_jobs', 'Manage background jobs'),
43
   ( 3, 'manage_curbside_pickups', 'Manage curbside pickups'),
44
   ( 3, 'manage_curbside_pickups', 'Manage curbside pickups'),
44
   ( 3, 'manage_search_filters', 'Manage custom search filters'),
45
   ( 3, 'manage_search_filters', 'Manage custom search filters'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc (-1 / +4 lines)
Lines 147-153 Link Here
147
        </ul>
147
        </ul>
148
    [% END %]
148
    [% END %]
149
149
150
    [% IF ( CAN_user_parameters_manage_identity_providers || CAN_user_parameters_manage_smtp_servers || CAN_user_parameters_manage_search_targets || CAN_user_parameters_manage_didyoumean || CAN_user_parameters_manage_column_config || CAN_user_parameters_manage_audio_alerts || ( CAN_user_parameters_manage_sms_providers && Koha.Preference('SMSSendDriver') == 'Email' ) || CAN_user_parameters_manage_usage_stats || CAN_user_parameters_manage_additional_fields || ( Koha.Preference('EnableAdvancedCatalogingEditor') && CAN_user_parameters_manage_keyboard_shortcuts ) ) %]
150
    [% IF ( CAN_user_parameters_manage_identity_providers || CAN_user_parameters_manage_smtp_servers || CAN_user_parameters_manage_sftp_servers || CAN_user_parameters_manage_search_targets || CAN_user_parameters_manage_didyoumean || CAN_user_parameters_manage_column_config || CAN_user_parameters_manage_audio_alerts || ( CAN_user_parameters_manage_sms_providers && Koha.Preference('SMSSendDriver') == 'Email' ) || CAN_user_parameters_manage_usage_stats || CAN_user_parameters_manage_additional_fields || ( Koha.Preference('EnableAdvancedCatalogingEditor') && CAN_user_parameters_manage_keyboard_shortcuts ) ) %]
151
        <h5>Additional parameters</h5>
151
        <h5>Additional parameters</h5>
152
        <ul>
152
        <ul>
153
            [% IF ( CAN_user_parameters_manage_identity_providers) %]
153
            [% IF ( CAN_user_parameters_manage_identity_providers) %]
Lines 160-165 Link Here
160
            [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
160
            [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
161
                <li><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></li>
161
                <li><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></li>
162
            [% END %]
162
            [% END %]
163
            [% IF ( CAN_user_parameters_manage_sftp_servers ) %]
164
                <li><a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a></li>
165
            [% END %]
163
            [% IF ( CAN_user_parameters_manage_didyoumean ) %]
166
            [% IF ( CAN_user_parameters_manage_didyoumean ) %]
164
                <li><a href="/cgi-bin/koha/admin/didyoumean.pl">Did you mean?</a></li>
167
                <li><a href="/cgi-bin/koha/admin/didyoumean.pl">Did you mean?</a></li>
165
            [% END %]
168
            [% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (+5 lines)
Lines 257-262 Link Here
257
            Manage SMTP servers
257
            Manage SMTP servers
258
        </span>
258
        </span>
259
        <span class="permissioncode">([% name | html %])</span>
259
        <span class="permissioncode">([% name | html %])</span>
260
    [%- CASE 'manage_sftp_servers' -%]
261
        <span class="sub_permission manage_manage_sftp_servers_subpermission">
262
            Manage FTP/SFTP servers
263
        </span>
264
        <span class="permissioncode">([% name | html %])</span>
260
    [%- CASE 'manage_column_config' -%]
265
    [%- CASE 'manage_column_config' -%]
261
        <span class="sub_permission manage_column_config_subpermission">
266
        <span class="sub_permission manage_column_config_subpermission">
262
            Manage column configuration
267
            Manage column configuration
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt (-1 / +5 lines)
Lines 253-259 Link Here
253
                </dl>
253
                </dl>
254
            [% END %]
254
            [% END %]
255
255
256
            [% IF ( ( CAN_user_parameters_manage_identity_providers || CAN_user_parameters_manage_smtp_servers || CAN_user_parameters_manage_search_targets || CAN_user_parameters_manage_didyoumean || CAN_user_parameters_manage_column_config || CAN_user_parameters_manage_audio_alerts || CAN_user_parameters_manage_sms_providers && Koha.Preference('SMSSendDriver') == 'Email' ) || CAN_user_parameters_manage_usage_stats || CAN_user_parameters_manage_additional_fields || CAN_user_parameters_manage_mana || (Koha.Preference('EnableAdvancedCatalogingEditor') && CAN_user_parameters_manage_keyboard_shortcuts) ) %]
256
            [% IF ( ( CAN_user_parameters_manage_identity_providers || CAN_user_parameters_manage_smtp_servers || CAN_user_parameters_manage_sftp_servers || CAN_user_parameters_manage_search_targets || CAN_user_parameters_manage_didyoumean || CAN_user_parameters_manage_column_config || CAN_user_parameters_manage_audio_alerts || CAN_user_parameters_manage_sms_providers && Koha.Preference('SMSSendDriver') == 'Email' ) || CAN_user_parameters_manage_usage_stats || CAN_user_parameters_manage_additional_fields || CAN_user_parameters_manage_mana || (Koha.Preference('EnableAdvancedCatalogingEditor') && CAN_user_parameters_manage_keyboard_shortcuts) ) %]
257
                <h3>Additional parameters</h3>
257
                <h3>Additional parameters</h3>
258
                <dl>
258
                <dl>
259
                        <!-- <dt><a href="/cgi-bin/koha/admin/printers.pl">Network Printers</a></dt>
259
                        <!-- <dt><a href="/cgi-bin/koha/admin/printers.pl">Network Printers</a></dt>
Lines 272-277 Link Here
272
                        <dt><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></dt>
272
                        <dt><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></dt>
273
                        <dd>Define which SMTP servers to use</dd>
273
                        <dd>Define which SMTP servers to use</dd>
274
                    [% END %]
274
                    [% END %]
275
                    [% IF ( CAN_user_parameters_manage_sftp_servers ) %]
276
                        <dt><a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a></dt>
277
                        <dd>Define available FTP/SFTP servers</dd>
278
                    [% END %]
275
                    [% IF ( CAN_user_parameters_manage_didyoumean ) %]
279
                    [% IF ( CAN_user_parameters_manage_didyoumean ) %]
276
                        <dt><a href="/cgi-bin/koha/admin/didyoumean.pl">Did you mean?</a></dt>
280
                        <dt><a href="/cgi-bin/koha/admin/didyoumean.pl">Did you mean?</a></dt>
277
                        <dd>Choose which plugins to use to suggest searches to patrons and staff</dd>
281
                        <dd>Choose which plugins to use to suggest searches to patrons and staff</dd>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/sftp_servers.tt (+963 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% PROCESS 'i18n.inc' %]
4
[% SET footerjs = 1 %]
5
[% INCLUDE 'doc-head-open.inc' %]
6
<title>[% FILTER collapse %]
7
    [% IF op == 'add_form' %]
8
        [% t("New FTP/SFTP server") | html %] &rsaquo;
9
    [% ELSIF op == 'edit_form' %]
10
        [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %] &rsaquo;
11
    [% ELSIF op == 'test_form' %]
12
        [% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %] &rsaquo;
13
    [% END %]
14
    [% t("FTP/SFTP Servers") | html %] &rsaquo;
15
    [% t("Administration") | html %] &rsaquo;
16
    [% t("Koha") | html %]
17
[% END %]</title>
18
[% INCLUDE 'doc-head-close.inc' %]
19
<style>
20
    #testOutput {
21
        font-size: 1.25rem;
22
    }
23
24
    #testOutput .pending-loading {
25
        font-size: 1.5rem;
26
        margin-left: 1.25rem;
27
    }
28
29
    #testOutput code {
30
        font-size: 87.5%;
31
        color: #e83e8c;
32
        background: transparent;
33
        word-break: break-word;
34
    }
35
</style>
36
37
</head>
38
39
<body id="admin_sftp_servers" class="admin">
40
[% WRAPPER 'header.inc' %]
41
    [% INCLUDE 'prefs-admin-search.inc' %]
42
[% END %]
43
44
[% WRAPPER 'sub-header.inc' %]
45
    [% WRAPPER breadcrumbs %]
46
        [% WRAPPER breadcrumb_item %]
47
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
48
        [% END %]
49
50
        [% IF op == 'add_form' || op == 'edit_form' || op == 'test_form' %]
51
            [% WRAPPER breadcrumb_item %]
52
                <a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a>
53
            [% END %]
54
        [% END %]
55
56
        [% IF op == 'add_form' %]
57
            [% WRAPPER breadcrumb_item bc_active= 1 %]
58
                <span>New FTP/SFTP server</span>
59
            [% END %]
60
61
        [% ELSIF op == 'edit_form' %]
62
            [% WRAPPER breadcrumb_item bc_active= 1 %]
63
                [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
64
            [% END %]
65
66
        [% ELSIF op == 'test_form' %]
67
            [% WRAPPER breadcrumb_item bc_active= 1 %]
68
                [% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
69
            [% END %]
70
71
        [% ELSE %]
72
            [% WRAPPER breadcrumb_item bc_active= 1 %]
73
                <span>FTP/SFTP servers</span>
74
            [% END %]
75
        [% END %]
76
    [% END #/ WRAPPER breadcrumbs %]
77
[% END #/ WRAPPER sub-header.inc %]
78
79
<div class="main container-fluid">
80
    <div class="row">
81
        <div class="col-md-10 order-md-2 order-sm-1">
82
            <main>
83
                [% INCLUDE 'messages.inc' %]
84
85
[% FOREACH m IN messages %]
86
    <div class="alert alert-[% m.type | html %]" id="sftp_action_result_dialog">
87
        [% SWITCH m.code %]
88
        [% CASE 'error_on_insert' %]
89
            <span>An error occurred when adding the server. The passed ID already exists.</span>
90
        [% CASE 'error_on_update' %]
91
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
92
        [% CASE 'error_on_edit' %]
93
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
94
        [% CASE 'error_on_test' %]
95
            <span>An error occurred when connecting to this server. Please see the text below for more information.</span>
96
        [% CASE 'success_on_update' %]
97
            <span>Server updated successfully.</span>
98
        [% CASE 'success_on_insert' %]
99
            <span>Server added successfully.</span>
100
        [% CASE %]
101
            <span>[% m.code | html %]</span>
102
        [% END %]
103
    </div>
104
[% END %]
105
106
    <div class="alert alert-info"    id="sftp_delete_success" style="display: none;"></div>
107
    <div class="alert alert-warning" id="sftp_delete_error"   style="display: none;"></div>
108
109
[% IF op == 'add_form' %]
110
    <!-- Modal -->
111
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
112
        <div class="modal-dialog modal-lg">
113
            <div class="modal-content modal-lg">
114
                    <div class="modal-header">
115
                        <h1 class="modal-title">Are you sure?</h1>
116
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
117
                    </div>
118
                    <div class="modal-body">
119
                        <div id="modal_message" class="alert alert-warning">Because you are using the SFTP transport, please run a test of this connection to accept the server's host key.</div>
120
                        <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
121
                        <table class="mx-4 mb-3">
122
                            <thead></thead>
123
                            <tbody>
124
                                <tr>
125
                                    <td><strong>Host</strong></td>
126
                                    <td id="modal_host"></td>
127
                                </tr>
128
                                <tr>
129
                                    <td><strong>Port</strong></td>
130
                                    <td id="modal_port"></td>
131
                                </tr>
132
                                <tr>
133
                                    <td><strong>Transport</strong></td>
134
                                    <td id="modal_transport"></td>
135
                                </tr>
136
                                <tr>
137
                                    <td><strong>Username</strong></td>
138
                                    <td id="modal_user_name"></td>
139
                                </tr>
140
                                <tr>
141
                                    <td><strong>Authentication mode</strong></td>
142
                                    <td id="modal_auth_mode"></td>
143
                                </tr>
144
                            </tbody>
145
                        </table>
146
                        <p>If you are ready to progress with these details, please click Save.</p>
147
                    </div>
148
                    <div class="modal-footer">
149
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
150
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
151
                    </div>
152
                </form>
153
            </div>
154
        </div>
155
    </div>
156
    <!-- END Modal -->
157
158
    <h1>New FTP/SFTP server</h1>
159
160
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="add" name="add" class="validated" method="post">
161
        [% INCLUDE 'csrf-token.inc' %]
162
        <input type="hidden" name="op" value="cud-add" />
163
        <fieldset class="rows">
164
            <ol>
165
                <li>
166
                    <label for="sftp_name" class="required">Name: </label>
167
                    <input type="text" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
168
                    <span class="required">Required</span>
169
                </li>
170
            </ol>
171
        </fieldset>
172
173
        <fieldset class="rows">
174
            <ol>
175
                <li>
176
                    <label for="sftp_host" class="required">Host: </label>
177
                    <input type="text" value="localhost" name="sftp_host" id="sftp_host" size="60" class="required" />
178
                    <span class="required">Required</span>
179
                </li>
180
                <li>
181
                    <label for="sftp_port" class="required">Port: </label>
182
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="22" name="sftp_port" id="sftp_port" size="20" class="required" />
183
                    <span class="required">Required</span>
184
                </li>
185
                <li>
186
                    <label for="sftp_transport" class="required">Transport: </label>
187
                    <select name="sftp_transport" id="sftp_transport" class="required">
188
                        <option value="ftp">FTP</option>
189
                        <option value="sftp" selected="selected">SFTP</option>
190
                    </select>
191
                    <span class="required">Required</span>
192
                </li>
193
                <li>
194
                    <label for="sftp_passiv">Passive mode: </label>
195
                    <select name="sftp_passiv" id="sftp_passiv" disabled="disabled">
196
                        <option value="1" selected="selected">On (Recommended)</option>
197
                        <option value="0">Off</option>
198
                    </select>
199
                    <span class="hint">Only applies to FTP connections</span>
200
                </li>
201
                <li>
202
                    <label for="sftp_auth_mode">Authentication mode: </label>
203
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
204
                        <option value="password" selected="selected">Password-based</option>
205
                        <option value="key_file">Key file-based</option>
206
                        <option value="noauth">No authentication</option>
207
                    </select>
208
                </li>
209
                <li>
210
                    <label for="sftp_user_name" class="required">Username: </label>
211
                    <input type="text" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
212
                    <span class="required">Required</span>
213
                </li>
214
                <li>
215
                    <label for="sftp_password">Password: </label>
216
                    <input type="password" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
217
                </li>
218
                <li>
219
                    <label for="sftp_key_file">Key file: </label>
220
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58"></textarea>
221
                    <span class="hint">Only applies to SFTP connections</span>
222
                </li>
223
                <li>
224
                    <label for="sftp_download_directory" >Remote download directory: </label>
225
                    <input type="text" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
226
                    <span class="hint">The path on the remote server where we will download from</span>
227
                </li>
228
                <li>
229
                    <label for="sftp_upload_directory" >Remote upload directory: </label>
230
                    <input type="text" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
231
                    <span class="hint">The path on the remote server where we will upload to</span>
232
                </li>
233
                <input type="hidden" value="" name="sftp_status" id="sftp_status">
234
                <li>
235
                    <label for="sftp_debug_mode">Debug mode: </label>
236
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
237
                        <option value="1">Enabled</option>
238
                        <option value="0" selected="selected">Disabled</option>
239
                    </select>
240
                    <span class="hint">Enables additional debug output in the logs</span>
241
                </li>
242
            </ol>
243
        </fieldset>
244
        <fieldset class="action">
245
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
246
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
247
        </fieldset>
248
    </form>
249
[% END %]
250
251
[% IF op == 'edit_form' && !messages %]
252
    <!-- Modal -->
253
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
254
        <div class="modal-dialog modal-lg">
255
            <div class="modal-content modal-lg">
256
                    <div class="modal-header">
257
                        <h1 class="modal-title">Are you sure?</h1>
258
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
259
                    </div>
260
                    <div class="modal-body">
261
                        <div id="modal_message" class="alert alert-warning">Because you are using the SFTP transport, please run a test of this connection to accept the server's host key.</div>
262
                        <p>Before saving, please check the following details again to make sure you are certain they are correct. Sending or receiving data from an unknown source potentially puts your system at risk.</p>
263
                        <table class="mx-4 mb-3">
264
                            <thead></thead>
265
                            <tbody>
266
                                <tr>
267
                                    <td><strong>Host</strong></td>
268
                                    <td id="modal_host"></td>
269
                                </tr>
270
                                <tr>
271
                                    <td><strong>Port</strong></td>
272
                                    <td id="modal_port"></td>
273
                                </tr>
274
                                <tr>
275
                                    <td><strong>Transport</strong></td>
276
                                    <td id="modal_transport"></td>
277
                                </tr>
278
                                <tr>
279
                                    <td><strong>Username</strong></td>
280
                                    <td id="modal_user_name"></td>
281
                                </tr>
282
                                <tr>
283
                                    <td><strong>Authentication mode</strong></td>
284
                                    <td id="modal_auth_mode"></td>
285
                                </tr>
286
                            </tbody>
287
                        </table>
288
                        <p>If you are ready to progress with these details, please click Save.</p>
289
                    </div>
290
                    <div class="modal-footer">
291
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
292
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
293
                    </div>
294
                </form>
295
            </div>
296
        </div>
297
    </div>
298
    <!-- END Modal -->
299
300
    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
301
302
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
303
        [% INCLUDE 'csrf-token.inc' %]
304
        <input type="hidden" name="op" value="cud-edit_save" />
305
        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
306
        <fieldset class="rows">
307
            <ol>
308
                <li>
309
                    <label for="sftp_name" class="required">Name: </label>
310
                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
311
                    <span class="required">Required</span>
312
                </li>
313
            </ol>
314
        </fieldset>
315
316
        <fieldset class="rows">
317
            <ol>
318
                <li>
319
                    <label for="sftp_host" class="required">Host: </label>
320
                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
321
                    <span class="required">Required</span>
322
                </li>
323
                <li>
324
                    <label for="sftp_port" class="required">Port: </label>
325
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required"/>
326
                    <span class="required">Required</span>
327
                </li>
328
                <li>
329
                    <label for="sftp_transport" class="required">Transport: </label>
330
                    <select name="sftp_transport" id="sftp_transport" class="required">
331
                        [% IF sftp_server.transport == 'ftp' %]
332
                        <option value="ftp" selected="selected">FTP</option>
333
                        [% ELSE %]
334
                        <option value="ftp">FTP</option>
335
                        [% END %]
336
                        [% IF sftp_server.transport == 'sftp' %]
337
                        <option value="sftp" selected="selected">SFTP</option>
338
                        [% ELSE %]
339
                        <option value="sftp">SFTP</option>
340
                        [% END %]
341
                    </select>
342
                    <span class="required">Required</span>
343
                </li>
344
                <li>
345
                    <label for="sftp_passiv">Passive mode: </label>
346
                    <select name="sftp_passiv" id="sftp_passiv" disabled="disabled">
347
                        [% IF sftp_server.passiv == 1 %]
348
                        <option value="1" selected="selected">Enabled (Recommended)</option>
349
                        [% ELSE %]
350
                        <option value="1">Enabled (Recommended)</option>
351
                        [% END %]
352
                        [% IF sftp_server.passiv == 0 %]
353
                        <option value="0" selected="selected">Disabled</option>
354
                        [% ELSE %]
355
                        <option value="0">Disabled</option>
356
                        [% END %]
357
                    </select>
358
                    <span class="hint">Only applies to FTP connections</span>
359
                </li>
360
                <li>
361
                    <label for="sftp_auth_mode">Authentication mode: </label>
362
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
363
                        [% IF sftp_server.auth_mode == 'password' %]
364
                        <option value="password" selected="selected">Password-based</option>
365
                        [% ELSE %]
366
                        <option value="password">Password-based</option>
367
                        [% END %]
368
                        [% IF sftp_server.auth_mode == 'key_file' %]
369
                        <option value="key_file" selected="selected">Key file-based</option>
370
                        [% ELSE %]
371
                        <option value="key_file">Key file-based</option>
372
                        [% END %]
373
                        [% IF sftp_server.auth_mode == 'noauth' %]
374
                        <option value="noauth" selected="selected">No authentication</option>
375
                        [% ELSE %]
376
                        <option value="noauth">No authentication</option>
377
                        [% END %]
378
                    </select>
379
                </li>
380
                <li>
381
                    <label for="sftp_user_name" class="required">Username: </label>
382
                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
383
                    <span class="required">Required</span>
384
                </li>
385
                <li>
386
                    <label for="sftp_password">Password: </label>
387
                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
388
                </li>
389
                <li>
390
                    <label for="sftp_key_file">Key file path: </label>
391
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
392
                    <span class="hint">Only applies to SFTP connections</span>
393
                </li>
394
                <li>
395
                    <label for="sftp_download_directory" >Remote download directory: </label>
396
                    <input type="text" value="[% sftp_server.download_directory | html %]" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
397
                    <span class="hint">The path on the remote server where we will download from</span>
398
                </li>
399
                <li>
400
                    <label for="sftp_upload_directory" >Remote upload directory: </label>
401
                    <input type="text" value="[% sftp_server.upload_directory | html %]" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
402
                    <span class="hint">The path on the remote server where we will upload to</span>
403
                </li>
404
                <input type="hidden" value="" name="sftp_status" id="sftp_status">
405
                <li>
406
                    <label for="sftp_debug_mode">Debug mode: </label>
407
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
408
                        [% IF sftp_server.debug == 1 %]
409
                        <option value="1" selected="selected">Enabled</option>
410
                        [% ELSE %]
411
                        <option value="1">Enabled</option>
412
                        [% END %]
413
                        [% IF sftp_server.debug == 0 %]
414
                        <option value="0" selected="selected">Disabled</option>
415
                        [% ELSE %]
416
                        <option value="0">Disabled</option>
417
                        [% END %]
418
                    </select>
419
                    <span class="hint">Enables additional debug output in the logs</span>
420
                </li>
421
            </ol>
422
        </fieldset>
423
        <fieldset class="action">
424
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
425
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
426
        </fieldset>
427
    </form>
428
[% END %]
429
430
[% IF op == 'test_form' %]
431
432
    <h1>[% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
433
    [% IF sftp_server.id %]
434
    <div class="page-section">
435
        [% IF sftp_server.transport == 'sftp' %]
436
        <div class="alert alert-warning">
437
            <strong>For your information:</strong> As the transport in use for this server is SFTP, please be aware that running a test attempt will also accept the current host key fingerprint of the destination server. If you have doubts about the authenticity of this server, remove its host key from your authorized_keys file immediately, and contact your System Administrator.
438
        </div>
439
        [% END %]
440
        <div class="row">
441
            <div class="col-12 col-lg-6 order-1 order-lg-0">
442
                <h3>Test results</h3>
443
                <div id="testOutput">
444
                    <!-- tests will appear here -->
445
                </div>
446
            </div>
447
            <div class="col-12 col-lg-6 order-0 order-lg-1">
448
                <h3>Test details</h3>
449
                <p>Connection details are as follows:</p>
450
                <table class="mx-4 mb-3">
451
                    <thead></thead>
452
                    <tbody>
453
                        <tr>
454
                            <td><strong>Host</strong></td>
455
                            <td>[% sftp_server.host | html %]</td>
456
                        </tr>
457
                        <tr>
458
                            <td><strong>Port</strong></td>
459
                            <td>[% sftp_server.port | html %]</td>
460
                        </tr>
461
                        <tr>
462
                            <td><strong>Transport</strong></td>
463
                            <td>[% sftp_server.transport FILTER upper | html %]</td>
464
                        </tr>
465
                        <tr>
466
                            <td><strong>Username</strong></td>
467
                            <td>[% sftp_server.user_name | html %]</td>
468
                        </tr>
469
                        <tr>
470
                            <td><strong>Authentication mode</strong></td>
471
                            <td>
472
                                [% IF sftp_server.auth_mode == 'password' %]
473
                                Password-based
474
                                [% ELSE %]
475
                                Key file-based
476
                                [% END %]
477
                            </td>
478
                        </tr>
479
                        <tr>
480
                            <td><strong>Status</strong></td>
481
                            <td>
482
                                [% SWITCH sftp_server.status %]
483
                                [% CASE 'tests_ok' %]
484
                                    Tests passing
485
                                [% CASE 'tests_failed' %]
486
                                    Tests failing
487
                                [% CASE %]
488
                                    <em>Never used</em>
489
                                [% END %]
490
                            </td>
491
                        </tr>
492
                    </tbody>
493
                </table>
494
            </div>
495
        </div>
496
    </div>
497
    <fieldset class="action">
498
        <a href="/cgi-bin/koha/admin/sftp_servers.pl?op=edit_form&sftp_server_id=[% sftp_server.id | uri %]" class="btn btn-primary"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit</a>
499
        <a href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&sftp_server_id=[% sftp_server.id | uri %]" class="cancel">Reset</a>
500
    </fieldset>
501
    [% ELSE %]
502
    <div class="page-section">
503
        <h3>Oops &ndash; Not Found</h3>
504
        <p>An FTP/SFTP server with that ID was not found. Please go back and try again.</p>
505
    </div>
506
    [% END %]
507
[% END %]
508
509
[% IF op == 'list' %]
510
511
    <div id="toolbar" class="btn-toolbar">
512
        <a class="btn btn-default" id="new_sftp_server" href="/cgi-bin/koha/admin/sftp_servers.pl?op=add_form"><i class="fa fa-plus"></i> New FTP/SFTP server</a>
513
    </div>
514
515
    <h1>FTP/SFTP servers</h1>
516
517
    [% IF servers_count < 1 %]
518
        <div class="alert alert-info" id="dno_servers_message">
519
            <p>
520
                <em>There are no FTP/SFTP servers defined.</em><br />
521
                To create one, use the <strong>new FTP/SFTP server</strong> button above.
522
            </p>
523
        </div>
524
    [% ELSE %]
525
        <div class="page-section">
526
            <table id="sftp_servers">
527
                <thead>
528
                    <tr>
529
                        <th>Name</th>
530
                        <th>Host</th>
531
                        <th>Port</th>
532
                        <th>Transport</th>
533
                        <th>Authentication mode</th>
534
                        <th>Username</th>
535
                        <th>Download directory</th>
536
                        <th>Upload directory</th>
537
                        <th>Status</th>
538
                        <th>Debug</th>
539
                        <th data-class-name="actions noExport">Actions</th>
540
                    </tr>
541
                </thead>
542
            </table>
543
        </div> <!-- /.page-section -->
544
    [% END %]
545
[% END %]
546
547
            <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
548
                <div class="modal-dialog">
549
                    <div class="modal-content">
550
                        <div class="modal-header">
551
                            <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
552
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
553
                        </div>
554
                        <div class="modal-body">
555
                            <div id="delete_confirm_dialog"></div>
556
                        </div>
557
                        <div class="modal-footer">
558
                            <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
559
                            <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
560
                        </div>
561
                    </div> <!-- /.modal-content -->
562
                </div> <!-- /.modal-dialog -->
563
            </div> <!-- #delete_confirm_modal -->
564
565
            </main>
566
        </div> <!-- /.col-md-10.order-md-2 -->
567
568
        <div class="col-md-2 order-sm-2 order-md-1">
569
            <aside>
570
                [% INCLUDE 'admin-menu.inc' %]
571
            </aside>
572
        </div> <!-- /.col-md-2.order-md-1 -->
573
     </div> <!-- /.row -->
574
575
576
[% MACRO jsinclude BLOCK %]
577
    [% Asset.js("js/admin-menu.js") | $raw %]
578
    [% INCLUDE 'datatables.inc' %]
579
    <script>
580
        $(document).ready(function() {
581
582
            var sftp_servers_url = '/api/v1/config/sftp_servers';
583
            window.sftp_servers = $("#sftp_servers").kohaTable({
584
                "ajax": {
585
                    "url": sftp_servers_url
586
                },
587
                "language": {
588
                    "emptyTable": "<div class=\"alert alert-info\">"+_("There are no FTP/SFTP servers defined.")+"</div>"
589
                },
590
                "columnDefs": [ {
591
                    "targets": [0,1],
592
                    "render": function(data, type, row, meta) {
593
                        if (type == "display") {
594
                            if(data != null) {
595
                                return data.escapeHtml();
596
                            } else {
597
                                return "Default";
598
                            }
599
                        }
600
                        return data;
601
                    }
602
                } ],
603
                "columns": [
604
                    {
605
                        "data": "name",
606
                        "searchable": true,
607
                        "orderable": true
608
                    },
609
                    {
610
                        "data": "host",
611
                        "searchable": true,
612
                        "orderable": true
613
                    },
614
                    {
615
                        "data": "port",
616
                        "searchable": true,
617
                        "orderable": false
618
                    },
619
                    {
620
                        "data": "transport",
621
                        "render": function(data, type, row, meta) {
622
                            return data.toUpperCase();
623
                        },
624
                        "searchable": true,
625
                        "orderable": false
626
                    },
627
                    {
628
                        "data": "auth_mode",
629
                        "render": function(data, type, row, meta) {
630
                            if(data == "password") {
631
                                return _("Password-based");
632
                            } else if(data == "key_file") {
633
                                return _("Key file-based");
634
                            } else {
635
                                return _("No authentication");
636
                            }
637
                        },
638
                        "searchable": false,
639
                        "orderable": false
640
                    },
641
                    {
642
                        "data": "user_name",
643
                        "searchable": false,
644
                        "orderable": false
645
                    },
646
                    {
647
                        "data": "download_directory",
648
                        "render": function(data, type, row, meta) {
649
                            if(data) {
650
                                return data;
651
                            } else {
652
                                return "<em>" + _("Not specified") + "</em>";
653
                            }
654
                        },
655
                        "searchable": false,
656
                        "orderable": false
657
                    },
658
                    {
659
                        "data": "upload_directory",
660
                        "render": function(data, type, row, meta) {
661
                            if(data) {
662
                                return data;
663
                            } else {
664
                                return "<em>" + _("Not specified") + "</em>";
665
                            }
666
                        },
667
                        "searchable": false,
668
                        "orderable": false
669
                    },
670
                    {
671
                        "data": "status",
672
                        "render": function(data, type, row, meta) {
673
                            if (data == "tests_ok") {
674
                                return _("Tests passing");
675
                            } else if (data == "tests_failed") {
676
                                return _("Tests failing");
677
                            } else {
678
                                return "<em>" + _("Never used") + "</em>";
679
                            }
680
                        },
681
                        "searchable": false,
682
                        "orderable": false
683
                    },
684
                    {
685
                        "data": "debug",
686
                        "render": function(data, type, row, meta) {
687
                            if(data == true) {
688
                                return "[% tp("Active", "On") | html %]";
689
                            }
690
                            else {
691
                                return _("Off");
692
                            }
693
                        },
694
                        "searchable": false,
695
                        "orderable": false
696
                    },
697
                    {
698
                        "data": function(row, type, val, meta) {
699
                            var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&amp;sftp_server_id='+ encodeURIComponent(row.sftp_server_id) +'"><i class="fa-solid fa-vial" aria-hidden="true"></i> '+_("Test")+'</a>'+"\n";
700
                            result += '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/sftp_servers.pl?op=edit_form&amp;sftp_server_id='+ encodeURIComponent(row.sftp_server_id) +'"><i class="fa-solid fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'+"\n";
701
                            result += '<a class="btn btn-default btn-xs delete_server" role="button" href="#" data-bs-toggle="modal" data-bs-target="#delete_confirm_modal" data-sftp-server-id="'+ encodeURIComponent(row.sftp_server_id) +'" data-sftp-server-name="'+ encodeURIComponent(row.name.escapeHtml()) +'"><i class="fa fa-trash-can" aria-hidden="true"></i> '+_("Delete")+'</a>';
702
                            return result;
703
                        },
704
                        "searchable": false,
705
                        "orderable": false
706
                    }
707
                ],
708
                createdRow: function(row, data, dataIndex) {
709
                    if(data.is_default) {
710
                        $(row).addClass('default warn');
711
                    }
712
                    if(data.debug) {
713
                        $(row).addClass('debug');
714
                    }
715
                },
716
            });
717
718
            $('#sftp_servers').on("click", '.delete_server', function() {
719
                var sftp_server_id   = $(this).data('sftp-server-id');
720
                var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
721
722
                $("#delete_confirm_dialog").html(
723
                    _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
724
                );
725
                $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
726
                $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
727
            });
728
729
            $("#delete_confirm_modal_button").on("click", function() {
730
731
                var sftp_server_id   = $(this).data('sftp-server-id');
732
                var sftp_server_name = $(this).data('sftp-server-name');
733
734
                $.ajax({
735
                    method: "DELETE",
736
                    url: "/api/v1/config/sftp_servers/"+sftp_server_id
737
                }).success(function() {
738
                    window.sftp_servers.api().ajax.reload(function(data) {
739
                        $("#sftp_action_result_dialog").hide();
740
                        $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
741
                    });
742
                }).fail(function() {
743
                    $("#sftp_delete_error").html(_("Error deleting server '%s'. Please ensure all linked EDI accounts are unlinked or deleted. Check the logs for details.").format(sftp_server_name)).show();
744
                }).done(function() {
745
                    $("#delete_confirm_modal").modal('hide');
746
                });
747
            });
748
749
            transportChange();
750
            $("#sftp_transport").on("change", function(event) {
751
                transportChange();
752
            });
753
754
            authModeChange();
755
            $("#sftp_auth_mode").on("change", function(event) {
756
                authModeChange();
757
            });
758
759
            $('#confirm_key_accept_submit').on('click', function(event) {
760
                event.preventDefault();
761
762
                if ( $('#add').length > 0 ) {
763
                    if( $('#add').valid() == true ) {
764
                        modalChange();
765
                        $('#confirm_key_accept').modal('show');
766
                    } else {
767
                        $('#confirm_key_accept').modal('hide');
768
                    }
769
                }
770
771
                if ( $('#edit_save').length > 0 ) {
772
                    if( $('#edit_save').valid() == true ) {
773
                        modalChange();
774
                        $('#confirm_key_accept').modal('show');
775
                    } else {
776
                        $('#confirm_key_accept').modal('hide');
777
                    }
778
                }
779
780
            });
781
782
            $('#confirm_key_accept .approve').on('click', function() {
783
                $('#confirm_key_accept .deny').click();
784
785
                if ( $('#add').length > 0 ) {
786
                    $('#add').submit();
787
                }
788
789
                if ( $('#edit_save').length > 0 ) {
790
                    $('#edit_save').submit();
791
                }
792
            });
793
794
        });
795
796
        function transportChange() {
797
            let sftp_transport = $("#sftp_transport");
798
799
            if(sftp_transport.val() == "ftp") {
800
                $("#sftp_host").removeAttr("disabled");
801
                $("#sftp_port").removeAttr("disabled");
802
                $("#sftp_passiv").removeAttr("disabled");
803
                $("#sftp_auth_mode").removeAttr("disabled");
804
                $("#sftp_user_name").removeAttr("disabled");
805
                $("#sftp_password").removeAttr("disabled");
806
                $("#sftp_key_file").attr("disabled", "disabled");
807
808
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
809
                $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
810
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
811
                if($("#sftp_auth_mode option:selected").val() == "key_file") {
812
                    $("#sftp_auth_mode option[value='password']").prop("selected", true);
813
                }
814
815
                let sftp_port = $("#sftp_port").val();
816
                if(sftp_port == 22) $("#sftp_port").val("21");
817
818
                authModeChange();
819
            } else if(sftp_transport.val() == "sftp") {
820
                $("#sftp_host").removeAttr("disabled");
821
                $("#sftp_port").removeAttr("disabled");
822
                $("#sftp_passiv").attr("disabled", "disabled");
823
                $("#sftp_auth_mode").removeAttr("disabled");
824
                $("#sftp_user_name").removeAttr("disabled");
825
                $("#sftp_password").removeAttr("disabled");
826
                $("#sftp_key_file").removeAttr("disabled");
827
828
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
829
                $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
830
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
831
                $("#sftp_passiv option[value='1']").prop("selected", true);
832
833
                let sftp_port = $("#sftp_port").val();
834
                if(sftp_port == 21) $("#sftp_port").val("22");
835
836
                return authModeChange();
837
            }
838
        }
839
840
        function authModeChange() {
841
            let sftp_auth_mode = $("#sftp_auth_mode").val();
842
843
            if(sftp_auth_mode == "password") {
844
                $("#sftp_password").removeAttr("disabled");
845
                $("#sftp_key_file").attr("disabled", "disabled");
846
            } else if(sftp_auth_mode == "key_file") {
847
                $("#sftp_password").attr("disabled", "disabled");
848
                $("#sftp_key_file").removeAttr("disabled");
849
            } else {
850
                $("#sftp_password").attr("disabled", "disabled");
851
                $("#sftp_key_file").attr("disabled", "disabled");
852
            }
853
        }
854
855
        function modalChange() {
856
            $('#modal_message').hide();
857
            if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
858
859
            $('#modal_host').text( $('#sftp_host').val() );
860
            $('#modal_port').text( $('#sftp_port').val() );
861
            $('#modal_transport').text( $('#sftp_transport option:selected').text() );
862
            $('#modal_user_name').text( $('#sftp_user_name').val() );
863
            $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
864
        }
865
    </script>
866
867
    [% IF op == 'test_form' %]
868
    <script>
869
        $(document).ready(function() {
870
            handleTests();
871
        });
872
873
        function handleTests() {
874
            var testOutput = $('#testOutput');
875
            var runTests   = $('#runTests');
876
877
            runTests.addClass('disabled');
878
            testOutput.html('<div class="spinner-border text-warning" style="height:1.5rem;width:1.5rem" role="status"><span class="sr-only">Loading...</span></div><span class="pending-loading">' + _("Running tests . . . ") + '</span>');
879
880
            return $.ajax({
881
                url: "/api/v1/sftp_server/[% sftp_server.id | html %]/test_connection",
882
            })
883
            .done(function(data) {
884
                testOutput.text('');
885
886
                for ( let [key, value] of Object.entries( data ) ) {
887
                    var title;
888
                    switch(key) {
889
                        case '1_sftp_conn':
890
                            title = _("Testing SFTP connectivity");
891
                            break;
892
                        case '1_ftp_conn':
893
                            title = _("Testing FTP connectivity");
894
                            break;
895
                        case '2_ftp_login':
896
                            title = _("Testing we can log in");
897
                            break;
898
                        case '2a_sftp_cwd_dl':
899
                        case '3a_ftp_cwd_dl':
900
                            title = _("Testing we can cwd to download directory");
901
                            break;
902
                        case '2b_sftp_ls_dl':
903
                        case '3b_ftp_ls_dl':
904
                            title = _("Testing we can list download directory");
905
                            break;
906
                        case '2c_sftp_cwd_ul':
907
                        case '3c_ftp_cwd_ul':
908
                            title = _("Testing we can cwd to upload directory");
909
                            break;
910
                        case '2d_sftp_ls_ul':
911
                        case '3d_ftp_ls_ul':
912
                            title = _("Testing we can list upload directory");
913
                            break;
914
                        case '3_sftp_write':
915
                        case '4_ftp_write':
916
                            title = _("Testing we can write a test file");
917
                            break;
918
                        case '4_sftp_del':
919
                        case '5_ftp_del':
920
                            title = _("Testing we can delete test file");
921
                            break;
922
                        default:
923
                            title = key
924
                    }
925
926
                    if ( value.passed ) {
927
                        testOutput.append(
928
                            '<i class="text-success fa-solid fa-circle-check"></i> '
929
                            + title
930
                            + '... <span class="text-success">'
931
                            + _("Passed")
932
                            + '</span><br />'
933
                        );
934
                        if( value.msg ) testOutput.append( _("Message: ") + '<code>' + value.msg + '</code><br />' );
935
                    } else {
936
                        testOutput.append(
937
                            '<i class="text-danger fa-solid fa-circle-xmark"></i> '
938
                            + title
939
                            + '... <span class="text-danger">'
940
                            + _("Failed")
941
                            + '</span><br />'
942
                        );
943
                        if( value.err ) testOutput.append( _("Error message: ") + '<code>' + value.err + '</code><br />' );
944
                    }
945
                    testOutput.append( '<br />' );
946
                }
947
            })
948
            .fail(function(data) {
949
                if( data.status == 404 ) {
950
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("FTP/SFTP Server not found") );
951
                } else {
952
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("Internal Server Error. Please check the server logs") );
953
                }
954
            })
955
            .always(function(data) {
956
                runTests.removeClass('disabled');
957
            });
958
        }
959
    </script>
960
    [% END %]
961
[% END %]
962
963
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/t/Koha/Auth/Permissions.t (+1 lines)
Lines 207-212 subtest 'superlibrarian tests' => sub { Link Here
207
        'CAN_user_parameters_manage_search_targets'                 => 1,
207
        'CAN_user_parameters_manage_search_targets'                 => 1,
208
        'CAN_user_parameters_manage_sms_providers'                  => 1,
208
        'CAN_user_parameters_manage_sms_providers'                  => 1,
209
        'CAN_user_parameters_manage_smtp_servers'                   => 1,
209
        'CAN_user_parameters_manage_smtp_servers'                   => 1,
210
        'CAN_user_parameters_manage_sftp_servers'                   => 1,
210
        'CAN_user_parameters_manage_sysprefs'                       => 1,
211
        'CAN_user_parameters_manage_sysprefs'                       => 1,
211
        'CAN_user_parameters_manage_transfers'                      => 1,
212
        'CAN_user_parameters_manage_transfers'                      => 1,
212
        'CAN_user_parameters_manage_usage_stats'                    => 1,
213
        'CAN_user_parameters_manage_usage_stats'                    => 1,
(-)a/t/db_dependent/Koha/SFTP/Server.t (+167 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 7;
21
use Test::Exception;
22
use Test::Warn;
23
24
use Koha::SFTP::Servers;
25
26
use t::lib::TestBuilder;
27
use t::lib::Mocks;
28
29
my $schema  = Koha::Database->new->schema;
30
my $builder = t::lib::TestBuilder->new;
31
32
subtest 'to_api() tests' => sub {
33
34
    plan tests => 2;
35
36
    $schema->storage->txn_begin;
37
38
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
39
40
    ok( !exists $sftp_server->to_api->{password}, 'Password is not part of the API representation' );
41
    ok( !exists $sftp_server->to_api->{key_file}, 'Key file is not part of the API representation' );
42
43
    $schema->storage->txn_rollback;
44
};
45
46
subtest 'update_password() tests' => sub {
47
48
    plan tests => 2;
49
50
    $schema->storage->txn_begin;
51
52
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
53
    $sftp_server->update_password('test123');
54
55
    ok( $sftp_server->password ne 'test123', 'Password should not be in plain text' );
56
    is( length( $sftp_server->password ), 64, 'Password has should be 64 characters long' );
57
58
    $schema->storage->txn_rollback;
59
};
60
61
subtest 'update_key_file() tests' => sub {
62
63
    plan tests => 2;
64
65
    $schema->storage->txn_begin;
66
67
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
68
    $sftp_server->update_key_file('321tset');
69
70
    ok( $sftp_server->key_file ne 'test123', 'Password should not be in plain text' );
71
    is( length( $sftp_server->key_file ), 64, 'Password has should be 64 characters long' );
72
73
    $schema->storage->txn_rollback;
74
};
75
76
subtest 'update_status() tests' => sub {
77
78
    plan tests => 2;
79
80
    $schema->storage->txn_begin;
81
82
    my $sftp_server        = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
83
    my $sftp_server_status = 'tests_ok';
84
85
    $sftp_server->set(
86
        {
87
            password => 'somepass',
88
            key_file => 'somekey',
89
        }
90
    )->store;
91
92
    isnt( $sftp_server_status, $sftp_server->status, 'Status should not by tests_ok' );
93
94
    $sftp_server->update_status($sftp_server_status);
95
96
    is( $sftp_server_status, $sftp_server->status, 'Status should be tests_ok' );
97
98
    $schema->storage->txn_rollback;
99
100
};
101
102
subtest 'plain_text_password() tests' => sub {
103
104
    plan tests => 2;
105
106
    $schema->storage->txn_begin;
107
108
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
109
110
    $sftp_server->update_password('test123');
111
112
    my $sftp_server_plain_text_password = $sftp_server->plain_text_password;
113
114
    isnt( $sftp_server_plain_text_password, $sftp_server->password, 'Password and password hash shouldn\'t match' );
115
    is( $sftp_server_plain_text_password, 'test123', 'Password should be in plain text' );
116
117
    $schema->storage->txn_rollback;
118
};
119
120
subtest 'plain_text_key() tests' => sub {
121
122
    plan tests => 2;
123
124
    $schema->storage->txn_begin;
125
126
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
127
128
    $sftp_server->update_key_file('321tset');
129
130
    my $sftp_server_plain_text_key = $sftp_server->plain_text_key;
131
132
    isnt( $sftp_server_plain_text_key, $sftp_server->key_file, 'Key file and key file hash shouldn\'t match' );
133
    is( $sftp_server_plain_text_key, "321tset\n", 'Key file should be in plain text' );
134
135
    $schema->storage->txn_rollback;
136
};
137
138
subtest 'write_key_file() tests' => sub {
139
140
    plan tests => 3;
141
142
    $schema->storage->txn_begin;
143
144
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
145
146
    $sftp_server->update_key_file('321tset');
147
148
    my $path = '/tmp/kohadev_test';
149
    t::lib::Mocks::mock_config( 'upload_path', $path );
150
    mkdir $path if !-d $path;
151
152
    my $first_test = $sftp_server->write_key_file;
153
154
    my $file        = $sftp_server->locate_key_file;
155
    my $second_test = ( -f $file );
156
157
    open( my $fh, '<', $sftp_server->locate_key_file );
158
    my $third_test = <$fh>;
159
160
    is( $first_test,  1,           'Writing key file should return 1' );
161
    is( $second_test, 1,           'Written key file should exist' );
162
    is( $third_test,  "321tset\n", 'The contents of the key file should be 321tset\n' );
163
164
    unlink $file;
165
166
    $schema->storage->txn_rollback;
167
};
(-)a/t/db_dependent/api/v1/sftp_servers.t (-1 / +443 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Test::More tests => 6;
21
use Test::Mojo;
22
23
use t::lib::TestBuilder;
24
use t::lib::Mocks;
25
26
use Koha::SFTP::Servers;
27
use Koha::Database;
28
29
my $schema  = Koha::Database->new->schema;
30
my $builder = t::lib::TestBuilder->new;
31
32
my $t = Test::Mojo->new('Koha::REST::V1');
33
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
34
35
subtest 'list() tests' => sub {
36
37
    plan tests => 11;
38
39
    $schema->storage->txn_begin;
40
41
    Koha::SFTP::Servers->search->delete;
42
43
    my $librarian = $builder->build_object(
44
        {
45
            class => 'Koha::Patrons',
46
            value => { flags => 3**2 }    # parameters flag = 3
47
        }
48
    );
49
    my $password = 'thePassword123';
50
    $librarian->set_password( { password => $password, skip_validation => 1 } );
51
    my $userid = $librarian->userid;
52
53
    my $patron = $builder->build_object(
54
        {
55
            class => 'Koha::Patrons',
56
            value => { flags => 0 }
57
        }
58
    );
59
60
    $patron->set_password( { password => $password, skip_validation => 1 } );
61
    my $unauth_userid = $patron->userid;
62
63
    ## Authorized user tests
64
    # No FTP/SFTP servers, so empty array should be returned
65
    $t->get_ok("//$userid:$password@/api/v1/config/sftp_servers")->status_is(200)->json_is( [] );
66
67
    my $sftp_server = $builder->build_object(
68
        {
69
            class => 'Koha::SFTP::Servers',
70
            value => {
71
                password => undef,
72
                key_file => undef,
73
            },
74
        }
75
    );
76
77
    # One sftp server created, should get returned
78
    $t->get_ok("//$userid:$password@/api/v1/config/sftp_servers")->status_is(200)->json_is( [ $sftp_server->to_api ] );
79
80
    my $another_sftp_server = $builder->build_object(
81
        {
82
            class => 'Koha::SFTP::Servers',
83
            value => {
84
                password => undef,
85
                key_file => undef,
86
            },
87
        }
88
    );
89
90
    # Two FTP/SFTP servers created, they should both be returned
91
    $t->get_ok("//$userid:$password@/api/v1/config/sftp_servers")->status_is(200)
92
        ->json_is( [ $sftp_server->to_api, $another_sftp_server->to_api, ] );
93
94
    # Unauthorized access
95
    $t->get_ok("//$unauth_userid:$password@/api/v1/config/sftp_servers")->status_is(403);
96
97
    $schema->storage->txn_rollback;
98
};
99
100
subtest 'get() tests' => sub {
101
102
    plan tests => 8;
103
104
    $schema->storage->txn_begin;
105
106
    my $sftp_server = $builder->build_object(
107
        {
108
            class => 'Koha::SFTP::Servers',
109
            value => {
110
                password => undef,
111
                key_file => undef,
112
            },
113
        }
114
    );
115
    my $librarian = $builder->build_object(
116
        {
117
            class => 'Koha::Patrons',
118
            value => { flags => 3**2 }    # parameters flag = 3
119
        }
120
    );
121
    my $password = 'thePassword123';
122
    $librarian->set_password( { password => $password, skip_validation => 1 } );
123
    my $userid = $librarian->userid;
124
125
    my $patron = $builder->build_object(
126
        {
127
            class => 'Koha::Patrons',
128
            value => { flags => 0 }
129
        }
130
    );
131
132
    $patron->set_password( { password => $password, skip_validation => 1 } );
133
    my $unauth_userid = $patron->userid;
134
135
    $t->get_ok( "//$userid:$password@/api/v1/config/sftp_servers/" . $sftp_server->id )->status_is(200)
136
        ->json_is( $sftp_server->to_api );
137
138
    $t->get_ok( "//$unauth_userid:$password@/api/v1/config/sftp_servers/" . $sftp_server->id )->status_is(403);
139
140
    my $sftp_server_to_delete = $builder->build_object(
141
        {
142
            class => 'Koha::SFTP::Servers',
143
            value => {
144
                password => undef,
145
                key_file => undef,
146
            },
147
        }
148
    );
149
    my $non_existent_id = $sftp_server_to_delete->id;
150
    $sftp_server_to_delete->delete;
151
152
    $t->get_ok("//$userid:$password@/api/v1/config/sftp_servers/$non_existent_id")->status_is(404)
153
        ->json_is( '/error' => 'FTP/SFTP server not found' );
154
155
    $schema->storage->txn_rollback;
156
};
157
158
subtest 'add() tests' => sub {
159
160
    plan tests => 15;
161
162
    $schema->storage->txn_begin;
163
164
    Koha::SFTP::Servers->search->delete;
165
166
    my $librarian = $builder->build_object(
167
        {
168
            class => 'Koha::Patrons',
169
            value => { flags => 3**2 }    # parameters flag = 3
170
        }
171
    );
172
    my $password = 'thePassword123';
173
    $librarian->set_password( { password => $password, skip_validation => 1 } );
174
    my $userid = $librarian->userid;
175
176
    my $patron = $builder->build_object(
177
        {
178
            class => 'Koha::Patrons',
179
            value => { flags => 0 }
180
        }
181
    );
182
183
    $patron->set_password( { password => $password, skip_validation => 1 } );
184
    my $unauth_userid = $patron->userid;
185
186
    my $sftp_server = $builder->build_object(
187
        {
188
            class => 'Koha::SFTP::Servers',
189
            value => {
190
                password => undef,
191
                key_file => undef,
192
            },
193
        }
194
    );
195
    my $sftp_server_data = $sftp_server->to_api;
196
    delete $sftp_server_data->{sftp_server_id};
197
    $sftp_server->delete;
198
199
    # Unauthorized attempt to write
200
    $t->post_ok( "//$unauth_userid:$password@/api/v1/config/sftp_servers" => json => $sftp_server_data )
201
        ->status_is(403);
202
203
    # Authorized attempt to write invalid data
204
    my $sftp_server_with_invalid_field = {
205
        name => 'Some other server',
206
        blah => 'blah'
207
    };
208
209
    $t->post_ok( "//$userid:$password@/api/v1/config/sftp_servers" => json => $sftp_server_with_invalid_field )
210
        ->status_is(400)->json_is(
211
        "/errors" => [
212
            {
213
                message => "Properties not allowed: blah.",
214
                path    => "/body"
215
            }
216
        ]
217
        );
218
219
    # Authorized attempt to write
220
    my $sftp_server_id =
221
        $t->post_ok( "//$userid:$password@/api/v1/config/sftp_servers" => json => $sftp_server_data )
222
        ->status_is( 201, 'SWAGGER3.2.1' )
223
        ->header_like( Location => qr|^\/api\/v1\/config\/sftp_servers\/\d*|, 'SWAGGER3.4.1' )
224
        ->json_is( '/name' => $sftp_server_data->{name} )->tx->res->json->{sftp_server_id};
225
226
    # Authorized attempt to create with null id
227
    $sftp_server_data->{sftp_server_id} = undef;
228
    $t->post_ok( "//$userid:$password@/api/v1/config/sftp_servers" => json => $sftp_server_data )->status_is(400)
229
        ->json_has('/errors');
230
231
    # Authorized attempt to create with existing id
232
    $sftp_server_data->{sftp_server_id} = $sftp_server_id;
233
    $t->post_ok( "//$userid:$password@/api/v1/config/sftp_servers" => json => $sftp_server_data )->status_is(400)
234
        ->json_is(
235
        "/errors" => [
236
            {
237
                message => "Read-only.",
238
                path    => "/body/sftp_server_id"
239
            }
240
        ]
241
        );
242
243
    $schema->storage->txn_rollback;
244
};
245
246
subtest 'update() tests' => sub {
247
248
    plan tests => 15;
249
250
    $schema->storage->txn_begin;
251
252
    my $librarian = $builder->build_object(
253
        {
254
            class => 'Koha::Patrons',
255
            value => { flags => 3**2 }    # parameters flag = 3
256
        }
257
    );
258
    my $password = 'thePassword123';
259
    $librarian->set_password( { password => $password, skip_validation => 1 } );
260
    my $userid = $librarian->userid;
261
262
    my $patron = $builder->build_object(
263
        {
264
            class => 'Koha::Patrons',
265
            value => { flags => 0 }
266
        }
267
    );
268
269
    $patron->set_password( { password => $password, skip_validation => 1 } );
270
    my $unauth_userid = $patron->userid;
271
272
    my $sftp_server_id = $builder->build_object(
273
        {
274
            class => 'Koha::SFTP::Servers',
275
            value => {
276
                password => undef,
277
                key_file => undef,
278
            },
279
        }
280
    )->id;
281
282
    # Unauthorized attempt to update
283
    $t->put_ok( "//$unauth_userid:$password@/api/v1/config/sftp_servers/$sftp_server_id" => json =>
284
            { name => 'New unauthorized name change' } )->status_is(403);
285
286
    # Attempt partial update on a PUT
287
    my $sftp_server_with_missing_field = {
288
        host   => 'localhost',
289
        passiv => '1'
290
    };
291
292
    $t->put_ok(
293
        "//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id" => json => $sftp_server_with_missing_field )
294
        ->status_is(400)->json_is( "/errors" => [ { message => "Missing property.", path => "/body/name" } ] );
295
296
    # Full object update on PUT
297
    my $sftp_server_with_updated_field = {
298
        name     => "Some name",
299
        password => "some_pass",
300
    };
301
302
    $t->put_ok(
303
        "//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id" => json => $sftp_server_with_updated_field )
304
        ->status_is(200)->json_is( '/name' => 'Some name' );
305
306
    # Authorized attempt to write invalid data
307
    my $sftp_server_with_invalid_field = {
308
        blah => "Blah",
309
        name => 'Some name'
310
    };
311
312
    $t->put_ok(
313
        "//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id" => json => $sftp_server_with_invalid_field )
314
        ->status_is(400)->json_is(
315
        "/errors" => [
316
            {
317
                message => "Properties not allowed: blah.",
318
                path    => "/body"
319
            }
320
        ]
321
        );
322
323
    my $sftp_server_to_delete = $builder->build_object(
324
        {
325
            class => 'Koha::SFTP::Servers',
326
            value => {
327
                password => undef,
328
                key_file => undef,
329
            },
330
        }
331
    );
332
    my $non_existent_id = $sftp_server_to_delete->id;
333
    $sftp_server_to_delete->delete;
334
335
    $t->put_ok(
336
        "//$userid:$password@/api/v1/config/sftp_servers/$non_existent_id" => json => $sftp_server_with_updated_field )
337
        ->status_is(404);
338
339
    # Wrong method (POST)
340
    $sftp_server_with_updated_field->{sftp_server_id} = 2;
341
342
    $t->post_ok(
343
        "//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id" => json => $sftp_server_with_updated_field )
344
        ->status_is(404);
345
346
    $schema->storage->txn_rollback;
347
};
348
349
subtest 'delete() tests' => sub {
350
351
    plan tests => 7;
352
353
    $schema->storage->txn_begin;
354
355
    my $librarian = $builder->build_object(
356
        {
357
            class => 'Koha::Patrons',
358
            value => { flags => 3**2 }    # parameters flag = 3
359
        }
360
    );
361
    my $password = 'thePassword123';
362
    $librarian->set_password( { password => $password, skip_validation => 1 } );
363
    my $userid = $librarian->userid;
364
365
    my $patron = $builder->build_object(
366
        {
367
            class => 'Koha::Patrons',
368
            value => { flags => 0 }
369
        }
370
    );
371
372
    $patron->set_password( { password => $password, skip_validation => 1 } );
373
    my $unauth_userid = $patron->userid;
374
375
    my $sftp_server_id = $builder->build_object(
376
        {
377
            class => 'Koha::SFTP::Servers',
378
            value => {
379
                password => undef,
380
                key_file => undef,
381
            },
382
        }
383
    )->id;
384
385
    # Unauthorized attempt to delete
386
    $t->delete_ok("//$unauth_userid:$password@/api/v1/config/sftp_servers/$sftp_server_id")->status_is(403);
387
388
    $t->delete_ok("//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id")->status_is( 204, 'SWAGGER3.2.4' )
389
        ->content_is( '', 'SWAGGER3.3.4' );
390
391
    $t->delete_ok("//$userid:$password@/api/v1/config/sftp_servers/$sftp_server_id")->status_is(404);
392
393
    $schema->storage->txn_rollback;
394
};
395
396
subtest 'test() tests' => sub {
397
398
    plan tests => 5;
399
400
    $schema->storage->txn_begin;
401
402
    my $librarian = $builder->build_object(
403
        {
404
            class => 'Koha::Patrons',
405
            value => { flags => 3**2 }    # parameters flag = 3
406
        }
407
    );
408
    my $password = 'thePassword123';
409
    $librarian->set_password( { password => $password, skip_validation => 1 } );
410
    my $userid = $librarian->userid;
411
412
    my $patron = $builder->build_object(
413
        {
414
            class => 'Koha::Patrons',
415
            value => { flags => 0 }
416
        }
417
    );
418
419
    $patron->set_password( { password => $password, skip_validation => 1 } );
420
    my $unauth_userid = $patron->userid;
421
422
    my $sftp_server = $builder->build_object(
423
        {
424
            class => 'Koha::SFTP::Servers',
425
            value => {
426
                password => undef,
427
                key_file => undef,
428
            },
429
        }
430
    );
431
    my $sftp_server_id = $sftp_server->id;
432
433
    # Unauthorized attempt to test
434
    $t->get_ok("//$unauth_userid:$password@/api/v1/sftp_server/$sftp_server_id/test_connection")->status_is(403);
435
436
    $t->get_ok("//$userid:$password@/api/v1/sftp_server/$sftp_server_id/test_connection")
437
        ->status_is( 200, 'SWAGGER3.2.4' )
438
        ->content_is( '{"1_ftp_conn":{"err":"cannot connect to '
439
            . $sftp_server->host
440
            . ': Name or service not known","msg":null,"passed":false}}', 'SWAGGER3.3.4' );
441
442
    $schema->storage->txn_rollback;
443
};

Return to bug 35761