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 (+412 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->password( Koha::Encryption->new->encrypt_hex( $self->password ) )
52
        if $self->password;
53
54
    $self->key_file( Koha::Encryption->new->encrypt_hex( _dos2unix( $self->key_file ) ) )
55
        if $self->key_file;
56
57
    $self->write_key_file
58
        if $self->key_file;
59
60
    $self->download_directory( $self->download_directory . '/' )
61
        if ( ( substr( $self->download_directory, -1 ) ne '/' ) && ( $self->download_directory ne '' ) );
62
    $self->upload_directory( $self->upload_directory . '/' )
63
        if ( ( substr( $self->upload_directory, -1 ) ne '/' ) && ( $self->upload_directory ne '' ) );
64
65
    return $self->SUPER::store;
66
}
67
68
=head3 to_api
69
70
    my $json = $sftp_server->to_api;
71
72
Overloaded method that returns a JSON representation of the Koha::SFTP::Server object,
73
suitable for API output.
74
75
=cut
76
77
sub to_api {
78
    my ( $self, $params ) = @_;
79
80
    my $json_sftp = $self->SUPER::to_api($params);
81
    return unless $json_sftp;
82
    delete $json_sftp->{password};
83
84
    return $json_sftp;
85
}
86
87
=head3 to_api_mapping
88
89
This method returns the mapping for representing a Koha::SFTP::Server object
90
on the API.
91
92
=cut
93
94
sub to_api_mapping {
95
    return { id => 'sftp_server_id' };
96
}
97
98
=head3 plain_text_password
99
100
    $server->plain_text_password;
101
Fetches the plaintext password, from the object
102
103
=cut
104
105
sub plain_text_password {
106
    my ($self) = @_;
107
108
    return Koha::Encryption->new->decrypt_hex( $self->password )
109
        if $self->password;
110
}
111
112
=head3 plain_text_key
113
114
    $server->plain_text_key;
115
Fetches the plaintext key file, from the object
116
117
=cut
118
119
sub plain_text_key {
120
    my ($self) = @_;
121
122
    return Koha::Encryption->new->decrypt_hex( $self->key_file ) . "\n"
123
        if $self->key_file;
124
}
125
126
=head3 write_key_file
127
128
    $server->write_key_file;
129
Writes the keyfile from the db into a file
130
131
=cut
132
133
sub write_key_file {
134
    my ($self)      = @_;
135
    my $upload_path = C4::Context->config('upload_path') or return;
136
    my $key_path    = $upload_path . '/ssh_keys';
137
    my $key_file    = $key_path . '/id_ssh_' . $self->id;
138
139
    mkdir $key_path if ( !-d $key_path );
140
141
    unlink $key_file if ( -f $key_file );
142
    my $fh;
143
    $fh = IO::File->new( $key_file, 'w' ) or return;
144
    chmod 0600, $key_file if ( -f $key_file );
145
146
    print $fh $self->plain_text_key;
147
148
    undef $fh;
149
150
    return 1;
151
}
152
153
=head3 locate_key_file
154
155
    $server->locate_key_file;
156
Returns the keyfile's expected path
157
158
=cut
159
160
sub locate_key_file {
161
    my ($self) = @_;
162
    my $upload_path = C4::Context->config('upload_path');
163
    return if not defined $upload_path;
164
165
    my $keyf_path = $upload_path . '/ssh_keys/id_ssh_' . $self->id;
166
167
    return ( -f $keyf_path ) ? $keyf_path : undef;
168
}
169
170
=head3 update_status
171
172
    $server->update_status;
173
Update the status of this FTP/SFTP server
174
175
=cut
176
177
sub update_status {
178
    my ( $self, $value ) = @_;
179
180
    return
181
        if not defined $value;
182
183
    return $self->set(
184
        {
185
            status   => $value,
186
            password => $self->plain_text_password,
187
            key_file => $self->plain_text_key,
188
        }
189
    )->store;
190
}
191
192
=head3 test_conn
193
194
    $server->test_conn;
195
Tests a connection to a given sftp server
196
197
=cut
198
199
sub test_conn {
200
    my ($self) = @_;
201
    my $default_result = {
202
        passed => Mojo::JSON->false,
203
        err    => undef,
204
        msg    => undef,
205
    };
206
    my $sftp_test_results;
207
208
    if ( $self->transport eq 'sftp' ) {
209
        $sftp_test_results->{'1_sftp_conn'} = {%$default_result};
210
        my $sftp = Net::SFTP::Foreign->new(
211
            host     => $self->host,
212
            user     => $self->user_name,
213
            password => $self->plain_text_password,
214
            key_path => $self->locate_key_file,
215
            port     => $self->port,
216
            timeout  => 10,
217
            more     => [
218
                qw(-vv),
219
                qw(-o StrictHostKeyChecking=no),
220
            ],
221
        );
222
        unless ( $sftp->error ) {
223
            $sftp_test_results->{'1_sftp_conn'}->{'passed'} = Mojo::JSON->true;
224
            $sftp_test_results->{'1_sftp_conn'}->{'msg'}    = $sftp->status;
225
        } else {
226
            $sftp_test_results->{'1_sftp_conn'}->{'err'} = $sftp->error;
227
        }
228
229
        unless ( $sftp->error ) {
230
            unless ( $self->download_directory eq '' ) {
231
                $sftp_test_results->{'2a_sftp_cwd_dl'} = {%$default_result};
232
                $sftp->setcwd( $self->download_directory );
233
                unless ( $sftp->error ) {
234
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'passed'} = Mojo::JSON->true;
235
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'msg'}    = $sftp->status;
236
                } else {
237
                    $sftp_test_results->{'2a_sftp_cwd_dl'}->{'err'} = $sftp->error;
238
                }
239
            }
240
241
            $sftp_test_results->{'2b_sftp_ls_dl'} = {%$default_result};
242
            $sftp->ls();
243
            unless ( $sftp->error ) {
244
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'passed'} = Mojo::JSON->true;
245
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'msg'}    = $sftp->status;
246
            } else {
247
                $sftp_test_results->{'2b_sftp_ls_dl'}->{'err'} = $sftp->error;
248
            }
