all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Daniel Kral <d.kral@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH access-control] tests: api: add tests for expected output of get permissions endpoint
Date: Wed,  6 Nov 2024 15:48:13 +0100	[thread overview]
Message-ID: <20241106144813.189056-1-d.kral@proxmox.com> (raw)

Adds test cases for the GET permissions API endpoint to ensure that:

- users with the Sys.Audit perm can access any user's permissions
- users with the Sys.Audit perm can access any token's permissions
- users without the Sys.Audit perm can access their own permissions
- users without the Sys.Audit perm can access their token's permissions
- tokens with the Sys.Audit perm can access any user's permissions
- tokens without the Sys.Audit perm can access their own permissions

These tests also separate whether a token has the privilege-separated
property or not.

Signed-off-by: Daniel Kral <d.kral@proxmox.com>
---
This is related to the following patch series and should be only applied
after it:

https://lore.proxmox.com/pve-devel/20241105083039.150454-1-f.gruenbichler@proxmox.com/

Without the patch series above applied, test cases 2-4, 8, 13, 29 should
fail, as these were not considered before.

 src/test/Makefile                     |   1 +
 src/test/api-get-permissions-test.cfg |  17 ++
 src/test/api-get-permissions-test.pl  | 325 ++++++++++++++++++++++++++
 src/test/api-tests.pl                 |  12 +
 4 files changed, 355 insertions(+)
 create mode 100644 src/test/api-get-permissions-test.cfg
 create mode 100644 src/test/api-get-permissions-test.pl
 create mode 100755 src/test/api-tests.pl

diff --git a/src/test/Makefile b/src/test/Makefile
index 859a84b..53f2da7 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -13,3 +13,4 @@ check:
 	perl -I.. perm-test7.pl
 	perl -I.. perm-test8.pl
 	perl -I.. realm_sync_test.pl
