+# The TFA configuration in priv/tfa.cfg format contains one line per user of
+# the form:
+# USER:TYPE:DATA
+# DATA is a base64 encoded json string and its format depends on the type.
+sub parse_priv_tfa_config {
+ my ($filename, $raw) = @_;
+
+ my $users = {};
+ my $cfg = { users => $users };
+
+ $raw = '' if !defined($raw);
+ while ($raw =~ /^\s*(.+?)\s*$/gm) {
+ my $line = $1;
+ my ($user, $type, $data) = split(/:/, $line, 3);
+
+ my (undef, undef, $realm) = PVE::Auth::Plugin::verify_username($user, 1);
+ if (!$realm) {
+ warn "user tfa config - ignore user '$user' - invalid user name\n";
+ next;
+ }
+
+ $data = decode_json(decode_base64($data));
+
+ $users->{$user} = {
+ type => $type,
+ data => $data,
+ };
+ }
+
+ return $cfg;
+}
+
+sub write_priv_tfa_config {
+ my ($filename, $cfg) = @_;
+
+ my $output = '';
+
+ my $users = $cfg->{users};
+ foreach my $user (sort keys %$users) {
+ my $info = $users->{$user};
+ next if !%$info; # skip empty entries
+
+ $info = {%$info}; # copy to verify contents:
+
+ my $type = delete $info->{type};
+ my $data = delete $info->{data};
+
+ if (my @keys = keys %$info) {
+ die "invalid keys in TFA config for user $user: " . join(', ', @keys) . "\n";
+ }
+
+ $data = encode_base64(encode_json($data), '');
+ $output .= "${user}:${type}:${data}\n";
+ }
+
+ return $output;
+}
+