[pve-devel] [PATCH access-control] Add OpenID group sync in pve-access-control

Alexis | Dawnalex alexis at danwalex.com
Thu Feb 15 22:27:00 CET 2024


This commit adds the group synchronization feature to OpenID authentication, allowing automatic user group mapping and updates based on the OpenID provider information. Enhances integration and access control within Proxmox VE.

Signed-off-by: Alexis | Dawnalex <alexis at danwalex.com>
---
 src/PVE/API2/OpenId.pm | 83 +++++++++++++++++++++++++++++++++++++++---
 src/PVE/Auth/OpenId.pm | 18 ++++++++-
 2 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/src/PVE/API2/OpenId.pm b/src/PVE/API2/OpenId.pm
index 77410e6..52511ce 100644
--- a/src/PVE/API2/OpenId.pm
+++ b/src/PVE/API2/OpenId.pm
@@ -35,6 +35,7 @@ my $lookup_openid_auth = sub {
 	issuer_url => $config->{'issuer-url'},
 	client_id => $config->{'client-id'},
 	client_key => $config->{'client-key'},
+
     };
     $openid_config->{prompt} = $config->{'prompt'} if defined($config->{'prompt'});

@@ -150,6 +151,7 @@ __PACKAGE__->register_method ({
 	    CSRFPreventionToken => { type => 'string' },
 	    cap => { type => 'object' },  # computed api permissions
 	    clustername => { type => 'string', optional => 1 },
+		groups => get_standard_option('group-list'),
 	},
     },
     permissions => { user => 'world' },
@@ -190,6 +192,8 @@ __PACKAGE__->register_method ({
 	    }

 	    my $username = "${unique_name}\@${realm}";
+
+

 	    # first, check if $username respects our naming conventions
 	    PVE::Auth::Plugin::verify_username($username);
@@ -210,16 +214,23 @@ __PACKAGE__->register_method ({
 		    if (defined(my $family_name = $info->{'family_name'})) {
 			$entry->{lastname} = $family_name;
 		    }
-
+			if (defined(my $groups = $info->{'groups'})) {
+				sync_group($username, $info, $config, $groups);
+			}
 		    $usercfg->{users}->{$username} = $entry;
-
 		    cfs_write_file("user.cfg", $usercfg);
 		}, "autocreate openid user failed");
 	    } else {
+
+		if (defined(my $groups = $info->{'groups'})) {
+			my $usercfg = cfs_read_file("user.cfg");
+			sync_group($username, $info, $config, $groups);
+		}
 		# test if user exists and is enabled
 		$rpcenv->check_user_enabled($username);
 	    }
-
+		#check
+
 	    my $ticket = PVE::AccessControl::assemble_ticket($username);
 	    my $csrftoken = PVE::AccessControl::assemble_csrf_prevention_token($username);
 	    my $cap = $rpcenv->compute_api_permission($username);
@@ -243,7 +254,69 @@ __PACKAGE__->register_method ({
 	    die PVE::Exception->new("authentication failure\n", code => 401);
 	}

-	PVE::Cluster::log_msg('info', 'root at pam', "successful openid auth for user '$res->{username}'");
-
+	PVE::Cluster::log_msg('info', 'root at pam', "successful authentication (OpenID) for user '$res->{username}'");
 	return $res;
     }});
+
+#function to create groups with lock user config and create group
+sub create_group {
+	my ($group, $comment) = @_;
+	my $param = {
+		groupid => $group,
+		comment => $comment,
+		};
+	eval {
+	PVE::AccessControl::lock_user_config(
+	    sub {
+
+		my $usercfg = cfs_read_file("user.cfg");
+
+		my $group = $param->{groupid};
+
+		die "group '$group' already exists\n"
+		    if $usercfg->{groups}->{$group};
+
+		$usercfg->{groups}->{$group} = { users => {} };
+
+		$usercfg->{groups}->{$group}->{comment} = $param->{comment} if $param->{comment};
+
+
+		cfs_write_file("user.cfg", $usercfg);
+	    }, "create group failed");
+	};
+}
+## function to sync groups waiting update to move without API2
+sub sync_group {
+ 	my ($username, $info, $config, $groups) = @_;
+	my $usercfg = cfs_read_file("user.cfg");
+	# First Check if the group name respects our naming conventions
+	my @filtered_groups = grep { /^[a-zA-Z0-9_\-]+$/ } @{$groups};
+	# Then add Add filter regex to check and check the regex is valid
+	my $filter_regex = $config->{'groups-filter'};
+
+	if (defined($filter_regex)) {
+		eval { qr/$filter_regex/ };
+		if ($@) {
+			die "Invalid groups-filter regex: $@\n";
+		}
+		@filtered_groups = grep { /$filter_regex/ } @filtered_groups;
+	}
+	if (defined($config->{'autocreate-groups'})){
+
+		foreach my $group (@filtered_groups) {
+
+			unless ($usercfg->{groups}->{$group}) {
+
+				create_group($group, "Autocreated by OpenID Connect");
+			}
+			PVE::AccessControl::add_user_group($username, $usercfg, $group);
+		};
+	}
+	else{
+		foreach my $group (@filtered_groups) {
+			PVE::AccessControl::add_user_group($username, $usercfg, $group);
+		};
+	}
+
+
+}
\ No newline at end of file
diff --git a/src/PVE/Auth/OpenId.pm b/src/PVE/Auth/OpenId.pm
index c8e4db9..6404b49 100755
--- a/src/PVE/Auth/OpenId.pm
+++ b/src/PVE/Auth/OpenId.pm
@@ -37,6 +37,18 @@ sub properties {
 	    type => 'boolean',
 	    default => 0,
 	},
+	"autocreate-groups" => {
+	    description => "Automatically create users if they do not exist.",
+	    optional => 1,
+	    type => 'boolean',
+	    default => 0,
+	},
+	"groups-filter" => {
+	    description => "Only allow users in these groups.",
+	    optional => 1,
+	    type => 'string',
+	    maxLength => 256,
+	},
 	"username-claim" => {
 	    description => "OpenID claim used to generate the unique username.",
 	    type => 'string',
@@ -62,7 +74,8 @@ sub properties {
 	    type => 'string',
 	    pattern => '^[^\x00-\x1F\x7F <>#"]*$', # Prohibit characters not allowed in URI RFC 2396.
 	    optional => 1,
-	},
+	},
+
    };
 }

@@ -72,12 +85,15 @@ sub options {
 	"client-id" => {},
 	"client-key" => { optional => 1 },
 	autocreate => { optional => 1 },
+	"autocreate-groups" => { optional => 1},
+	"groups-filter" => { optional => 1 },
 	"username-claim" => { optional => 1, fixed => 1 },
 	prompt => { optional => 1 },
 	scopes => { optional => 1 },
 	"acr-values" => { optional => 1 },
 	default => { optional => 1 },
 	comment => { optional => 1 },
+
     };
 }

--
2.43.0.windows.1




More information about the pve-devel mailing list