File Coverage

blib/lib/Apache/Sling/User.pm
Criterion Covered Total %
statement 112 237 47.2
branch 27 74 36.4
condition n/a
subroutine 25 27 92.5
pod 11 15 73.3
total 175 353 49.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             package Apache::Sling::User;
4              
5 2     2   1904 use 5.008001;
  2         7  
  2         78  
6 2     2   12 use strict;
  2         5  
  2         71  
7 2     2   11 use warnings;
  2         3  
  2         73  
8 2     2   11 use Carp;
  2         5  
  2         145  
9 2     2   1963 use Text::CSV;
  2         769471  
  2         20  
10 2     2   1470 use Getopt::Long qw(:config bundling);
  2         10228  
  2         17  
11 2     2   467 use Apache::Sling;
  2         5  
  2         94  
12 2     2   11 use Apache::Sling::Print;
  2         5  
  2         75  
13 2     2   12 use Apache::Sling::Request;
  2         5  
  2         68  
14 2     2   1501 use Apache::Sling::UserUtil;
  2         8  
  2         97  
15              
16             require Exporter;
17              
18 2     2   9 use base qw(Exporter);
  2         4  
  2         5311  
19              
20             our @EXPORT_OK = qw(command_line);
21              
22             our $VERSION = '0.27';
23              
24             #{{{sub new
25              
26             sub new {
27 2     2 1 15 my ( $class, $authn, $verbose, $log ) = @_;
28 2 50       10 if ( !defined $authn ) { croak 'no authn provided!'; }
  0         0  
29 2         4 my $response;
30 2 50       13 $verbose = ( defined $verbose ? $verbose : 0 );
31 2         21 my $user = {
32 2         6 BaseURL => ${$authn}->{'BaseURL'},
33             Authn => $authn,
34             Message => q{},
35             Response => \$response,
36             Verbose => $verbose,
37             Log => $log
38             };
39 2         6 bless $user, $class;
40 2         12 return $user;
41             }
42              
43             #}}}
44              
45             #{{{sub set_results
46             sub set_results {
47 1     1 1 1862 my ( $user, $message, $response ) = @_;
48 1         3 $user->{'Message'} = $message;
49 1         3 $user->{'Response'} = $response;
50 1         4 return 1;
51             }
52              
53             #}}}
54              
55             #{{{sub add
56             sub add {
57 1     1 1 551 my ( $user, $act_on_user, $act_on_pass, $properties ) = @_;
58 1         8 my $res = Apache::Sling::Request::request(
59             \$user,
60             Apache::Sling::UserUtil::add_setup(
61             $user->{'BaseURL'}, $act_on_user, $act_on_pass, $properties
62             )
63             );
64 0         0 my $success = Apache::Sling::UserUtil::add_eval($res);
65 0         0 my $message = "User: \"$act_on_user\" ";
66 0 0       0 $message .= ( $success ? 'added!' : 'was not added!' );
67 0         0 $user->set_results( "$message", $res );
68 0         0 return $success;
69             }
70              
71             #}}}
72              
73             #{{{sub add_from_file
74             sub add_from_file {
75 3     3 1 3235 my ( $user, $file, $fork_id, $number_of_forks ) = @_;
76 3 50       12 $fork_id = defined $fork_id ? $fork_id : 0;
77 3 50       7 $number_of_forks = defined $number_of_forks ? $number_of_forks : 1;
78 3         25 my $csv = Text::CSV->new();
79 3         260 my $count = 0;
80 3         5 my $number_of_columns = 0;
81 3         5 my @column_headings;
82 3 100       10 if ( !defined $file ) {
83 1         13 croak 'File to upload from not defined';
84             }
85 2 100   1   94 if ( open my ($input), '<', $file ) {
  1         11  
  1         2  
  1         9  
86 1         1511 while (<$input>) {
87 1 50       6 if ( $count++ == 0 ) {
    0          
88              
89             # Parse file column headings first to determine field names:
90 1 50       65 if ( $csv->parse($_) ) {
91 1         361 @column_headings = $csv->fields();
92              
93             # First field must be site:
94 1 50       11 if ( $column_headings[0] !~ /^[Uu][Ss][Ee][Rr]$/msx ) {
95 1         24 croak
96             'First CSV column must be the user ID, column heading must be "user". Found: "'
97             . $column_headings[0] . "\".\n";
98             }
99 0 0       0 if ( $column_headings[1] !~
100             /^[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd]$/msx )
101             {
102 0         0 croak
103             'Second CSV column must be the user password, column heading must be "password". Found: "'
104             . $column_headings[1] . "\".\n";
105             }
106 0         0 $number_of_columns = @column_headings;
107             }
108             else {
109 0         0 croak 'CSV broken, failed to parse line: '
110             . $csv->error_input;
111             }
112             }
113             elsif ( $fork_id == ( $count++ % $number_of_forks ) ) {
114 0         0 my @properties;
115 0 0       0 if ( $csv->parse($_) ) {
116 0         0 my @columns = $csv->fields();
117 0         0 my $columns_size = @columns;
118              
119             # Check row has same number of columns as there were column headings:
120 0 0       0 if ( $columns_size != $number_of_columns ) {
121 0         0 croak
122             "Found \"$columns_size\" columns. There should have been \"$number_of_columns\".\nRow contents was: $_";
123             }
124 0         0 my $id = $columns[0];
125 0         0 my $password = $columns[1];
126 0         0 for ( my $i = 2 ; $i < $number_of_columns ; $i++ ) {
127 0         0 my $heading = $column_headings[$i];
128 0         0 my $data = $columns[$i];
129 0         0 my $value = "$heading=$data";
130 0         0 push @properties, $value;
131             }
132 0         0 $user->add( $id, $password, \@properties );
133 0         0 Apache::Sling::Print::print_result($user);
134             }
135             else {
136 0         0 croak q{CSV broken, failed to parse line: }
137             . $csv->error_input;
138             }
139             }
140             }
141 0 0       0 close $input or croak q{Problem closing input};
142             }
143             else {
144 1         18 croak "Problem opening file: '$file'";
145             }
146 0         0 return 1;
147             }
148              
149             #}}}
150              
151             #{{{sub change_password
152             sub change_password {
153 1     1 1 1257 my ( $user, $act_on_user, $act_on_pass, $new_pass, $new_pass_confirm ) = @_;
154 1         7 my $res = Apache::Sling::Request::request(
155             \$user,
156             Apache::Sling::UserUtil::change_password_setup(
157             $user->{'BaseURL'}, $act_on_user, $act_on_pass,
158             $new_pass, $new_pass_confirm
159             )
160             );
161 0         0 my $success = Apache::Sling::UserUtil::change_password_eval($res);
162 0         0 my $message = "User: \"$act_on_user\" ";
163 0 0       0 $message .= ( $success ? 'password changed!' : 'password not changed!' );
164 0         0 $user->set_results( "$message", $res );
165 0         0 return $success;
166             }
167              
168             #}}}
169              
170             #{{{sub check_exists
171             sub check_exists {
172 1     1 1 983 my ( $user, $act_on_user ) = @_;
173 1         7 my $res = Apache::Sling::Request::request(
174             \$user,
175             Apache::Sling::UserUtil::exists_setup(
176             $user->{'BaseURL'}, $act_on_user
177             )
178             );
179 0         0 my $success = Apache::Sling::UserUtil::exists_eval($res);
180 0         0 my $message = "User \"$act_on_user\" ";
181 0 0       0 $message .= ( $success ? 'exists!' : 'does not exist!' );
182 0         0 $user->set_results( "$message", $res );
183 0         0 return $success;
184             }
185              
186             #}}}
187              
188             #{{{ sub command_line
189             sub command_line {
190 0     0 0 0 my ( $user, @ARGV ) = @_;
191 0         0 my $sling = Apache::Sling->new;
192 0         0 my $config = $user->config( $sling, @ARGV );
193 0         0 return $user->run( $sling, $config );
194             }
195              
196             #}}}
197              
198             #{{{sub config
199              
200             sub config {
201 1     1 1 1031 my ( $user, $sling, @ARGV ) = @_;
202              
203 1         7 my $user_config = $user->config_hash( $sling, @ARGV );
204              
205 1 50       10 GetOptions(
206             $user_config, 'auth=s',
207             'help|?', 'log|L=s',
208             'man|M', 'pass|p=s',
209             'threads|t=s', 'url|U=s',
210             'user|u=s', 'verbose|v+',
211             'add|a=s', 'additions|A=s',
212             'change-password|c=s', 'delete|d=s',
213             'email|E=s', 'first-name|f=s',
214             'exists|e=s', 'last-name|l=s',
215             'new-password|n=s', 'password|w=s',
216             'property|P=s', 'update=s',
217             'view|V=s'
218             ) or $user->help();
219              
220 1         1481 return $user_config;
221             }
222              
223             #}}}
224              
225             #{{{sub config_hash
226              
227             sub config_hash {
228 1     1 0 5 my ( $user, $sling, @ARGV ) = @_;
229 1         3 my $password;
230             my $additions;
231 0         0 my $add;
232 0         0 my $change_password;
233 0         0 my $delete;
234 0         0 my $email;
235 0         0 my $exists;
236 0         0 my $first_name;
237 0         0 my $last_name;
238 0         0 my $new_password;
239 0         0 my @property;
240 0         0 my $update;
241 0         0 my $view;
242              
243 1         42 my %user_config = (
244             'auth' => \$sling->{'Auth'},
245             'help' => \$sling->{'Help'},
246             'log' => \$sling->{'Log'},
247             'man' => \$sling->{'Man'},
248             'pass' => \$sling->{'Pass'},
249             'threads' => \$sling->{'Threads'},
250             'url' => \$sling->{'URL'},
251             'user' => \$sling->{'User'},
252             'verbose' => \$sling->{'Verbose'},
253             'add' => \$add,
254             'additions' => \$additions,
255             'change-password' => \$change_password,
256             'delete' => \$delete,
257             'email' => \$email,
258             'exists' => \$exists,
259             'first-name' => \$first_name,
260             'last-name' => \$last_name,
261             'new-password' => \$new_password,
262             'password' => \$password,
263             'property' => \@property,
264             'update' => \$update,
265             'view' => \$view
266             );
267              
268 1         4 return \%user_config;
269             }
270              
271             #}}}
272              
273             #{{{sub del
274             sub del {
275 1     1 1 1044 my ( $user, $act_on_user ) = @_;
276 1         8 my $res = Apache::Sling::Request::request(
277             \$user,
278             Apache::Sling::UserUtil::delete_setup(
279             $user->{'BaseURL'}, $act_on_user
280             )
281             );
282 0         0 my $success = Apache::Sling::UserUtil::delete_eval($res);
283 0         0 my $message = "User: \"$act_on_user\" ";
284 0 0       0 $message .= ( $success ? 'deleted!' : 'was not deleted!' );
285 0         0 $user->set_results( "$message", $res );
286 0         0 return $success;
287             }
288              
289             #}}}
290              
291             #{{{ sub help
292             sub help {
293              
294 1     1 0 1891 print <<"EOF";
295             Usage: perl $0 [-OPTIONS [-MORE_OPTIONS]] [--] [PROGRAM_ARG1 ...]
296             The following options are accepted:
297              
298             --add or -a (actOnUser) - add specified user name.
299             --additions or -A (file) - file containing list of users to be added.
300             --auth (type) - Specify auth type. If ommitted, default is used.
301             --change-password or -c (actOnUser) - change password of specified user name.
302             --delete or -d (actOnUser) - delete specified user name.
303             --email or -E (email) - specify email address property for user.
304             --exists or -e (actOnUser) - check whether specified user exists.
305             --first-name or -f (firstName) - specify first name property for user.
306             --help or -? - view the script synopsis and options.
307             --last-name or -l (lastName) - specify last name property for user.
308             --log or -L (log) - Log script output to specified log file.
309             --man or -M - view the full script documentation.
310             --me or -m - me returns json representing authenticated user.
311             --new-password or -n (newPassword) - Used with -c, new password to set.
312             --password or -w (actOnPass) - Password of user being actioned.
313             --pass or -p (password) - Password of user performing actions.
314             --property or -P (property=value) - Specify property to set on user.
315             --threads or -t (threads) - Used with -A, defines number of parallel
316             processes to have running through file.
317             --update (actOnUser) - update specified user name, used with -P.
318             --url or -U (URL) - URL for system being tested against.
319             --user or -u (username) - Name of user to perform any actions as.
320             --verbose or -v or -vv or -vvv - Increase verbosity of output.
321             --view or -V (actOnUser) - view details for specified user in json format.
322              
323             Options may be merged together. -- stops processing of options.
324             Space is not required between options and their arguments.
325             For full details run: perl $0 --man
326             EOF
327              
328 1         4 return 1;
329             }
330              
331             #}}}
332              
333             #{{{ sub man
334             sub man {
335              
336 0     0 0 0 my ($user) = @_;
337              
338 0         0 print <<'EOF';
339             user perl script. Provides a means of managing users in sling from the command
340             line. The script also acts as a reference implementation for the User perl
341             library.
342              
343             EOF
344              
345 0         0 $user->help();
346              
347 0         0 print <<"EOF";
348             Example Usage
349              
350             * Add user "testuser" with password "test"
351              
352             perl $0 -U http://localhost:8080 -a testuser -w test
353              
354             * View information about authenticated user "testuser"
355              
356             perl $0 -U http://localhost:8080 --me -u testuser -p test
357              
358             * Authenticate as admin and check whether user "testuser" exists
359              
360             perl $0 -U http://localhost:8080 -e testuser -u admin -p admin
361              
362             * Authenticate and update "testuser" to set property p1=v1
363              
364             perl $0 -U http://localhost:8080 --update testuser -P "p1=v1" -u admin -p admin
365              
366             * Authenticate and delete "testuser"
367              
368             perl $0 -U http://localhost:8080 -d testuser -u admin -p admin
369             EOF
370              
371 0         0 return 1;
372             }
373              
374             #}}}
375              
376             #{{{sub run
377             sub run {
378 2     2 1 38 my ( $user, $sling, $config ) = @_;
379 2 100       9 if ( !defined $config ) {
380 1         22 croak 'No user config supplied!';
381             }
382 1         8 $sling->check_forks;
383 1         3 my $authn =
384             defined $sling->{'Authn'}
385 1 50       5 ? ${ $sling->{'Authn'} }
386             : new Apache::Sling::Authn( \$sling );
387              
388             # Handle the three special case commonly used properties:
389 1 50       2 if ( defined ${ $config->{'email'} } ) {
  1         8  
390 0         0 push @{ $config->{'property'} }, "email=" . ${ $config->{'email'} };
  0         0  
  0         0  
391             }
392 1 50       3 if ( defined ${ $config->{'first-name'} } ) {
  1         6  
393 0         0 push @{ $config->{'property'} },
  0         0  
394 0         0 "firstName=" . ${ $config->{'first-name'} };
395             }
396 1 50       2 if ( defined ${ $config->{'last-name'} } ) {
  1         4  
397 0         0 push @{ $config->{'property'} },
  0         0  
398 0         0 "lastName=" . ${ $config->{'last-name'} };
399             }
400              
401 1         2 my $success = 1;
402              
403 1 50       8 if ( $sling->{'Help'} ) { $user->help(); }
  0 50       0  
    50          
404 0         0 elsif ( $sling->{'Man'} ) { $user->man(); }
  1         4  
405             elsif ( defined ${ $config->{'additions'} } ) {
406 0         0 my $message =
407 0         0 "Adding users from file \"" . ${ $config->{'additions'} } . "\":\n";
408 0         0 Apache::Sling::Print::print_with_lock( "$message", $sling->{'Log'} );
409 0         0 my @childs = ();
410 0         0 for my $i ( 0 .. $sling->{'Threads'} ) {
411 0         0 my $pid = fork;
412 0 0       0 if ($pid) { push @childs, $pid; } # parent
  0 0       0  
413             elsif ( $pid == 0 ) { # child
414             # Create a new separate user agent per fork in order to
415             # ensure cookie stores are separate, then log the user in:
416 0         0 $authn->{'LWP'} = $authn->user_agent( $sling->{'Referer'} );
417 0         0 $authn->login_user();
418 0         0 my $user =
419             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
420             $sling->{'Log'} );
421 0         0 $user->add_from_file( ${ $config->{'additions'} },
  0         0  
422             $i, $sling->{'Threads'} );
423 0         0 exit 0;
424             }
425             else {
426 0         0 croak "Could not fork $i!";
427             }
428             }
429 0         0 foreach (@childs) { waitpid $_, 0; }
  0         0  
430             }
431             else {
432 1         8 $authn->login_user();
433 1 50       1 if ( defined ${ $config->{'exists'} } ) {
  1 50       4  
  1 50       4  
    50          
    50          
    50          
434 0         0 $user =
435             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
436             $sling->{'Log'} );
437 0         0 $success = $user->check_exists( ${ $config->{'exists'} } );
  0         0  
438             }
439 1         4 elsif ( defined ${ $config->{'add'} } ) {
440 0         0 $user =
441             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
442             $sling->{'Log'} );
443 0         0 $success = $user->add(
444 0         0 ${ $config->{'add'} },
445 0         0 ${ $config->{'password'} },
446             $config->{'property'}
447             );
448             }
449 1         3 elsif ( defined ${ $config->{'update'} } ) {
450 0         0 $user =
451             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
452             $sling->{'Log'} );
453 0         0 $success =
454 0         0 $user->update( ${ $config->{'update'} }, $config->{'property'} );
455             }
456 1         3 elsif ( defined ${ $config->{'change-password'} } ) {
457 0         0 $user =
458             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
459             $sling->{'Log'} );
460 0         0 $success = $user->change_password(
461 0         0 ${ $config->{'change-password'} },
462 0         0 ${ $config->{'password'} },
463 0         0 ${ $config->{'new-password'} },
464 0         0 ${ $config->{'new-password'} }
465             );
466             }
467 1         4 elsif ( defined ${ $config->{'delete'} } ) {
468 0         0 $user =
469             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
470             $sling->{'Log'} );
471 0         0 $success = $user->del( ${ $config->{'delete'} } );
  0         0  
