1 package PMG
::API2
::ACMEPlugin
;
6 use Storable
qw(dclone);
8 use PVE
::ACME
::Challenge
;
9 use PVE
::ACME
::DNSChallenge
;
10 use PVE
::ACME
::StandAlone
;
12 use PVE
::JSONSchema
qw(get_standard_option);
13 use PVE
::Tools
qw(extract_param);
15 use base
qw(PVE::RESTHandler);
17 my $inotify_file_id = 'pmg-acme-plugins-config.conf';
18 my $config_filename = '/etc/pmg/acme/plugins.conf';
19 my $lockfile = "/var/lock/pmg-acme-plugins-config.lck";
21 PVE
::ACME
::DNSChallenge-
>register();
22 PVE
::ACME
::StandAlone-
>register();
23 PVE
::ACME
::Challenge-
>init();
25 PVE
::JSONSchema
::register_standard_option
('pmg-acme-pluginid', {
27 format
=> 'pve-configid',
28 description
=> 'Unique identifier for ACME plugin instance.',
31 sub read_pmg_acme_challenge_config
{
32 my ($filename, $fh) = @_;
33 my $raw = defined($fh) ?
do { local $/ = undef; <$fh> } : '';
34 return PVE
::ACME
::Challenge-
>parse_config($filename, $raw);
37 sub write_pmg_acme_challenge_config
{
38 my ($filename, $fh, $cfg) = @_;
39 my $raw = PVE
::ACME
::Challenge-
>write_config($filename, $cfg);
40 PVE
::Tools
::safe_print
($filename, $fh, $raw);
43 PVE
::INotify
::register_file
(
44 $inotify_file_id, $config_filename,
45 \
&read_pmg_acme_challenge_config
,
46 \
&write_pmg_acme_challenge_config
,
48 always_call_parser
=> 1,
53 my $p = PVE
::Tools
::lock_file
($lockfile, undef, $code);
59 # auto-adds the standalone plugin if no config is there for backwards
60 # compatibility, so ALWAYS call the cfs registered parser
61 return PVE
::INotify
::read_file
($inotify_file_id);
66 return PVE
::INotify
::write_file
($inotify_file_id, $self);
69 my $plugin_type_enum = PVE
::ACME
::Challenge-
>lookup_types();
71 my $modify_cfg_for_api = sub {
72 my ($cfg, $pluginid) = @_;
74 die "ACME plugin '$pluginid' not defined\n" if !defined($cfg->{ids
}->{$pluginid});
76 my $plugin_cfg = dclone
($cfg->{ids
}->{$pluginid});
77 $plugin_cfg->{plugin
} = $pluginid;
78 $plugin_cfg->{digest
} = $cfg->{digest
};
83 __PACKAGE__-
>register_method ({
87 permissions
=> { check
=> [ 'admin' ] },
88 description
=> "ACME plugin index.",
91 additionalProperties
=> 0,
94 description
=> "Only list ACME plugins of a specific type",
96 enum
=> $plugin_type_enum,
106 plugin
=> get_standard_option
('pmg-acme-pluginid'),
109 links
=> [ { rel
=> 'child', href
=> "{plugin}" } ],
114 my $cfg = load_config
();
117 foreach my $pluginid (keys %{$cfg->{ids
}}) {
118 my $plugin_cfg = $modify_cfg_for_api->($cfg, $pluginid);
119 next if $param->{type
} && $param->{type
} ne $plugin_cfg->{type
};
120 push @$res, $plugin_cfg;
127 __PACKAGE__-
>register_method({
128 name
=> 'get_plugin_config',
131 description
=> "Get ACME plugin configuration.",
132 permissions
=> { check
=> [ 'admin' ] },
135 additionalProperties
=> 0,
137 id
=> get_standard_option
('pmg-acme-pluginid'),
146 my $cfg = load_config
();
147 return $modify_cfg_for_api->($cfg, $param->{id
});
151 __PACKAGE__-
>register_method({
152 name
=> 'add_plugin',
155 description
=> "Add ACME plugin configuration.",
156 permissions
=> { check
=> [ 'admin' ] },
158 parameters
=> PVE
::ACME
::Challenge-
>createSchema(),
165 my $id = extract_param
($param, 'id');
166 my $type = extract_param
($param, 'type');
169 my $cfg = load_config
();
170 die "ACME plugin ID '$id' already exists\n" if defined($cfg->{ids
}->{$id});
172 my $plugin = PVE
::ACME
::Challenge-
>lookup($type);
173 my $opts = $plugin->check_config($id, $param, 1, 1);
175 $cfg->{ids
}->{$id} = $opts;
176 $cfg->{ids
}->{$id}->{type
} = $type;
186 __PACKAGE__-
>register_method({
187 name
=> 'update_plugin',
190 description
=> "Update ACME plugin configuration.",
191 permissions
=> { check
=> [ 'admin' ] },
193 parameters
=> PVE
::ACME
::Challenge-
>updateSchema(),
200 my $id = extract_param
($param, 'id');
201 my $delete = extract_param
($param, 'delete');
202 my $digest = extract_param
($param, 'digest');
205 my $cfg = load_config
();
206 PVE
::Tools
::assert_if_modified
($cfg->{digest
}, $digest);
207 my $plugin_cfg = $cfg->{ids
}->{$id};
208 die "ACME plugin ID '$id' does not exist\n" if !$plugin_cfg;
210 my $type = $plugin_cfg->{type
};
211 my $plugin = PVE
::ACME
::Challenge-
>lookup($type);
213 if (defined($delete)) {
214 my $schema = $plugin->private();
215 my $options = $schema->{options
}->{$type};
216 for my $k (PVE
::Tools
::split_list
($delete)) {
217 my $d = $options->{$k} || die "no such option '$k'\n";
218 die "unable to delete required option '$k'\n" if !$d->{optional
};
220 delete $cfg->{ids
}->{$id}->{$k};
224 my $opts = $plugin->check_config($id, $param, 0, 1);
225 for my $k (keys %$opts) {
226 $plugin_cfg->{$k} = $opts->{$k};
237 __PACKAGE__-
>register_method({
238 name
=> 'delete_plugin',
241 description
=> "Delete ACME plugin configuration.",
242 permissions
=> { check
=> [ 'admin' ] },
245 additionalProperties
=> 0,
247 id
=> get_standard_option
('pmg-acme-pluginid'),
256 my $id = extract_param
($param, 'id');
259 my $cfg = load_config
();
261 delete $cfg->{ids
}->{$id};