File Coverage

lib/Rex/Commands/User.pm
Criterion Covered Total %
statement 26 107 24.3
branch 0 40 0.0
condition 0 13 0.0
subroutine 9 23 39.1
pod 13 14 92.8
total 48 197 24.3


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             =head1 NAME
6              
7             Rex::Commands::User - Manipulate users and groups
8              
9             =head1 DESCRIPTION
10              
11             With this module you can manage user and groups.
12              
13             =head1 SYNOPSIS
14              
15             use Rex::Commands::User;
16              
17             task "create-user", "remoteserver", sub {
18             create_user "root",
19             uid => 0,
20             home => '/root',
21             comment => 'Root Account',
22             expire => '2011-05-30',
23             groups => [ 'root', '...' ],
24             password => 'blahblah',
25             system => 1,
26             create_home => TRUE,
27             shell => '/bin/bash',
28             ssh_key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQChUw...";
29             };
30              
31             =head1 EXPORTED FUNCTIONS
32              
33             =cut
34              
35             package Rex::Commands::User;
36              
37 32     32   498 use v5.12.5;
  32         146  
38 32     32   262 use warnings;
  32         996  
  32         1530  
39              
40             our $VERSION = '1.14.2.2'; # TRIAL VERSION
41              
42             require Rex::Exporter;
43 32     32   893 use Rex::Commands::Fs;
  32         3462  
  32         266  
44 32     32   561 use Rex::Commands::File;
  32         1710  
  32         319  
45 32     32   256 use Rex::Logger;
  32         72  
  32         302  
46 32     32   1183 use Rex::User;
  32         107  
  32         375  
47 32     32   1299 use Rex::Hook;
  32         86  
  32         1567  
48              
49 32     32   362 use vars qw(@EXPORT);
  32         90  
  32         1224  
50 32     32   207 use base qw(Rex::Exporter);
  32         68  
  32         41258  