249
250
            unless ( $self->upload_directory eq '' ) {
251
                $sftp_test_results->{'2c_sftp_cwd_ul'} = {%$default_result};
252
                $sftp->setcwd( $self->upload_directory );
253
                unless ( $sftp->error ) {
254
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'passed'} = Mojo::JSON->true;
255
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'msg'}    = $sftp->status;
256
                } else {
257
                    $sftp_test_results->{'2c_sftp_cwd_ul'}->{'err'} = $sftp->error;
258
                }
259
            }
260
261
            $sftp_test_results->{'2d_sftp_ls_ul'} = {%$default_result};
262
            $sftp->ls( $self->upload_directory );
263
            unless ( $sftp->error ) {
264
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'passed'} = Mojo::JSON->true;
265
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'msg'}    = $sftp->status;
266
            } else {
267
                $sftp_test_results->{'2d_sftp_ls_ul'}->{'err'} = $sftp->error;
268
            }
269
270
            $sftp_test_results->{'3_sftp_write'} = {%$default_result};
271
            open my $fh, '<', \"Hello, world!\n";
272
            close $fh if ( $sftp->put( $fh, $self->upload_directory . '.koha_test_file' ) );
273
            unless ( $sftp->error ) {
274
                $sftp_test_results->{'3_sftp_write'}->{'passed'} = Mojo::JSON->true;
275
                $sftp_test_results->{'3_sftp_write'}->{'msg'}    = $sftp->status;
276
            } else {
277
                $sftp_test_results->{'3_sftp_write'}->{'err'} = $sftp->error;
278
            }
279
280
            $sftp_test_results->{'4_sftp_del'} = {%$default_result};
281
            $sftp->remove( $self->upload_directory . '.koha_test_file' );
282
            unless ( $sftp->error ) {
283
                $sftp_test_results->{'4_sftp_del'}->{'passed'} = Mojo::JSON->true;
284
                $sftp_test_results->{'4_sftp_del'}->{'msg'}    = $sftp->status;
285
            } else {
286
                $sftp_test_results->{'4_sftp_del'}->{'err'} = $sftp->error;
287
            }
288
        }
289
    } elsif ( $self->transport eq 'ftp' ) {
290
        $sftp_test_results->{'1_ftp_conn'} = {%$default_result};
291
        my $ftp = Net::FTP->new(
292
            $self->host,
293
            Port    => $self->port,
294
            Timeout => 10,
295
            Passive => ( scalar $self->passiv ) ? 1 : 0,
296
        );
297
        if ($ftp) {
298
            $sftp_test_results->{'1_ftp_conn'}->{'passed'} = Mojo::JSON->true;
299
            $sftp_test_results->{'1_ftp_conn'}->{'msg'}    = $ftp->message;
300
        } else {
301
            $sftp_test_results->{'1_ftp_conn'}->{'err'} = 'cannot connect to ' . $self->host . ': ' . $@;
302
        }
303
304
        if ($ftp) {
305
            $sftp_test_results->{'2_ftp_login'} = {%$default_result};
306
            my $login = $ftp->login(
307
                $self->user_name,
308
                $self->plain_text_password,
309
            );
310
            if ($login) {
311
                $sftp_test_results->{'2_ftp_login'}->{'passed'} = Mojo::JSON->true;
312
                $sftp_test_results->{'2_ftp_login'}->{'msg'}    = $ftp->message;
313
            } else {
314
                $sftp_test_results->{'2_ftp_login'}->{'err'} = $ftp->message;
315
            }
316
317
            unless ( $self->download_directory eq '' ) {
318
                $sftp_test_results->{'3a_ftp_cwd_dl'} = {%$default_result};
319
                my $cwd_dl = $ftp->cwd( $self->download_directory );
320
                if ($cwd_dl) {
321
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'passed'} = Mojo::JSON->true;
322
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'msg'}    = $ftp->message;
323
                } else {
324
                    $sftp_test_results->{'3a_ftp_cwd_dl'}->{'err'} = $ftp->message;
325
                }
326
            }
327
328
            $sftp_test_results->{'3b_ftp_ls_dl'} = {%$default_result};
329
            my $ls_dl = $ftp->ls();
330
            if ($ls_dl) {
331
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'passed'} = Mojo::JSON->true;
332
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'msg'}    = $ftp->message;
333
            } else {
334
                $sftp_test_results->{'3b_ftp_ls_dl'}->{'err'} = $ftp->message;
335
            }
336
337
            unless ( $self->upload_directory eq '' ) {
338
                $sftp_test_results->{'3c_ftp_cwd_ul'} = {%$default_result};
339
                my $cwd_ul = $ftp->cwd( $self->upload_directory );
340
                if ($cwd_ul) {
341
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'passed'} = Mojo::JSON->true;
342
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'msg'}    = $ftp->message;
343
                } else {
344
                    $sftp_test_results->{'3c_ftp_cwd_ul'}->{'err'} = $ftp->message;
345
                }
346
            }
347
348
            $sftp_test_results->{'3d_ftp_ls_ul'} = {%$default_result};
349
            my $ls_ul = $ftp->ls();
350
            if ($ls_ul) {
351
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'passed'} = Mojo::JSON->true;
352
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'msg'}    = $ftp->message;
353
            } else {
354
                $sftp_test_results->{'3d_ftp_ls_ul'}->{'err'} = $ftp->message;
355
            }
356
357
            $sftp_test_results->{'4_ftp_write'} = {%$default_result};
358
            open my $fh, '<', \"Hello, world!\n";
359
            close $fh if ( my $put = $ftp->put( $fh, $self->upload_directory . '.koha_test_file' ) );
