use PVE::AccessControl;
use PVE::Cluster;
-use PVE::Exception qw(raise raise_perm_exc);
+use PVE::Exception qw(raise raise_param_exc raise_perm_exc);
use PVE::INotify;
use PVE::ProcFSTools;
use PVE::RESTEnvironment;
return { map { $_ => 1 } keys %{$cfg->{roles}->{'Administrator'}} };
}
+ if (!defined($path)) {
+ # this shouldn't happen!
+ warn "internal error: ACL check called for undefined ACL path!\n";
+ return {};
+ }
+
if (PVE::AccessControl::pve_verify_tokenid($user, 1)) {
my ($username, $token) = PVE::AccessControl::split_tokenid($user);
my $cfg = $self->{user_cfg};
storage => qr/Datastore\.|Permissions\.Modify/,
nodes => qr/Sys\.|Permissions\.Modify/,
sdn => qr/SDN\.|Permissions\.Modify/,
- dc => qr/Sys\.Audit|SDN\./,
+ dc => qr/Sys\.Audit|Sys\.Modify|SDN\./,
+ mapping => qr/Mapping\.|Permissions.Modify/,
};
map { $res->{$_} = {} } keys %$priv_re_map;
- my $required_paths = ['/', '/nodes', '/access/groups', '/vms', '/storage', '/sdn'];
+ my $required_paths = ['/', '/nodes', '/access/groups', '/vms', '/storage', '/sdn', '/mapping'];
+ my $defined_paths = [];
+ PVE::AccessControl::iterate_acl_tree("/", $usercfg->{acl_root}, sub {
+ my ($path, $node) = @_;
+ push @$defined_paths, $path;
+ });
my $checked_paths = {};
- foreach my $path (@$required_paths, keys %{$usercfg->{acl}}) {
+ foreach my $path (@$required_paths, @$defined_paths) {
next if $checked_paths->{$path};
$checked_paths->{$path} = 1;
'/access' => 1,
'/access/groups' => 1,
'/nodes' => 1,
- '/pools' => 1,
+ '/pool' => 1,
'/sdn' => 1,
'/storage' => 1,
'/vms' => 1,
my $cfg = $self->{user_cfg};
# paths explicitly listed in ACLs
- foreach my $acl_path (keys %{$cfg->{acl}}) {
- $paths->{$acl_path} = 1;
- }
+ PVE::AccessControl::iterate_acl_tree("/", $cfg->{acl_root}, sub {
+ my ($path, $node) = @_;
+ $paths->{$path} = 1;
+ });
# paths referenced by pool definitions
foreach my $pool (keys %{$cfg->{pools}}) {
}
}
+# check for any fashion of access to vnet/bridge
+sub check_sdn_bridge {
+ my ($self, $username, $zone, $bridge, $privs, $noerr) = @_;
+
+ my $path = "/sdn/zones/$zone/$bridge";
+ # check access to bridge itself
+ return 1 if $self->check_any($username, $path, $privs, 1);
+
+ my $cfg = $self->{user_cfg};
+ my $bridge_acl = PVE::AccessControl::find_acl_tree_node($cfg->{acl_root}, $path);
+ if ($bridge_acl) {
+ # check access to VLANs
+ my $vlans = $bridge_acl->{children};
+ for my $vlan (keys %$vlans) {
+ my $vlanpath = "$path/$vlan";
+ return 1 if $self->check_any($username, $vlanpath, $privs, 1);
+ }
+ }
+
+ # repeat check, but fatal
+ $self->check_any($username, $path, $privs, 0) if !$noerr;
+
+ return;
+}
+
sub check_user_enabled {
my ($self, $user, $noerr) = @_;
raise_perm_exc();
}
my $path = PVE::Tools::template_replace($tmplpath, $param);
- $path = PVE::AccessControl::normalize_path($path);
- return $self->check_full($username, $path, $privs, $any, $noerr);
+ my $normpath = PVE::AccessControl::normalize_path($path);
+ warn "Failed to normalize '$path'\n" if !defined($normpath) && defined($path);
+
+ return $self->check_full($username, $normpath, $privs, $any, $noerr);
} elsif ($test eq 'userid-group') {
my $userid = $param->{userid};
my ($t, $privs, %options) = @$check;
my ($t, $tmplpath) = @$check;
my $path = PVE::Tools::template_replace($tmplpath, $param);
$path = PVE::AccessControl::normalize_path($path);
+ return 0 if !defined($path); # should already die in API2::ACL
return $self->check_perm_modify($username, $path, $noerr);
} else {
die "unknown permission test";
return PVE::RESTEnvironment->is_worker();
}
+# Permission helper for TFA and password API endpoints modifying users.
+# Only root may modify root, regular users need to specify their password.
+#
+# Returns the same as `verify_username` in list context (userid, ruid, realm),
+# or just the userid in scalar context.
+sub reauth_user_for_user_modification : prototype($$$$;$) {
+ my ($rpcenv, $authuser, $userid, $password, $param_name) = @_;
+
+ $param_name //= 'password';
+
+ ($userid, my $ruid, my $realm) = PVE::AccessControl::verify_username($userid);
+ $rpcenv->check_user_exist($userid);
+
+ raise_perm_exc() if $userid eq 'root@pam' && $authuser ne 'root@pam';
+
+ # Regular users need to confirm their password to change TFA settings.
+ if ($authuser ne 'root@pam') {
+ raise_param_exc({ $param_name => 'password is required to modify user' })
+ if !defined($password);
+
+ ($authuser, my $auth_username, my $auth_realm) =
+ PVE::AccessControl::verify_username($authuser);
+
+ my $domain_cfg = PVE::Cluster::cfs_read_file('domains.cfg');
+ my $cfg = $domain_cfg->{ids}->{$auth_realm};
+ die "auth domain '$auth_realm' does not exist\n" if !$cfg;
+ my $plugin = PVE::Auth::Plugin->lookup($cfg->{type});
+ $plugin->authenticate_user($cfg, $auth_realm, $auth_username, $password);
+ }
+
+ return wantarray ? ($userid, $ruid, $realm) : $userid;
+}
+
1;