File Coverage

blib/lib/Test/Unix/User.pm
Criterion Covered Total %
statement 83 120 69.1
branch 34 54 62.9
condition 5 12 41.6
subroutine 8 9 88.8
pod 2 2 100.0
total 132 197 67.0


line stmt bran cond sub pod time code
1             package Test::Unix::User;
2              
3             # Copyright (c) 2005 Nik Clayton
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions
8             # are met:
9             # 1. Redistributions of source code must retain the above copyright
10             # notice, this list of conditions and the following disclaimer.
11             # 2. Redistributions in binary form must reproduce the above copyright
12             # notice, this list of conditions and the following disclaimer in the
13             # documentation and/or other materials provided with the distribution.
14             #
15             # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16             # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18             # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19             # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20             # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21             # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22             # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23             # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24             # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25             # SUCH DAMAGE.
26              
27 2     2   19400 use warnings;
  2         4  
  2         61  
28 2     2   10 use strict;
  2         3  
  2         61  
29              
30 2     2   9 use Test::Builder;
  2         6  
  2         136  
31              
32             require Exporter;
33             our @ISA = qw(Exporter);
34             our @EXPORT = qw(user_ok homedir_ok);
35              
36             my $Test = Test::Builder->new;
37              
38 2     2   795 use User::pwent;
  2         9549  
  2         16  
39 2     2   971 use File::stat;
  2         6152  
  2         13  
