--- /dev/null
+package PVE::TokenConfig;
+
+use strict;
+use warnings;
+
+use UUID;
+
+use PVE::AccessControl;
+use PVE::Cluster;
+
+my $parse_token_cfg = sub {
+ my ($filename, $raw) = @_;
+
+ my $parsed = {};
+ my @lines = split(/\n/, $raw);
+
+ foreach my $line (@lines) {
+ next if $line =~ m/^\s*$/;
+
+ if ($line =~ m/^(\S+) (\S+)$/) {
+ if (PVE::AccessControl::pve_verify_tokenid($1, 1)) {
+ $parsed->{$1} = $2;
+ next;
+ }
+ }
+
+ warn "skipping invalid token.cfg entry\n";
+ }
+
+ return $parsed;
+};
+
+my $write_token_cfg = sub {
+ my ($filename, $data) = @_;
+
+ my $raw = '';
+ foreach my $tokenid (sort keys %$data) {
+ $raw .= "$tokenid $data->{$tokenid}\n";
+ }
+
+ return $raw;
+};
+
+PVE::Cluster::cfs_register_file('priv/token.cfg', $parse_token_cfg, $write_token_cfg);
+
+sub generate_token {
+ my ($tokenid) = @_;
+
+ PVE::AccessControl::pve_verify_tokenid($tokenid);
+
+ my $token_value = PVE::Cluster::cfs_lock_file('priv/token.cfg', 10, sub {
+ my $uuid = UUID::uuid();
+ my $token_cfg = PVE::Cluster::cfs_read_file('priv/token.cfg');
+
+ $token_cfg->{$tokenid} = $uuid;
+
+ PVE::Cluster::cfs_write_file('priv/token.cfg', $token_cfg);
+
+ return $uuid;
+ });
+
+ die "$@\n" if defined($@);
+
+ return $token_value;
+}
+
+sub delete_token {
+ my ($tokenid) = @_;
+
+ PVE::Cluster::cfs_lock_file('priv/token.cfg', 10, sub {
+ my $token_cfg = PVE::Cluster::cfs_read_file('priv/token.cfg');
+
+ delete $token_cfg->{$tokenid};
+
+ PVE::Cluster::cfs_write_file('priv/token.cfg', $token_cfg);
+ });
+
+ die "$@\n" if defined($@);
+}