[pve-devel] [PATCH access-control 7/9] Auth/LDAP: add get_{users, groups} subs for syncing

Fabian Grünbichler f.gruenbichler at proxmox.com
Mon Mar 9 11:44:55 CET 2020


On March 6, 2020 11:05 am, Dominik Csapak wrote:
> this adds the subs which actually query the LDAP for users/groups
> and returns the value in format which makes it easy to insert
> in our parsed user.cfg
> 
> when we find a user/groupname which cannot be in our config,
> we warn the verification error
> 
> for groups, we append "-$realm" to the groupname, to lower the chance of
> accidental overwriting of existing groups (this will be documented
> in the api call since it technically does not prevent overwriting, just
> makes it more unlikely)
> 
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
>  PVE/Auth/LDAP.pm | 135 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 135 insertions(+)
> 
> diff --git a/PVE/Auth/LDAP.pm b/PVE/Auth/LDAP.pm
> index 6047dfb..45e4299 100755
> --- a/PVE/Auth/LDAP.pm
> +++ b/PVE/Auth/LDAP.pm
> @@ -189,6 +189,141 @@ sub connect_and_bind {
>      return $ldap;
>  }
>  
> +# returns:
> +# {
> +#     'username at realm' => {
> +# 	'attr1' => 'value1',
> +# 	'attr2' => 'value2',
> +# 	...
> +#     },
> +#     ...
> +# }
> +#
> +# or in list context:
> +# (
> +#     {
> +# 	'username at realm' => {
> +# 	    'attr1' => 'value1',
> +# 	    'attr2' => 'value2',
> +# 	    ...
> +# 	},
> +# 	...
> +#     },
> +#     {
> +# 	'uid=username,dc=....' => 'username at realm',
> +# 	...
> +#     }
> +# )
> +# the map of dn->username is needed for group membership sync
> +sub get_users {
> +    my ($class, $config, $realm) = @_;
> +
> +    my $ldap = $class->connect_and_bind($config, $realm);
> +
> +    my $user_attr = $config->{user_attr} // 'uid';
> +    my $attrs = {
> +	$user_attr => 'username',
> +	enable => 'enable',
> +	expire => 'expire',
> +	firstname => 'firstname',
> +	lastname => 'lastname',
> +	email => 'email',
> +	comment => 'comment',
> +	keys => 'keys',
> +    };
> +
> +    foreach my $attr (PVE::Tools::split_list($config->{sync_attributes})) {
> +	my ($ours, $ldap) = ($attr =~ m/^\s*(\w+)=(.*)\s*$/);

IMHO, this is now exactly the other way round compared to the API schema 
description. Which likely means that we should specify it clearly there 
;)

> +	$attrs->{$ldap} = $ours;
> +    }
> +
> +    my $filter = $config->{filter};
> +    my $basedn = $config->{base_dn};
> +
> +    $config->{user_classes} //= 'inetorgperson, posixaccount, person, user';
> +    my $classes = [PVE::Tools::split_list($config->{user_classes})];
> +
> +    my $users = PVE::LDAP::query_users($ldap, $filter, [keys %$attrs], $basedn, $classes);
> +
> +    my $ret = {};
> +    my $dnmap = {};
> +
> +    foreach my $user (@$users) {
> +	my $user_attrs = $user->{attributes};
> +	my $userid = $user_attrs->{$user_attr}->[0];
> +	my $username = "$userid\@$realm";
> +
> +	# we cannot sync usernames that do not meet our criteria
> +	eval { PVE::Auth::Plugin::verify_username($username) };
> +	if (my $err = $@) {
> +	    warn "$err";
> +	    next;
> +	}
> +
> +	$ret->{$username} = {
> +	    enable => 1,
> +	    expire => 0,

expire is already defaulted to 0 by user.cfg's write_config
enable defaults to 0 there, but to 1 here - intentionally?

> +	};
> +
> +	foreach my $attr (keys %$user_attrs) {
> +	    if (my $key = $attrs->{$attr}) {
> +		$ret->{$username}->{$key} = $user_attrs->{$attr}->[0];

there are too many variables with 'attr' in the name here. could we at 
least rename $attrs to make it clear that it contains the mapping of LDAP 
-> user.cfg attributes/keys?

> +	    }
> +	}
> +
> +	if (!wantarray) {

accidental negation?

> +	    my $dn = $user->{dn};
> +	    $dnmap->{$dn} = $username;
> +	}
> +    }
> +
> +    return wantarray ? ($ret, $dnmap) : $ret;
> +}
> +
> +# needs a map for dn -> username, we get this from the get_users call
> +# otherwise we cannot determine the group membership
> +sub get_groups {
> +    my ($class, $config, $realm, $dnmap) = @_;
> +
> +    my $filter = $config->{group_filter};
> +    my $basedn = $config->{group_dn} // $config->{base_dn};
> +    my $attr = $config->{group_attr};
> +    $config->{group_classes} //= 'groupOfNames, group, univentionGroup, ipausergroup';
> +    my $classes = [PVE::Tools::split_list($config->{group_classes})];
> +
> +    my $ldap = $class->connect_and_bind($config, $realm);
> +
> +    my $groups = PVE::LDAP::query_groups($ldap, $basedn, $classes, $filter, $attr);
> +
> +    my $ret = {};
> +
> +    foreach my $group (@$groups) {
> +	my $name = $group->{name};
> +	if (!$name && $group->{dn} =~ m/^[^=]+=([^,]+),/){
> +	    $name = PVE::Tools::trim($1);
> +	}
> +	if ($name) {
> +	    $name .= "-$realm";

maybe it would be a further improvement to mark users and groups as 
'synced' and 
- only allow overwriting previously synced entries when syncing
- disallow editing of synced entries via the regular API
?

> +
> +	    # we cannot sync groups that do not meet our criteria
> +	    eval { PVE::AccessControl::verify_groupname($name) };
> +	    if (my $err = $@) {
> +		warn "$err";
> +		next;
> +	    }
> +
> +	    $ret->{$name} = { users => {} };
> +	    foreach my $member (@{$group->{members}}) {
> +		if (my $user = $dnmap->{$member}) {
> +		    $ret->{$name}->{users}->{$user} = 1;
> +		}
> +	    }
> +	}
> +    }
> +
> +    return $ret;
> +}
> +
>  sub authenticate_user {
>      my ($class, $config, $realm, $username, $password) = @_;
>  
> -- 
> 2.20.1
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel at pve.proxmox.com
> https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
> 




More information about the pve-devel mailing list