Do we really want a enable/disable property? Wouldn't it be enough to delete the token?
> Fabian Grünbichler <f.gruenbich...@proxmox.com> hat am 17. Oktober 2019 15:14 > geschrieben: > > > and integration for user API endpoints. > > Signed-off-by: Fabian Grünbichler <f.gruenbich...@proxmox.com> > --- > > Notes: > pveum integration will come in a future version, but > > pveum token add/modify/delete <user> <token> [OPTIONS] > > or > > pveum user token add/modify/delete <user> <token> [OPTIONS] > > seem like likely candidates. > > PVE/API2/User.pm | 287 ++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 284 insertions(+), 3 deletions(-) > > diff --git a/PVE/API2/User.pm b/PVE/API2/User.pm > index fb5b22a..1646418 100644 > --- a/PVE/API2/User.pm > +++ b/PVE/API2/User.pm > @@ -2,9 +2,9 @@ package PVE::API2::User; > > use strict; > use warnings; > -use PVE::Exception qw(raise raise_perm_exc); > +use PVE::Exception qw(raise raise_perm_exc raise_param_exc); > use PVE::Cluster qw (cfs_read_file cfs_write_file); > -use PVE::Tools qw(split_list); > +use PVE::Tools qw(split_list extract_param); > use PVE::AccessControl; > use PVE::JSONSchema qw(get_standard_option register_standard_option); > > @@ -40,6 +40,39 @@ register_standard_option('group-list', { > optional => 1, > completion => \&PVE::AccessControl::complete_group, > }); > +register_standard_option('token-subid', { > + type => 'string', > + pattern => $PVE::AccessControl::token_subid_regex, > + description => 'User-specific token identifier.', > +}); > +register_standard_option('token-enable', { > + description => "Enable the API token (default). You can set this to '0' > to disable this specific token.", > + type => 'boolean', > + optional => 1, > + default => 1, > +}); > +register_standard_option('token-expire', { > + description => "API token expiration date (seconds since epoch). '0' > means no expiration date.", > + type => 'integer', > + minimum => 0, > + optional => 1, > +}); > +register_standard_option('token-privsep', { > + description => "Restrict API token privileges with separate ACLs > (default), or give full privileges of corresponding user.", > + type => 'boolean', > + optional => 1, > + default => 1, > +}); > +register_standard_option('token-comment', { type => 'string', optional => 1 > }); > +register_standard_option('token-info', { > + type => 'object', > + properties => { > + enable => get_standard_option('token-enable'), > + expire => get_standard_option('token-expire'), > + privsep => get_standard_option('token-privsep'), > + comment => get_standard_option('token-comment'), > + } > +}); > > my $extract_user_data = sub { > my ($data, $full) = @_; > @@ -53,6 +86,7 @@ my $extract_user_data = sub { > return $res if !$full; > > $res->{groups} = $data->{groups} ? [ keys %{$data->{groups}} ] : []; > + $res->{tokens} = $data->{tokens} ? [ keys %{$data->{tokens}} ] : []; > > return $res; > }; > @@ -228,7 +262,17 @@ __PACKAGE__->register_method ({ > email => get_standard_option('user-email'), > comment => get_standard_option('user-comment'), > keys => get_standard_option('user-keys'), > - groups => { type => 'array' }, > + groups => { > + type => 'array', > + items => { > + type => 'string', > + format => 'pve-groupid', > + }, > + }, > + tokens => { > + type => 'array', > + items => get_standard_option('token-subid'), > + }, > }, > type => "object" > }, > @@ -428,4 +472,241 @@ __PACKAGE__->register_method ({ > return $res; > }}); > > +my $token_info_extend = sub { > + my ($props) = @_; > + > + my $obj = get_standard_option('token-info'); > + my $base_props = $obj->{properties}; > + $obj->{properties} = {}; > + > + foreach my $prop (keys %$base_props) { > + $obj->{properties}->{$prop} = $base_props->{$prop}; > + } > + > + foreach my $add_prop (keys %$props) { > + $obj->{properties}->{$add_prop} = $props->{$add_prop}; > + } > + > + return $obj; > +}; > + > +__PACKAGE__->register_method ({ > + name => 'token_index', > + path => '{userid}/token', > + method => 'GET', > + description => "Get user API tokens.", > + permissions => { > + check => ['userid-param', 'self'], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + userid => get_standard_option('userid-completed'), > + }, > + }, > + returns => { > + type => "array", > + items => $token_info_extend->({ > + tokenid => get_standard_option('token-subid'), > + }), > + links => [ { rel => 'child', href => "{tokenid}" } ], > + }, > + code => sub { > + my ($param) = @_; > + > + my $userid = PVE::AccessControl::verify_username($param->{userid}); > + my $usercfg = cfs_read_file("user.cfg"); > + > + my $user = PVE::AccessControl::check_user_exist($usercfg, $userid); > + > + my $tokens = $user->{tokens} // {}; > + return [ map { $tokens->{$_}->{tokenid} = $_; $tokens->{$_} } keys > %$tokens]; > + }}); > + > +__PACKAGE__->register_method ({ > + name => 'read_token', > + path => '{userid}/token/{tokenid}', > + method => 'GET', > + description => "Get specific API token information.", > + permissions => { > + check => ['userid-param', 'self'], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + userid => get_standard_option('userid-completed'), > + tokenid => get_standard_option('token-subid'), > + }, > + }, > + returns => get_standard_option('token-info'), > + code => sub { > + my ($param) = @_; > + > + my $userid = PVE::AccessControl::verify_username($param->{userid}); > + my $tokenid = $param->{tokenid}; > + > + my $usercfg = cfs_read_file("user.cfg"); > + > + return PVE::AccessControl::check_token_exist($usercfg, $userid, > $tokenid); > + }}); > + > +__PACKAGE__->register_method ({ > + name => 'generate_token', > + path => '{userid}/token/{tokenid}', > + method => 'POST', > + description => "Generate a new API token for a specific user. NOTE: > returns API token value, which needs to be stored as it cannot be retrieved > afterwards!", > + permissions => { > + check => ['userid-param', 'self'], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + userid => get_standard_option('userid-completed'), > + tokenid => get_standard_option('token-subid'), > + enable => get_standard_option('token-enable'), > + expire => get_standard_option('token-expire'), > + privsep => get_standard_option('token-privsep'), > + comment => get_standard_option('token-comment'), > + }, > + }, > + returns => { > + additionalProperties => 0, > + type => "object", > + properties => { > + info => get_standard_option('token-info'), > + value => { > + type => 'string', > + description => 'API token value used for authentication.', > + }, > + }, > + }, > + code => sub { > + my ($param) = @_; > + > + my $userid = PVE::AccessControl::verify_username(extract_param($param, > 'userid')); > + my $tokenid = extract_param($param, 'tokenid'); > + > + my $usercfg = cfs_read_file("user.cfg"); > + > + my $token = PVE::AccessControl::check_token_exist($usercfg, $userid, > $tokenid, 1); > + my $value; > + > + PVE::AccessControl::check_user_exist($usercfg, $userid); > + raise_param_exc({ 'tokenid' => 'Token already exists.' }) if > defined($token); > + > + my $generate_and_add_token = sub { > + $usercfg = cfs_read_file("user.cfg"); > + PVE::AccessControl::check_user_exist($usercfg, $userid); > + die "Token already exists.\n" if > defined(PVE::AccessControl::check_token_exist($usercfg, $userid, $tokenid, > 1)); > + > + my $full_tokenid = PVE::AccessControl::join_tokenid($userid, > $tokenid); > + $value = PVE::AccessControl::generate_token($full_tokenid); > + > + $token = {}; > + $token->{enable} = defined($param->{enable}) ? $param->{enable} : 1; > + $token->{privsep} = defined($param->{privsep}) ? $param->{privsep} > : 1; > + $token->{expire} = $param->{expire} if defined($param->{expire}); > + $token->{comment} = $param->{comment} if $param->{comment}; > + > + $usercfg->{users}->{$userid}->{tokens}->{$tokenid} = $token; > + cfs_write_file("user.cfg", $usercfg); > + }; > + > + PVE::AccessControl::lock_user_config($generate_and_add_token, > 'generating token failed'); > + > + return { info => $token, value => $value }; > + }}); > + > + > +__PACKAGE__->register_method ({ > + name => 'update_token_info', > + path => '{userid}/token/{tokenid}', > + method => 'PUT', > + description => "Update API token for a specific user.", > + permissions => { > + check => ['userid-param', 'self'], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + userid => get_standard_option('userid-completed'), > + tokenid => get_standard_option('token-subid'), > + enable => get_standard_option('token-enable'), > + expire => get_standard_option('token-expire'), > + privsep => get_standard_option('token-privsep'), > + comment => get_standard_option('token-comment'), > + }, > + }, > + returns => get_standard_option('token-info', { description => "Updated > token information." }), > + code => sub { > + my ($param) = @_; > + > + my $userid = PVE::AccessControl::verify_username(extract_param($param, > 'userid')); > + my $tokenid = extract_param($param, 'tokenid'); > + > + my $usercfg = cfs_read_file("user.cfg"); > + my $token = PVE::AccessControl::check_token_exist($usercfg, $userid, > $tokenid); > + > + my $update_token = sub { > + $usercfg = cfs_read_file("user.cfg"); > + $token = PVE::AccessControl::check_token_exist($usercfg, $userid, > $tokenid); > + > + my $full_tokenid = PVE::AccessControl::join_tokenid($userid, > $tokenid); > + > + $token->{enable} = $param->{enable} if defined($param->{enable}); > + $token->{privsep} = $param->{privsep} if defined($param->{privsep}); > + $token->{expire} = $param->{expire} if defined($param->{expire}); > + $token->{comment} = $param->{comment} if $param->{comment}; > + > + $usercfg->{users}->{$userid}->{tokens}->{$tokenid} = $token; > + cfs_write_file("user.cfg", $usercfg); > + }; > + > + PVE::AccessControl::lock_user_config($update_token, 'updating token > info failed'); > + > + return $token; > + }}); > + > + > +__PACKAGE__->register_method ({ > + name => 'remove_token', > + path => '{userid}/token/{tokenid}', > + method => 'DELETE', > + description => "Remove API token for a specific user.", > + permissions => { > + check => ['userid-param', 'self'], > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + userid => get_standard_option('userid-completed'), > + tokenid => get_standard_option('token-subid'), > + }, > + }, > + returns => { type => 'null' }, > + code => sub { > + my ($param) = @_; > + > + my $userid = PVE::AccessControl::verify_username(extract_param($param, > 'userid')); > + my $tokenid = extract_param($param, 'tokenid'); > + > + my $usercfg = cfs_read_file("user.cfg"); > + my $token = PVE::AccessControl::check_token_exist($usercfg, $userid, > $tokenid); > + > + my $update_token = sub { > + $usercfg = cfs_read_file("user.cfg"); > + > + PVE::AccessControl::check_token_exist($usercfg, $userid, $tokenid); > + > + my $full_tokenid = PVE::AccessControl::join_tokenid($userid, > $tokenid); > + PVE::AccessControl::delete_token($full_tokenid); > + delete $usercfg->{users}->{$userid}->{tokens}->{$tokenid}; > + > + cfs_write_file("user.cfg", $usercfg); > + }; > + > + PVE::AccessControl::lock_user_config($update_token, 'deleting token > failed'); > + > + return; > + }}); > 1; > -- > 2.20.1 > > > _______________________________________________ > pve-devel mailing list > pve-devel@pve.proxmox.com > https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel