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

(-)a/Koha/REST/V1/Config/SFTP/Servers.pm (+169 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
1;
(-)a/Koha/SFTP/Server.pm (+315 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 Net::SFTP::Foreign;
27
use Net::FTP;
28
29
use base qw(Koha::Object);
30
31
=head1 NAME
32
33
Koha::SFTP::Server - Koha SFTP Server Object class
34
35
=head1 API
36
37
=head2 Class methods
38
39
=head3 store
40
41
    $server->store;
42
43
Overloaded store method.
44
45
=cut
46
47
sub store {
48
    my ($self) = @_;
49
50
    # catch password and encrypt it
51
    $self->password(
52
        $self->password
53
        ? Koha::Encryption->new->encrypt_hex( $self->password )
54
        : undef
55
    );
56
57
    # clean up the keyfile & encrypt
58
    $self->key_file(
59
        $self->key_file
60
        ? Koha::Encryption->new->encrypt_hex( _dos2unix( $self->key_file ) )
61
        : undef
62
    );
63
64
    # catch key file and write it
65
    $self->write_key_file;
66
67
    return $self->SUPER::store;
68
}
69
70
=head3 to_api
71
72
    my $json = $sftp_server->to_api;
73
74
Overloaded method that returns a JSON representation of the Koha::SFTP::Server object,
75
suitable for API output.
76
77
=cut
78
79
sub to_api {
80
    my ( $self, $params ) = @_;
81
82
    my $json_sftp = $self->SUPER::to_api($params);
83
    return unless $json_sftp;
84
    delete $json_sftp->{password};
85
86
    return $json_sftp;
87
}
88
89
=head3 to_api_mapping
90
91
This method returns the mapping for representing a Koha::SFTP::Server object
92
on the API.
93
94
=cut
95
96
sub to_api_mapping {
97
    return { id => 'sftp_server_id' };
98
}
99
100
=head3 plain_text_password
101
102
    $server->plain_text_password;
103
Fetches the plaintext password, from the object
104
105
=cut
106
107
sub plain_text_password {
108
    my ($self) = @_;
109
110
    return Koha::Encryption->new->decrypt_hex( $self->password )
111
        if $self->password;
112
113
}
114
115
=head3 plain_text_key
116
117
    $server->plain_text_key;
118
Fetches the plaintext key file, from the object
119
120
=cut
121
122
sub plain_text_key {
123
    my ($self) = @_;
124
125
    return Koha::Encryption->new->decrypt_hex( $self->key_file ) . "\n"    ## newline needed
126
        if $self->key_file;
127
}
128
129
=head3 write_key_file
130
131
    $server->write_key_file;
132
Writes the keyfile from the db into a file
133
134
=cut
135
136
sub write_key_file {
137
    my ($self)      = @_;
138
    my $upload_path = C4::Context->config('upload_path') or return;
139
    my $key_path    = $upload_path . '/ssh_keys';
140
    my $key_file    = $key_path . '/id_ssh_' . $self->id;
141
142
    mkdir $key_path if ( !-d $key_path );
143
144
    unlink $key_file if ( -f $key_file );
145
    my $fh;
146
    $fh = IO::File->new( $key_file, 'w' ) or return;
147
    chmod 0600, $key_file if ( -f $key_file );
148
149
    print $fh $self->plain_text_key;
150
151
    undef $fh;
152
153
    return 1;
154
}
155
156
=head3 locate_key_file
157
158
    $server->locate_key_file;
159
Returns the keyfile's expected path
160
161
=cut
162
163
sub locate_key_file {
164
    my ($self)      = @_;
165
    my $upload_path = C4::Context->config('upload_path');
166
    return if not defined $upload_path;
167
168
    my $keyf_path   = $upload_path . '/ssh_keys/id_ssh_' . $self->id;
169
170
    return ( -f $keyf_path ) ? $keyf_path : undef;
171
}
172
173
=head3 test_conn
174
175
    $server->test_conn;
176
Tests a connection to a given sftp server
177
178
=cut
179
180
sub test_conn {
181
    my ($self) = @_;
182
    my $test_results = {};
183
184
    if ( $self->transport eq 'sftp' ) {
185
        ## test connectivity
186
        my $sftp = Net::SFTP::Foreign->new(
187
            host     => $self->host,
188
            user     => $self->user_name,
189
            password => $self->plain_text_password,
190
            key_path => $self->locate_key_file,
191
            port     => $self->port,
192
            timeout  => 10,
193
            more     => [
194
                qw(-vv),
195
                qw(-o StrictHostKeyChecking=no),
196
            ],
197
        );
198
        $test_results->{'1_sftp_conn'}->{'err'} = $sftp->error
199
            if $sftp->error;
200
        $test_results->{'1_sftp_conn'}->{'passed'} = 1
201
            unless $sftp->error;
202
203
        ## continue
204
        unless ( $sftp->error ) {
205
            ## get directory listings
206
            $sftp->ls();
207
            $test_results->{'2_sftp_ls'}->{'err'} = $sftp->error
208
                if $sftp->error;
209
            $test_results->{'2_sftp_ls'}->{'passed'} = 1
210
                unless $sftp->error;
211
212
            ## write file to server
213
            open my $fh, '<', \"Hello, world!\n";
214
            close $fh if ( $sftp->put( $fh, '.koha_test_file' ) );
215
            $test_results->{'3_sftp_write'}->{'err'} = $sftp->error
216
                if $sftp->error;
217
            $test_results->{'3_sftp_write'}->{'passed'} = 1
218
                unless $sftp->error;
219
220
            ## delete test file from server
221
            $sftp->remove('.koha_test_file');
222
            $test_results->{'4_sftp_del'}->{'err'} = $sftp->error
223
                if $sftp->error;
224
            $test_results->{'4_sftp_del'}->{'passed'} = 1
225
                unless $sftp->error;
226
        }
227
    } elsif ( $self->transport eq 'ftp' ) {
228
        ## test connectivity
229
        my $ftp = Net::FTP->new(
230
            $self->host,
231
            Port    => $self->port,
232
            Timeout => 10,
233
            Passive => ( scalar $self->passiv ) ? 1 : 0,
234
        );
235
        if ($ftp) {
236
            $test_results->{'1_ftp_conn'}->{'passed'} = 1;
237
            $test_results->{'1_ftp_conn'}->{'msg'}    = $ftp->message;
238
        } else {
239
            $test_results->{'1_ftp_conn'}->{'err'} = 'cannot connect to ' . $self->host . ': ' . $@;
240
        }
241
242
        ## continue
243
        if ($ftp) {
244
            ## try to login
245
            my $login = $ftp->login(
246
                $self->user_name,
247
                $self->plain_text_password,
248
            );
249
            if ($login) {
250
                $test_results->{'2_ftp_login'}->{'passed'} = 1;
251
                $test_results->{'2_ftp_login'}->{'msg'}    = $ftp->message;
252
            } else {
253
                $test_results->{'2_ftp_login'}->{'err'} = $ftp->message;
254
            }
255
256
            ## get directory listings
257
            my $ls = $ftp->ls('~');
258
            if ($ls) {
259
                $test_results->{'3_ftp_ls'}->{'passed'} = 1;
260
                $test_results->{'3_ftp_ls'}->{'msg'}    = $ftp->message;
261
            } else {
262
                $test_results->{'3_ftp_ls'}->{'err'} = $ftp->message;
263
            }
264
265
            ## write file to server
266
            open my $fh, '<', \"Hello, world!\n";
267
            close $fh if ( my $put = $ftp->put( $fh, '.koha_test_file' ) );
268
            if ($put) {
269
                $test_results->{'4_ftp_write'}->{'passed'} = 1;
270
                $test_results->{'4_ftp_write'}->{'msg'}    = $ftp->message;
271
            } else {
272
                $test_results->{'4_ftp_write'}->{'err'} = $ftp->message;
273
            }
274
275
            ## delete test file from server
276
            my $delete = $ftp->delete('.koha_test_file');
277
            if ($delete) {
278
                $test_results->{'5_ftp_del'}->{'passed'} = 1;
279
                $test_results->{'5_ftp_del'}->{'msg'}    = $ftp->message;
280
            } else {
281
                $test_results->{'5_ftp_del'}->{'err'} = $ftp->message;
282
            }
283
        }
284
    }
285
286
    ## return (note that we return 1 here to signify the run finished)
287
    my $test_status = 1;
288
    return ( $test_status, $test_results );
289
}
290
291
=head2 Internal methods
292
293
=head3 _dos2unix
294
295
Return a CR-free string from an input
296
297
=cut
298
299
sub _dos2unix {
300
    my $dosStr = shift;
301
302
    return $dosStr =~ s/\015\012/\012/gr;
303
}
304
305
=head3 _type
306
307
Return type of Object relating to Schema ResultSet
308
309
=cut
310
311
sub _type {
312
    return 'SftpServer';
313
}
314
315
1;
(-)a/Koha/SFTP/Servers.pm (+57 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 Class methods
34
35
=head2 Internal methods
36
37
=head3 _type
38
39
Return type of object, relating to Schema ResultSet
40
41
=cut
42
43
sub _type {
44
    return 'SftpServer';
45
}
46
47
=head3 object_class
48
49
Return object class
50
51
=cut
52
53
sub object_class {
54
    return 'Koha::SFTP::Server';
55
}
56
57
1;
(-)a/Koha/Schema/Result/SftpServer.pm (+183 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","file"]}
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: 'varchar'
73
  is_nullable: 1
74
  size: 80
75
76
=head2 key_file
77
78
  data_type: 'varchar'
79
  is_nullable: 1
80
  size: 4096
81
82
=head2 auth_mode
83
84
  data_type: 'enum'
85
  default_value: 'password'
86
  extra: {list => ["password","key_file","noauth"]}
87
  is_nullable: 0
88
89
=head2 debug
90
91
  data_type: 'tinyint'
92
  default_value: 0
93
  is_nullable: 0
94
95
=cut
96
97
__PACKAGE__->add_columns(
98
  "id",
99
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
100
  "name",
101
  { data_type => "varchar", is_nullable => 0, size => 80 },
102
  "host",
103
  {
104
    data_type => "varchar",
105
    default_value => "localhost",
106
    is_nullable => 0,
107
    size => 80,
108
  },
109
  "port",
110
  { data_type => "integer", default_value => 22, is_nullable => 0 },
111
  "transport",
112
  {
113
    data_type => "enum",
114
    default_value => "sftp",
115
    extra => { list => ["ftp", "sftp", "file"] },
116
    is_nullable => 0,
117
  },
118
  "passiv",
119
  { data_type => "tinyint", default_value => 1, is_nullable => 0 },
120
  "user_name",
121
  { data_type => "varchar", is_nullable => 1, size => 80 },
122
  "password",
123
  { data_type => "varchar", is_nullable => 1, size => 80 },
124
  "key_file",
125
  { data_type => "varchar", is_nullable => 1, size => 4096 },
126
  "auth_mode",
127
  {
128
    data_type => "enum",
129
    default_value => "password",
130
    extra => { list => ["password", "key_file", "noauth"] },
131
    is_nullable => 0,
132
  },
133
  "debug",
134
  { data_type => "tinyint", default_value => 0, is_nullable => 0 },
135
);
136
137
=head1 PRIMARY KEY
138
139
=over 4
140
141
=item * L</id>
142
143
=back
144
145
=cut
146
147
__PACKAGE__->set_primary_key("id");
148
149
=head1 RELATIONS
150
151
=head2 vendor_edi_accounts
152
153
Type: has_many
154
155
Related object: L<Koha::Schema::Result::VendorEdiAccount>
156
157
=cut
158
159
__PACKAGE__->has_many(
160
  "vendor_edi_accounts",
161
  "Koha::Schema::Result::VendorEdiAccount",
162
  { "foreign.sftp_server_id" => "self.id" },
163
  { cascade_copy => 0, cascade_delete => 0 },
164
);
165
166
167
# Created by DBIx::Class::Schema::Loader v0.07051 @ 2024-10-16 09:01:20
168
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LV/voK/j3DzE3wpY0uul3A
169
170
__PACKAGE__->add_columns(
171
    '+passiv'     => { is_boolean => 1 },
172
    '+debug'      => { is_boolean => 1 },
173
);
174
175
sub koha_objects_class {
176
    'Koha::SFTP::Servers';
177
}
178
179
sub koha_object_class {
180
    'Koha::SFTP::Server';
181
}
182
183
1;
(-)a/admin/sftp_servers.pl (+223 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 $debug     = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
58
59
    try {
60
        Koha::SFTP::Server->new(
61
            {
62
                name      => $name,
63
                host      => $host,
64
                port      => $port,
65
                transport => $transport,
66
                passiv    => $passiv,
67
                auth_mode => $auth_mode,
68
                user_name => $user_name,
69
                password  => $password,
70
                key_file  => $key_file,
71
                debug     => $debug,
72
            }
73
        )->store;
74
75
        push @messages, {
76
            type => 'message',
77
            code => 'success_on_insert'
78
        };
79
    } catch {
80
        if ( blessed $_ and $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
81
            push @messages, {
82
                type   => 'alert',
83
                code   => 'error_on_insert',
84
                reason => 'duplicate_id'
85
            };
86
        }
87
    };
88
89
    # list servers after adding
90
    $op = 'list';
91
92
} elsif ( $op eq 'edit_form' ) {
93
    my $sftp_server_id = $input->param('sftp_server_id');
94
    my $sftp_server;
95
    my $sftp_server_plain_text_password;
96
    my $sftp_server_plain_text_key;
97
98
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
99
        unless !$sftp_server_id;
100
101
    unless ( !$sftp_server ) {
102
        $sftp_server_plain_text_password = $sftp_server->plain_text_password;
103
        $sftp_server_plain_text_key      = $sftp_server->plain_text_key;
104
    }
105
106
    if ($sftp_server) {
107
        $template->param(
108
            sftp_server                     => $sftp_server,
109
            sftp_server_plain_text_password => $sftp_server_plain_text_password,
110
            sftp_server_plain_text_key      => $sftp_server_plain_text_key,
111
        );
112
    } else {
113
        push @messages, {
114
            type   => 'alert',
115
            code   => 'error_on_edit',
116
            reason => 'invalid_id'
117
        };
118
    }
119
120
} elsif ( $op eq 'cud-edit_save' ) {
121
    my $sftp_server_id = $input->param('sftp_server_id');
122
    my $sftp_server_plain_text_password;
123
    my $sftp_server;
124
125
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
126
        unless !$sftp_server_id;
127
128
    $sftp_server_plain_text_password = $sftp_server->plain_text_password
129
        unless !$sftp_server_id;
130
131
    if ($sftp_server) {
132
        my $name      = $input->param('sftp_name');
133
        my $host      = $input->param('sftp_host')      || 'localhost';
134
        my $port      = $input->param('sftp_port')      || 22;
135
        my $transport = $input->param('sftp_transport') || 'sftp';
136
        my $passiv    = ( scalar $input->param('sftp_passiv') ) ? 1 : 0;
137
        my $auth_mode = $input->param('sftp_auth_mode') || 'password';
138
        my $user_name = $input->param('sftp_user_name') || undef;
139
        my $password  = $input->param('sftp_password')  || undef;
140
        my $key_file  = $input->param('sftp_key_file')  || undef;
141
        my $debug     = ( scalar $input->param('sftp_debug_mode') ) ? 1 : 0;
142
143
        try {
144
            $sftp_server->password($password)
145
                if defined $password and $password ne '****'
146
                or not defined $password;
147
148
            $sftp_server->set(
149
                {
150
                    name      => $name,
151
                    host      => $host,
152
                    port      => $port,
153
                    transport => $transport,
154
                    passiv    => $passiv,
155
                    auth_mode => $auth_mode,
156
                    user_name => $user_name,
157
                    password  => $password,
158
                    key_file  => $key_file,
159
                    debug     => $debug,
160
                }
161
            )->store;
162
163
            push @messages, {
164
                type => 'message',
165
                code => 'success_on_update'
166
            };
167
168
        } catch {
169
170
            push @messages, {
171
                type => 'alert',
172
                code => 'error_on_update'
173
            };
174
175
        };
176
177
        # list servers after adding
178
        $op = 'list';
179
    } else {
180
        push @messages, {
181
            type   => 'alert',
182
            code   => 'error_on_update',
183
            reason => 'invalid_id'
184
        };
185
    }
186
187
} elsif ( $op eq 'test_form' ) {
188
    my $sftp_server_id = $input->param('sftp_server_id');
189
    my $sftp_server;
190
191
    $sftp_server = Koha::SFTP::Servers->find($sftp_server_id)
192
        unless !$sftp_server_id;
193
194
    if ($sftp_server) {
195
        ## do the test
196
        my ( $sftp_server_test_status, $sftp_server_test_result ) = $sftp_server->test_conn;
197
198
        $template->param(
199
            sftp_server             => $sftp_server,
200
            sftp_server_test_result => $sftp_server_test_result,
201
            sftp_server_test_status => $sftp_server_test_status,
202
        );
203
    } else {
204
        push @messages, {
205
            type   => 'alert',
206
            code   => 'error_on_test',
207
            reason => 'invalid_id',
208
        };
209
    }
210
}
211
212
if ( $op eq 'list' ) {
213
    $template->param(
214
        servers_count => $sftp_servers->count,
215
    );
216
}
217
218
$template->param(
219
    op       => $op,
220
    messages => \@messages,
221
);
222
223
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/api/v1/swagger/definitions/sftp_server.yaml (+54 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
14
  port:
15
    type: integer
16
    description: TCP port number
17
  transport:
18
    type: string
19
    enum:
20
      - ftp
21
      - sftp
22
      - file
23
    description: Transport of FTP/SFTP server
24
  auth_mode:
25
    type: string
26
    enum:
27
      - password
28
      - key_file
29
      - noauth
30
    description: Authentication mode to use
31
  passiv:
32
    type: boolean
33
    description: Whether or not to use passive mode
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
  debug:
50
    type: boolean
51
    description: If the SMTP connection is set to debug mode
52
additionalProperties: false
53
required:
54
  - 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/swagger.yaml (+15 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 931-936 parameters: Link Here
931
    name: smtp_server_id
937
    name: smtp_server_id
932
    required: true
938
    required: true
933
    type: integer
939
    type: integer
940
  sftp_server_id_pp:
941
    description: FTP/SFTP server internal identifier
942
    in: path
943
    name: sftp_server_id
944
    required: true
945
    type: integer
934
  suggestion_id_pp:
946
  suggestion_id_pp:
935
    description: Internal suggestion identifier
947
    description: Internal suggestion identifier
936
    in: path
948
    in: path
Lines 1278-1283 tags: Link Here
1278
  - description: "Manage SMTP servers configurations\n"
1290
  - description: "Manage SMTP servers configurations\n"
1279
    name: smtp_servers
1291
    name: smtp_servers
1280
    x-displayName: SMTP servers
1292
    x-displayName: SMTP servers
1293
  - description: "Manage FTP/SFTP servers configurations\n"
1294
    name: sftp_servers
1295
    x-displayName: FTP/SFTP servers
1281
  - description: "Manage tickets\n"
1296
  - description: "Manage tickets\n"
1282
    name: tickets
1297
    name: tickets
1283
    x-displayName: Tickets
1298
    x-displayName: Tickets
(-)a/installer/data/mysql/atomicupdate/bug_35761-add_SFTP_tables_and_perm.pl (+41 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` varchar(80) DEFAULT NULL,
30
                    `key_file` varchar(4096) DEFAULT NULL,
31
                    `auth_mode` enum('password','key_file','noauth') NOT NULL DEFAULT 'password',
32
                    `debug` tinyint(1) NOT NULL DEFAULT 0,
33
                    PRIMARY KEY (`id`),
34
                    KEY `host_idx` (`host`)
35
                    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
36
                }
37
            );
38
            say $out "Added new sftp_servers table";
39
        }
40
    },
41
};
(-)a/installer/data/mysql/kohastructure.sql (+24 lines)
Lines 5918-5923 CREATE TABLE `sessions` ( Link Here
5918
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5918
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5919
/*!40101 SET character_set_client = @saved_cs_client */;
5919
/*!40101 SET character_set_client = @saved_cs_client */;
5920
5920
5921
--
5922
-- Table structure for table `sftp_servers`
5923
--
5924
5925
DROP TABLE IF EXISTS `sftp_servers`;
5926
/*!40101 SET @saved_cs_client     = @@character_set_client */;
5927
/*!40101 SET character_set_client = utf8 */;
5928
CREATE TABLE `sftp_servers` (
5929
  `id` int(11) NOT NULL AUTO_INCREMENT,
5930
  `name` varchar(80) NOT NULL,
5931
  `host` varchar(80) NOT NULL DEFAULT 'localhost',
5932
  `port` int(11) NOT NULL DEFAULT 22,
5933
  `transport` enum('ftp','sftp') NOT NULL DEFAULT 'sftp',
5934
  `passiv` tinyint(1) NOT NULL DEFAULT 1,
5935
  `user_name` varchar(80) DEFAULT NULL,
5936
  `password` varchar(80) DEFAULT NULL,
5937
  `key_file` varchar(4096) DEFAULT NULL,
5938
  `auth_mode` enum('password','key_file','noauth') NOT NULL DEFAULT 'password',
5939
  `debug` tinyint(1) NOT NULL DEFAULT 0,
5940
  PRIMARY KEY (`id`),
5941
  KEY `host_idx` (`host`)
5942
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5943
/*!40101 SET character_set_client = @saved_cs_client */;
5944
5921
--
5945
--
5922
-- Table structure for table `sms_providers`
5946
-- Table structure for table `sms_providers`
5923
--
5947
--
(-)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 (+788 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
</head>
20
21
<body id="admin_sftp_servers" class="admin">
22
[% WRAPPER 'header.inc' %]
23
    [% INCLUDE 'prefs-admin-search.inc' %]
24
[% END %]
25
26
[% WRAPPER 'sub-header.inc' %]
27
    [% WRAPPER breadcrumbs %]
28
        [% WRAPPER breadcrumb_item %]
29
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
30
        [% END %]
31
32
        [% IF op == 'add_form' || op == 'edit_form' || op == 'test_form' %]
33
            [% WRAPPER breadcrumb_item %]
34
                <a href="/cgi-bin/koha/admin/sftp_servers.pl">FTP/SFTP servers</a>
35
            [% END %]
36
        [% END %]
37
38
        [% IF op == 'add_form' %]
39
            [% WRAPPER breadcrumb_item bc_active= 1 %]
40
                <span>New FTP/SFTP server</span>
41
            [% END %]
42
43
        [% ELSIF op == 'edit_form' %]
44
            [% WRAPPER breadcrumb_item bc_active= 1 %]
45
                [% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
46
            [% END %]
47
48
        [% ELSIF op == 'test_form' %]
49
            [% WRAPPER breadcrumb_item bc_active= 1 %]
50
                [% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]
51
            [% END %]
52
53
        [% ELSE %]
54
            [% WRAPPER breadcrumb_item bc_active= 1 %]
55
                <span>FTP/SFTP servers</span>
56
            [% END %]
57
        [% END %]
58
    [% END #/ WRAPPER breadcrumbs %]
59
[% END #/ WRAPPER sub-header.inc %]
60
61
<div class="main container-fluid">
62
    <div class="row">
63
        <div class="col-md-10 order-md-2 order-sm-1">
64
            <main>
65
                [% INCLUDE 'messages.inc' %]
66
67
[% FOREACH m IN messages %]
68
    <div class="alert alert-[% m.type | html %]" id="sftp_action_result_dialog">
69
        [% SWITCH m.code %]
70
        [% CASE 'error_on_insert' %]
71
            <span>An error occurred when adding the server. The passed ID already exists.</span>
72
        [% CASE 'error_on_update' %]
73
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
74
        [% CASE 'error_on_edit' %]
75
            <span>An error occurred trying to open the server for editing. The passed ID is invalid.</span>
76
        [% CASE 'error_on_test' %]
77
            <span>An error occurred when connecting to this server. Please see the text below for more information.</span>
78
        [% CASE 'success_on_update' %]
79
            <span>Server updated successfully.</span>
80
        [% CASE 'success_on_insert' %]
81
            <span>Server added successfully.</span>
82
        [% CASE %]
83
            <span>[% m.code | html %]</span>
84
        [% END %]
85
    </div>
86
[% END %]
87
88
    <div class="alert alert-info"    id="sftp_delete_success" style="display: none;"></div>
89
    <div class="alert alert-warning" id="sftp_delete_error"   style="display: none;"></div>
90
91
[% IF op == 'add_form' %]
92
    <!-- Modal -->
93
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
94
        <div class="modal-dialog modal-lg">
95
            <div class="modal-content modal-lg">
96
                    <div class="modal-header">
97
                        <h1 class="modal-title">Are you sure?</h1>
98
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
99
                    </div>
100
                    <div class="modal-body">
101
                        <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>
102
                        <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>
103
                        <table class="mx-4 mb-3">
104
                            <thead></thead>
105
                            <tbody>
106
                                <tr>
107
                                    <td><strong>Host</strong></td>
108
                                    <td id="modal_host"></td>
109
                                </tr>
110
                                <tr>
111
                                    <td><strong>Port</strong></td>
112
                                    <td id="modal_port"></td>
113
                                </tr>
114
                                <tr>
115
                                    <td><strong>Transport</strong></td>
116
                                    <td id="modal_transport"></td>
117
                                </tr>
118
                                <tr>
119
                                    <td><strong>Username</strong></td>
120
                                    <td id="modal_user_name"></td>
121
                                </tr>
122
                                <tr>
123
                                    <td><strong>Authentication mode</strong></td>
124
                                    <td id="modal_auth_mode"></td>
125
                                </tr>
126
                            </tbody>
127
                        </table>
128
                        <p>If you are ready to progress with these details, please click Save.</p>
129
                    </div>
130
                    <div class="modal-footer">
131
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
132
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
133
                    </div>
134
                </form>
135
            </div>
136
        </div>
137
    </div>
138
    <!-- END Modal -->
139
140
    <h1>New FTP/SFTP server</h1>
141
142
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="add" name="add" class="validated" method="post">
143
        [% INCLUDE 'csrf-token.inc' %]
144
        <input type="hidden" name="op" value="cud-add" />
145
        <fieldset class="rows">
146
            <ol>
147
                <li>
148
                    <label for="sftp_name" class="required">Name: </label>
149
                    <input type="text" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
150
                    <span class="required">Required</span>
151
                </li>
152
            </ol>
153
        </fieldset>
154
155
        <fieldset class="rows">
156
            <ol>
157
                <li>
158
                    <label for="sftp_host" class="required">Host: </label>
159
                    <input type="text" value="localhost" name="sftp_host" id="sftp_host" size="60" class="required" />
160
                    <span class="required">Required</span>
161
                </li>
162
                <li>
163
                    <label for="sftp_port" class="required">Port: </label>
164
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="22" name="sftp_port" id="sftp_port" size="20" class="required" />
165
                    <span class="required">Required</span>
166
                </li>
167
                <li>
168
                    <label for="sftp_transport" class="required">Transport: </label>
169
                    <select name="sftp_transport" id="sftp_transport" class="required">
170
                        <option value="ftp">FTP</option>
171
                        <option value="sftp" selected="selected">SFTP</option>
172
                    </select>
173
                    <span class="required">Required</span>
174
                </li>
175
                <li>
176
                    <label for="sftp_passiv">Passive mode: </label>
177
                    <select name="sftp_passiv" id="sftp_passiv" disabled="disabled">
178
                        <option value="1" selected="selected">On (Recommended)</option>
179
                        <option value="0">Off</option>
180
                    </select>
181
                    <span class="hint">Only applies to FTP connections</span>
182
                </li>
183
                <li>
184
                    <label for="sftp_auth_mode">Authentication mode: </label>
185
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
186
                        <option value="password" selected="selected">Password-based</option>
187
                        <option value="key_file">Key file-based</option>
188
                        <option value="noauth">No authentication</option>
189
                    </select>
190
                </li>
191
                <li>
192
                    <label for="sftp_user_name" class="required">Username: </label>
193
                    <input type="text" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
194
                    <span class="required">Required</span>
195
                </li>
196
                <li>
197
                    <label for="sftp_password">Password: </label>
198
                    <input type="password" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
199
                </li>
200
                <li>
201
                    <label for="sftp_key_file">Key file: </label>
202
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58"></textarea>
203
                    <span class="hint">Only applies to SFTP connections</span>
204
                </li>
205
                <li>
206
                    <label for="sftp_debug_mode">Debug mode: </label>
207
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
208
                        <option value="1">Enabled</option>
209
                        <option value="0" selected="selected">Disabled</option>
210
                    </select>
211
                    <span class="hint">Enables additional debug output in the logs</span>
212
                </li>
213
            </ol>
214
        </fieldset>
215
        <fieldset class="action">
216
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
217
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
218
        </fieldset>
219
    </form>
220
[% END %]
221
222
[% IF op == 'edit_form' && !messages %]
223
    <!-- Modal -->
224
    <div id="confirm_key_accept" class="modal" tabindex="-1" role="dialog" aria-labelledby="confirm_key_accept_submit" aria-hidden="true">
225
        <div class="modal-dialog modal-lg">
226
            <div class="modal-content modal-lg">
227
                    <div class="modal-header">
228
                        <h1 class="modal-title">Are you sure?</h1>
229
                        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
230
                    </div>
231
                    <div class="modal-body">
232
                        <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>
233
                        <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>
234
                        <table class="mx-4 mb-3">
235
                            <thead></thead>
236
                            <tbody>
237
                                <tr>
238
                                    <td><strong>Host</strong></td>
239
                                    <td id="modal_host"></td>
240
                                </tr>
241
                                <tr>
242
                                    <td><strong>Port</strong></td>
243
                                    <td id="modal_port"></td>
244
                                </tr>
245
                                <tr>
246
                                    <td><strong>Transport</strong></td>
247
                                    <td id="modal_transport"></td>
248
                                </tr>
249
                                <tr>
250
                                    <td><strong>Username</strong></td>
251
                                    <td id="modal_user_name"></td>
252
                                </tr>
253
                                <tr>
254
                                    <td><strong>Authentication mode</strong></td>
255
                                    <td id="modal_auth_mode"></td>
256
                                </tr>
257
                            </tbody>
258
                        </table>
259
                        <p>If you are ready to progress with these details, please click Save.</p>
260
                    </div>
261
                    <div class="modal-footer">
262
                        <button class="btn btn-default approve" type="submit"><i class="fa fa-check"></i> Save</button>
263
                        <button class="btn btn-default deny cancel" type="button" data-bs-dismiss="modal"><i class="fa fa-times"></i> Cancel</button>
264
                    </div>
265
                </form>
266
            </div>
267
        </div>
268
    </div>
269
    <!-- END Modal -->
270
271
    <h1>[% tx("Modify FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
272
273
    <form action="/cgi-bin/koha/admin/sftp_servers.pl" id="edit_save" name="edit_save" class="validated" method="post">
274
        [% INCLUDE 'csrf-token.inc' %]
275
        <input type="hidden" name="op" value="cud-edit_save" />
276
        <input type="hidden" name="sftp_server_id" value="[%- sftp_server.id | html -%]" />
277
        <fieldset class="rows">
278
            <ol>
279
                <li>
280
                    <label for="sftp_name" class="required">Name: </label>
281
                    <input type="text" value="[% sftp_server.name | html %]" name="sftp_name" id="sftp_name" size="60" class="required focus" required="required" />
282
                    <span class="required">Required</span>
283
                </li>
284
            </ol>
285
        </fieldset>
286
287
        <fieldset class="rows">
288
            <ol>
289
                <li>
290
                    <label for="sftp_host" class="required">Host: </label>
291
                    <input type="text" value="[% sftp_server.host | html %]" name="sftp_host" id="sftp_host" size="60" class="required" />
292
                    <span class="required">Required</span>
293
                </li>
294
                <li>
295
                    <label for="sftp_port" class="required">Port: </label>
296
                    <input type="text" inputmode="numeric" pattern="[0-9]*" value="[% sftp_server.port | html %]" name="sftp_port" id="sftp_port" size="20" class="required"/>
297
                    <span class="required">Required</span>
298
                </li>
299
                <li>
300
                    <label for="sftp_transport" class="required">Transport: </label>
301
                    <select name="sftp_transport" id="sftp_transport" class="required">
302
                        [% IF sftp_server.transport == 'ftp' %]
303
                        <option value="ftp" selected="selected">FTP</option>
304
                        [% ELSE %]
305
                        <option value="ftp">FTP</option>
306
                        [% END %]
307
                        [% IF sftp_server.transport == 'sftp' %]
308
                        <option value="sftp" selected="selected">SFTP</option>
309
                        [% ELSE %]
310
                        <option value="sftp">SFTP</option>
311
                        [% END %]
312
                    </select>
313
                    <span class="required">Required</span>
314
                </li>
315
                <li>
316
                    <label for="sftp_passiv">Passive mode: </label>
317
                    <select name="sftp_passiv" id="sftp_passiv" disabled="disabled">
318
                        [% IF sftp_server.passiv == 1 %]
319
                        <option value="1" selected="selected">Enabled (Recommended)</option>
320
                        [% ELSE %]
321
                        <option value="1">Enabled (Recommended)</option>
322
                        [% END %]
323
                        [% IF sftp_server.passiv == 0 %]
324
                        <option value="0" selected="selected">Disabled</option>
325
                        [% ELSE %]
326
                        <option value="0">Disabled</option>
327
                        [% END %]
328
                    </select>
329
                    <span class="hint">Only applies to FTP connections</span>
330
                </li>
331
                <li>
332
                    <label for="sftp_auth_mode">Authentication mode: </label>
333
                    <select name="sftp_auth_mode" id="sftp_auth_mode">
334
                        [% IF sftp_server.auth_mode == 'password' %]
335
                        <option value="password" selected="selected">Password-based</option>
336
                        [% ELSE %]
337
                        option value="password">Password-based</option>
338
                        [% END %]
339
                        [% IF sftp_server.auth_mode == 'key_file' %]
340
                        <option value="key_file" selected="selected">Key file-based</option>
341
                        [% ELSE %]
342
                        <option value="key_file">Key file-based</option>
343
                        [% END %]
344
                        [% IF sftp_server.auth_mode == 'noauth' %]
345
                        <option value="noauth" selected="selected">No authentication</option>
346
                        [% ELSE %]
347
                        <option value="noauth">No authentication</option>
348
                        [% END %]
349
                    </select>
350
                </li>
351
                <li>
352
                    <label for="sftp_user_name" class="required">Username: </label>
353
                    <input type="text" value="[% sftp_server.user_name | html %]" name="sftp_user_name" id="sftp_user_name" size="60" autocomplete="off" class="required" />
354
                    <span class="required">Required</span>
355
                </li>
356
                <li>
357
                    <label for="sftp_password">Password: </label>
358
                    <input type="password" value="[% sftp_server_plain_text_password | html %]" name="sftp_password" id="sftp_password" size="60" autocomplete="off" />
359
                </li>
360
                <li>
361
                    <label for="sftp_key_file">Key file path: </label>
362
                    <textarea name="sftp_key_file" id="sftp_key_file" rows="10" cols="58">[% sftp_server_plain_text_key | html %]</textarea>
363
                    <span class="hint">Only applies to SFTP connections</span>
364
                </li>
365
                <li>
366
                    <label for="sftp_debug_mode">Debug mode: </label>
367
                    <select name="sftp_debug_mode" id="sftp_debug_mode">
368
                        [% IF sftp_server.debug == 1 %]
369
                        <option value="1" selected="selected">Enabled</option>
370
                        [% ELSE %]
371
                        <option value="1">Enabled</option>
372
                        [% END %]
373
                        [% IF sftp_server.debug == 0 %]
374
                        <option value="0" selected="selected">Disabled</option>
375
                        [% ELSE %]
376
                        <option value="0">Disabled</option>
377
                        [% END %]
378
                    </select>
379
                    <span class="hint">Enables additional debug output in the logs</span>
380
                </li>
381
            </ol>
382
        </fieldset>
383
        <fieldset class="action">
384
            <a id="confirm_key_accept_submit" data-bs-target="#confirm_key_accept" class="btn btn-primary" data-bs-toggle="modal">Submit</a>
385
            <a class="cancel" href="/cgi-bin/koha/admin/sftp_servers.pl">Cancel</a>
386
        </fieldset>
387
    </form>
388
[% END %]
389
390
[% IF op == 'test_form' %]
391
    <div id="toolbar" class="btn-toolbar">
392
        <a class="btn btn-default" id="newtest" href="/cgi-bin/koha/admin/sftp_servers.pl?op=test_form&amp;sftp_server_id=[% sftp_server.id | html %]"><i class="fa-solid fa-rotate-right"></i> Retry test</a>
393
    </div>
394
    <h1>[% tx("Test FTP/SFTP server '{sftp_server}'", { sftp_server = sftp_server.name }) | html %]</h1>
395
    <div class="page-section">
396
        <pre>>> Testing the FTP/SFTP server for you</pre>
397
        <pre>>> Connection details will be as follows:</pre>
398
        <pre>Transport: [% sftp_server.transport FILTER upper | html %]</pre>
399
        <pre>Username:  [% sftp_server.user_name | html %]</pre>
400
        <pre>Host:      [% sftp_server.host | html %]</pre>
401
        <pre>Port:      [% sftp_server.port | html %]</pre>
402
        <pre>>> Okay, starting tests . . . </pre>
403
        [% FOREACH result IN sftp_server_test_result.pairs %]
404
        <pre>=================================================</pre>
405
        [% SWITCH result.key %]
406
        [% CASE '1_sftp_conn' %]
407
            <pre>>> Testing SFTP connecivity</pre>
408
        [% CASE '1_ftp_conn' %]
409
            <pre>>> Testing FTP connecivity</pre>
410
        [% CASE ['2_sftp_ls', '3_ftp_ls'] %]
411
            <pre>>> Testing we can list directories</pre>
412
        [% CASE '2_ftp_login' %]
413
            <pre>>> Testing we can log in</pre>
414
        [% CASE ['3_sftp_write', '4_ftp_write'] %]
415
            <pre>>> Testing we can write a test file</pre>
416
        [% CASE ['4_sftp_del', '5_ftp_del'] %]
417
            <pre>>> Testing we can delete test file</pre>
418
        [% CASE DEFAULT %]
419
            <pre>>> [% result.key | html %]</pre>
420
        [% END %]
421
        <pre>Executing [% result.key | html %] test . . . </pre>
422
        [% IF ( result.value.err ) %]
423
        <pre>ERROR: [% result.value.err | html %]</pre>
424
        [% ELSE %]
425
            [% IF ( result.value.msg ) %]
426
            <pre>PASSED: [% result.value.msg | html %]</pre>
427
            [% ELSE %]
428
            <pre>PASSED</pre>
429
            [% END %]
430
        [% END %]
431
        [% END %]
432
        <pre>=================================================</pre>
433
    </div>
434
[% END %]
435
436
[% IF op == 'list' %]
437
438
    <div id="toolbar" class="btn-toolbar">
439
        <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>
440
    </div>
441
442
    <h1>FTP/SFTP servers</h1>
443
444
    [% IF servers_count < 1 %]
445
        <div class="alert alert-info" id="dno_servers_message">
446
            <p>
447
                <em>There are no FTP/SFTP servers defined.</em><br />
448
                To create one, use the <strong>new FTP/SFTP server</strong> button above.
449
            </p>
450
        </div>
451
    [% ELSE %]
452
        <div class="page-section">
453
            <table id="sftp_servers">
454
                <thead>
455
                    <tr>
456
                        <th>Name</th>
457
                        <th>Host</th>
458
                        <th>Port</th>
459
                        <th>Transport</th>
460
                        <th>Passive mode</th>
461
                        <th>Authentication mode</th>
462
                        <th>Username</th>
463
                        <th>Debug</th>
464
                        <th data-class-name="actions noExport">Actions</th>
465
                    </tr>
466
                </thead>
467
            </table>
468
        </div> <!-- /.page-section -->
469
    [% END %]
470
[% END %]
471
472
            <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
473
                <div class="modal-dialog">
474
                    <div class="modal-content">
475
                        <div class="modal-header">
476
                            <h1 class="modal-title" id="delete_confirm_modal_label">Delete server</h1>
477
                            <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
478
                        </div>
479
                        <div class="modal-body">
480
                            <div id="delete_confirm_dialog"></div>
481
                        </div>
482
                        <div class="modal-footer">
483
                            <button type="button" class="btn btn-danger" id="delete_confirm_modal_button" data-bs-toggle="modal">Delete</button>
484
                            <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
485
                        </div>
486
                    </div> <!-- /.modal-content -->
487
                </div> <!-- /.modal-dialog -->
488
            </div> <!-- #delete_confirm_modal -->
489
490
            </main>
491
        </div> <!-- /.col-md-10.order-md-2 -->
492
493
        <div class="col-md-2 order-sm-2 order-md-1">
494
            <aside>
495
                [% INCLUDE 'admin-menu.inc' %]
496
            </aside>
497
        </div> <!-- /.col-md-2.order-md-1 -->
498
     </div> <!-- /.row -->
499
500
501
[% MACRO jsinclude BLOCK %]
502
    [% Asset.js("js/admin-menu.js") | $raw %]
503
    [% INCLUDE 'datatables.inc' %]
504
    <script>
505
        $(document).ready(function() {
506
507
            var sftp_servers_url = '/api/v1/config/sftp_servers';
508
            window.sftp_servers = $("#sftp_servers").kohaTable({
509
                "ajax": {
510
                    "url": sftp_servers_url
511
                },
512
                'language': {
513
                    'emptyTable': '<div class="alert alert-info">'+_("There are no FTP/SFTP servers defined.")+'</div>'
514
                },
515
                "columnDefs": [ {
516
                    "targets": [0,1],
517
                    "render": function(data, type, row, meta) {
518
                        if(type == 'display') {
519
                            if(data != null) {
520
                                return data.escapeHtml();
521
                            }
522
                            else {
523
                                return "Default";
524
                            }
525
                        }
526
                        return data;
527
                    }
528
                } ],
529
                "columns": [
530
                    {
531
                        "data": "name",
532
                        "searchable": true,
533
                        "orderable": true
534
                    },
535
                    {
536
                        "data": "host",
537
                        "searchable": true,
538
                        "orderable": true
539
                    },
540
                    {
541
                        "data": "port",
542
                        "searchable": true,
543
                        "orderable": false
544
                    },
545
                    {
546
                        "data": "transport",
547
                        "render": function(data, type, row, meta) {
548
                            return data.toUpperCase();
549
                        },
550
                        "searchable": true,
551
                        "orderable": false
552
                    },
553
                    {
554
                        "data": "passiv",
555
                        "render": function(data, type, row, meta) {
556
                            if(data == true) {
557
                                return "[% tp("Active", "On") | html %]";
558
                            }
559
                            else {
560
                                return _("Off");
561
                            }
562
                        },
563
                        "searchable": false,
564
                        "orderable": false
565
                    },
566
                    {
567
                        "data": "auth_mode",
568
                        "render": function(data, type, row, meta) {
569
                            if(data == "password") {
570
                                return _("Password-based");
571
                            }
572
                            else if(data == "key_file") {
573
                                return _("Key file-based");
574
                            }
575
                            else {
576
                                return _("No authentication");
577
                            }
578
                        },
579
                        "searchable": false,
580
                        "orderable": false
581
                    },
582
                    {
583
                        "data": "user_name",
584
                        "searchable": false,
585
                        "orderable": false
586
                    },
587
                    {
588
                        "data": "debug",
589
                        "render": function(data, type, row, meta) {
590
                            if(data == true) {
591
                                return "[% tp("Active", "On") | html %]";
592
                            }
593
                            else {
594
                                return _("Off");
595
                            }
596
                        },
597
                        "searchable": false,
598
                        "orderable": false
599
                    },
600
                    {
601
                        "data": function(row, type, val, meta) {
602
                            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";
603
                            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";
604
                            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>';
605
                            return result;
606
                        },
607
                        "searchable": false,
608
                        "orderable": false
609
                    }
610
                ],
611
                createdRow: function(row, data, dataIndex) {
612
                    if(data.is_default) {
613
                        $(row).addClass('default warn');
614
                    }
615
                    if(data.debug) {
616
                        $(row).addClass('debug');
617
                    }
618
                },
619
            });
620
621
            $('#sftp_servers').on("click", '.delete_server', function() {
622
                var sftp_server_id   = $(this).data('sftp-server-id');
623
                var sftp_server_name = decodeURIComponent($(this).data('sftp-server-name'));
624
625
                $("#delete_confirm_dialog").html(
626
                    _("You are about to delete the '%s' FTP/SFTP server.").format(sftp_server_name)
627
                );
628
                $("#delete_confirm_modal_button").data('sftp-server-id', sftp_server_id);
629
                $("#delete_confirm_modal_button").data('sftp-server-name', sftp_server_name);
630
            });
631
632
            $("#delete_confirm_modal_button").on("click", function() {
633
634
                var sftp_server_id   = $(this).data('sftp-server-id');
635
                var sftp_server_name = $(this).data('sftp-server-name');
636
637
                $.ajax({
638
                    method: "DELETE",
639
                    url: "/api/v1/config/sftp_servers/"+sftp_server_id
640
                }).success(function() {
641
                    window.sftp_servers.api().ajax.reload(function(data) {
642
                        $("#sftp_action_result_dialog").hide();
643
                        $("#sftp_delete_success").html(_("Server '%s' deleted successfully.").format(sftp_server_name)).show();
644
                    });
645
                }).fail(function() {
646
                    $("#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();
647
                }).done(function() {
648
                    $("#delete_confirm_modal").modal('hide');
649
                });
650
            });
651
652
            // run transportChange on pageload, and again every time sftp_transport changes
653
            transportChange();
654
            $("#sftp_transport").on("change", function(event) {
655
                transportChange();
656
            });
657
658
            // run authModeChange on pageload, and again every time sftp_auth_mode changes
659
            authModeChange();
660
            $("#sftp_auth_mode").on("change", function(event) {
661
                authModeChange();
662
            });
663
664
            $('#confirm_key_accept_submit').on('click', function(event) {
665
                event.preventDefault();
666
667
                if ( $('#add').length > 0 ) { // has to be nested to avoid errors :-(
668
                    if( $('#add').valid() == true ) {
669
                        modalChange();
670
                        $('#confirm_key_accept').modal('show');
671
                    } else {
672
                        $('#confirm_key_accept').modal('hide');
673
                    }
674
                }
675
676
                if ( $('#edit_save').length > 0 ) { // has to be nested to avoid errors :-(
677
                    if( $('#edit_save').valid() == true ) {
678
                        modalChange();
679
                        $('#confirm_key_accept').modal('show');
680
                    } else {
681
                        $('#confirm_key_accept').modal('hide');
682
                    }
683
                }
684
685
            });
686
687
            $('#confirm_key_accept .approve').on('click', function() {
688
                $('#confirm_key_accept .deny').click();
689
690
                if ( $('#add').length > 0 ) {
691
                    $('#add').submit();
692
                }
693
694
                if ( $('#edit_save').length > 0 ) {
695
                    $('#edit_save').submit();
696
                }
697
            });
698
699
        });
700
701
        function transportChange() {
702
            let sftp_transport = $("#sftp_transport");
703
704
            if(sftp_transport.val() == "ftp") {
705
                // disable / enable relevant options
706
                $("#sftp_host").removeAttr("disabled");
707
                $("#sftp_port").removeAttr("disabled");
708
                $("#sftp_passiv").removeAttr("disabled");
709
                $("#sftp_auth_mode").removeAttr("disabled");
710
                $("#sftp_user_name").removeAttr("disabled");
711
                $("#sftp_password").removeAttr("disabled");
712
                $("#sftp_key_file").attr("disabled", "disabled");
713
714
                // ... for auth_mode dropdown
715
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
716
                $("#sftp_auth_mode option[value='key_file']").attr("disabled", "disabled");
717
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
718
                // also reset the selected value CONDITIONALLY
719
                if($("#sftp_auth_mode option:selected").val() == "key_file") {
720
                    $("#sftp_auth_mode option[value='password']").prop("selected", true);
721
                }
722
723
                // check the port
724
                let sftp_port = $("#sftp_port").val();
725
                if(sftp_port == 22) $("#sftp_port").val("21");
726
727
                // trigger authModeChange so the auth fields are correct
728
                authModeChange();
729
            } else if(sftp_transport.val() == "sftp") {
730
                // disable / enable relevant options
731
                $("#sftp_host").removeAttr("disabled");
732
                $("#sftp_port").removeAttr("disabled");
733
                $("#sftp_passiv").attr("disabled", "disabled");
734
                $("#sftp_auth_mode").removeAttr("disabled");
735
                $("#sftp_user_name").removeAttr("disabled");
736
                $("#sftp_password").removeAttr("disabled");
737
                $("#sftp_key_file").removeAttr("disabled");
738
739
                // ... for auth_mode dropdown
740
                $("#sftp_auth_mode option[value='password']").removeAttr("disabled");
741
                $("#sftp_auth_mode option[value='key_file']").removeAttr("disabled");
742
                $("#sftp_auth_mode option[value='noauth']").removeAttr("disabled");
743
                // also reset the selected value CONDITIONALLY
744
                $("#sftp_passiv option[value='1']").prop("selected", true);
745
746
                // check the port
747
                let sftp_port = $("#sftp_port").val();
748
                if(sftp_port == 21) $("#sftp_port").val("22");
749
750
                // trigger authModeChange so the auth fields are correct
751
                return authModeChange();
752
            }
753
        }
754
755
        function authModeChange() {
756
            let sftp_auth_mode = $("#sftp_auth_mode").val();
757
758
            if(sftp_auth_mode == "password") {
759
                // disable / enable relevant options
760
                $("#sftp_password").removeAttr("disabled");
761
                $("#sftp_key_file").attr("disabled", "disabled");
762
            } else if(sftp_auth_mode == "key_file") {
763
                // disable / enable relevant options
764
                $("#sftp_password").attr("disabled", "disabled");
765
                $("#sftp_key_file").removeAttr("disabled");
766
            } else {
767
                // disable / enable relevant options
768
                $("#sftp_password").attr("disabled", "disabled");
769
                $("#sftp_key_file").attr("disabled", "disabled");
770
            }
771
        }
772
773
        function modalChange() {
774
            // should we show the sftp warning?
775
            $('#modal_message').hide();
776
            if ( $('#sftp_transport').val() == 'sftp' ) $('#modal_message').show();
777
778
            // populate modal
779
            $('#modal_host').text( $('#sftp_host').val() );
780
            $('#modal_port').text( $('#sftp_port').val() );
781
            $('#modal_transport').text( $('#sftp_transport option:selected').text() );
782
            $('#modal_user_name').text( $('#sftp_user_name').val() );
783
            $('#modal_auth_mode').text( $('#sftp_auth_mode option:selected').text() );
784
        }
785
    </script>
786
[% END %]
787
788
[% 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 (+170 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 => 5;
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(
39
        {
40
            class => 'Koha::SFTP::Servers',
41
            value => {
42
                password => undef,
43
                key_file => undef,
44
            },
45
        }
46
    );
47
    $sftp_server->set( { password => 'test123' } )->store;
48
49
    ok( $sftp_server->password ne 'test123', 'Password should not be in plain text' );
50
    is( length( $sftp_server->password ), 64, 'Password has should be 64 characters long' );
51
52
    $schema->storage->txn_rollback;
53
};
54
55
subtest 'to_api() tests' => sub {
56
57
    plan tests => 1;
58
59
    $schema->storage->txn_begin;
60
61
    my $sftp_server = $builder->build_object(
62
        {
63
            class => 'Koha::SFTP::Servers',
64
            value => {
65
                password => undef,
66
                key_file => undef,
67
            },
68
        }
69
    );
70
71
    ok( !exists $sftp_server->to_api->{password}, 'Password is not part of the API representation' );
72
73
    $schema->storage->txn_rollback;
74
};
75
76
subtest 'plain_text_password() tests' => sub {
77
78
    plan tests => 2;
79
80
    $schema->storage->txn_begin;
81
82
    my $sftp_server = $builder->build_object(
83
        {
84
            class => 'Koha::SFTP::Servers',
85
            value => {
86
                password => undef,
87
                key_file => undef,
88
            },
89
        }
90
    );
91
92
    ## create a password
93
    $sftp_server->set( { password => 'test123' } )->store;
94
95
    ## retrieve it back out
96
    my $sftp_server_plain_text_password = $sftp_server->plain_text_password;
97
98
    isnt( $sftp_server_plain_text_password, $sftp_server->password, 'Password and password hash shouldn\'t match' );
99
    is( $sftp_server_plain_text_password, 'test123', 'Password should be in plain text' );
100
101
    $schema->storage->txn_rollback;
102
};
103
104
subtest 'plain_text_key() tests' => sub {
105
106
    plan tests => 2;
107
108
    $schema->storage->txn_begin;
109
110
    my $sftp_server = $builder->build_object(
111
        {
112
            class => 'Koha::SFTP::Servers',
113
            value => {
114
                password => undef,
115
                key_file => undef,
116
            },
117
        }
118
    );
119
120
    ## create a key
121
    $sftp_server->set( { key_file => '321tset' } )->store;
122
123
    ## retrieve it back out
124
    my $sftp_server_plain_text_key = $sftp_server->plain_text_key;
125
126
    isnt( $sftp_server_plain_text_key, $sftp_server->key_file, 'Key file and key file hash shouldn\'t match' );
127
    is( $sftp_server_plain_text_key, "321tset\n", 'Key file should be in plain text' );
128
129
    $schema->storage->txn_rollback;
130
};
131
132
subtest 'write_key_file() tests' => sub {
133
134
    plan tests => 3;
135
136
    $schema->storage->txn_begin;
137
138
    my $sftp_server = $builder->build_object(
139
        {
140
            class => 'Koha::SFTP::Servers',
141
            value => {
142
                password => undef,
143
                key_file => undef,
144
            },
145
        }
146
    );
147
148
    ## create a key
149
    $sftp_server->set( { key_file => '321tset' } )->store;
150
151
    my $path = '/tmp/kohadev_test';
152
    t::lib::Mocks::mock_config( 'upload_path', $path );
153
    mkdir $path if !-d $path;
154
155
    my $first_test  = $sftp_server->write_key_file;
156
157
    my $file        = $sftp_server->locate_key_file;
158
    my $second_test = ( -f $file );
159
160
    open my $fh, $sftp_server->locate_key_file;
161
    my $third_test = <$fh>;
162
163
    is( $first_test, 1, 'Writing key file should return 1' );
164
    is( $second_test, 1, 'Written key file should exist' );
165
    is( $third_test, "321tset\n", 'The contents of the key file should be 321tset\n' );
166
167
    unlink $file;
168
169
    $schema->storage->txn_rollback;
170
};
(-)a/t/db_dependent/api/v1/sftp_servers.t (-1 / +394 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 => 5;
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
};

Return to bug 35761