+	perl -I.. api-tests.pl
diff --git a/src/test/api-get-permissions-test.cfg b/src/test/api-get-permissions-test.cfg
new file mode 100644
index 0000000..d9e415e
--- /dev/null
+++ b/src/test/api-get-permissions-test.cfg
@@ -0,0 +1,17 @@
+user:auditor@pam:1:
+token:auditor@pam!noprivsep::0:
+token:auditor@pam!privsep::1:
+user:stranger@pve:1:
+token:stranger@pve!noprivsep::0:
+token:stranger@pve!privsep::1:
+
+role:sys_auditor:Sys.Audit:
+role:vm_lurker:VM.Audit,VM.Console:
+role:vm_manager:VM.PowerMgmt:
+
+acl:1:/access:auditor@pam:sys_auditor:
+acl:1:/vms:auditor@pam:vm_lurker:
+acl:1:/vms:auditor@pam!privsep:vm_lurker:
+acl:1:/vms:stranger@pve:vm_lurker:
+acl:1:/vms:stranger@pve:vm_manager:
+acl:1:/vms:stranger@pve!privsep:vm_lurker:
diff --git a/src/test/api-get-permissions-test.pl b/src/test/api-get-permissions-test.pl
new file mode 100644
index 0000000..f1af49f
--- /dev/null
+++ b/src/test/api-get-permissions-test.pl
@@ -0,0 +1,325 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+
+use PVE::Tools;
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::AccessControl;
+use PVE::RPCEnvironment;
+use PVE::API2::AccessControl;
+
+my $cluster_module = Test::MockModule->new('PVE::Cluster');
+# make cfs_update a stub as it's not relevant to the test cases and will
+# make these tests fail if the user doesn't have access to the cluster ipcc
+$cluster_module->noop('cfs_update');
+
+my $rpcenv = PVE::RPCEnvironment->init('cli');
+$rpcenv->init_request(userconfig => 'api-get-permissions-test.cfg');
+
+my ($handler, $handler_info) = PVE::API2::AccessControl->find_handler('GET', 'permissions');
+
+# stranger = user without Sys.Audit permission
+my $stranger_perms = $rpcenv->get_effective_permissions('stranger@pve');
+my $stranger_privsep_perms = $rpcenv->get_effective_permissions('stranger@pve!privsep');
+
+my $stranger_user_tests = [
+    {
+	description => 'get stranger\'s perms without passing the user\'s userid',
+	rpcuser => 'stranger@pve',
+	params => {},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger\'s perms with passing the user\'s userid',
+	rpcuser => 'stranger@pve',
+	params => {
+	    userid => 'stranger@pve',
+	},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger-owned non-priv-sep\'d token\'s perms from stranger user',
+	rpcuser => 'stranger@pve',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger-owned priv-sep\'d token\'s perms from stranger user',
+	rpcuser => 'stranger@pve',
+	params => {
+	    userid => 'stranger@pve!privsep',
+	},
+	result => $stranger_privsep_perms,
+    },
+    {
+	description => 'get auditor\'s perms from stranger user',
+	should_fail => 1,
+	rpcuser => 'stranger@pve',
+	params => {
+	    userid => 'auditor@pam',
+	},
+    },
+    {
+	description => 'get auditor-owned token\'s perms from stranger user',
+	should_fail => 1,
+	rpcuser => 'stranger@pve',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	},
+    },
+];
+
+my $stranger_nonprivsep_tests = [
+    {
+	description => 'get stranger-owned non-priv-sep\'d token\'s perms without passing the token',
+	rpcuser => 'stranger@pve!noprivsep',
+	params => {},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger-owned non-priv-sep\'d token\'s perms with passing the token',
+	rpcuser => 'stranger@pve!noprivsep',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger\'s perms from stranger-owned non-priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!noprivsep',
+	params => {
+	    userid => 'stranger@pve',
+	},
+    },
+    {
+	description => 'get stranger-owned priv-sep\'d token\'s perms '
+	    . 'from stranger-owned non-priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!noprivsep',
+	params => {
+	    userid => 'stranger@pve!privsep',
+	},
+    },
+    {
+	description => 'get auditor-owned token\'s perms from stranger-owned non-priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!noprivsep',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	}
+    },
+];
+
+my $stranger_privsep_tests = [
+    {
+	description => 'get stranger-owned priv-sep\'d token\'s perms without passing the token',
+	rpcuser => 'stranger@pve!privsep',
+	params => {},
+	result => $stranger_privsep_perms,
+    },
+    {
+	description => 'get stranger-owned priv-sep\'d token\'s perms with passing the token',
+	rpcuser => 'stranger@pve!privsep',
+	params => {
+	    userid => 'stranger@pve!privsep',
+	},
+	result => $stranger_privsep_perms,
+    },
+    {
+	description => 'get stranger\'s perms from stranger-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!privsep',
+	params => {
+	    userid => 'stranger@pve',
+	},
+    },
+    {
+	description => 'get stranger-owned non-priv-sep\'d token\'s perms '
+	    . 'from stranger-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!privsep',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+    },
+    {
+	description => 'get auditor-owned token\'s perms from stranger-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'stranger@pve!privsep',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	}
+    },
+];
+
+# auditor = user with Sys.Audit permission
+my $auditor_perms = $rpcenv->get_effective_permissions('auditor@pam');
+my $auditor_privsep_perms = $rpcenv->get_effective_permissions('auditor@pam!privsep');
+
+my $auditor_user_tests = [
+    {
+	description => 'get auditor\'s perms without passing the user\'s userid',
+	rpcuser => 'auditor@pam',
+	params => {},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor\'s perms with passing the user\'s userid',
+	rpcuser => 'auditor@pam',
+	params => {
+	    userid => 'auditor@pam',
+	},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor-owned non-priv-sep\'d token\'s perms from auditor user',
+	rpcuser => 'auditor@pam',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor-owned priv-sep\'d token\'s perms from auditor user',
+	rpcuser => 'auditor@pam',
+	params => {
+	    userid => 'auditor@pam!privsep',
+	},
+	result => $auditor_privsep_perms,
+    },
+    {
+	description => 'get stranger\'s perms from auditor user',
+	rpcuser => 'auditor@pam',
+	params => {
+	    userid => 'stranger@pve',
+	},
+	result => $stranger_perms,
+    },
+    {
+	description => 'get stranger-owned token\'s perms from auditor user',
+	rpcuser => 'auditor@pam',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+	result => $stranger_perms,
+    },
+];
+
+my $auditor_nonprivsep_tests = [
+    {
+	description => 'get auditor-owned non-priv-sep\'d token\'s perms without passing the token',
+	rpcuser => 'auditor@pam!noprivsep',
+	params => {},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor-owned non-priv-sep\'d token\'s perms with passing the token',
+	rpcuser => 'auditor@pam!noprivsep',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor\'s perms from auditor-owned non-priv-sep\'d token',
+	rpcuser => 'auditor@pam!noprivsep',
+	params => {
+	    userid => 'auditor@pam',
+	},
+	result => $auditor_perms,
+    },
+    {
+	description => 'get auditor-owned priv-sep\'d token\'s perms '
+	    . 'from auditor-owned non-priv-sep\'d token',
+	rpcuser => 'auditor@pam!noprivsep',
+	params => {
+	    userid => 'auditor@pam!privsep',
+	},
+	result => $auditor_privsep_perms,
+    },
+    {
+	description => 'get stranger-owned token\'s perms from auditor-owned non-priv-sep\'d token',
+	rpcuser => 'auditor@pam!noprivsep',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+	result => $stranger_perms,
+    },
+];
+
+my $auditor_privsep_tests = [
+    {
+	description => 'get auditor-owned priv-sep\'d token\'s perms without passing the token',
+	rpcuser => 'auditor@pam!privsep',
+	params => {},
+	result => $auditor_privsep_perms,
+    },
+    {
+	description => 'get auditor-owned priv-sep\'d token\'s perms with passing the token',
+	rpcuser => 'auditor@pam!privsep',
+	params => {
+	    userid => 'auditor@pam!privsep',
+	},
+	result => $auditor_privsep_perms,
+    },
+    {
+	description => 'get auditor\'s perms from auditor-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'auditor@pam!privsep',
+	params => {
+	    userid => 'auditor@pam',
+	},
+    },
+    {
+	description => 'get auditor-owned non-priv-sep\'d token\'s perms '
+	    . 'from auditor-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'auditor@pam!privsep',
+	params => {
+	    userid => 'auditor@pam!noprivsep',
+	},
+    },
+    {
+	description => 'get stranger-owned token\'s perms from auditor-owned priv-sep\'d token',
+	should_fail => 1,
+	rpcuser => 'auditor@pam!privsep',
+	params => {
+	    userid => 'stranger@pve!noprivsep',
+	},
+    },
+];
+
+my $tests = [
+    @$stranger_user_tests,
+    @$stranger_nonprivsep_tests,
+    @$stranger_privsep_tests,
+    @$auditor_user_tests,
+    @$auditor_nonprivsep_tests,
+    @$auditor_privsep_tests,
+];
+
+plan(tests => scalar($tests->@*));
+
+for my $case ($tests->@*) {
+    $rpcenv->set_user($case->{rpcuser});
+
+    my $result = eval { $handler->handle($handler_info, $case->{params}) };
+
+    if ($@) {
+	my $should_fail = exists($case->{should_fail}) ? $case->{should_fail} : 0;
+	is(defined($@), $should_fail, "should fail: $case->{description}") || diag explain $@;
+    } else {
+	is_deeply($result, $case->{result}, $case->{description});
+    }
+}
+
+done_testing();
diff --git a/src/test/api-tests.pl b/src/test/api-tests.pl
new file mode 100755
index 0000000..a1a987a
--- /dev/null
+++ b/src/test/api-tests.pl
@@ -0,0 +1,12 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use TAP::Harness;
+
+my $harness = TAP::Harness->new({ verbosity => -1 });
+
+my $result = $harness->runtests('api-get-permissions-test.pl');
+
+exit -1 if $result->{failed};
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


             reply	other threads:[~2024-11-06 14:48 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-06 14:48 Daniel Kral [this message]
2024-11-10 19:09 ` [pve-devel] applied: " Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241106144813.189056-1-d.kral@proxmox.com \
    --to=d.kral@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal