pve-manager/PVE/API2/ACMEPlugin.pm
Thomas Lamprecht b1bc9372ec api: acme plugins: we're not the storage content API endpoint
Drop various leftovers from the storage content API module this was
based on, e.g., ACME plugins have no fixed options and the like.
Also, the descriptions shouldn't mention "storage".

Further, drop the "update_config" "helper" with its operations
effectively only increasing code complexity and adding another rabbit
hole to jump into.

IF, this should have been factoring out the lock+read+write cycle
only, living the rest to a passed CODE-ref, but honestly that saves
only really the read and write config lines, and at this point
nothing is really gained, so just let it be.

Should have been actually three or so separate patches, but to deep
into this rabbit hole to care..

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2020-05-03 16:21:24 +02:00

252 lines
5.7 KiB
Perl

package PVE::API2::ACMEPlugin;
use strict;
use warnings;
use MIME::Base64;
use Storable qw(dclone);
use PVE::ACME::Challenge;
use PVE::ACME::DNSChallenge;
use PVE::ACME::StandAlone;
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
use PVE::JSONSchema qw(register_standard_option get_standard_option);
use PVE::Tools qw(extract_param);
use base qw(PVE::RESTHandler);
my $plugin_config_file = "priv/acme/plugins.cfg";
cfs_register_file($plugin_config_file,
sub { PVE::ACME::Challenge->parse_config(@_); },
sub { PVE::ACME::Challenge->write_config(@_); },
);
PVE::ACME::DNSChallenge->register();
PVE::ACME::StandAlone->register();
PVE::ACME::Challenge->init();
PVE::JSONSchema::register_standard_option('pve-acme-pluginid', {
type => 'string',
format => 'pve-configid',
description => 'Unique identifier for ACME plugin instance.',
});
my $plugin_type_enum = PVE::ACME::Challenge->lookup_types();
my $modify_cfg_for_api = sub {
my ($cfg, $pluginid) = @_;
die "ACME plugin '$pluginid' not defined\n" if !defined($cfg->{ids}->{$pluginid});
my $plugin_cfg = dclone($cfg->{ids}->{$pluginid});
$plugin_cfg->{plugin} = $pluginid;
$plugin_cfg->{digest} = $cfg->{digest};
return $plugin_cfg;
};
__PACKAGE__->register_method ({
name => 'index',
path => '',
method => 'GET',
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
description => "ACME plugin index.",
protected => 1,
parameters => {
additionalProperties => 0,
properties => {
type => {
description => "Only list ACME plugins of a specific type",
type => 'string',
enum => $plugin_type_enum,
optional => 1,
},
},
},
returns => {
type => 'array',
items => {
type => "object",
properties => {
plugin => get_standard_option('pve-acme-pluginid'),
},
},
links => [ { rel => 'child', href => "{plugin}" } ],
},
code => sub {
my ($param) = @_;
my $cfg = load_config();
my $res = [];
foreach my $pluginid (keys %{$cfg->{ids}}) {
my $plugin_cfg = $modify_cfg_for_api->($cfg, $pluginid);
next if $param->{type} && $param->{type} ne $plugin_cfg->{type};
push @$res, $plugin_cfg;
}
return $res;
}
});
__PACKAGE__->register_method({
name => 'get_plugin_config',
path => '{id}',
method => 'GET',
description => "Get ACME plugin configuration.",
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
protected => 1,
parameters => {
additionalProperties => 0,
properties => {
id => get_standard_option('pve-acme-pluginid'),
},
},
returns => {
type => 'object',
},
code => sub {
my ($param) = @_;
my $cfg = load_config();
return $modify_cfg_for_api->($cfg, $param->{id});
}
});
__PACKAGE__->register_method({
name => 'add_plugin',
path => '',
method => 'POST',
description => "Add ACME plugin configuration.",
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
protected => 1,
parameters => PVE::ACME::Challenge->createSchema(),
returns => {
type => "null"
},
code => sub {
my ($param) = @_;
my $id = extract_param($param, 'id');
my $type = extract_param($param, 'type');
cfs_lock_file($plugin_config_file, undef, sub {
my $cfg = load_config();
die "ACME plugin ID '$id' already exists\n" if defined($cfg->{ids}->{$id});
my $plugin = PVE::ACME::Challenge->lookup($type);
my $opts = $plugin->check_config($id, $param, 1, 1);
$cfg->{ids}->{$id} = $opts;
$cfg->{ids}->{$id}->{type} = $type;
cfs_write_file($plugin_config_file, $cfg);
});
die "$@" if $@;
return undef;
}
});
__PACKAGE__->register_method({
name => 'update_plugin',
path => '{id}',
method => 'PUT',
description => "Update ACME plugin configuration.",
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
protected => 1,
parameters => PVE::ACME::Challenge->updateSchema(),
returns => {
type => "null"
},
code => sub {
my ($param) = @_;
my $id = extract_param($param, 'id');
my $delete = extract_param($param, 'delete');
cfs_lock_file($plugin_config_file, undef, sub {
my $cfg = load_config();
my $plugin_cfg = $cfg->{ids}->{$id};
die "ACME plugin ID '$id' does not exist\n" if !$plugin_cfg;
my $type = $plugin_cfg->{type};
my $plugin = PVE::ACME::Challenge->lookup($type);
if (defined($delete)) {
my $schema = $plugin->private();
my $options = $schema->{options}->{$type};
for my $k (PVE::Tools::split_list($delete)) {
my $d = $options->{$k} || die "no such option '$k'\n";
die "unable to delete required option '$k'\n" if !$d->{optional};
delete $cfg->{ids}->{$id}->{$k};
}
}
my $opts = $plugin->check_config($id, $param, 0, 1);
for my $k (sort keys %$opts) {
$plugin_cfg->{$k} = $opts->{$k};
}
cfs_write_file($plugin_config_file, $cfg);
});
die "$@" if $@;
return undef;
}
});
__PACKAGE__->register_method({
name => 'delete_plugin',
path => '{id}',
method => 'DELETE',
description => "Delete ACME plugin configuration.",
permissions => {
check => ['perm', '/', [ 'Sys.Modify' ]],
},
protected => 1,
parameters => {
additionalProperties => 0,
properties => {
id => get_standard_option('pve-acme-pluginid'),
},
},
returns => {
type => "null"
},
code => sub {
my ($param) = @_;
my $id = extract_param($param, 'id');
cfs_lock_file($plugin_config_file, undef, sub {
my $cfg = load_config();
delete $cfg->{ids}->{$id};
cfs_write_file($plugin_config_file, $cfg);
});
die "$@" if $@;
return undef;
}
});
sub load_config {
my $cfg = {};
$cfg = cfs_read_file($plugin_config_file) if -e "/etc/pve/$plugin_config_file";
return $cfg;
}
1;