File Coverage

blib/lib/PixieLdap.pm
Criterion Covered Total %
statement 24 249 9.6
branch 0 120 0.0
condition 0 51 0.0
subroutine 8 24 33.3
pod 0 16 0.0
total 32 460 6.9


line stmt bran cond sub pod time code
1             package PixieLdap;
2              
3 1     1   24867 use 5.010000;
  1         4  
  1         83  
4 1     1   5 use strict;
  1         2  
  1         35  
5 1     1   5 use warnings;
  1         7  
  1         32  
6 1     1   885 use Net::LDAPS;
  1         220206  
  1         9  
7 1     1   139 use Net::LDAP;
  1         2  
  1         4  
8 1     1   10313 use Crypt::PasswdMD5;
  1         1564  
  1         166  
9 1     1   4791 use YAML;
  1         16195  
  1         3515  
10              
11             require Exporter;
12              
13             our @ISA = qw(Exporter);
14              
15             our %EXPORT_TAGS = ( 'all' => [ qw(viewSearch deleteMember deleteEntry addMember addGroup getMaxUID getMaxGID getInput viewBind addUser getGIDNumber changeUserPasswd
16             ) ] );
17              
18             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
19              
20             our @EXPORT = qw(viewSearch deleteMember deleteEntry addMember addGroup getMaxUID getMaxGID getInput viewBind addUser getGIDNumber changeUserPasswd
21             );
22              
23             our $VERSION = '0.01';
24              
25             my $scope = 'sub';
26              
27              
28             sub viewBind {
29 0     0 0   my $configfile = shift;
30 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
31 0           my $config = YAML::LoadFile($configfile);
32 0           my $conn;
33            
34 0 0         if ($config->{secure} == '1'){
35 0 0         $conn = Net::LDAPS->new($config->{server}, verify => 'none' ) or die "Unable to Connect: $@\n";
36             }
37             else {
38 0 0         $conn = Net::LDAP->new($config->{server} ) or die "Unable to Connect: $@\n";
39             }
40 0           my $message = $conn->bind($config->{user}->[1]->{dn}, password => $config->{user}->[1]->{password});
41              
42 0 0         if ( $message->code){
43 0           die 'Unable to bind: '. $message->error . "\n";
44             }
45            
46 0           return ($conn, $config->{basedn});
47             }
48              
49              
50             sub rootBind {
51 0     0 0   my $configfile = shift;
52 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
53 0           my $config = YAML::LoadFile($configfile);
54 0           my $conn;
55              
56 0 0         if ($config->{secure} == '1'){
57 0 0         $conn = Net::LDAPS->new($config->{server}, verify => 'none' ) or die "Unable to Connect: $@\n";
58             }
59             else {
60 0 0         $conn = Net::LDAP->new($config->{server}, verify => 'none' ) or die "Unable to Connect: $@\n";
61             }
62 0           my $message = $conn->bind($config->{user}->[0]->{dn}, password => $config->{user}->[0]->{password});
63              
64 0 0         if ( $message->code){
65 0           die 'Unable to bind: '. $message->error . "\n";
66             }
67            
68 0           return ($conn, $config->{basedn});
69             }
70              
71            
72             sub viewSearch {
73 0     0 0   my $configfile = shift;
74 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
75 0           my $filter = shift;
76 0 0         unless (defined $filter) { die "No Filter Specified\n";}
  0            
77 0           my $base = shift;
78 0 0         unless (defined $base) { die "No Group/User Base Specified\n";}
  0            
79            
80 0           my ($vconn, $basedn) = viewBind($configfile);
81 0           my $search = $vconn->search(base => $base.$basedn,
82             scope => $scope,
83             filter => $filter );
84 0 0         die "Bad Search: " . $search->error() if $search->code();
85              
86 0           $vconn->unbind;
87 0           return $search;
88             }
89              
90              
91             sub addMember{
92 0     0 0   my $configfile = shift;
93 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
94 0           my $group = shift;
95 0 0         unless (defined $group) { die "No Group Specified\n";}
  0            
96 0           my $uid = shift;
97 0 0         unless (defined $uid) { die "No UserID Specified\n";}
  0            
98              
99 0           my ($rconn, $basedn) = rootBind($configfile);
100 0           my $search = $rconn->search(base => "ou=group,".$basedn,
101             scope => $scope,
102             filter => "cn=".$group,
103             attrs => [''],
104             typesonly => 1 );
105 0 0         die "Error in Search: " . $search->error() if $search->code();
106            
107 0 0         if ($search){
108 0           my @entries = $search->entries;
109 0           for (@entries){
110 0           print "Adding " . $uid . " to " . $_->dn() ."\n";
111 0           my $modify = $rconn->modify($_->dn(), add => {'memberUid'=> $uid});
112 0 0         die 'Unable to modify, errorcode #' . $modify->error() if $modify->code();
113             }
114             }
115              
116 0           $rconn->unbind;
117 0           return;
118             }
119              
120              
121             sub addGroup{
122 0     0 0   my $configfile = shift;
123 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
124 0           my $group = shift;
125 0 0         unless (defined $group) { die "No Group Specified\n";}
  0            
126              
127 0           my $gidNumber = (&getMaxGID($configfile) + 1 );
128              
129 0           my ($rconn, $basedn) = rootBind($configfile);
130 0           my $dn = "cn=" . $group . ",ou=group," . $basedn;
131              
132 0           my $add = $rconn->add(
133             dn => $dn,
134             attr => [ 'cn' => $group,
135             'gidNumber' => $gidNumber,
136             'objectClass' => [qw( top posixGroup)]]
137             );
138 0 0         die 'Error in add: ' . $add->error()."\n" if $add->code();
139            
140 0           $rconn->unbind;
141            
142 0           my $answer='O';
143 0           my $member;
144             my $memberadd;
145 0           while ( lc $answer ne 'n' ) {
146 0           $answer = getInput("Would you like to add a user to the group Y/N? ");
147 0 0         if ( lc $answer eq 'y' ) {
148 0           $member = getInput("Enter Member UID :");
149 0           $memberadd = addMember($group, $member);
150             }
151             }
152              
153 0           return;
154             }
155              
156              
157             sub addUser {
158 0     0 0   my $configfile = shift;
159 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
160 0           my $uid = shift;
161 0 0         unless (defined $uid) { die "No UserID Specified\n";}
  0            
162 0           my $cli = shift;
163 0 0         unless (defined $cli) { die "No CLI Specified, 1 eq cli, 0 eq non interactive\n";}
  0            
164 0   0       my $cn = shift || getAddUserInput("Enter Users First Name: ", $cli);
165 0   0       my $sn = shift || getAddUserInput("Enter Users Surname Name: ", $cli);
166 0   0       my $shadowMax = shift || getAddUserInput("Enter Max Password Valid Days [90]: ", $cli);
167 0   0       my $loginShell = shift || getAddUserInput("Enter Required Login Shell [/bin/bash]: ", $cli);
168 0   0       my $homeDirectory = shift || getAddUserInput("Enter Users Home Directory: ", $cli);
169 0   0       my $group = shift || getAddUserInput("Enter Users Primary Group: ", $cli);
170 0   0       my $street = shift || getAddUserInput("Enter Users Street: ", $cli);
171 0   0       my $mail = shift || getAddUserInput("Enter Users Email Address: ", $cli);
172 0   0       my $o = shift || getAddUserInput("Enter Users Organisation: ", $cli);
173 0   0       my $ou = shift || getAddUserInput("Enter Users Department: ", $cli);
174 0   0       my $title = shift || getAddUserInput("Enter Users title: ", $cli);
175 0   0       my $mobile = shift || getAddUserInput("Enter Users Mobile Number: ", $cli);
176 0   0       my $telephoneNumber = shift || getAddUserInput("Enter Users Telephone Number: ", $cli);
177 0   0       my $facsimileTelephoneNumber = shift || getAddUserInput("Enter Users Faxcimile Telephone Number: ", $cli);
178 0   0       my $l = shift || getAddUserInput("Enter Users City: ", $cli);
179 0   0       my $st = shift || getAddUserInput("Enter Users State: ", $cli);
180 0   0       my $postalCode = shift || getAddUserInput("Enter Users Post Code: ", $cli);
181 0           my $givenName = $cn . " " . $sn;
182 0           my $gecos = $givenName . " " . $group;
183            
184 0 0         $shadowMax = '90' if ($shadowMax eq 'Unknown');
185 0 0         $loginShell = '/bin/bash' if ($loginShell eq 'Unknown');
186              
187 0           my ($passwd, $cryptPasswd) = genPasswd();
188 0           my $gidNumber = getGIDNumber($configfile, $group);
189            
190 0 0         unless (defined $gidNumber){
191 0           die "No Such Group\n";
192             }
193              
194 0           my $uidNumber = (&getMaxUID($configfile) + 1 );
195              
196 0           my ($rconn, $basedn) = rootBind($configfile);
197 0           my $dn = "uid=" . $uid . ",ou=people," . $basedn;
198              
199 0           my $add = $rconn->add(
200             dn => $dn,
201             attr => [ 'uid' => $uid,
202             'cn' => $cn,
203             'sn' => $sn,
204             'shadowMax' => $shadowMax,
205             'shadowWarning' => '7',
206             'shadowInactive' => '3',
207             'shadowLastChange' => today(),
208             'loginShell' => $loginShell,
209             'userPassword' => $cryptPasswd,
210             'uidNumber' => $uidNumber,
211             'homeDirectory' => $homeDirectory,
212             'street' => $street,
213             'gecos' => $gecos,
214             'mail' => $mail,
215             'o' => $o,
216             'ou' => $ou,
217             'title' => $title,
218             'mobile' => $mobile,
219             'telephoneNumber' => $telephoneNumber,
220             'facsimileTelephoneNumber' => $facsimileTelephoneNumber,
221             'givenName' => $cn,
222             'l' => $l,
223             'st' => $st,
224             'postalCode' => $postalCode,
225             'gidNumber' => $gidNumber,
226             'objectClass' => [qw( top posixAccount inetOrgPerson organizationalPerson shadowAccount)]]
227             );
228 0 0         die 'Error in add: ' . $add->error()."\n" if $add->code();
229            
230 0           $rconn->unbind;
231 0           return ($givenName, $passwd);
232             }
233              
234              
235             sub deleteMember{
236 0     0 0   my $configfile = shift;
237 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
238 0           my $filter = shift;
239 0 0         unless (defined $filter) { die "No Filter Specified\n";}
  0            
240 0           my $base = shift;
241 0 0         unless (defined $base) { die "No base Group or People Specified\n";}
  0            
242 0           my $uid = shift;
243 0 0         unless (defined $uid) { die "No UserID Specified\n";}
  0            
244              
245 0           my ($rconn, $basedn) = rootBind($configfile);
246 0           my $search = $rconn->search(base => $base.$basedn,
247             scope => $scope,
248             filter => $filter,
249             attrs => [''],
250             typesonly => 1 );
251 0 0         die "Error in Search: " . $search->error() if $search->code();
252            
253 0 0         if ($search){
254 0           my @entries = $search->entries;
255 0           for (@entries){
256 0           print "Removing " . $uid . " from " . $_->dn() ."\n";
257 0           my $delete = $rconn->modify($_->dn(), delete => {'memberUid'=> $uid});
258 0 0         die 'Unable to modify, errorcode #' . $delete->error() if $delete->code();
259             }
260             }
261              
262 0           $rconn->unbind;
263 0           return;
264             }
265              
266              
267             sub deleteEntry{
268 0     0 0   my $configfile = shift;
269 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
270 0           my $filter = shift;
271 0 0         unless (defined $filter) { die "No Filter Specified\n";}
  0            
272 0           my $base = shift;
273 0 0         unless (defined $base) { die "No base Group or People Specified\n";}
  0            
274              
275 0           my ($rconn, $basedn) = rootBind($configfile);
276 0           my $dn=$filter.",".$base.$basedn;
277 0           print "Deleting ".$dn."\n";
278 0           my $delete = $rconn->delete($dn);
279 0 0         die 'Error in delete: ' . $delete->error() . "\n" if $delete->code();
280              
281 0           $rconn->unbind;
282 0           return;
283             }
284              
285              
286             sub changeUserPasswd{
287 0     0 0   my $configfile = shift;
288 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
289 0           my $uid = shift;
290 0 0         unless (defined $uid) { die "No UserID Specified\n";}
  0            
291              
292 0           my ($passwd, $cryptPasswd) = genPasswd();
293              
294 0           my ($rconn, $basedn) = rootBind($configfile);
295 0           my $dn = "uid=" . $uid . ",ou=people," . $basedn;
296              
297 0           my $modify = $rconn->modify(
298             dn => $dn,
299             replace => [ 'shadowLastChange' => today(),
300             'userPassword' => $cryptPasswd,]
301             );
302 0 0         die 'Error in Password Change: ' . $modify->error()."\n" if $modify->code();
303              
304 0           $rconn->unbind;
305 0           return $passwd;
306             }
307              
308              
309             sub getMaxUID {
310 0     0 0   my $configfile = shift;
311 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
312              
313 0           my ($vconn, $basedn) = viewBind($configfile);
314 0           my $uids = $vconn->search(base => "ou=people,".$basedn,
315             scope => $scope,
316             filter => "uidNumber=*",
317             attrs => [ 'uidNumber' ]
318             );
319 0 0         die "Bad Search: " . $uids->error() if $uids->code();
320              
321 0 0         return unless $uids->count;
322              
323 0           my ($highest) = sort {$b <=> $a} grep $_ ne 65534, map $_->get_value('uidNumber'), $uids->all_entries;
  0            
324 0 0         die "Couldn't find new id" unless ($highest);
325              
326 0           return $highest;
327             }
328              
329              
330             sub getMaxGID {
331 0     0 0   my $configfile = shift;
332 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
333              
334 0           my ($vconn, $basedn) = viewBind($configfile);
335 0           my $gids = $vconn->search(base => "ou=group,".$basedn,
336             scope => $scope,
337             filter => "gidNumber=*",
338             attrs => [ 'gidNumber' ]
339             );
340 0 0         die "Bad Search: " . $gids->error() if $gids->code();
341              
342 0 0         return unless $gids->count;
343              
344 0           my ($highest) = sort {$b <=> $a} grep $_ ne 65534, map $_->get_value('gidNumber'), $gids->all_entries;
  0            
345 0 0         die "Couldn't find new id" unless ($highest);
346              
347 0           return $highest;
348             }
349              
350              
351             sub getGIDNumber{
352 0     0 0   my $configfile = shift;
353 0 0         unless (defined $configfile) { die "No Config File Specified\n";}
  0            
354 0           my $group = shift;
355 0 0         unless (defined $group) { die "No Group Specified\n";}
  0            
356              
357 0           my $search = viewSearch( $configfile, "cn=".$group, 'ou=group,' );
358 0           my $entry;
359 0 0         return ($entry->get_value('gidNumber')) if defined ($entry = $search->entry('0'));
360              
361 0           return;
362             }
363              
364              
365             sub getInput {
366 0     0 0   my $question = shift;
367 0           print $question;
368 0           my $answer = ;
369 0           chomp ($answer);
370 0           return $answer;
371             }
372              
373              
374             sub getAddUserInput {
375 0     0 0   my $question = shift;
376 0 0         unless (defined $question) { die "No Question Specified\n";}
  0            
377 0           my $cli = shift;
378 0 0         unless (defined $cli) { die "No CLI Specified, 1 eq cli, 0 eq non interactive\n";}
  0            
379 0 0         return 'Unknown' if ($cli != '1' );
380 0           my $answer = getInput($question);
381 0 0         $answer = 'Unknown' if (!$answer);
382 0           return $answer;
383             }
384              
385              
386             sub today {
387 0     0 0   my $sdt = time();
388 0           return int($sdt / (60 * 60 * 24));
389             }
390              
391              
392             sub genPasswd {
393 1     1   15 no strict "subs";
  1         2  
  1         262  
394 0     0 0   my $passwd = join '',map{((A..Z),(0..9))[int(rand(36))]}(0..7);
  0            
395 0           my $salt = join '', (qw#. /#,(0..9),('A'..'Z'),('a'..'z'))[map rand(64), (1..8)];
396 0           my $cryptPasswd = "{crypt}".unix_md5_crypt($passwd, $salt);
397 0           return ($passwd, $cryptPasswd);
398             }
399              
400             # Preloaded methods go here.
401              
402             1;
403             __END__