51              
52             @EXPORT = qw(create_user delete_user get_uid get_user user_list
53             user_groups create_group delete_group get_group get_gid
54             account lock_password unlock_password
55             );
56              
57             =head2 account($name, %option)
58              
59             Manage user account.
60              
61             account "krimdomu",
62             ensure => "present", # default
63             uid => 509,
64             home => '/root',
65             comment => 'User Account',
66             expire => '2011-05-30',
67             groups => [ 'root', '...' ],
68             login_class => 'staff', # on OpenBSD
69             password => 'blahblah',
70             crypt_password => '*', # on Linux, OpenBSD and NetBSD
71             system => 1,
72             create_home => TRUE,
73             shell => '/bin/bash',
74             ssh_key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQChUw...";
75              
76             There is also a no_create_home option similar to create_home but doing the
77             opposite. If both used, create_home takes precedence as it the preferred option
78             to specify home directory creation policy.
79              
80             If none of them are specified, Rex follows the remote system's home creation
81             policy.
82              
83             The crypt_password option specifies the encrypted value as found in
84             /etc/shadow; on Linux special values are '*' and '!' which mean
85             'disabled password' and 'disabled login' respectively.
86              
87             =cut
88              
89             sub account {
90 0     0 1   my ( $name, %option ) = @_;
91              
92 0 0         if ( !ref $name ) {
93 0           $name = [$name];
94             }
95              
96 0   0       $option{ensure} ||= "present";
97              
98 0           for my $n ( @{$name} ) {
  0            
99             Rex::get_current_connection()->{reporter}
100 0           ->report_resource_start( type => "account", name => $n );
101              
102 0           my $real_name = $n;
103 0 0         if ( exists $option{name} ) {
104 0           $real_name = $option{name};
105             }
106              
107 0 0 0       if ( exists $option{ensure} && $option{ensure} eq "present" ) {
    0 0        
108 0           delete $option{ensure};
109 0           my $data = &create_user( $real_name, %option, __ret_changed => 1 );
110             Rex::get_current_connection()->{reporter}
111 0           ->report( changed => $data->{changed}, );
112             }
113             elsif ( exists $option{ensure} && $option{ensure} eq "absent" ) {
114 0           &delete_user($real_name);
115 0           Rex::get_current_connection()->{reporter}->report( changed => 1, );
116             }
117              
118             Rex::get_current_connection()->{reporter}
119 0           ->report_resource_end( type => "account", name => $n );
120             }
121             }
122              
123             =head2 create_user($user => {})
124              
125             Create or update a user.
126              
127             This function supports the following L:
128              
129             =over 4
130              
131             =item before
132              
133             This gets executed before the user is created. All original parameters are passed to it.
134              
135             =item after
136              
137             This gets executed after the user is created. All original parameters, and the user's C are passed to it.
138              
139             =back
140              
141             =cut
142              
143             sub create_user {
144 0     0 1   my ( $user, @_data ) = @_;
145              
146             #### check and run before hook
147             eval {
148 0           my @new_args = Rex::Hook::run_hook( create_user => "before", @_ );
149 0 0         if (@new_args) {
150 0           ( $user, @_data ) = @new_args;
151             }
152 0           1;
153 0 0         } or do {
154 0           die("Before-Hook failed. Canceling create_user() action: $@");
155             };
156             ##############################
157              
158 0           my $data = {};
159              
160 0 0         if ( !ref( $_data[0] ) ) {
161 0           $data = {@_data};
162             }
163             else {
164 0           $data = $_data[0];
165             }
166              
167 0           my $uid = Rex::User->get()->create_user( $user, $data );
168              
169 0 0 0       if ( defined $data->{"ssh_key"} && !defined $data->{"home"} ) {
170 0           Rex::Logger::debug(
171             "If ssh_key option is used you have to specify home, too.");
172 0           die("If ssh_key option is used you have to specify home, too.");
173             }
174              
175 0 0         if ( defined $data->{"ssh_key"} ) {
176              
177 0 0         if ( !is_dir( $data->{"home"} . "/.ssh" ) ) {
178              
179             eval {
180 0           mkdir $data->{"home"} . "/.ssh",
181             owner => $user,
182             mode => 700,
183             not_recursive => 1;
184 0 0         } or do {
185              
186             # error creating .ssh directory
187 0           Rex::Logger::debug(
188             "Not creating .ssh directory because parent doesn't exists.");
189             };
190             }
191              
192 0 0         if ( is_dir( $data->{"home"} . "/.ssh" ) ) {
193              
194             file $data->{"home"} . "/.ssh/authorized_keys",
195 0           content => $data->{"ssh_key"},
196             owner => $user,
197             mode => 600;
198              
199             }
200              
201             }
202              
203             #### check and run before hook
204 0           Rex::Hook::run_hook( create_user => "after", @_, $uid );
205             ##############################
206              
207 0 0         if ( $data->{__ret_changed} ) {
208 0           return $uid;
209             }
210              
211 0           return $uid->{ret};
212             }
213              
214             =head2 get_uid($user)
215              
216             Returns the uid of $user.
217              
218             =cut
219              
220             sub get_uid {
221 0     0 1   Rex::User->get()->get_uid(@_);
222             }
223              
224             =head2 get_user($user)
225              
226             Returns all information about $user.
227              
228             =cut
229              
230             sub get_user {
231 0     0 1   Rex::User->get()->get_user(@_);
232             }
233              
234             =head2 user_groups($user)
235              
236             Returns group membership about $user.
237              
238             =cut
239              
240             sub user_groups {
241 0     0 1   Rex::User->get()->user_groups(@_);
242             }
243              
244             =head2 user_list()
245              
246             Returns user list via getent passwd.
247              
248             task "list_user", "server01", sub {
249             for my $user (user_list) {
250             print "name: $user / uid: " . get_uid($user) . "\n";
251             }
252             };
253              
254             =cut
255              
256             sub user_list {
257 0     0 1   Rex::User->get()->user_list(@_);
258             }
259              
260             =head2 delete_user($user)
261              
262             Delete a user from the system.
263              
264             delete_user "trak", {
265             delete_home => 1,
266             force => 1,
267             };
268              
269             =cut
270              
271             sub delete_user {
272 0     0 1   my ( $user, @_data ) = @_;
273              
274 0           my $data = {};
275              
276 0 0         if ( !ref( $_data[0] ) ) {
277 0           $data = {@_data};
278             }
279             else {
280 0           $data = $_data[0];
281             }
282              
283 0           Rex::User->get()->rm_user( $user, $data );
284             }
285              
286             =head2 lock_password($user)
287              
288             Lock the password of a user account. Currently this is only
289             available on Linux (see passwd --lock) and OpenBSD.
290              
291             =cut
292              
293             sub lock_password {
294 0     0 1   Rex::User->get()->lock_password(@_);
295             }
296              
297             =head2 unlock_password($user)
298              
299             Unlock the password of a user account. Currently this is only
300             available on Linux (see passwd --unlock) and OpenBSD.
301              
302             =cut
303              
304             sub unlock_password {
305 0     0 1   Rex::User->get()->unlock_password(@_);
306             }
307              
308             # internal wrapper for resource style calling
309             # will be called from Rex::Commands::group() function
310             sub group_resource {
311 0     0 0   my @params = @_;
312              
313 0           my $name = shift @params;
314 0           my %option = @params;
315              
316 0 0         if ( ref $name ne "ARRAY" ) {
317 0           $name = [$name];
318             }
319 0   0       $option{ensure} ||= "present";
320              
321 0           for my $group_name ( @{$name} ) {
  0            
322              
323             Rex::get_current_connection()->{reporter}
324 0           ->report_resource_start( type => "group", name => $group_name );
325              
326 0           my $gid = get_gid($group_name);
327              
328 0 0         if ( $option{ensure} eq "present" ) {
    0          
329 0 0         if ( !defined $gid ) {
330 0           Rex::Commands::User::create_group( $group_name, %option );
331             }
332             }
333             elsif ( $option{ensure} eq "absent" ) {
334 0 0         if ( defined $gid ) {
335 0           Rex::Commands::User::delete_group($group_name);
336             }
337             }
338             else {
339 0           die "Unknown 'ensure' value. Valid values are 'present' and 'absent'.";
340             }
341              
342             Rex::get_current_connection()->{reporter}
343 0           ->report_resource_end( type => "group", name => $group_name );
344             }
345             }
346              
347             =head2 create_group($group, {})
348              
349             Create or update a group.
350              
351             create_group $group, {
352             gid => 1500,
353             system => 1,
354             };
355              
356             =cut
357              
358             sub create_group {
359 0     0 1   my $group = shift;
360 0           my @params;
361              
362 0 0         if ( !ref $_[0] ) {
363 0           push @params, {@_};
364             }
365             else {
366 0           push @params, @_;
367             }
368              
369 0           Rex::User->get()->create_group( $group, @params );
370             }
371              
372             =head2 get_gid($group)
373              
374             Return the group id of $group.
375              
376             =cut
377              
378             sub get_gid {
379 0     0 1   Rex::User->get()->get_gid(@_);
380             }
381              
382             =head2 get_group($group)
383              
384             Return information of $group.
385              
386             $info = get_group("wheel");
387              
388             =cut
389              
390             sub get_group {
391 0     0 1   Rex::User->get()->get_group(@_);
392             }
393              
394             =head2 delete_group($group)
395              
396             Delete a group.
397              
398             =cut
399              
400             sub delete_group {
401 0     0 1   Rex::User->get()->rm_group(@_);
402             }
403              
404             1;