472             }
473             elsif ( defined ${ $config->{'view'} } ) {
474 0         0 $user =
475             new Apache::Sling::User( \$authn, $sling->{'Verbose'},
476             $sling->{'Log'} );
477 0         0 $success = $user->view( ${ $config->{'view'} } );
  0         0  
478             }
479             else {
480 1         5 $user->help();
481 1         8 return 1;
482             }
483 0         0 Apache::Sling::Print::print_result($user);
484             }
485 0         0 return $success;
486             }
487              
488             #}}}
489              
490             #{{{sub update
491             sub update {
492 1     1 1 1060 my ( $user, $act_on_user, $properties ) = @_;
493 1         7 my $res = Apache::Sling::Request::request(
494             \$user,
495             Apache::Sling::UserUtil::update_setup(
496             $user->{'BaseURL'}, $act_on_user, $properties
497             )
498             );
499 0         0 my $success = Apache::Sling::UserUtil::update_eval($res);
500 0         0 my $message = "User: \"$act_on_user\" ";
501 0 0       0 $message .= ( $success ? 'updated!' : 'was not updated!' );
502 0         0 $user->set_results( "$message", $res );
503 0         0 return $success;
504             }
505              
506             #}}}
507              
508             #{{{sub view
509             sub view {
510 1     1 1 1092 my ( $user, $act_on_user ) = @_;
511 1         5 my $res = Apache::Sling::Request::request(
512             \$user,
513             Apache::Sling::UserUtil::exists_setup(
514             $user->{'BaseURL'}, $act_on_user
515             )
516             );
517 0         0 my $success = Apache::Sling::UserUtil::exists_eval($res);
518 0         0 my $message = (
519             $success
520 0 0       0 ? ${$res}->content
521             : "Problem viewing user: \"$act_on_user\""
522             );
523 0         0 $user->set_results( "$message", $res );
524 0         0 return $success;
525             }
526              
527             #}}}
528              
529             1;
530              
531             __END__