]> git.proxmox.com Git - pve-access-control.git/blobdiff - src/PVE/AccessControl.pm
use rust parser for TFA config
[pve-access-control.git] / src / PVE / AccessControl.pm
index 3d8d01c0dfb99d1331b3f57a23f2724cfbeb2b0f..2fa269548f9846355e9404101ad087d3c521269e 100644 (file)
@@ -19,6 +19,8 @@ use PVE::Tools qw(run_command lock_file file_get_contents split_list safe_print)
 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
 use PVE::JSONSchema qw(register_standard_option get_standard_option);
 
+use PVE::RS::TFA;
+
 use PVE::Auth::Plugin;
 use PVE::Auth::AD;
 use PVE::Auth::LDAP;
@@ -944,6 +946,7 @@ sub check_path {
        |/pool/[[:alnum:]\.\-\_]+
        |/sdn
        |/sdn/zones/[[:alnum:]\.\-\_]+
+       |/sdn/vnets/[[:alnum:]\.\-\_]+
        |/storage
        |/storage/[[:alnum:]\.\-\_]+
        |/vms
@@ -1352,33 +1355,21 @@ sub write_user_config {
     return $data;
 }
 
-# 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.
+# Creates a `PVE::RS::TFA` instance from the raw config data.
+# Its contained hash will also support the legacy functionality.
 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 $cfg = PVE::RS::TFA->new($raw);
 
+    # Purge invalid users:
+    foreach my $user ($cfg->users()->@*) {
        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;
+           $cfg->remove_user($user);
        }
-
-       $data = decode_json(decode_base64($data));
-
-       $users->{$user} = {
-           type => $type,
-           data => $data,
-       };
     }
 
     return $cfg;
@@ -1387,27 +1378,9 @@ sub parse_priv_tfa_config {
 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;
+    # FIXME: Only allow this if the complete cluster has been upgraded to understand the json
+    # config format.
+    return $cfg->write();
 }
 
 sub roles {
@@ -1599,8 +1572,7 @@ sub user_set_tfa {
     }
 
     my $user_cfg = $cached_usercfg || cfs_read_file('user.cfg');
-    my $user = $user_cfg->{users}->{$userid}
-       or die "user '$userid' not found\n";
+    my $user = $user_cfg->{users}->{$userid};
 
     my $domain_cfg = $cached_domaincfg || cfs_read_file('domains.cfg');
     my $realm_cfg = $domain_cfg->{ids}->{$realm};
@@ -1611,6 +1583,7 @@ sub user_set_tfa {
        $realm_tfa = PVE::Auth::Plugin::parse_tfa_config($realm_tfa);
        # If the realm has a TFA setting, we're only allowed to use that.
        if (defined($data)) {
+           die "user '$userid' not found\n" if !defined($user);
            my $required_type = $realm_tfa->{type};
            if ($required_type ne $type) {
                die "realm '$realm' only allows TFA of type '$required_type\n";
@@ -1623,9 +1596,11 @@ sub user_set_tfa {
            # realm-configured tfa always uses a simple key list, so use the user.cfg
            $user->{keys} = $data->{keys};
        } else {
-           die "realm '$realm' does not allow removing the 2nd factor\n";
+           # TFA is enforce by realm, only allow deletion if the whole user gets delete
+           die "realm '$realm' does not allow removing the 2nd factor\n" if defined($user);
        }
     } else {
+       die "user '$userid' not found\n" if !defined($user) && defined($data);
        # Without a realm-enforced TFA setting the user can add a u2f or totp entry by themselves.
        # The 'yubico' type requires yubico server settings, which have to be configured on the
        # realm, so this is not supported here:
@@ -1649,10 +1624,10 @@ sub user_set_tfa {
        delete $tfa_cfg->{users}->{$userid};
        cfs_write_file('priv/tfa.cfg', $tfa_cfg);
 
-       delete $user->{keys};
+       delete $user->{keys} if defined($user);
     }
 
-    cfs_write_file('user.cfg', $user_cfg);
+    cfs_write_file('user.cfg', $user_cfg) if defined($user);
 }
 
 sub user_get_tfa {