1 package PVE
::CLI
::pveum
;
6 use PVE
::AccessControl
;
7 use PVE
::RPCEnvironment
;
12 use PVE
::API2
::AccessControl
;
13 use PVE
::API2
::Domains
;
15 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
16 use PVE
::CLIFormatter
;
18 use PVE
::JSONSchema
qw(get_standard_option);
21 use PVE
::Tools
qw(extract_param);
23 use base
qw(PVE::CLIHandler);
25 sub setup_environment
{
26 PVE
::RPCEnvironment-
>setup_default_cli_env();
33 'change_password' => [
34 PVE
::CLIHandler
::get_standard_mapping
('pve-password'),
37 PVE
::CLIHandler
::get_standard_mapping
('pve-password', {
39 # do not accept values given on cmdline
40 return PVE
::PTY
::read_password
('Enter password: ');
46 return $mapping->{$name};
49 my $print_api_result = sub {
50 my ($data, $schema, $options) = @_;
51 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
54 my $print_perm_result = sub {
55 my ($data, $schema, $options) = @_;
57 if (!defined($options->{'output-format'}) || $options->{'output-format'} eq 'text') {
63 'path' => { type
=> 'string', title
=> 'ACL path' },
64 'permissions' => { type
=> 'string', title
=> 'Permissions' },
69 foreach my $path (sort keys %$data) {
71 my $curr = $data->{$path};
72 foreach my $perm (sort keys %$curr) {
73 $value .= "\n" if $value;
75 $value .= " (*)" if $curr->{$perm};
77 push @$table_data, { path
=> $path, permissions
=> $value };
79 PVE
::CLIFormatter
::print_api_result
($table_data, $table_schema, undef, $options);
80 print "Permissions marked with '(*)' have the 'propagate' flag set.\n";
82 PVE
::CLIFormatter
::print_api_result
($data, $schema, undef, $options);
86 __PACKAGE__-
>register_method({
87 name
=> 'token_permissions',
88 path
=> 'token_permissions',
90 description
=> 'Retrieve effective permissions of given token.',
92 additionalProperties
=> 0,
94 userid
=> get_standard_option
('userid'),
95 tokenid
=> get_standard_option
('token-subid'),
96 path
=> get_standard_option
('acl-path', {
97 description
=> "Only dump this specific path, not the whole tree.",
104 description
=> 'Hash of structure "path" => "privilege" => "propagate boolean".',
109 my $token_subid = extract_param
($param, "tokenid");
110 $param->{userid
} = PVE
::AccessControl
::join_tokenid
($param->{userid
}, $token_subid);
112 return PVE
::API2
::AccessControl-
>permissions($param);
115 __PACKAGE__-
>register_method({
116 name
=> 'delete_tfa',
117 path
=> 'delete_tfa',
119 description
=> 'Delete TFA entries from a user.',
121 additionalProperties
=> 0,
123 userid
=> get_standard_option
('userid'),
125 description
=> "The TFA ID, if none provided, all TFA entries will be deleted.",
131 returns
=> { type
=> 'null' },
135 my $userid = extract_param
($param, "userid");
136 my $tfa_id = extract_param
($param, "id");
138 PVE
::AccessControl
::assert_new_tfa_config_available
();
140 PVE
::AccessControl
::lock_tfa_config
(sub {
141 my $tfa_cfg = cfs_read_file
('priv/tfa.cfg');
142 if (defined($tfa_id)) {
143 $tfa_cfg->api_delete_tfa($userid, $tfa_id);
145 $tfa_cfg->remove_user($userid);
147 cfs_write_file
('priv/tfa.cfg', $tfa_cfg);
152 __PACKAGE__-
>register_method({
156 description
=> "List TFA entries.",
158 additionalProperties
=> 0,
160 userid
=> get_standard_option
('userid', { optional
=> 1 }),
163 returns
=> { type
=> 'null' },
167 my $userid = extract_param
($param, "userid");
169 PVE
::AccessControl
::assert_new_tfa_config_available
();
171 my sub format_tfa_entries
: prototype($;$) {
172 my ($entries, $indent) = @_;
177 for my $entry (@$entries) {
178 my ($id, $ty, $desc) = ($entry->@{qw
/id type description/});
179 printf("${nl}${indent}%-9s %s\n${indent} %s\n", "$ty:", $id, $desc // '');
184 my $tfa_cfg = cfs_read_file
('priv/tfa.cfg');
185 if (defined($userid)) {
186 format_tfa_entries
($tfa_cfg->api_list_user_tfa($userid));
188 my $result = $tfa_cfg->api_list_tfa('', 1);
190 for my $entry (sort { $a->{userid
} cmp $b->{userid
} } @$result) {
191 print "${nl}$entry->{userid}:\n";
192 format_tfa_entries
($entry->{entries
}, ' ');
201 add
=> [ 'PVE::API2::User', 'create_user', ['userid'] ],
202 modify
=> [ 'PVE::API2::User', 'update_user', ['userid'] ],
203 delete => [ 'PVE::API2::User', 'delete_user', ['userid'] ],
204 list
=> [ 'PVE::API2::User', 'index', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
205 permissions
=> [ 'PVE::API2::AccessControl', 'permissions', ['userid'], {}, $print_perm_result, $PVE::RESTHandler
::standard_output_options
],
207 delete => [ __PACKAGE__
, 'delete_tfa', ['userid'] ],
208 list
=> [ __PACKAGE__
, 'list_tfa', ['userid'] ],
209 unlock
=> [ 'PVE::API2::User', 'unlock_tfa', ['userid'] ],
212 add
=> [ 'PVE::API2::User', 'generate_token', ['userid', 'tokenid'], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
213 modify
=> [ 'PVE::API2::User', 'update_token_info', ['userid', 'tokenid'], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
214 remove
=> [ 'PVE::API2::User', 'remove_token', ['userid', 'tokenid'], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
215 list
=> [ 'PVE::API2::User', 'token_index', ['userid'], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
216 permissions
=> [ __PACKAGE__
, 'token_permissions', ['userid', 'tokenid'], {}, $print_perm_result, $PVE::RESTHandler
::standard_output_options
],
220 add
=> [ 'PVE::API2::Group', 'create_group', ['groupid'] ],
221 modify
=> [ 'PVE::API2::Group', 'update_group', ['groupid'] ],
222 delete => [ 'PVE::API2::Group', 'delete_group', ['groupid'] ],
223 list
=> [ 'PVE::API2::Group', 'index', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
226 add
=> [ 'PVE::API2::Role', 'create_role', ['roleid'] ],
227 modify
=> [ 'PVE::API2::Role', 'update_role', ['roleid'] ],
228 delete => [ 'PVE::API2::Role', 'delete_role', ['roleid'] ],
229 list
=> [ 'PVE::API2::Role', 'index', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
232 modify
=> [ 'PVE::API2::ACL', 'update_acl', ['path'], { delete => 0 }],
233 delete => [ 'PVE::API2::ACL', 'update_acl', ['path'], { delete => 1 }],
234 list
=> [ 'PVE::API2::ACL', 'read_acl', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
237 add
=> [ 'PVE::API2::Domains', 'create', ['realm'] ],
238 modify
=> [ 'PVE::API2::Domains', 'update', ['realm'] ],
239 delete => [ 'PVE::API2::Domains', 'delete', ['realm'] ],
240 list
=> [ 'PVE::API2::Domains', 'index', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],
241 sync
=> [ 'PVE::API2::Domains', 'sync', ['realm'], ],
244 ticket
=> [ 'PVE::API2::AccessControl', 'create_ticket', ['username'], undef,
247 print "$res->{ticket}\n";
250 passwd
=> [ 'PVE::API2::AccessControl', 'change_password', ['userid'] ],
252 useradd
=> { alias
=> 'user add' },
253 usermod
=> { alias
=> 'user modify' },
254 userdel
=> { alias
=> 'user delete' },
256 groupadd
=> { alias
=> 'group add' },
257 groupmod
=> { alias
=> 'group modify' },
258 groupdel
=> { alias
=> 'group delete' },
260 roleadd
=> { alias
=> 'role add' },
261 rolemod
=> { alias
=> 'role modify' },
262 roledel
=> { alias
=> 'role delete' },
264 aclmod
=> { alias
=> 'acl modify' },
265 acldel
=> { alias
=> 'acl delete' },
268 # FIXME: HACK! The pool API is in pve-manager as it needs access to storage guest and RRD stats,
269 # so we only add the pool commands if the API module is available (required for boots-trapping)
272 require PVE
::API2
::Pool
;
273 PVE
::API2
::Pool-
>import();
277 if ($have_pool_api) {
279 add
=> [ 'PVE::API2::Pool', 'create_pool', ['poolid'] ],
280 modify
=> [ 'PVE::API2::Pool', 'update_pool', ['poolid'] ],
281 delete => [ 'PVE::API2::Pool', 'delete_pool', ['poolid'] ],
282 list
=> [ 'PVE::API2::Pool', 'index', [], {}, $print_api_result, $PVE::RESTHandler
::standard_output_options
],