40              
41             my @USER_FIELDS = qw(name passwd uid gid change age quota
42             comment class gecos dir shell expire);
43             my %USER_FIELDS = map {$_ => 1} @USER_FIELDS;
44             my @HDIR_FIELDS = qw(name uid gid perm owner group);
45             my %HDIR_FIELDS = map {$_ => 1} @HDIR_FIELDS;
46              
47             sub import {
48 2     2   22 my($self) = shift;
49 2         4 my $pack = caller;
50              
51 2         16 $Test->exported_to($pack);
52 2         58 $Test->plan(@_);
53              
54 2         230 $self->export_to_level(1, $self, qw(user_ok homedir_ok));
55             }
56              
57             =head1 NAME
58              
59             Test::Unix::User - Test::Builder based tests for Unix users and home directories
60              
61             =head1 VERSION
62              
63             Version 0.02
64              
65             =cut
66              
67             our $VERSION = '0.02';
68              
69             =head1 SYNOPSIS
70              
71             use Test::Unix::User tests => 2;
72              
73             user_ok({ name => 'nik', uid => 1000, ... },
74             "Verify nik's account");
75              
76             homedir_ok({ name => 'nik', perm => 0755, ... },
77             "Verify nik's home directory");
78              
79             Test::Unix::User B exports C and C
80             to make it easier to test whether or not the Unix users and home
81             directories on the system have been correctly configured.
82              
83             Test::Unix::User uses Test::Builder, so plays nicely with Test::Simple,
84             Test::More, and other Test::Builder based modules.
85              
86             =head1 FUNCTIONS
87              
88             =head2 user_ok($spec, [ $test_name ]);
89              
90             user_ok() tests that an account exists that matches the given
91             specification.
92              
93             The specification is a hashref that consists of one or more keys. Keys
94             are taken from the L module, and are C, C, C,
95             C, C, C, C, C, C, C,
96             C, C, and C. Some of these may not be supported on
97             your platform. See User::pwent for more details.
98              
99             Each value associated with a key is the value that that entry is supposed
100             to have.
101              
102             Only the C key is mandatory, the others are optional. If they are
103             not present in the specification then they are not checked.
104              
105             The C<$test_name> is optional. If it is not present then a sensible one
106             is generated following the form
107              
108             Checking user '$user' ($key, $key, $key, ...)
109              
110             =cut
111              
112             sub user_ok {
113 0 0   0 1 0 return unless _check_spec(@_);
114              
115 0         0 my($spec, $test_name) = @_;
116              
117 0 0       0 if(! defined $test_name) {
118 0         0 $test_name = "Checking user '$spec->{name}'";
119 0         0 $test_name .= ' (' . join(', ', sort keys %$spec) . ')';
120             }
121              
122 0         0 my($u, @diag);
123              
124 0         0 $u = getpwnam($spec->{name});
125              
126 0 0       0 if(! defined $u) {
127 0         0 my $ok = $Test->ok(0, $test_name);
128 0         0 $Test->diag(" User '$spec->{name}' does not exist");
129 0         0 return $ok;
130             }
131              
132 0         0 foreach my $field (keys %$spec) {
133 0 0       0 if(! exists $USER_FIELDS{$field}) {
134 0         0 push @diag, " Invalid field '$field' given";
135 0         0 next;
136             }
137              
138 0 0 0     0 if(! defined $spec->{$field} or $spec->{$field} =~ /^\s*$/) {
139 0         0 push @diag, " Empty field '$field' given";
140 0         0 next;
141             }
142              
143 0 0       0 if($u->$field ne $spec->{$field}) {
144 0         0 push @diag, " Field: $field\n";
145 0         0 push @diag, " expected: $spec->{$field}\n";
146 0         0 push @diag, " got: " . $u->$field . "\n";
147             }
148             }
149              
150 0 0       0 if(@diag) {
151 0         0 my $ok = $Test->ok(0, $test_name);
152 0         0 $Test->diag(@diag);
153 0         0 return $ok;
154             }
155              
156 0         0 return $Test->ok(1, $test_name);
157             }
158              
159             =head2 homedir_ok($spec, [ $test_name ]);
160              
161             C checks that the home directory for a given user exists and
162             matches the specification.
163              
164             The specification is a hashref that consists of one or more keys. Valid
165             keys are C, C, C, C, C, and C.
166              
167             The C key is mandatory, the other keys are optional.
168              
169             The C<$test_name> is optional. If it is not present then a sensible one
170             is generated following the form.
171              
172             Home directory for user '$user' ($key, $key, $key, ...)
173              
174             Use C when you want to check the numeric user id assigned to the
175             directory, irrespective of the user name that is assigned to that uid.
176             Use C when you are interested in the name of the owner, without
177             being concerned about the numeric UID. Use both of these together to
178             ensure that the UID and the owner name match.
179              
180             C is to C as C is to C.
181              
182             =cut
183              
184             sub homedir_ok {
185 15 100   15 1 49944 return unless _check_spec(@_);
186              
187 14         23 my($spec, $test_name) = @_;
188              
189 14 100       32 if(! defined $test_name) {
190 13         53 $test_name = "Home directory for user '$spec->{name}'";
191 13         86 $test_name .= ' (' . join(', ', sort keys %$spec) . ')';
192             }
193              
194 14         25 my @diag;
195              
196 14         31 foreach my $field (keys %$spec) {
197 30 100       69 if(! exists $HDIR_FIELDS{$field}) {
198 1         4 push @diag, " Invalid field '$field' given";
199 1         2 delete $spec->{$field};
200 1         3 next;
201             }
202              
203 29 100 100     169 if(! defined $spec->{$field} or $spec->{$field} =~ /^\s*$/) {
204 2         6 push @diag, " Empty field '$field' given";
205 2         4 delete $spec->{$field};
206 2         6 next;
207             }
208             }
209            
210 14         70 my $u = getpwnam($spec->{name});
211              
212 14 100       84 if(! defined $u) {
213 1         44 my $ok = $Test->ok(0, $test_name);
214 1         138 $Test->diag(" User '$spec->{name}' does not exist");
215 1         36 return $ok;
216             }
217              
218 13 100       347 if(! -d $u->dir) {
219 1         33 my $ok = $Test->ok(0, $test_name);
220 1         105 $Test->diag(" Home directory '" . $u->dir . "' for '$spec->{name}' is not a directory");
221 1         36 return $ok;
222             }
223              
224 12         701 my $sb = stat($u->dir);
225              
226 12         1501 foreach my $field (qw(uid gid)) {
227 24 100       96 if(exists $spec->{$field}) {
228 6 100       128 if($sb->$field != $spec->{$field}) {
229 1         10 push @diag, " Field: $field\n";
230 1         3 push @diag, " expected: $spec->{$field}\n";
231 1         18 push @diag, " got: " . $sb->$field . "\n";
232             }
233             }
234             }
235              
236 12 100       53 if(exists $spec->{owner}) {
237 2         47 my $owner = getpwuid($sb->uid)->name();
238 2 100       1002 if($spec->{owner} ne $owner) {
239 1         4 push @diag, " Field: owner\n";
240 1         6 push @diag, " expected: $spec->{owner}\n";
241 1         3 push @diag, " got: $owner\n";
242             }
243             }
244              
245 12 100       39 if(exists $spec->{group}) {
246 2         35 my $group = getgrgid($sb->gid);
247 2 100       193 if($spec->{group} ne $group) {
248 1         3 push @diag, " Field: group\n";
249 1         3 push @diag, " expected: $spec->{group}\n";
250 1         3 push @diag, " got: $group\n";
251             }
252             }
253              
254 12 100       32 if(exists $spec->{perm}) {
255 3 100       57 if(($sb->mode & 07777) != $spec->{perm}) {
256 1         9 push @diag, " Field: perm\n";
257 1         6 push @diag, sprintf(" expected: %04o\n", $spec->{perm});
258 1         22 push @diag, sprintf(" got: %04o\n", $sb->mode & 07777);
259             }
260             }
261              
262 12 100       65 if(@diag) {
263 7         56 my $ok = $Test->ok(0, $test_name);
264 7         796 $Test->diag(@diag);
265 7         229 return $ok;
266             }
267              
268 5         36 return $Test->ok(1, $test_name);
269             }
270              
271             sub _check_spec {
272 15     15   31 my($spec, $test_name) = @_;
273 15         75 my $sub = (caller(1))[3];
274              
275 15         61 $sub =~ s/Test::Unix::User:://;
276              
277 15 100       47 if(! defined $spec) {
278 1         9 my $ok = $Test->ok(0, "$sub()");
279 1         115 $Test->diag(" $sub() called with no arguments");
280 1         27 return $ok;
281             }
282              
283 14 50       45 if(ref($spec) ne 'HASH') {
284 0         0 my $t = $test_name;
285 0 0       0 $t = "$sub(...)" unless defined $t;
286 0         0 my $ok = $Test->ok(0, $t);
287 0         0 $Test->diag(" First argument to $sub() must be a hash ref");
288 0         0 return $ok;
289             }
290              
291 14 50 33     145 if(! exists $spec->{name} or
      33        
292             ! defined $spec->{name} or
293             $spec->{name} =~ /^\s*$/) {
294 0         0 my $t = $test_name;
295 0 0       0 $t = "$sub(...)" unless defined $t;
296 0         0 my $ok = $Test->ok(0, $t);
297 0         0 $Test->diag(" $sub() called with no user name");
298 0         0 return $ok;
299             }
300              
301 14         40 return 1;
302             }
303              
304             1;
305              
306             =head1 EXAMPLES
307              
308             Verify that an account exists
309              
310             user_ok({ name => 'nik' }, "'nik' exists as a user");
311              
312             Verify that the account exists, that it has a given UID, and
313             that the home directory and shell match. Omit the test name,
314             rely on the default.
315              
316             user_ok({ name => 'nik', uid => 1001, dir => '/home/nik',
317             shell => '/bin/sh');
318              
319             Check that the home directory for 'nik' exists. Use an automatically
320             generated test name.
321              
322             homedir_ok({ name => 'nik' });
323              
324             Test that nik's home directory is owned by the 'nik' user, without
325             worrying what UID is assigned to that user.
326              
327             homedir_ok({ name => 'nik', owner => 'nik' });
328              
329             Ensure that nik's home directory is owned by uid 1000, and that
330             uid 1000 maps back to the 'nik' user
331              
332             homedir_ok({ name => 'nik', uid => 1000, owner => 'nik');
333              
334             Check the permissions on the home directory, and supply our own test
335             name.
336              
337             homedir_ok({ name => 'nik', perm => 0755 },
338             "Nik's home directory is correctly set");
339              
340             =head1 SEE ALSO
341              
342             Test::Simple, Test::Builder, User::pwent.
343              
344             =head1 AUTHOR
345              
346             Nik Clayton, C
347              
348             =head1 BUGS
349              
350             Please report any bugs or feature requests to
351             C, or through the web interface at
352             L.
353             I will be notified, and then you'll automatically be notified of progress on
354             your bug as I make changes.
355              
356             =head1 COPYRIGHT & LICENSE
357              
358             Copyright (c) 2005 Nik Clayton
359             All rights reserved.
360              
361             Redistribution and use in source and binary forms, with or without
362             modification, are permitted provided that the following conditions
363             are met:
364              
365             1. Redistributions of source code must retain the above copyright
366             notice, this list of conditions and the following disclaimer.
367             2. Redistributions in binary form must reproduce the above copyright
368             notice, this list of conditions and the following disclaimer in the
369             documentation and/or other materials provided with the distribution.
370              
371             THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
372             ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
373             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
374             ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
375             FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
376             DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
377             OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
378             HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
379             LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
380             OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
381             SUCH DAMAGE.
382              
383             =cut
384              
385             1; # End of Test::Unix::User