360
            if ($put) {
361
                $sftp_test_results->{'4_ftp_write'}->{'passed'} = Mojo::JSON->true;
362
                $sftp_test_results->{'4_ftp_write'}->{'msg'}    = $ftp->message;
363
            } else {
364
                $sftp_test_results->{'4_ftp_write'}->{'err'} = $ftp->message;
365
            }
366
367
            $sftp_test_results->{'5_ftp_del'} = {%$default_result};
368
            my $delete = $ftp->delete( $self->upload_directory . '.koha_test_file' );
369
            if ($delete) {
370
                $sftp_test_results->{'5_ftp_del'}->{'passed'} = Mojo::JSON->true;
371
                $sftp_test_results->{'5_ftp_del'}->{'msg'}    = $ftp->message;
372
            } else {
373
                $sftp_test_results->{'5_ftp_del'}->{'err'} = $ftp->message;
374
            }
375
        }
376
    }
377
378
    $self->update_status('tests_ok');
379
    foreach my $val ( values %$sftp_test_results ) {
380
        if ( defined $val->{'err'} ) {
381
            $self->update_status('tests_failed');
382
        }
383
    }
384
385
    return ( 1, $sftp_test_results );
386
}
387
388
=head2 Internal methods
389
390
=head3 _dos2unix
391
392
Return a CR-free string from an input
393
394
=cut
395
396
sub _dos2unix {
397
    my $dosStr = shift;
398
399
    return $dosStr =~ s/\015\012/\012/gr;
400
}
401
402
=head3 _type
403
404
Return type of Object relating to Schema ResultSet
405
406
=cut
407
408
sub _type {
409
    return 'SftpServer';
410
}
411
412
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 (+226 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 $debug              = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
60
61
    try {
62
        Koha::SFTP::Server->new(
63
            {
64
                name               => $name,
65
                host               => $host,
66
                port               => $port,
67
                transport          => $transport,
68
                passiv             => $passiv,
69
                auth_mode          => $auth_mode,
70
                user_name          => $user_name,
71
                password           => $password,
72
                key_file           => $key_file,
73
                download_directory => $download_directory,
74
                upload_directory   => $upload_directory,
75
                debug              => $debug,
76
            }
77
        )->store;
78
79
        push @messages, {
80
            type => 'message',
81
            code => 'success_on_insert'
82
        };
83
    } catch {
84
        if ( blessed $_ and $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
85
            push @messages, {
86
                type   => 'alert',
87
                code   => 'error_on_insert',
88
                reason => 'duplicate_id'
89
            };
90
        }
91
    };
92
93
    # list servers after adding
94
    $op = 'list';
95
96
} elsif ( $op eq 'edit_form' ) {
97
    my $sftp_server_id = $input->param('sftp_server_id');
98
    my $sftp_server;
99
    my $sftp_server_plain_text_password;
100
    my $sftp_server_plain_text_key;
101
102
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
103
        unless !$sftp_server_id;
104
105
    unless ( !$sftp_server ) {
106
        $sftp_server_plain_text_password = $sftp_server->plain_text_password || '';
107
        $sftp_server_plain_text_key      = $sftp_server->plain_text_key      || '';
108
    }
109
110
    if ($sftp_server) {
111
        $template->param(
112
            sftp_server                     => $sftp_server,
113
            sftp_server_plain_text_password => $sftp_server_plain_text_password,
114
            sftp_server_plain_text_key      => $sftp_server_plain_text_key,
115
        );
116
    } else {
117
        push @messages, {
118
            type   => 'alert',
119
            code   => 'error_on_edit',
120
            reason => 'invalid_id'
121
        };
122
    }
123
124
} elsif ( $op eq 'cud-edit_save' ) {
125
    my $sftp_server_id = $input->param('sftp_server_id');
126
    my $sftp_server_plain_text_password;
127
    my $sftp_server;
128
129
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
130
        unless !$sftp_server_id;
131
132
    $sftp_server_plain_text_password = $sftp_server->plain_text_password
133
        unless !$sftp_server_id;
134
135
    if ($sftp_server) {
136
        my $name               = $input->param('sftp_name');
137
        my $host               = $input->param('sftp_host')      || 'localhost';
138
        my $port               = $input->param('sftp_port')      || 22;
139
        my $transport          = $input->param('sftp_transport') || 'sftp';
140
        my $passiv             = ( scalar $input->param('sftp_passiv') ) ? 1 : 0;
141
        my $auth_mode          = $input->param('sftp_auth_mode')          || 'password';
142
        my $user_name          = $input->param('sftp_user_name')          || undef;
143
        my $password           = $input->param('sftp_password')           || undef;
144
        my $key_file           = $input->param('sftp_key_file')           || undef;
145
        my $download_directory = $input->param('sftp_download_directory') || undef;
146
        my $upload_directory   = $input->param('sftp_upload_directory')   || undef;
147
        my $debug              = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
148
149
        try {
150
            $sftp_server->password($password)
151
                if defined $password and $password ne '****'
152
                or not defined $password;
153
154
            $sftp_server->set(
155
                {
156
                    name               => $name,
157
                    host               => $host,
158
                    port               => $port,
159
                    transport          => $transport,
160
                    passiv             => $passiv,
161
                    auth_mode          => $auth_mode,
162
                    user_name          => $user_name,
163
                    password           => $password,
164
                    key_file           => $key_file,
165
                    download_directory => $download_directory,
166
                    upload_directory   => $upload_directory,
167
                    debug              => $debug,
168
                }
169
            )->store;
170
171
            push @messages, {
172
                type => 'message',
173
                code => 'success_on_update'
174
            };
175
176
        } catch {
177
178
            push @messages, {
179
                type => 'alert',
180
                code => 'error_on_update'
181
            };
182
183
        };
184
185
        # list servers after adding
186
        $op = 'list';
187
    } else {
188
        push @messages, {
189
            type   => 'alert',
190
            code   => 'error_on_update',
191
            reason => 'invalid_id'
192
        };
193
    }
194
195
} elsif ( $op eq 'test_form' ) {
196
    my $sftp_server_id = $input->param('sftp_server_id');
197
    my $sftp_server;
198
199
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
200
        unless !$sftp_server_id;
201
202
    if ($sftp_server) {
203
        $template->param(
204
            sftp_server => $sftp_server,
205
        );
206
    } else {
207
        push @messages, {
208
            type   => 'alert',
209
            code   => 'error_on_test',
210
            reason => 'invalid_id',
211
        };
212
    }
213
}
214
215
if ( $op eq 'list' ) {
216
    $template->param(
217
        servers_count => $sftp_servers->count,
218
    );
219
}
220
221
$template->param(
222
    op       => $op,
223
    messages => \@messages,
224
);
225
226
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 (+974 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
                <li>
234
                    <label for="sftp_debug_mode">Debug mode: </label>
235
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
236
                        <option value="1">Enabled</option>
237
                        <option value="0" selected="selected">Disabled</option>
238
                    </select>
239
                    <span class="hint">Enables additional debug output in the logs</span>
240
                </li>
241
            </ol>
242
        </fieldset>
243
        <fieldset class="action">
244
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
245
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
246
        </fieldset>
247
    </form>
248
[% END %]
249
250
[% IF op == 'edit_form' && !messages %]
251
    <!-- Modal -->
252
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
253
        <div class="modal-dialog modal-lg">
254
            <div class="modal-content modal-lg">
255
                    <div class="modal-header">
256
                        <h1 class="modal-title">Are you sure?</h1>
257
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
258
                    </div>
259
                    <div class="modal-body">
260
                        <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>
261
                        <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>
262
                        <table class="mx-4 mb-3">
263
                            <thead></thead>
264
                            <tbody>
265
                                <tr>
266
                                    <td><strong>Host</strong></td>
267
                                    <td id="modal_host"></td>
268
                                </tr>
269
                                <tr>
270
                                    <td><strong>Port</strong></td>
271
                                    <td id="modal_port"></td>
272
                                </tr>
273
                                <tr>
274
                                    <td><strong>Transport</strong></td>
275
                                    <td id="modal_transport"></td>
276
                                </tr>
277
                                <tr>
278
                                    <td><strong>Username</strong></td>
279
                                    <td id="modal_user_name"></td>
280
                                </tr>
281
                                <tr>
282
                                    <td><strong>Authentication mode</strong></td>
283
                                    <td id="modal_auth_mode"></td>
284
                                </tr>
285
                            </tbody>
286
                        </table>
287
                        <p>If you are ready to progress with these details, please click Save.</p>
288
                    </div>
289
                    <div class="modal-footer">
290
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
291
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
292
                    </div>
293
                </form>
294
            </div>
295
        </div>
296
    </div>
297
    <!-- END Modal -->
298
299
    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
300
301
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
302
        [% INCLUDE 'csrf-token.inc' %]
303
        <input type="hidden" name="op" value="cud-edit_save" />
304
        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
305
        <fieldset class="rows">
306
            <ol>
307
                <li>
308
                    <label for="sftp_name" class="required">Name: </label>
309
                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
310
                    <span class="required">Required</span>
311
                </li>
312
            </ol>
313
        </fieldset>
314
315
        <fieldset class="rows">
316
            <ol>
317
                <li>
318
                    <label for="sftp_host" class="required">Host: </label>
319
                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
320
                    <span class="required">Required</span>
321
                </li>
322
                <li>
323
                    <label for="sftp_port" class="required">Port: </label>
324
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required"/>
325
                    <span class="required">Required</span>
326
                </li>
327
                <li>
328
                    <label for="sftp_transport" class="required">Transport: </label>
329
                    <select name="sftp_transport" id="sftp_transport" class="required">
330
                        [% IF sftp_server.transport == 'ftp' %]
331
                        <option value="ftp" selected="selected">FTP</option>
332
                        [% ELSE %]
333
                        <option value="ftp">FTP</option>
334
                        [% END %]
335
                        [% IF sftp_server.transport == 'sftp' %]
336
                        <option value="sftp" selected="selected">SFTP</option>
337
                        [% ELSE %]
338
                        <option value="sftp">SFTP</option>
339
                        [% END %]
340
                    </select>
341
                    <span class="required">Required</span>
342
                </li>
343
                <li>
344
                    <label for="sftp_passiv">Passive mode: </label>
345
                    <select name="sftp_passiv" id="sftp_passiv" disabled="disabled">
346
                        [% IF sftp_server.passiv == 1 %]
347
                        <option value="1" selected="selected">Enabled (Recommended)</option>
348
                        [% ELSE %]
349
                        <option value="1">Enabled (Recommended)</option>
350
                        [% END %]
351
                        [% IF sftp_server.passiv == 0 %]
352
                        <option value="0" selected="selected">Disabled</option>
353
                        [% ELSE %]
354
                        <option value="0">Disabled</option>
355
                        [% END %]
356
                    </select>
357
                    <span class="hint">Only applies to FTP connections</span>
358
                </li>
359
                <li>
360
                    <label for="sftp_auth_mode">Authentication mode: </label>
361
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
362
                        [% IF sftp_server.auth_mode == 'password' %]
363
                        <option value="password" selected="selected">Password-based</option>
364
                        [% ELSE %]
365
                        option value="password">Password-based</option>
366
                        [% END %]
367
                        [% IF sftp_server.auth_mode == 'key_file' %]
368
                        <option value="key_file" selected="selected">Key file-based</option>
369
                        [% ELSE %]
370
                        <option value="key_file">Key file-based</option>
371
                        [% END %]
372
                        [% IF sftp_server.auth_mode == 'noauth' %]
373
                        <option value="noauth" selected="selected">No authentication</option>
374
                        [% ELSE %]
375
                        <option value="noauth">No authentication</option>
376
                        [% END %]
377
                    </select>
378
                </li>
379
                <li>
380
                    <label for="sftp_user_name" class="required">Username: </label>
381
                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
382
                    <span class="required">Required</span>
383
                </li>
384
                <li>
385
                    <label for="sftp_password">Password: </label>
386
                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
387
                </li>
388
                <li>
389
                    <label for="sftp_key_file">Key file path: </label>
390
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
391
                    <span class="hint">Only applies to SFTP connections</span>
392
                </li>
393
                <li>
394
                    <label for="sftp_download_directory" >Remote download directory: </label>
395
                    <input type="text" value="[% sftp_server.download_directory | html %]" name="sftp_download_directory" id="sftp_download_directory" size="60" autocomplete="off" /><br />
396
                    <span class="hint">The path on the remote server where we will download from</span>
397
                </li>
398
                <li>
399
                    <label for="sftp_upload_directory" >Remote upload directory: </label>
400
                    <input type="text" value="[% sftp_server.upload_directory | html %]" name="sftp_upload_directory" id="sftp_upload_directory" size="60" autocomplete="off" /><br />
401
                    <span class="hint">The path on the remote server where we will upload to</span>
402
                </li>
403
                <li>
404
                    <label for="sftp_debug_mode">Debug mode: </label>
405
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
406
                        [% IF sftp_server.debug == 1 %]
407
                        <option value="1" selected="selected">Enabled</option>
408
                        [% ELSE %]
409
                        <option value="1">Enabled</option>
410
                        [% END %]
411
                        [% IF sftp_server.debug == 0 %]
412
                        <option value="0" selected="selected">Disabled</option>
413
                        [% ELSE %]
414
                        <option value="0">Disabled</option>
415
                        [% END %]
416
                    </select>
417
                    <span class="hint">Enables additional debug output in the logs</span>
418
                </li>
419
            </ol>
420
        </fieldset>
421
        <fieldset class="action">
422
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
423
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
424
        </fieldset>
425
    </form>
426
[% END %]
427
428
[% IF op == 'test_form' %]
429
430
    <h1>[% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
431
    [% IF sftp_server.id %]
432
    <div class="page-section">
433
        [% IF sftp_server.transport == 'sftp' %]
434
        <div class="alert alert-warning">
435
            <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. There are security implications in doing this, so check the connection details carefully before proceeding.
436
        </div>
437
        [% END %]
438
        <div class="row">
439
            <div class="col-12 col-lg-6 order-1 order-lg-0">
440
                <h3>Test results</h3>
441
                <div id="testOutput">
442
                    <strong>Current status:</strong>
443
                    [% SWITCH sftp_server.status %]
444
                    [% CASE 'tests_ok' %]
445
                        Tests passing
446
                    [% CASE 'tests_failed' %]
447
                        Tests failing
448
                    [% CASE %]
449
                        <em>Never used</em>
450
                    [% END %]
451
                    <p>Please click 'Run tests' to start new attempt</p>
452
                </div>
453
            </div>
454
            <div class="col-12 col-lg-6 order-0 order-lg-1">
455
                <h3>Test details</h3>
456
                <p>Connection details are as follows:</p>
457
                <table class="mx-4 mb-3">
458
                    <thead></thead>
459
                    <tbody>
460
                        <tr>
461
                            <td><strong>Host</strong></td>
462
                            <td>[% sftp_server.host | html %]</td>
463
                        </tr>
464
                        <tr>
465
                            <td><strong>Port</strong></td>
466
                            <td>[% sftp_server.port | html %]</td>
467
                        </tr>
468
                        <tr>
469
                            <td><strong>Transport</strong></td>
470
                            <td>[% sftp_server.transport FILTER upper | html %]</td>
471
                        </tr>
472
                        <tr>
473
                            <td><strong>Username</strong></td>
474
                            <td>[% sftp_server.user_name | html %]</td>
475
                        </tr>
476
                        <tr>
477
                            <td><strong>Authentication mode</strong></td>
478
                            <td>
479
                                [% IF sftp_server.auth_mode == 'password' %]
480
                                Password-based
481
                                [% ELSE %]
482
                                Key file-based
483
                                [% END %]
484
                            </td>
485
                        </tr>
486
                        <tr>
487
                            <td><strong>Status</strong></td>
488
                            <td>
489
                                [% SWITCH sftp_server.status %]
490
                                [% CASE 'tests_ok' %]
491
                                    Tests passing
492
                                [% CASE 'tests_failed' %]
493
                                    Tests failing
494
                                [% CASE %]
495
                                    <em>Never used</em>
496
                                [% END %]
497
                            </td>
498
                        </tr>
499
                    </tbody>
500
                </table>
501
            </div>
502
        </div>
503
    </div>
504
    <fieldset class="action">
505
        <a href="#" class="btn btn-primary" id="runTests">Run tests</a>
506
        <a href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&sftp_server_id=[% sftp_server.id | html %]" class="cancel">Reset</a>
507
    </fieldset>
508
    [% ELSE %]
509
    <div class="page-section">
510
        <h3>Oops &ndash; Not Found</h3>
511
        <p>An FTP/SFTP server with that ID was not found. Please go back and try again.</p>
512
    </div>
513
    [% END %]
514
[% END %]
515
516
[% IF op == 'list' %]
517
518
    <div id="toolbar" class="btn-toolbar">
519
        <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>
520
    </div>
521
522
    <h1>FTP/SFTP servers</h1>
523
524
    [% IF servers_count < 1 %]
525
        <div class="alert alert-info" id="dno_servers_message">
526
            <p>
527
                <em>There are no FTP/SFTP servers defined.</em><br />
528
                To create one, use the <strong>new FTP/SFTP server</strong> button above.
529
            </p>
530
        </div>
531
    [% ELSE %]
532
        <div class="page-section">
533
            <table id="sftp_servers">
534
                <thead>
535
                    <tr>
536
                        <th>Name</th>
537
                        <th>Host</th>
538
                        <th>Port</th>
539
                        <th>Transport</th>
540
                        <th>Authentication mode</th>
541
                        <th>Username</th>
542
                        <th>Download directory</th>
543
                        <th>Upload directory</th>
544
                        <th>Status</th>
545
                        <th>Debug</th>
546
                        <th data-class-name="actions noExport">Actions</th>
547
                    </tr>
548
                </thead>
549
            </table>
550
        </div> <!-- /.page-section -->
551
    [% END %]
552
[% END %]
553
554
            <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
555
                <div class="modal-dialog">
556
                    <div class="modal-content">
557
                        <div class="modal-header">
558
                            <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
559
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
560
                        </div>
561
                        <div class="modal-body">
562
                            <div id="delete_confirm_dialog"></div>
563
                        </div>
564
                        <div class="modal-footer">
565
                            <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
566
                            <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
567
                        </div>
568
                    </div> <!-- /.modal-content -->
569
                </div> <!-- /.modal-dialog -->
570
            </div> <!-- #delete_confirm_modal -->
571
572
            </main>
573
        </div> <!-- /.col-md-10.order-md-2 -->
574
575
        <div class="col-md-2 order-sm-2 order-md-1">
576
            <aside>
577
                [% INCLUDE 'admin-menu.inc' %]
578
            </aside>
579
        </div> <!-- /.col-md-2.order-md-1 -->
580
     </div> <!-- /.row -->
581
582
583
[% MACRO jsinclude BLOCK %]
584
    [% Asset.js("js/admin-menu.js") | $raw %]
585
    [% INCLUDE 'datatables.inc' %]
586
    <script>
587
        $(document).ready(function() {
588
589
            var sftp_servers_url = '/api/v1/config/sftp_servers';
590
            window.sftp_servers = $("#sftp_servers").kohaTable({
591
                "ajax": {
592
                    "url": sftp_servers_url
593
                },
594
                "language": {
595
                    "emptyTable": "<div class=\"alert alert-info\">"+_("There are no FTP/SFTP servers defined.")+"</div>"
596
                },
597
                "columnDefs": [ {
598
                    "targets": [0,1],
599
                    "render": function(data, type, row, meta) {
600
                        if (type == "display") {
601
                            if(data != null) {
602
                                return data.escapeHtml();
603
                            } else {
604
                                return "Default";
605
                            }
606
                        }
607
                        return data;
608
                    }
609
                } ],
610
                "columns": [
611
                    {
612
                        "data": "name",
613
                        "searchable": true,
614
                        "orderable": true
615
                    },
616
                    {
617
                        "data": "host",
618
                        "searchable": true,
619
                        "orderable": true
620
                    },
621
                    {
622
                        "data": "port",
623
                        "searchable": true,
624
                        "orderable": false
625
                    },
626
                    {
627
                        "data": "transport",
628
                        "render": function(data, type, row, meta) {
629
                            return data.toUpperCase();
630
                        },
631
                        "searchable": true,
632
                        "orderable": false
633
                    },
634
                    {
635
                        "data": "auth_mode",
636
                        "render": function(data, type, row, meta) {
637
                            if(data == "password") {
638
                                return _("Password-based");
639
                            } else if(data == "key_file") {
640
                                return _("Key file-based");
641
                            } else {
642
                                return _("No authentication");
643
                            }
644
                        },
645
                        "searchable": false,
646
                        "orderable": false
647
                    },
648
                    {
649
                        "data": "user_name",
650
                        "searchable": false,
651
                        "orderable": false
652
                    },
653
                    {
654
                        "data": "download_directory",
655
                        "render": function(data, type, row, meta) {
656
                            if(data) {
657
                                return data;
658
                            } else {
659
                                return "<em>" + _("Not specified") + "</em>";
660
                            }
661
                        },
662
                        "searchable": false,
663
                        "orderable": false
664
                    },
665
                    {
666
                        "data": "upload_directory",
667
                        "render": function(data, type, row, meta) {
668
                            if(data) {
669
                                return data;
670
                            } else {
671
                                return "<em>" + _("Not specified") + "</em>";
672
                            }
673
                        },
674
                        "searchable": false,
675
                        "orderable": false
676
                    },
677
                    {
678
                        "data": "status",
679
                        "render": function(data, type, row, meta) {
680
                            if (data == "tests_ok") {
681
                                return _("Tests passing");
682
                            } else if (data == "tests_failed") {
683
                                return _("Tests failing");
684
                            } else {
685
                                return "<em>" + _("Never used") + "</em>";
686
                            }
687
                        },
688
                        "searchable": false,
689
                        "orderable": false
690
                    },
691
                    {
692
                        "data": "debug",
693
                        "render": function(data, type, row, meta) {
694
                            if(data == true) {
695
                                return "[% tp("Active", "On") | html %]";
696
                            }
697
                            else {
698
                                return _("Off");
699
                            }
700
                        },
701
                        "searchable": false,
702
                        "orderable": false
703
                    },
704
                    {
705
                        "data": function(row, type, val, meta) {
706
                            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";
707
                            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";
708
                            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>';
709
                            return result;
710
                        },
711
                        "searchable": false,
712
                        "orderable": false
713
                    }
714
                ],
715
                createdRow: function(row, data, dataIndex) {
716
                    if(data.is_default) {
717
                        $(row).addClass('default warn');
718
                    }
719
                    if(data.debug) {
720
                        $(row).addClass('debug');
721
                    }
722
                },
723
            });
724
725
            $('#sftp_servers').on("click", '.delete_server', function() {
726
                var sftp_server_id   = $(this).data('sftp-server-id');
727
                var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
728
729
                $("#delete_confirm_dialog").html(
730
                    _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
731
                );
732
                $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
733
                $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
734
            });
735
736
            $("#delete_confirm_modal_button").on("click", function() {
737
738
                var sftp_server_id   = $(this).data('sftp-server-id');
739
                var sftp_server_name = $(this).data('sftp-server-name');
740
741
                $.ajax({
742
                    method: "DELETE",
743
                    url: "/api/v1/config/sftp_servers/"+sftp_server_id
744
                }).success(function() {
745
                    window.sftp_servers.api().ajax.reload(function(data) {
746
                        $("#sftp_action_result_dialog").hide();
747
                        $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
748
                    });
749
                }).fail(function() {
750
                    $("#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();
751
                }).done(function() {
752
                    $("#delete_confirm_modal").modal('hide');
753
                });
754
            });
755
756
            transportChange();
757
            $("#sftp_transport").on("change", function(event) {
758
                transportChange();
759
            });
760
761
            authModeChange();
762
            $("#sftp_auth_mode").on("change", function(event) {
763
                authModeChange();
764
            });
765
766
            $('#confirm_key_accept_submit').on('click', function(event) {
767
                event.preventDefault();
768
769
                if ( $('#add').length > 0 ) {
770
                    if( $('#add').valid() == true ) {
771
                        modalChange();
772
                        $('#confirm_key_accept').modal('show');
773
                    } else {
774
                        $('#confirm_key_accept').modal('hide');
775
                    }
776
                }
777
778
                if ( $('#edit_save').length > 0 ) {
779
                    if( $('#edit_save').valid() == true ) {
780
                        modalChange();
781
                        $('#confirm_key_accept').modal('show');
782
                    } else {
783
                        $('#confirm_key_accept').modal('hide');
784
                    }
785
                }
786
787
            });
788
789
            $('#confirm_key_accept .approve').on('click', function() {
790
                $('#confirm_key_accept .deny').click();
791
792
                if ( $('#add').length > 0 ) {
793
                    $('#add').submit();
794
                }
795
796
                if ( $('#edit_save').length > 0 ) {
797
                    $('#edit_save').submit();
798
                }
799
            });
800
801
        });
802
803
        function transportChange() {
804
            let sftp_transport = $("#sftp_transport");
805
806
            if(sftp_transport.val() == "ftp") {
807
                $("#sftp_host").removeAttr("disabled");
808
                $("#sftp_port").removeAttr("disabled");
809
                $("#sftp_passiv").removeAttr("disabled");
810
                $("#sftp_auth_mode").removeAttr("disabled");
811
                $("#sftp_user_name").removeAttr("disabled");
812
                $("#sftp_password").removeAttr("disabled");
813
                $("#sftp_key_file").attr("disabled", "disabled");
814
815
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
816
                $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
817
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
818
                if($("#sftp_auth_mode option:selected").val() == "key_file") {
819
                    $("#sftp_auth_mode option[value='password']").prop("selected", true);
820
                }
821
822
                let sftp_port = $("#sftp_port").val();
823
                if(sftp_port == 22) $("#sftp_port").val("21");
824
825
                authModeChange();
826
            } else if(sftp_transport.val() == "sftp") {
827
                $("#sftp_host").removeAttr("disabled");
828
                $("#sftp_port").removeAttr("disabled");
829
                $("#sftp_passiv").attr("disabled", "disabled");
830
                $("#sftp_auth_mode").removeAttr("disabled");
831
                $("#sftp_user_name").removeAttr("disabled");
832
                $("#sftp_password").removeAttr("disabled");
833
                $("#sftp_key_file").removeAttr("disabled");
834
835
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
836
                $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
837
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
838
                $("#sftp_passiv option[value='1']").prop("selected", true);
839
840
                let sftp_port = $("#sftp_port").val();
841
                if(sftp_port == 21) $("#sftp_port").val("22");
842
843
                return authModeChange();
844
            }
845
        }
846
847
        function authModeChange() {
848
            let sftp_auth_mode = $("#sftp_auth_mode").val();
849
850
            if(sftp_auth_mode == "password") {
851
                $("#sftp_password").removeAttr("disabled");
852
                $("#sftp_key_file").attr("disabled", "disabled");
853
            } else if(sftp_auth_mode == "key_file") {
854
                $("#sftp_password").attr("disabled", "disabled");
855
                $("#sftp_key_file").removeAttr("disabled");
856
            } else {
857
                $("#sftp_password").attr("disabled", "disabled");
858
                $("#sftp_key_file").attr("disabled", "disabled");
859
            }
860
        }
861
862
        function modalChange() {
863
            $('#modal_message').hide();
864
            if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
865
866
            $('#modal_host').text( $('#sftp_host').val() );
867
            $('#modal_port').text( $('#sftp_port').val() );
868
            $('#modal_transport').text( $('#sftp_transport option:selected').text() );
869
            $('#modal_user_name').text( $('#sftp_user_name').val() );
870
            $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
871
        }
872
    </script>
873
874
    [% IF op == 'test_form' %]
875
    <script>
876
        $(document).ready(function() {
877
            $('#runTests').on('click', function(event) {
878
                event.preventDefault();
879
880
                handleTests();
881
            });
882
        });
883
884
        function handleTests() {
885
            var testOutput = $('#testOutput');
886
            var runTests   = $('#runTests');
887
888
            runTests.addClass('disabled');
889
            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>');
890
891
            return $.ajax({
892
                url: "/api/v1/sftp_server/[% sftp_server.id | html %]/test_connection",
893
            })
894
            .done(function(data) {
895
                testOutput.text('');
896
897
                for ( let [key, value] of Object.entries( data ) ) {
898
                    var title;
899
                    switch(key) {
900
                        case '1_sftp_conn':
901
                            title = _("Testing SFTP connectivity");
902
                            break;
903
                        case '1_ftp_conn':
904
                            title = _("Testing FTP connectivity");
905
                            break;
906
                        case '2_ftp_login':
907
                            title = _("Testing we can log in");
908
                            break;
909
                        case '2a_sftp_cwd_dl':
910
                        case '3a_ftp_cwd_dl':
911
                            title = _("Testing we can cwd to download directory");
912
                            break;
913
                        case '2b_sftp_ls_dl':
914
                        case '3b_ftp_ls_dl':
915
                            title = _("Testing we can list download directory");
916
                            break;
917
                        case '2c_sftp_cwd_ul':
918
                        case '3c_ftp_cwd_ul':
919
                            title = _("Testing we can cwd to upload directory");
920
                            break;
921
                        case '2d_sftp_ls_ul':
922
                        case '3d_ftp_ls_ul':
923
                            title = _("Testing we can list upload directory");
924
                            break;
925
                        case '3_sftp_write':
926
                        case '4_ftp_write':
927
                            title = _("Testing we can write a test file");
928
                            break;
929
                        case '4_sftp_del':
930
                        case '5_ftp_del':
931
                            title = _("Testing we can delete test file");
932
                            break;
933
                        default:
934
                            title = key
935
                    }
936
937
                    if ( value.passed ) {
938
                        testOutput.append(
939
                            '<i class="text-success fa-solid fa-circle-check"></i> '
940
                            + title
941
                            + '... <span class="text-success">'
942
                            + _("Passed")
943
                            + '</span><br />'
944
                        );
945
                        if( value.msg ) testOutput.append( _("Message: ") + '<code>' + value.msg + '</code><br />' );
946
                    } else {
947
                        testOutput.append(
948
                            '<i class="text-danger fa-solid fa-circle-xmark"></i> '
949
                            + title
950
                            + '... <span class="text-danger">'
951
                            + _("Failed")
952
                            + '</span><br />'
953
                        );
954
                        if( value.err ) testOutput.append( _("Error message: ") + '<code>' + value.err + '</code><br />' );
955
                    }
956
                    testOutput.append( '<br />' );
957
                }
958
            })
959
            .fail(function(data) {
960
                if( data.status == 404 ) {
961
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("FTP/SFTP Server not found") );
962
                } else {
963
                    return testOutput.text( '<i class="text-success fa-solid fa-circle-check"></i> ' + _("Internal Server Error. Please check the server logs") );
964
                }
965
            })
966
            .always(function(data) {
967
                runTests.removeClass('disabled');
968
            });
969
        }
970
    </script>
971
    [% END %]
972
[% END %]
973
974
[% 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 (+151 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 => 6;
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 'store() 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
    $sftp_server->set( { password => 'test123' } )->store;
40
41
    ok( $sftp_server->password ne 'test123', 'Password should not be in plain text' );
42
    is( length( $sftp_server->password ), 64, 'Password has should be 64 characters long' );
43
44
    $schema->storage->txn_rollback;
45
};
46
47
subtest 'to_api() tests' => sub {
48
49
    plan tests => 1;
50
51
    $schema->storage->txn_begin;
52
53
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
54
55
    ok( !exists $sftp_server->to_api->{password}, 'Password is not part of the API representation' );
56
57
    $schema->storage->txn_rollback;
58
};
59
60
subtest 'plain_text_password() tests' => sub {
61
62
    plan tests => 2;
63
64
    $schema->storage->txn_begin;
65
66
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
67
68
    $sftp_server->set( { password => 'test123' } )->store;
69
70
    my $sftp_server_plain_text_password = $sftp_server->plain_text_password;
71
72
    isnt( $sftp_server_plain_text_password, $sftp_server->password, 'Password and password hash shouldn\'t match' );
73
    is( $sftp_server_plain_text_password, 'test123', 'Password should be in plain text' );
74
75
    $schema->storage->txn_rollback;
76
};
77
78
subtest 'plain_text_key() tests' => sub {
79
80
    plan tests => 2;
81
82
    $schema->storage->txn_begin;
83
84
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
85
86
    $sftp_server->set( { key_file => '321tset' } )->store;
87
88
    my $sftp_server_plain_text_key = $sftp_server->plain_text_key;
89
90
    isnt( $sftp_server_plain_text_key, $sftp_server->key_file, 'Key file and key file hash shouldn\'t match' );
91
    is( $sftp_server_plain_text_key, "321tset\n", 'Key file should be in plain text' );
92
93
    $schema->storage->txn_rollback;
94
};
95
96
subtest 'write_key_file() tests' => sub {
97
98
    plan tests => 3;
99
100
    $schema->storage->txn_begin;
101
102
    my $sftp_server = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
103
104
    $sftp_server->set( { key_file => '321tset' } )->store;
105
106
    my $path = '/tmp/kohadev_test';
107
    t::lib::Mocks::mock_config( 'upload_path', $path );
108
    mkdir $path if !-d $path;
109
110
    my $first_test = $sftp_server->write_key_file;
111
112
    my $file        = $sftp_server->locate_key_file;
113
    my $second_test = ( -f $file );
114
115
    open( my $fh, '<', $sftp_server->locate_key_file );
116
    my $third_test = <$fh>;
117
118
    is( $first_test,  1,           'Writing key file should return 1' );
119
    is( $second_test, 1,           'Written key file should exist' );
120
    is( $third_test,  "321tset\n", 'The contents of the key file should be 321tset\n' );
121
122
    unlink $file;
123
124
    $schema->storage->txn_rollback;
125
};
126
127
subtest 'update_status() tests' => sub {
128
129
    plan tests => 2;
130
131
    $schema->storage->txn_begin;
132
133
    my $sftp_server        = $builder->build_object( { class => 'Koha::SFTP::Servers' } );
134
    my $sftp_server_status = 'tests_ok';
135
136
    $sftp_server->set(
137
        {
138
            password => 'somepass',
139
            key_file => 'somekey',
140
        }
141
    )->store;
142
143
    isnt( $sftp_server_status, $sftp_server->status, 'Status should not by tests_ok' );
144
145
    $sftp_server->update_status($sftp_server_status);
146
147
    is( $sftp_server_status, $sftp_server->status, 'Status should be tests_ok' );
148
149
    $schema->storage->txn_rollback;
150
151
};
(-)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