* [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge @ 2023-06-06 13:19 Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier ` (9 more replies) 0 siblings, 10 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel add vnet/localbridge permissions management Hi, as we has discuted some weeks ago, this patche serie introduce management of acl for vnets && local bridges The permission path is: /sdn/zones/<zone>/<vnet> where the local vmbr are in a virtual "localnetwork" zone /sdn/zones/localnetwork/<vnet> Vlans permissions are also handled with /sdn/zones/<zone>/<vnet>/<tag> if user have permissions on the vnet/tag, he have access to only the specific vlan. if user have permissions on the vnet with propagate, he have access to all vlans of the vnet if user have permissions on the vnet without propagate, he have access to bridge only without any vlan I have reworked the sdn zone panel from the tree, to manage permissions on displayed vnets. (patch 3 && 4 pve-manager) some screenshots: https://mutulin1.odiso.net/sdnzone-perm.png https://mutulin1.odiso.net/localzone-perm.png changelog v2: - use /vnets/vlan instead /vnets.vlan - rework the bridge filtering when user have access only to a specific vlan - api2 network: always check bridge access if no filter is defined changelog v3: - use /sdn/zones/<zone>/vnets/vlan instead /sdn/vnets/vnets.vlan - add SDN.Use permission - pve-manager: split ui code (could be applied later) - remove check on zone (it's now propagate with new path) - move check_vnet_access to pve-guest-common for lxc reuse - pve-network: fix vnet/tag perm check todo: - implement check permissions on trunks - qemu : check bridge permissions on restore. (how to check restore config before restore ?) - lxc: check bridge permissions pve-access-control: Alexandre Derumier (3): access control: add /sdn/zones/<zone>/<vnet>/<vlan> path rpcenvironnment: add check_sdn_bridge add new SDN.use privilege in PVESDNUser role src/PVE/AccessControl.pm | 6 +++++- src/PVE/RPCEnvironment.pm | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) pve-manager: Alexandre Derumier (4): api2: network: check permissions for local bridges api2: cluster: ressources: add "localnetwork" zone ui: add vnet permissions panel ui: add permissions management for "localnetwork" zone PVE/API2/Cluster.pm | 14 ++ PVE/API2/Network.pm | 25 ++- www/manager6/Makefile | 2 + www/manager6/sdn/Browser.js | 17 +- www/manager6/sdn/VnetACLView.js | 289 +++++++++++++++++++++++++++ www/manager6/sdn/ZoneContentPanel.js | 41 ++++ www/manager6/sdn/ZoneContentView.js | 52 ++++- 7 files changed, 411 insertions(+), 29 deletions(-) create mode 100644 www/manager6/sdn/VnetACLView.js create mode 100644 www/manager6/sdn/ZoneContentPanel.js pve-guest-common: Alexandre Derumier (1): helpers : add check_vnet_access src/PVE/GuestHelpers.pm | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) qemu-server: Alexandre Derumier (1): api2: add check_bridge_access for create/update/clone vm PVE/API2/Qemu.pm | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) pve-network: Alexandre Derumier (1): get_local_vnets: fix permission path && perm PVE/Network/SDN.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier ` (8 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- src/PVE/AccessControl.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm index 89b7d90..6a3d203 100644 --- a/src/PVE/AccessControl.pm +++ b/src/PVE/AccessControl.pm @@ -1283,7 +1283,8 @@ sub check_path { |/pool/[[:alnum:]\.\-\_]+ |/sdn |/sdn/zones/[[:alnum:]\.\-\_]+ - |/sdn/vnets/[[:alnum:]\.\-\_]+ + |/sdn/zones/[[:alnum:]\.\-\_]+/[[:alnum:]\.\-\_]+ + |/sdn/zones/[[:alnum:]\.\-\_]+/[[:alnum:]\.\-\_]+/[1-9][0-9]{0,3} |/storage |/storage/[[:alnum:]\.\-\_]+ |/vms -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier ` (7 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- PVE/API2/Qemu.pm | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 587bb22..c290c81 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -23,7 +23,7 @@ use PVE::Storage; use PVE::JSONSchema qw(get_standard_option); use PVE::RESTHandler; use PVE::ReplicationConfig; -use PVE::GuestHelpers qw(assert_tag_permissions); +use PVE::GuestHelpers qw(assert_tag_permissions check_vnet_access); use PVE::QemuConfig; use PVE::QemuServer; use PVE::QemuServer::Cloudinit; @@ -601,6 +601,21 @@ my $check_vm_create_usb_perm = sub { return 1; }; +my $check_bridge_access = sub { + my ($rpcenv, $authuser, $param) = @_; + + return 1 if $authuser eq 'root@pam'; + + foreach my $opt (keys %{$param}) { + next if $opt !~ m/^net\d+$/; + my $net = PVE::QemuServer::parse_net($param->{$opt}); + my $bridge = $net->{bridge}; + my $tag = $net->{tag}; + check_vnet_access($rpcenv, $authuser, $bridge, $tag); + } + return 1; +}; + my $check_vm_modify_config_perm = sub { my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_; @@ -878,7 +893,7 @@ __PACKAGE__->register_method({ &$check_vm_create_serial_perm($rpcenv, $authuser, $vmid, $pool, $param); &$check_vm_create_usb_perm($rpcenv, $authuser, $vmid, $pool, $param); - + &$check_bridge_access($rpcenv, $authuser, $param); &$check_cpu_model_access($rpcenv, $authuser, $param); $check_drive_param->($param, $storecfg); @@ -1578,6 +1593,8 @@ my $update_vm_api = sub { &$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param); + &$check_bridge_access($rpcenv, $authuser, $param); + my $updatefn = sub { my $conf = PVE::QemuConfig->load_config($vmid); @@ -3355,7 +3372,7 @@ __PACKAGE__->register_method({ permissions => { description => "You need 'VM.Clone' permissions on /vms/{vmid}, and 'VM.Allocate' permissions " . "on /vms/{newid} (or on the VM pool /pool/{pool}). You also need " . - "'Datastore.AllocateSpace' on any used storage.", + "'Datastore.AllocateSpace' on any used storage and 'SDN.Use' on any used bridge/vnet", check => [ 'and', ['perm', '/vms/{vmid}', [ 'VM.Clone' ]], @@ -3489,6 +3506,8 @@ __PACKAGE__->register_method({ my $sharedvm = &$check_storage_access_clone($rpcenv, $authuser, $storecfg, $oldconf, $storage); + &$check_bridge_access($rpcenv, $authuser, $oldconf); + die "can't clone VM to node '$target' (VM uses local storage)\n" if $target && !$sharedvm; -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier ` (6 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel always check permissions, also when not filtered Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- PVE/API2/Network.pm | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm index a43579fa..8dc56482 100644 --- a/PVE/API2/Network.pm +++ b/PVE/API2/Network.pm @@ -209,7 +209,7 @@ __PACKAGE__->register_method({ type => { description => "Only list specific interface types.", type => 'string', - enum => [ @$network_type_enum, 'any_bridge' ], + enum => [ @$network_type_enum, 'any_bridge', 'any_local_bridge' ], optional => 1, }, }, @@ -240,22 +240,17 @@ __PACKAGE__->register_method({ if (my $tfilter = $param->{type}) { my $vnets; - my $vnet_cfg; - my $can_access_vnet = sub { # only matters for the $have_sdn case, checked implict - return 1 if $authuser eq 'root@pam' || !defined($vnets); - return 1 if !defined(PVE::Network::SDN::Vnets::sdn_vnets_config($vnet_cfg, $_[0], 1)); # not a vnet - $rpcenv->check_any($authuser, "/sdn/vnets/$_[0]", ['SDN.Audit', 'SDN.Allocate'], 1) - }; if ($have_sdn && $param->{type} eq 'any_bridge') { $vnets = PVE::Network::SDN::get_local_vnets(); # returns already access-filtered - $vnet_cfg = PVE::Network::SDN::Vnets::config(); } for my $k (sort keys $ifaces->%*) { my $type = $ifaces->{$k}->{type}; - my $match = $tfilter eq $type || ($tfilter eq 'any_bridge' && ($type eq 'bridge' || $type eq 'OVSBridge')); - delete $ifaces->{$k} if !($match && $can_access_vnet->($k)); + my $match = ($param->{type} eq $type) || ( + ($param->{type} =~ /^any(_local)?_bridge$/) && + ($type eq 'bridge' || $type eq 'OVSBridge')); + delete $ifaces->{$k} if !$match; } if (defined($vnets)) { @@ -263,6 +258,16 @@ __PACKAGE__->register_method({ } } + #always check bridge access + my $can_access_vnet = sub { + return 1 if $authuser eq 'root@pam'; + return 1 if $rpcenv->check_sdn_bridge($authuser, "localnetwork", $_[0], ['SDN.Audit', 'SDN.Use'], 1); + }; + for my $k (sort keys $ifaces->%*) { + my $type = $ifaces->{$k}->{type}; + delete $ifaces->{$k} if ($type eq 'bridge' || $type eq 'OVSBridge') && !$can_access_vnet->($k); + } + return PVE::RESTHandler::hash_to_array($ifaces, 'iface'); }}); -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (2 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier ` (5 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel new path is /zones/<zone>/<vnetid> Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- PVE/Network/SDN.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm index b95dd5b..1ad85e5 100644 --- a/PVE/Network/SDN.pm +++ b/PVE/Network/SDN.pm @@ -190,10 +190,10 @@ sub get_local_vnets { my $zoneid = $vnet->{zone}; my $comments = $vnet->{alias}; - my $privs = [ 'SDN.Audit', 'SDN.Allocate' ]; + my $privs = [ 'SDN.Audit', 'SDN.Use' ]; next if !$zoneid; - next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1) && !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid", $privs, 1); + next if !$rpcenv->check_sdn_bridge($authuser, $zoneid, $vnetid, $privs, 1); my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid); -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (3 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier ` (4 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel if a tag is defined, test if user have a specific access to the vlan (or propagate from full bridge acl or zone) if no tag, test if user have access to full bridge. (if trunks are defined, it need also access to full bridge) Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- src/PVE/GuestHelpers.pm | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/PVE/GuestHelpers.pm b/src/PVE/GuestHelpers.pm index b4ccbaa..53c63e8 100644 --- a/src/PVE/GuestHelpers.pm +++ b/src/PVE/GuestHelpers.pm @@ -10,10 +10,17 @@ use PVE::Storage; use POSIX qw(strftime); use Scalar::Util qw(weaken); +my $have_sdn; +eval { + require PVE::Network::SDN; + $have_sdn = 1; +}; + use base qw(Exporter); our @EXPORT_OK = qw( assert_tag_permissions +check_vnet_access get_allowed_tags safe_boolean_ne safe_num_ne @@ -366,4 +373,22 @@ sub get_unique_tags { return !$no_join_result ? join(';', $res->@*) : $res; } +sub check_vnet_access { + my ($rpcenv, $authuser, $vnet, $tag) = @_; + + my $zone = 'localnetwork'; + + if ($have_sdn) { + my $vnet_cfg = PVE::Network::SDN::Vnets::config(); + if (defined(my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnet_cfg, $vnet, 1))) { + $zone = $vnet->{zone}; + } + } + + # if a tag is defined, test if user have a specific access to the vlan (or propagated from full bridge acl) + $rpcenv->check($authuser, "/sdn/zones/$zone/$vnet/$tag", ['SDN.Use']) if $tag; + # if no tag, test if user have access to full bridge. (if trunks are defined, it need also access to full bridge) + $rpcenv->check($authuser, "/sdn/zones/$zone/$vnet", ['SDN.Use']); +} + 1; -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (4 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier ` (3 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- PVE/API2/Cluster.pm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/PVE/API2/Cluster.pm b/PVE/API2/Cluster.pm index 2e942368..a7224d7f 100644 --- a/PVE/API2/Cluster.pm +++ b/PVE/API2/Cluster.pm @@ -474,6 +474,20 @@ __PACKAGE__->register_method({ } } + #add default "localnetwork" zone + if ($rpcenv->check($authuser, "/sdn/zones/localnetwork", [ 'SDN.Audit' ], 1)) { + foreach my $node (@$nodelist) { + my $local_sdn = { + id => "sdn/$node/localnetwork", + sdn => 'localnetwork', + node => $node, + type => 'sdn', + status => 'ok', + }; + push @$res, $local_sdn; + } + } + if ($have_sdn) { if (!$param->{type} || $param->{type} eq 'sdn') { -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (5 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier ` (2 subsequent siblings) 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel check if user have access to 1 vlan of the bridge or the bridge itself Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- src/PVE/RPCEnvironment.pm | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm index 8586938..e0a101f 100644 --- a/src/PVE/RPCEnvironment.pm +++ b/src/PVE/RPCEnvironment.pm @@ -324,6 +324,24 @@ sub check_full { } } +sub check_sdn_bridge { + my ($self, $username, $zone, $bridge, $privs, $noerr) = @_; + + my $path = "/sdn/zones/$zone/$bridge"; + my $cfg = $self->{user_cfg}; + my $bridge_acl = PVE::AccessControl::find_acl_tree_node($cfg->{acl_root}, $path); + if ($bridge_acl) { + my $vlans = $bridge_acl->{children}; + for my $vlan (keys %$vlans) { + my $vlanpath = "$path/$vlan"; + return 1 if $self->check_any($username, $vlanpath, $privs, $noerr); + } + # check access to bridge itself + return 1 if $self->check_any($username, $path, $privs, $noerr); + } + return; +} + sub check_user_enabled { my ($self, $user, $noerr) = @_; -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (6 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- src/PVE/AccessControl.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm index 6a3d203..326ed5c 100644 --- a/src/PVE/AccessControl.pm +++ b/src/PVE/AccessControl.pm @@ -1131,6 +1131,9 @@ my $privgroups = { 'SDN.Allocate', 'SDN.Audit', ], + user => [ + 'SDN.Use', + ], audit => [ 'SDN.Audit', ], -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (7 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- www/manager6/Makefile | 2 + www/manager6/sdn/Browser.js | 17 +- www/manager6/sdn/VnetACLView.js | 289 +++++++++++++++++++++++++++ www/manager6/sdn/ZoneContentPanel.js | 41 ++++ www/manager6/sdn/ZoneContentView.js | 25 ++- 5 files changed, 356 insertions(+), 18 deletions(-) create mode 100644 www/manager6/sdn/VnetACLView.js create mode 100644 www/manager6/sdn/ZoneContentPanel.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index 2b577c8e..fb9930af 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -251,10 +251,12 @@ JSSRC= \ sdn/StatusView.js \ sdn/VnetEdit.js \ sdn/VnetView.js \ + sdn/VnetACLView.js \ sdn/VnetPanel.js \ sdn/SubnetEdit.js \ sdn/SubnetView.js \ sdn/ZoneContentView.js \ + sdn/ZoneContentPanel.js \ sdn/ZoneView.js \ sdn/OptionsPanel.js \ sdn/controllers/Base.js \ diff --git a/www/manager6/sdn/Browser.js b/www/manager6/sdn/Browser.js index 09b0c4fe..3dc5a5ad 100644 --- a/www/manager6/sdn/Browser.js +++ b/www/manager6/sdn/Browser.js @@ -25,14 +25,15 @@ Ext.define('PVE.sdn.Browser', { const caps = Ext.state.Manager.get('GuiCap'); - if (caps.sdn['SDN.Audit']) { - me.items.push({ - xtype: 'pveSDNZoneContentView', - title: gettext('Content'), - iconCls: 'fa fa-th', - itemId: 'content', - }); - } + me.items.push({ + nodename: nodename, + zone: sdnId, + xtype: 'pveSDNZoneContentPanel', + title: gettext('Content'), + iconCls: 'fa fa-th', + itemId: 'content', + }); + if (caps.sdn['Permissions.Modify']) { me.items.push({ xtype: 'pveACLView', diff --git a/www/manager6/sdn/VnetACLView.js b/www/manager6/sdn/VnetACLView.js new file mode 100644 index 00000000..af10d954 --- /dev/null +++ b/www/manager6/sdn/VnetACLView.js @@ -0,0 +1,289 @@ +Ext.define('PVE.sdn.VnetACLAdd', { + extend: 'Proxmox.window.Edit', + alias: ['widget.pveSDNVnetACLAdd'], + + url: '/access/acl', + method: 'PUT', + isAdd: true, + isCreate: true, + + width: 400, + initComponent: function() { + let me = this; + + let items = [ + { + xtype: 'hiddenfield', + name: 'path', + value: me.path, + allowBlank: false, + fieldLabel: gettext('Path'), + }, + ]; + + if (me.aclType === 'group') { + me.subject = gettext("Group Permission"); + items.push({ + xtype: 'pveGroupSelector', + name: 'groups', + fieldLabel: gettext('Group'), + }); + } else if (me.aclType === 'user') { + me.subject = gettext("User Permission"); + items.push({ + xtype: 'pmxUserSelector', + name: 'users', + fieldLabel: gettext('User'), + }); + } else if (me.aclType === 'token') { + me.subject = gettext("API Token Permission"); + items.push({ + xtype: 'pveTokenSelector', + name: 'tokens', + fieldLabel: gettext('API Token'), + }); + } else { + throw "unknown ACL type"; + } + + items.push({ + xtype: 'pmxRoleSelector', + name: 'roles', + value: 'NoAccess', + fieldLabel: gettext('Role'), + }); + + items.push({ + xtype: 'proxmoxintegerfield', + name: 'vlan', + minValue: 1, + maxValue: 4096, + allowBlank: true, + fieldLabel: 'Vlan', + emptyText: gettext('All'), + }); + + let ipanel = Ext.create('Proxmox.panel.InputPanel', { + items: items, + onlineHelp: 'pveum_permission_management', + onGetValues: function(values) { + if (values.vlan) { + values.path = values.path + "/" + values.vlan; + delete values.vlan; + } + return values; + }, + }); + + Ext.apply(me, { + items: [ipanel], + }); + + me.callParent(); + }, +}); + +Ext.define('PVE.sdn.VnetACLView', { + extend: 'Ext.grid.GridPanel', + + alias: ['widget.pveSDNVnetACLView'], + + onlineHelp: 'chapter_user_management', + + stateful: true, + stateId: 'grid-acls', + + // use fixed path + path: undefined, + + setPath: function(path) { + let me = this; + + me.path = path; + + if (path === undefined) { + me.down('#groupmenu').setDisabled(true); + me.down('#usermenu').setDisabled(true); + me.down('#tokenmenu').setDisabled(true); + } else { + me.down('#groupmenu').setDisabled(false); + me.down('#usermenu').setDisabled(false); + me.down('#tokenmenu').setDisabled(false); + me.store.load(); + } + }, + initComponent: function() { + let me = this; + + let store = Ext.create('Ext.data.Store', { + model: 'pve-acl', + proxy: { + type: 'proxmox', + url: "/api2/json/access/acl", + }, + sorters: { + property: 'path', + direction: 'ASC', + }, + }); + + store.addFilter(Ext.create('Ext.util.Filter', { + filterFn: item => item.data.path.replace(/(\/sdn\/zones\/(.*)\/(.*))\/[0-9]*$/, '$1') === me.path, + })); + + let render_ugid = function(ugid, metaData, record) { + if (record.data.type === 'group') { + return '@' + ugid; + } + + return Ext.String.htmlEncode(ugid); + }; + + let render_vlan = function(path, metaData, record) { + let vlan = 'any'; + const match = path.match(/(\/sdn\/zones\/)(.*)\/(.*)\/([0-9]*)$/); + if (match) { + vlan = match[4]; + } + + return Ext.String.htmlEncode(vlan); + }; + + let columns = [ + { + header: gettext('User') + '/' + gettext('Group') + '/' + gettext('API Token'), + flex: 1, + sortable: true, + renderer: render_ugid, + dataIndex: 'ugid', + }, + { + header: gettext('Role'), + flex: 1, + sortable: true, + dataIndex: 'roleid', + }, + { + header: gettext('Vlan'), + flex: 1, + sortable: true, + renderer: render_vlan, + dataIndex: 'path', + }, + ]; + + + let sm = Ext.create('Ext.selection.RowModel', {}); + + let remove_btn = new Proxmox.button.Button({ + text: gettext('Remove'), + disabled: true, + selModel: sm, + confirmMsg: gettext('Are you sure you want to remove this entry'), + handler: function(btn, event, rec) { + var params = { + 'delete': 1, + path: rec.data.path, + roles: rec.data.roleid, + }; + if (rec.data.type === 'group') { + params.groups = rec.data.ugid; + } else if (rec.data.type === 'user') { + params.users = rec.data.ugid; + } else if (rec.data.type === 'token') { + params.tokens = rec.data.ugid; + } else { + throw 'unknown data type'; + } + + Proxmox.Utils.API2Request({ + url: '/access/acl', + params: params, + method: 'PUT', + waitMsgTarget: me, + callback: () => store.load(), + failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus), + }); + }, + }); + + Proxmox.Utils.monStoreErrors(me, store); + + Ext.apply(me, { + store: store, + selModel: sm, + tbar: [ + { + text: gettext('Add'), + menu: { + xtype: 'menu', + items: [ + { + text: gettext('Group Permission'), + disabled: !me.path, + itemId: 'groupmenu', + iconCls: 'fa fa-fw fa-group', + handler: function() { + var win = Ext.create('PVE.sdn.VnetACLAdd', { + aclType: 'group', + path: me.path, + }); + win.on('destroy', () => store.load()); + win.show(); + }, + }, + { + text: gettext('User Permission'), + disabled: !me.path, + itemId: 'usermenu', + iconCls: 'fa fa-fw fa-user', + handler: function() { + var win = Ext.create('PVE.sdn.VnetACLAdd', { + aclType: 'user', + path: me.path, + }); + win.on('destroy', () => store.load()); + win.show(); + }, + }, + { + text: gettext('API Token Permission'), + disabled: !me.path, + itemId: 'tokenmenu', + iconCls: 'fa fa-fw fa-user-o', + handler: function() { + let win = Ext.create('PVE.sdn.VnetACLAdd', { + aclType: 'token', + path: me.path, + }); + win.on('destroy', () => store.load()); + win.show(); + }, + }, + ], + }, + }, + remove_btn, + ], + viewConfig: { + trackOver: false, + }, + columns: columns, + listeners: { + }, + }); + + me.callParent(); + }, +}, function() { + Ext.define('pve-acl-vnet', { + extend: 'Ext.data.Model', + fields: [ + 'path', 'type', 'ugid', 'roleid', + { + name: 'propagate', + type: 'boolean', + }, + ], + }); +}); diff --git a/www/manager6/sdn/ZoneContentPanel.js b/www/manager6/sdn/ZoneContentPanel.js new file mode 100644 index 00000000..5bb081bb --- /dev/null +++ b/www/manager6/sdn/ZoneContentPanel.js @@ -0,0 +1,41 @@ +Ext.define('PVE.sdn.ZoneContentPanel', { + extend: 'Ext.panel.Panel', + alias: 'widget.pveSDNZoneContentPanel', + + title: 'Vnet', + + onlineHelp: 'pvesdn_config_vnet', + + initComponent: function() { + var me = this; + + var permissions_panel = Ext.createWidget('pveSDNVnetACLView', { + title: gettext('Vnet Permissions'), + region: 'center', + border: false, + }); + + var vnetview_panel = Ext.createWidget('pveSDNZoneContentView', { + title: 'Vnets', + region: 'west', + permissions_panel: permissions_panel, + nodename: me.nodename, + zone: me.zone, + width: '50%', + border: false, + split: true, + }); + + Ext.apply(me, { + layout: 'border', + items: [vnetview_panel, permissions_panel], + listeners: { + show: function() { + permissions_panel.fireEvent('show', permissions_panel); + }, + }, + }); + + me.callParent(); + }, +}); diff --git a/www/manager6/sdn/ZoneContentView.js b/www/manager6/sdn/ZoneContentView.js index 1ea65450..4bc92718 100644 --- a/www/manager6/sdn/ZoneContentView.js +++ b/www/manager6/sdn/ZoneContentView.js @@ -17,17 +17,15 @@ Ext.define('PVE.sdn.ZoneContentView', { initComponent: function() { var me = this; - var nodename = me.pveSelNode.data.node; - if (!nodename) { + if (!me.nodename) { throw "no node name specified"; } - var zone = me.pveSelNode.data.sdn; - if (!zone) { + if (!me.zone) { throw "no zone ID specified"; } - var baseurl = "/nodes/" + nodename + "/sdn/zones/" + zone + "/content"; + var baseurl = "/nodes/" + me.nodename + "/sdn/zones/" + me.zone + "/content"; var store = Ext.create('Ext.data.Store', { model: 'pve-sdnzone-content', groupField: 'content', @@ -48,7 +46,6 @@ Ext.define('PVE.sdn.ZoneContentView', { }; Proxmox.Utils.monStoreErrors(me, store); - Ext.apply(me, { store: store, selModel: sm, @@ -79,11 +76,19 @@ Ext.define('PVE.sdn.ZoneContentView', { dataIndex: 'statusmsg', }, ], - listeners: { - activate: reload, - }, + listeners: { + activate: reload, + show: reload, + select: function(_sm, rec) { + let path = `/sdn/zones/${me.zone}/${rec.data.vnet}`; + me.permissions_panel.setPath(path); + }, + deselect: function() { + me.permissions_panel.setPath(undefined); + }, + }, }); - + store.load(); me.callParent(); }, }, function() { -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier ` (8 preceding siblings ...) 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier @ 2023-06-06 13:19 ` Alexandre Derumier 9 siblings, 0 replies; 11+ messages in thread From: Alexandre Derumier @ 2023-06-06 13:19 UTC (permalink / raw) To: pve-devel add a default virtual zone called 'localnetwork' in the ressource tree, and handle permissions like a true sdn zone (no conflict with true sdn zone is possible, as they have 8 characters max) Signed-off-by: Alexandre Derumier <aderumier@odiso.com> --- www/manager6/sdn/ZoneContentView.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/www/manager6/sdn/ZoneContentView.js b/www/manager6/sdn/ZoneContentView.js index 4bc92718..2a3cbf52 100644 --- a/www/manager6/sdn/ZoneContentView.js +++ b/www/manager6/sdn/ZoneContentView.js @@ -26,6 +26,9 @@ Ext.define('PVE.sdn.ZoneContentView', { } var baseurl = "/nodes/" + me.nodename + "/sdn/zones/" + me.zone + "/content"; + if (me.zone === 'localnetwork') { + baseurl = "/nodes/" + me.nodename + "/network?type=any_local_bridge"; + } var store = Ext.create('Ext.data.Store', { model: 'pve-sdnzone-content', groupField: 'content', @@ -95,7 +98,29 @@ Ext.define('PVE.sdn.ZoneContentView', { Ext.define('pve-sdnzone-content', { extend: 'Ext.data.Model', fields: [ - 'vnet', 'status', 'statusmsg', + { + name: 'iface', + convert: function(value, record) { + //map local vmbr to vnet + if (record.data.iface) { + record.data.vnet = record.data.iface; + } + return value; + }, + }, + { + name: 'comments', + convert: function(value, record) { + //map local vmbr comments to vnet alias + if (record.data.comments) { + record.data.alias = record.data.comments; + } + return value; + }, + }, + 'vnet', + 'status', + 'statusmsg', { name: 'text', convert: function(value, record) { -- 2.30.2 ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2023-06-06 13:20 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-06-06 13:19 [pve-devel] [PATCH-SERIE pve-access-control/pve-manager/pve-guest-common/qemu-server/pve-network] check permissions on local bridge Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 1/3] access control: add /sdn/zones/<zone>/<vnet>/<vlan> path Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 qemu-server 1/1] api2: add check_bridge_access for create/update/clone vm Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 1/4] api2: network: check permissions for local bridges Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH pve-network 1/1] get_local_vnets: fix permission path && perm Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH pve-guest-common 1/1] helpers : add check_vnet_access Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 2/4] api2: cluster: ressources: add "localnetwork" zone Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 2/3] rpcenvironnment: add check_sdn_bridge Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v2 pve-access-control 3/3] add new SDN.use privilege in PVESDNUser role Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 3/4] ui: add vnet permissions panel Alexandre Derumier 2023-06-06 13:19 ` [pve-devel] [PATCH v3 pve-manager 4/4] ui: add permissions management for "localnetwork" zone Alexandre Derumier
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox