]> git.proxmox.com Git - pve-access-control.git/blobdiff - src/PVE/RPCEnvironment.pm
rpcenv->permissions() ensure propagate is always defined
[pve-access-control.git] / src / PVE / RPCEnvironment.pm
index e66107be7c1567cdc045c303ae0fd89980144a87..b1348fab8f38141606cbce5a61d2d6a4ded3253d 100644 (file)
@@ -3,15 +3,14 @@ package PVE::RPCEnvironment;
 use strict;
 use warnings;
 
-use PVE::RESTEnvironment;
-
+use PVE::AccessControl;
+use PVE::Cluster;
 use PVE::Exception qw(raise raise_perm_exc);
-use PVE::SafeSyslog;
-use PVE::Tools;
 use PVE::INotify;
-use PVE::Cluster;
 use PVE::ProcFSTools;
-use PVE::AccessControl;
+use PVE::RESTEnvironment;
+use PVE::SafeSyslog;
+use PVE::Tools;
 
 use base qw(PVE::RESTEnvironment);
 
@@ -83,7 +82,13 @@ my $compile_acl_path = sub {
     if ($username && $username ne 'root@pam') {
        # intersect user and token permissions
        my $user_privs = $cache->{$username}->{privs}->{$path};
-       $privs = { map { $_ => $user_privs->{$_} && $privs->{$_} } keys %$privs };
+       my $filtered_privs = [ grep { $user_privs->{$_} } keys %$privs ];
+       $privs = { map { $_ => $user_privs->{$_} && $privs->{$_} } @$filtered_privs };
+    }
+
+    foreach my $priv (keys %$privs) {
+       # safeguard, this should never happen anyway
+       delete $privs->{$priv} if !defined($privs->{$priv});
     }
 
     $data->{privs}->{$path} = $privs;
@@ -127,6 +132,59 @@ sub permissions {
     return &$compile_acl_path($self, $user, $path);
 }
 
+sub compute_api_permission {
+    my ($self, $authuser) = @_;
+
+    my $usercfg = $self->{user_cfg};
+
+    my $res = {};
+    my $priv_re_map = {
+       vms => qr/VM\.|Permissions\.Modify/,
+       access => qr/(User|Group)\.|Permissions\.Modify/,
+       storage => qr/Datastore\.|Permissions\.Modify/,
+       nodes => qr/Sys\.|Permissions\.Modify/,
+       sdn => qr/SDN\.|Permissions\.Modify/,
+       dc => qr/Sys\.Audit|SDN\./,
+    };
+    map { $res->{$_} = {} } keys %$priv_re_map;
+
+    my $required_paths = ['/', '/nodes', '/access/groups', '/vms', '/storage', '/sdn'];
+
+    my $checked_paths = {};
+    foreach my $path (@$required_paths, keys %{$usercfg->{acl}}) {
+       next if $checked_paths->{$path};
+       $checked_paths->{$path} = 1;
+
+       my $path_perm = $self->permissions($authuser, $path);
+
+       my $toplevel = ($path =~ /^\/(\w+)/) ? $1 : 'dc';
+       if ($toplevel eq 'pool') {
+           foreach my $priv (keys %$path_perm) {
+               next if !defined($path_perm->{$priv});
+
+               if ($priv =~ m/^VM\./) {
+                   $res->{vms}->{$priv} = 1;
+               } elsif ($priv =~ m/^Datastore\./) {
+                   $res->{storage}->{$priv} = 1;
+               } elsif ($priv eq 'Permissions.Modify') {
+                   $res->{storage}->{$priv} = 1;
+                   $res->{vms}->{$priv} = 1;
+               }
+           }
+       } else {
+           my $priv_regex = $priv_re_map->{$toplevel} // next;
+           foreach my $priv (keys %$path_perm) {
+               next if !defined($path_perm->{$priv});
+
+               next if $priv !~ m/^($priv_regex)/;
+               $res->{$toplevel}->{$priv} = 1;
+           }
+       }
+    }
+
+    return $res;
+}
+
 sub get_effective_permissions {
     my ($self, $user) = @_;
 
@@ -137,6 +195,7 @@ sub get_effective_permissions {
        '/access/groups' => 1,
        '/nodes' => 1,
        '/pools' => 1,
+       '/sdn' => 1,
        '/storage' => 1,
        '/vms' => 1,
     };
@@ -162,6 +221,9 @@ sub get_effective_permissions {
     my $perms = {};
     foreach my $path (keys %$paths) {
        my $path_perms = $self->permissions($user, $path);
+       foreach my $priv (keys %$path_perms) {
+           delete $path_perms->{$priv} if !defined($path_perms->{$priv});
+       }
        # filter paths where user has NO permissions
        $perms->{$path} = $path_perms if %$path_perms;
     }