Project

General

Profile

« Previous | Next » 

Revision 2341

Added by sgarg about 20 years ago

Integrating ldapweb.cgi & create-ldap-account.pl into metacat CVS tree.

View differences:

src/perl/ldapweb.cgi
1
#!/usr/bin/perl -w
2
 #
3
 #  '$RCSfile$'
4
 #  Copyright: 2001 Regents of the University of California 
5
 #
6
 #   '$Author$'
7
 #     '$Date$'
8
 # '$Revision$' 
9
 # 
10
 # This program is free software; you can redistribute it and/or modify
11
 # it under the terms of the GNU General Public License as published by
12
 # the Free Software Foundation; either version 2 of the License, or
13
 # (at your option) any later version.
14
 #
15
 # This program is distributed in the hope that it will be useful,
16
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 # GNU General Public License for more details.
19
 #
20
 # You should have received a copy of the GNU General Public License
21
 # along with this program; if not, write to the Free Software
22
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 #
24

  
25
#
26
# This is a web-based application for allowing users to register a new
27
# account for Metacat access.  We currently only support LDAP even
28
# though metacat could potentially support other types of directories.
29
#
30
use strict;       # turn on strict syntax checking.
31
use Template;     # load the template-toolkit module.
32
use CGI;          # load the CGI module. 
33
use Net::LDAP;    # load the LDAP net libraries
34
use Net::SMTP;    # load the SMTP net libraries
35
use Digest::SHA1; # for creating the password hash
36
use MIME::Base64; # for creating the password hash
37
use URI;          # for parsing URL syntax
38
use AppConfig qw(:expand :argcount);
39

  
40
# Set up our default configuration
41
my $ldapurl = "@ldapurl@";
42
my $root = "@user@";
43
my $rootpw = "@password@";
44
my $searchBase = "@ldapSearchBase@";
45
my $templatesDir = "@templates.dir@";
46
my $mailhost = "@mailhost@";
47
my $sender = "@sender@";
48

  
49
# Get the CGI input variables
50
my $query = new CGI;
51

  
52
my $debug = 0;
53

  
54
#--------------------------------------------------------------------------80c->
55
# Set up the Template Toolkit to read html form templates
56

  
57

  
58
# set some configuration options for the template object
59
my $config_templates = {
60
             INCLUDE_PATH => $templatesDir, 
61
             INTERPOLATE  => 0,                    
62
             POST_CHOMP   => 1,                   
63
             };
64

  
65
# create an instance of the template
66
my $template = Template->new($config_templates) || die $Template::ERROR, "\n";
67

  
68
# Read the ldapweb.cfg file
69
my $config = AppConfig->new({ 
70
    GLOBAL => { ARGCOUNT => ARGCOUNT_ONE, } });
71

  
72
$config->define("ldapurl", { ARGCOUNT => ARGCOUNT_HASH} );           
73
$config->define("ldapsearchbase", { ARGCOUNT => ARGCOUNT_HASH} );
74
$config->define("dn", { ARGCOUNT => ARGCOUNT_HASH} );
75
$config->define("filter", { ARGCOUNT => ARGCOUNT_HASH} );
76
$config->define("user", { ARGCOUNT => ARGCOUNT_HASH} );
77
$config->define("password", { ARGCOUNT => ARGCOUNT_HASH} );
78

  
79
my $cfgfile = "ldapweb.cfg";
80
$config->file($cfgfile);
81
my $config_ldapurl = $config->get('ldapurl');
82
my $config_ldapsearchbase = $config->get('ldapsearchbase');
83
my $config_dn = $config->get('dn');
84
my $config_filter = $config->get('filter');
85
my $config_user = $config->get('user');
86
my $config_password = $config->get('password');
87

  
88
my @orglist;
89
foreach my $neworg (keys %$config_dn) {
90
    push(@orglist, $neworg);
91
    debug($neworg);
92
}
93

  
94

  
95
#--------------------------------------------------------------------------80c->
96
# Define the main program logic that calls subroutines to do the work
97
#--------------------------------------------------------------------------80c->
98

  
99

  
100
# The processing step we are handling
101
my $stage = $query->param('stage') || '@defaultStage@';
102

  
103
my $cfg = $query->param('cfg');
104

  
105
# define the possible stages
106
my %stages = (
107
              'initregister'      => \&handleInitRegister,
108
              'register'          => \&handleRegister,
109
              'registerconfirmed' => \&handleRegisterConfirmed,
110
              'changepass'        => \&handleChangePassword,
111
              'simplesearch'      => \&handleSimpleSearch,
112
              'initaddentry'      => \&handleInitAddEntry,
113
              'addentry'          => \&handleAddEntry,
114
              'initmodifyentry'   => \&handleInitModifyEntry,
115
              'modifyentry'       => \&handleModifyEntry,
116
              'resetpass'         => \&handleResetPassword,
117
             );
118

  
119
# call the appropriate routine based on the stage
120
if ( $stages{$stage} ) {
121
  $stages{$stage}->();
122
} else {
123
  &handleResponseMessage();
124
}
125

  
126
#--------------------------------------------------------------------------80c->
127
# Define the subroutines to do the work
128
#--------------------------------------------------------------------------80c->
129

  
130

  
131
#
132
# create the initial registration form 
133
#
134
sub handleInitRegister {
135
  my $vars = shift;
136

  
137
  print "Content-type: text/html\n\n";
138
  # process the template files:
139
  my $templateVars = { stage => "register", cfg => $cfg };
140
  $$templateVars{'orgList'} = \@orglist;
141
  $template->process( "@defaultHeader@", $templateVars);
142
  $template->process( "@register@", $templateVars);
143
  $template->process( "@defaultFooter@", $templateVars);
144

  
145
  exit();
146
}
147

  
148
#
149
# process input from the register stage, which occurs when
150
# a user submits form data to create a new account
151
#
152
sub handleRegister {
153
    
154
    print "Content-type: text/html\n\n";
155

  
156
    my $allParams = { 'givenName' => $query->param('givenName'), 
157
                      'sn' => $query->param('sn'),
158
                      'o' => $query->param('o'), 
159
                      'mail' => $query->param('mail'), 
160
                      'uid' => $query->param('uid'), 
161
                      'userPassword' => $query->param('userPassword'), 
162
                      'userPassword2' => $query->param('userPassword2'), 
163
                      'title' => $query->param('title'), 
164
                      'telephoneNumber' => $query->param('telephoneNumber') };
165
    # Check that all required fields are provided and not null
166
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail', 
167
                           'uid', 'userPassword', 'userPassword2');
168
    if (! paramsAreValid(@requiredParams)) {
169
        my $errorMessage = "Required information is missing. " .
170
            "Please fill in all required fields and resubmit the form.";
171
        my $templateVars = { stage => "register",
172
                             cfg => $cfg,
173
                             allParams => $allParams,
174
                             errorMessage => $errorMessage };
175
	$$templateVars{'orgList'} = \@orglist;
176
        $template->process( "@defaultHeader@", $templateVars);
177
        $template->process( "@register@", $templateVars);
178
        $template->process( "@defaultFooter@", $templateVars);
179
        exit(0);
180
    } else {
181
	my $o = $query->param('o');    
182
	$ldapurl = $config_ldapurl->{$o};
183
	$searchBase = $config_ldapsearchbase->{$o};  
184
    }
185

  
186
    # Search LDAP for matching entries that already exist
187
    # Some forms use a single text search box, whereas others search per
188
    # attribute.
189
    my $filter;
190
    if ($query->param('searchField')) {
191

  
192
      $filter = "(|" . 
193
                "(uid=" . $query->param('searchField') . ") " .
194
                "(mail=" . $query->param('searchField') . ")" .
195
                "(&(sn=" . $query->param('searchField') . ") " . 
196
                "(givenName=" . $query->param('searchField') . "))" . 
197
                ")";
198
    } else {
199
      $filter = "(|" . 
200
                "(uid=" . $query->param('uid') . ") " .
201
                "(mail=" . $query->param('mail') . ")" .
202
                "(&(sn=" . $query->param('sn') . ") " . 
203
                "(givenName=" . $query->param('givenName') . "))" . 
204
                ")";
205
    }
206

  
207
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
208

  
209
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
210

  
211
    # If entries match, send back a request to confirm new-user creation
212
    if ($found) {
213
      my $templateVars = { stage => "registerconfirmed",
214
                           cfg => $cfg,
215
                           allParams => $allParams,
216
                           foundAccounts => $found };
217
      $$templateVars{'orgList'} = \@orglist;
218
      $template->process( "@defaultHeader@", $templateVars);
219
      $template->process( "@registerMatch@", $templateVars);
220
      $template->process( "@register@", $templateVars);
221
      $template->process( "@defaultFooter@", $templateVars);
222

  
223
    # Otherwise, create a new user in the LDAP directory
224
    } else {
225
        createAccount($allParams);
226
    }
227

  
228
    exit();
229
}
230

  
231
#
232
# process input from the registerconfirmed stage, which occurs when
233
# a user chooses to create an account despite similarities to other
234
# existing accounts
235
#
236
sub handleRegisterConfirmed {
237
  
238
    my $allParams = { 'givenName' => $query->param('givenName'), 
239
                      'sn' => $query->param('sn'),
240
                      'o' => $query->param('o'), 
241
                      'mail' => $query->param('mail'), 
242
                      'uid' => $query->param('uid'), 
243
                      'userPassword' => $query->param('userPassword'), 
244
                      'userPassword2' => $query->param('userPassword2'), 
245
                      'title' => $query->param('title'), 
246
                      'telephoneNumber' => $query->param('telephoneNumber') };
247
    print "Content-type: text/html\n\n";
248
    createAccount($allParams);
249

  
250
    exit();
251
}
252

  
253
#
254
# change a user's password upon request
255
#
256
sub handleChangePassword {
257

  
258
    print "Content-type: text/html\n\n";
259

  
260
    my $allParams = { 'test' => "1", };
261
    if ($query->param('uid')) {
262
        $$allParams{'uid'} = $query->param('uid');
263
    }
264
    if ($query->param('o')) {
265
        $$allParams{'o'} = $query->param('o');
266
	my $o = $query->param('o');
267
	
268
	$ldapurl = $config_ldapurl->{$o};
269
        $searchBase = $config_ldapsearchbase->{$o};
270
    }
271

  
272

  
273
    # Check that all required fields are provided and not null
274
    my @requiredParams = ( 'uid', 'o', 'oldpass', 
275
                           'userPassword', 'userPassword2');
276
    if (! paramsAreValid(@requiredParams)) {
277
        my $errorMessage = "Required information is missing. " .
278
            "Please fill in all required fields and submit the form.";
279
        my $templateVars = { stage => "changepass",
280
                             cfg => $cfg,
281
                             allParams => $allParams,
282
                             errorMessage => $errorMessage };
283
	$$templateVars{'orgList'} = \@orglist;
284
        $template->process( "@defaultHeader@", $templateVars);
285
        $template->process( "@defaultChangePass@", $templateVars);
286
        $template->process( "@defaultFooter@", $templateVars);
287
        exit(0);
288
    }
289

  
290
    # We have all of the info we need, so try to change the password
291
    if ($query->param('userPassword') =~ $query->param('userPassword2')) {
292

  
293
	my $o = $query->param('o');
294
	$ldapurl = $config_ldapurl->{$o};
295
        $searchBase = $config_ldapsearchbase->{$o};
296
	$root = $config_user->{$o};
297
	$rootpw = $config_password->{$o};
298

  
299
        my $dn = "uid=" . $query->param('uid') . "," . $config_dn->{$o};;
300
        if ($query->param('o') =~ "LTER") {
301
            $template->process( "@defaultHeader@");
302
            $template->process( "@registerLter@");
303
            $template->process( "@defaultFooter@");
304
        } else {
305
            my $errorMessage = changePassword(
306
                    $dn, $query->param('userPassword'), 
307
                    $dn, $query->param('oldpass'), $query->param('o'));
308
            if ($errorMessage) {	
309
                my $templateVars = { stage => "changepass",
310
                                     cfg => $cfg,
311
                                     allParams => $allParams,
312
                                     errorMessage => $errorMessage };
313
		$$templateVars{'orgList'} = \@orglist;
314
                $template->process( "@defaultHeader@", $templateVars);
315
                $template->process( "@defaultChangePass@", $templateVars);
316
                $template->process( "@defaultFooter@", $templateVars);
317
                exit(0);
318
            } else {
319
                $template->process( "@defaultHeader@");
320
                $template->process( "@changePassSuccess@");
321
                $template->process( "@defaultFooter@");
322
                exit(0);
323
            }
324
        }
325
    } else {
326
        my $errorMessage = "The passwords do not match. Try again.";
327
        my $templateVars = { stage => "changepass",
328
                             cfg => $cfg,
329
                             allParams => $allParams,
330
                             errorMessage => $errorMessage };
331
	$$templateVars{'orgList'} = \@orglist;
332
        $template->process( "@defaultHeader@", $templateVars);
333
        $template->process( "@defaultChangePass@", $templateVars);
334
        $template->process( "@defaultFooter@", $templateVars);
335
        exit(0);
336
    }
337
}
338

  
339
#
340
# reset a user's password upon request
341
#
342
sub handleResetPassword {
343

  
344
    print "Content-type: text/html\n\n";
345

  
346
    my $allParams = { 'test' => "1", };
347
    if ($query->param('uid')) {
348
        $$allParams{'uid'} = $query->param('uid');
349
    }
350
    if ($query->param('o')) {
351
        $$allParams{'o'} = $query->param('o');
352
	my $o = $query->param('o');
353
	
354
	$ldapurl = $config_ldapurl->{$o};
355
        $searchBase = $config_ldapsearchbase->{$o};
356
	$root = $config_user->{$o};
357
	$rootpw = $config_password->{$o};
358
    }
359

  
360
    # Check that all required fields are provided and not null
361
    my @requiredParams = ( 'uid', 'o' );
362
    if (! paramsAreValid(@requiredParams)) {
363
        my $errorMessage = "Required information is missing. " .
364
            "Please fill in all required fields and submit the form.";
365
        my $templateVars = { stage => "resetpass",
366
                             cfg => $cfg,
367
                             allParams => $allParams,
368
                             errorMessage => $errorMessage };
369
	$$templateVars{'orgList'} = \@orglist;
370
        $template->process( "@defaultHeader@", $templateVars);
371
        $template->process( "@defaultResetPass@", $templateVars);
372
        $template->process( "@defaultFooter@", $templateVars);
373
        exit(0);
374
    }
375

  
376
    # We have all of the info we need, so try to change the password
377
    my $o = $query->param('o');
378
    my $dn = "uid=" . $query->param('uid') . "," . $config_dn->{$o};
379
    if ($query->param('o') =~ "LTER") {
380
        $template->process( "@defaultHeader@");
381
        $template->process( "@registerLter@");
382
        $template->process( "@defaultFooter@");
383
        exit(0);
384
    } else {
385
        my $errorMessage = "";
386
        my $recipient;
387
        my $userPass;
388
        my $entry = getLdapEntry($ldapurl, $searchBase, 
389
                $query->param('uid'), $query->param('o'));
390

  
391
        if ($entry) {
392
            $recipient = $entry->get_value('mail');
393
            $userPass = getRandomPassword();
394
            $errorMessage = changePassword($dn, $userPass, $root, $rootpw, $query->param('o'));
395
        } else {
396
            $errorMessage = "User not found in database.  Please try again.";
397
        }
398

  
399
        if ($errorMessage) {
400
            my $templateVars = { stage => "resetpass",
401
                                 cfg => $cfg,
402
                                 allParams => $allParams,
403
                                 errorMessage => $errorMessage };
404
	    $$templateVars{'orgList'} = \@orglist;
405
            $template->process( "@defaultHeader@", $templateVars);
406
            $template->process( "@defaultResetPass@", $templateVars);
407
            $template->process( "@defaultFooter@", $templateVars);
408
            exit(0);
409
        } else {
410
            my $errorMessage = sendPasswordNotification($query->param('uid'),
411
                    $query->param('o'), $userPass, $recipient);
412
            my $templateVars = { stage => "resetpass",
413
                                 cfg => $cfg,
414
                                 allParams => $allParams,
415
                                 errorMessage => $errorMessage };
416
	    $$templateVars{'orgList'} = \@orglist;
417
            $template->process( "@defaultHeader@", $templateVars);
418
            $template->process( "@resetPassSuccess@", $templateVars);
419
            $template->process( "@defaultFooter@", $templateVars);
420
            exit(0);
421
        }
422
    }
423
}
424

  
425
#
426
# Construct a random string to use for a newly reset password
427
#
428
sub getRandomPassword {
429
    my $length = shift;
430
    if (!$length) {
431
        $length = 8;
432
    }
433
    my $newPass = "";
434

  
435
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
436
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
437
    return $newPass;
438
}
439

  
440
#
441
# Change a password to a new value, binding as the provided user
442
#
443
sub changePassword {
444
    my $userDN = shift;
445
    my $userPass = shift;
446
    my $bindDN = shift;
447
    my $bindPass = shift;
448
    my $o = shift;
449

  
450
    my $ldapurl = $config_ldapurl->{$o};
451
    my $searchBase = $config_ldapsearchbase->{$o};
452
    
453
    my $errorMessage = 0;
454
    my $ldap = Net::LDAP->new($ldapurl) or die "$@"; 
455
    my $bindresult = $ldap->bind( version => 3, dn => $bindDN, 
456
                                  password => $bindPass );
457
    if ($bindresult->code) {
458
        $errorMessage = "Failed to log in. Are you sure your old " .
459
                        "password is correct? Try again...";
460
        return $errorMessage;
461
    }
462

  
463
    # Find the user here and change their entry
464
    my $newpass = createSeededPassHash($userPass);
465
    my $modifications = { userPassword => $newpass };
466
    my $result = $ldap->modify( $userDN, replace => { %$modifications });
467
    
468
    if ($result->code()) {
469
        my $errorMessage = "There was an error changing the password." .
470
                           "<br />\n" . $result->error;
471
    } 
472
    $ldap->unbind;   # take down session
473

  
474
    return $errorMessage;
475
}
476

  
477
#
478
# generate a Seeded SHA1 hash of a plaintext password
479
#
480
sub createSeededPassHash {
481
    my $secret = shift;
482

  
483
    my $salt = "";
484
    for (my $i=0; $i < 4; $i++) {
485
        $salt .= int(rand(10));
486
    }
487

  
488
    my $ctx = Digest::SHA1->new;
489
    $ctx->add($secret);
490
    $ctx->add($salt);
491
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
492

  
493
    return $hashedPasswd;
494
}
495

  
496
#
497
# Look up an ldap entry for a user
498
#
499
sub getLdapEntry {
500
    my $ldapurl = shift;
501
    my $base = shift;
502
    my $username = shift;
503
    my $org = shift;
504

  
505
    my $entry = "";
506
    my $mesg;
507
    my $ldap = Net::LDAP->new($ldapurl) or die "$@";
508
    my $bindresult = $ldap->bind;
509
    if ($bindresult->code) {
510
        return $entry;
511
    }
512

  
513
    if($config_filter->{$org}){
514
	$mesg = $ldap->search ( base   => $base,
515
				   filter => "(&(uid=$username)($config_filter->{$org}))"
516
				   );
517
    } else {
518
	$mesg = $ldap->search ( base   => $base,
519
				   filter => "(&(uid=$username))"
520
				   );
521
    }
522

  
523
    if ($mesg->count > 0) {
524
        $entry = $mesg->pop_entry;
525
        $ldap->unbind;   # take down session
526
    } else {
527
        $ldap->unbind;   # take down session
528
        # Follow references by recursive call to self
529
        my @references = $mesg->references();
530
        for (my $i = 0; $i <= $#references; $i++) {
531
            my $uri = URI->new($references[$i]);
532
            my $host = $uri->host();
533
            my $path = $uri->path();
534
            $path =~ s/^\///;
535
            $entry = &getLdapEntry($host, $path, $username, $org);
536
            if ($entry) {
537
                return $entry;
538
            }
539
        }
540
    }
541
    return $entry;
542
}
543

  
544
# 
545
# send an email message notifying the user of the pw change
546
#
547
sub sendPasswordNotification {
548
    my $username = shift;
549
    my $org = shift;
550
    my $newPass = shift;
551
    my $recipient = shift;
552

  
553
    my $errorMessage = "";
554
    if ($recipient) {
555
        # Send the email message to them
556
        my $smtp = Net::SMTP->new($mailhost);
557
        $smtp->mail($sender);
558
        $smtp->to($recipient);
559

  
560
        my $message = <<"        ENDOFMESSAGE";
561
        To: $recipient
562
        From: $sender
563
        Subject: KNB Password Reset
564
        
565
        Somebody (hopefully you) requested that your KNB password be reset.  
566
        This is generally done when somebody forgets their password.  Your 
567
        password can be changed by visiting the following URL:
568

  
569
        @cgiurl@?stage=changepass
570

  
571
            Username: $username
572
        Organization: $org
573
        New Password: $newPass
574

  
575
        Thanks,
576
            The KNB Development Team
577
    
578
        ENDOFMESSAGE
579
        $message =~ s/^[ \t\r\f]+//gm;
580
    
581
        $smtp->data($message);
582
        $smtp->quit;
583
    } else {
584
        $errorMessage = "Failed to send password because I " .
585
                        "couldn't find a valid email address.";
586
    }
587
    return $errorMessage;
588
}
589

  
590
#
591
# search the LDAP directory to see if a similar account already exists
592
#
593
sub findExistingAccounts {
594
    my $ldapurl = shift;
595
    my $base = shift;
596
    my $filter = shift;
597
    my $attref = shift;
598

  
599
    my $foundAccounts = 0;
600

  
601
    my $ldap = Net::LDAP->new($ldapurl) or die "$@";
602
    $ldap->bind( version => 3, anonymous => 1);
603
    my $mesg = $ldap->search (
604
        base   => $base,
605
        filter => $filter,
606
        attrs => @$attref,
607
    );
608

  
609
    if ($mesg->count() > 0) {
610
        $foundAccounts = "";
611
        my $entry;
612
        foreach $entry ($mesg->all_entries) { 
613
            $foundAccounts .= "<p>\n<b><u>Account:</u> ";
614
            $foundAccounts .= $entry->dn();
615
            $foundAccounts .= "</b><br />\n";
616
            foreach my $attribute ($entry->attributes()) {
617
                $foundAccounts .= "$attribute: ";
618
                $foundAccounts .= $entry->get_value($attribute);
619
                $foundAccounts .= "<br />\n";
620
            }
621
            $foundAccounts .= "</p>\n";
622
        }
623
    }
624
    $ldap->unbind;   # take down session
625

  
626
    # Follow references
627
    my @references = $mesg->references();
628
    for (my $i = 0; $i <= $#references; $i++) {
629
        my $uri = URI->new($references[$i]);
630
        my $host = $uri->host();
631
        my $path = $uri->path();
632
        $path =~ s/^\///;
633
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
634
        if ($refFound) {
635
            $foundAccounts .= $refFound;
636
        }
637
    }
638

  
639
    #print "<p>Checking referrals...</p>\n";
640
    #my @referrals = $mesg->referrals();
641
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
642
    #for (my $i = 0; $i <= $#referrals; $i++) {
643
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
644
    #}
645

  
646
    return $foundAccounts;
647
}
648

  
649
#
650
# Validate that we have the proper set of input parameters
651
#
652
sub paramsAreValid {
653
    my @pnames = @_;
654

  
655
    my $allValid = 1;
656
    foreach my $parameter (@pnames) {
657
        if (!defined($query->param($parameter)) || 
658
            ! $query->param($parameter) ||
659
            $query->param($parameter) =~ /^\s+$/) {
660
            $allValid = 0;
661
        }
662
    }
663

  
664
    return $allValid;
665
}
666

  
667
#
668
# Bind to LDAP and create a new account using the information provided
669
# by the user
670
#
671
sub createAccount {
672
    my $allParams = shift;
673

  
674
    if ($query->param('o') =~ "LTER") {
675
        $template->process( "@defaultHeader@");
676
        $template->process( "@registerLter@");
677
        $template->process( "@defaultFooter@");
678
    } else {
679

  
680
        # Be sure the passwords match
681
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
682
            my $errorMessage = "The passwords do not match. Try again.";
683
            my $templateVars = { stage => "register",
684
                                 cfg => $cfg,
685
                                 allParams => $allParams,
686
                                 errorMessage => $errorMessage };
687
	    $$templateVars{'orgList'} = \@orglist;
688
            $template->process( "@defaultHeader@", $templateVars);
689
            $template->process( "@register@", $templateVars);
690
            $template->process( "@defaultFooter@", $templateVars);
691
            exit(0);
692
        }
693

  
694
	my $o = $query->param('o');
695

  
696
	my $ldapurl = $config_ldapurl->{$o};
697
	my $root = $config_user->{$o};
698
	my $rootpw = $config_password->{$o};
699
	my $searchBase = $config_ldapsearchbase->{$o};
700
	my $dnBase = $config_dn->{$o};
701

  
702
        my $ldap = Net::LDAP->new($ldapurl) or die "$@";
703
        $ldap->bind( version => 3, dn => $root, password => $rootpw );
704
        #print "Inserting new entry...\n";
705
        my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
706

  
707
        # Create a hashed version of the password
708
        my $shapass = createSeededPassHash($query->param('userPassword'));
709

  
710
        # Do the insertion
711
        my $additions = [ 
712
                'uid'   => $query->param('uid'),
713
                'o'   => $query->param('o'),
714
                'cn'   => join(" ", $query->param('givenName'), 
715
                                    $query->param('sn')),
716
                'sn'   => $query->param('sn'),
717
                'givenName'   => $query->param('givenName'),
718
                'mail' => $query->param('mail'),
719
                'userPassword' => $shapass,
720
                'objectclass' => ['top', 'person', 'organizationalPerson', 
721
                                'inetOrgPerson', 'uidObject' ]
722
            ];
723
        if (defined($query->param('telephoneNumber')) && 
724
            $query->param('telephoneNumber') &&
725
            ! $query->param('telephoneNumber') =~ /^\s+$/) {
726
            $$additions[$#$additions + 1] = 'telephoneNumber';
727
            $$additions[$#$additions + 1] = $query->param('telephoneNumber');
728
        }
729
        if (defined($query->param('title')) && 
730
            $query->param('title') &&
731
            ! $query->param('title') =~ /^\s+$/) {
732
            $$additions[$#$additions + 1] = 'title';
733
            $$additions[$#$additions + 1] = $query->param('title');
734
        }
735
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
736
    
737
        if ($result->code()) {
738
            my $templateVars = { stage => "register",
739
                                 cfg => $cfg,
740
                                 allParams => $allParams,
741
                                 errorMessage => $result->error };
742
	    $$templateVars{'orgList'} = \@orglist;
743
            $template->process( "@defaultHeader@");
744
            $template->process( "@registerFailed@", $templateVars);
745
            $templateVars    = { stage => "register",
746
                                 cfg => $cfg,
747
                                 allParams => $allParams };
748
	    $$templateVars{'orgList'} = \@orglist;
749
            $template->process( "@register@", $templateVars);
750
            $template->process( "@defaultFooter@");
751
        } else {
752
            $template->process( "@defaultHeader@");
753
            $template->process( "@registerSuccess@");
754
            $template->process( "@defaultFooter@");
755
        }
756

  
757
        $ldap->unbind;   # take down session
758
    }
759
}
760

  
761
sub handleResponseMessage {
762

  
763
  print "Content-type: text/html\n\n";
764
  my $errorMessage = "You provided invalid input to the script. " .
765
                     "Try again please.";
766
  my $templateVars = { stage => "@defaultStage@",
767
                       cfg => $cfg,
768
                       errorMessage => $errorMessage };
769
  $$templateVars{'orgList'} = \@orglist;
770
  $template->process( "@defaultHeader@", $templateVars);
771
  $template->process( "@defaultFooter@", $templateVars);
772
  exit(0);
773
}
774

  
775
#
776
# perform a simple search against the LDAP database using 
777
# a small subset of attributes of each dn and return it
778
# as a table to the calling browser.
779
#
780
sub handleSimpleSearch {
781

  
782
    my $o = $query->param('o');
783

  
784
    my $ldapurl = $config_ldapurl->{$o};
785
    my $searchBase = $config_ldapsearchbase->{$o};
786

  
787
    print "Content-type: text/html\n\n";
788

  
789
    my $allParams = { 
790
                      'cn' => $query->param('cn'),
791
                      'sn' => $query->param('sn'),
792
                      'gn' => $query->param('gn'),
793
                      'o'  => $query->param('o'),
794
                      'facsimiletelephonenumber' 
795
                      => $query->param('facsimiletelephonenumber'),
796
                      'mail' => $query->param('cmail'),
797
                      'telephonenumber' => $query->param('telephonenumber'),
798
                      'title' => $query->param('title'),
799
                      'uid' => $query->param('uid'),
800
                      'ou' => $query->param('ou'),
801
                    };
802

  
803
    # Search LDAP for matching entries that already exist
804
    my $filter = "(" . 
805
                 $query->param('searchField') . "=" .
806
                 "*" .
807
                 $query->param('searchValue') .
808
                 "*" .
809
                 ")";
810

  
811
    my @attrs = [ 'sn', 
812
                  'gn', 
813
                  'cn', 
814
                  'o', 
815
                  'facsimiletelephonenumber', 
816
                  'mail', 
817
                  'telephoneNumber', 
818
                  'title', 
819
                  'uid', 
820
                  'labeledURI', 
821
                  'ou' ];
822

  
823
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
824

  
825
    # Send back the search results
826
    if ($found) {
827
      my $templateVars = { stage => "searchResults",
828
                           cfg => $cfg,
829
                           allParams => $allParams,
830
                           foundAccounts => $found };
831
      $$templateVars{'orgList'} = \@orglist;
832
      $template->process( "@defaultHeader@", $templateVars);
833
      $template->process( "@searchResults@", $templateVars);
834
      $template->process( "@defaultFooter@", $templateVars);
835

  
836
    } else {
837
      $found = "No entries matched your criteria.  Please try again\n";
838

  
839
      my $templateVars = { stage => "searchResults",
840
                           cfg => $cfg,
841
                           allParams => $allParams,
842
                           foundAccounts => $found };
843
      $$templateVars{'orgList'} = \@orglist;
844
      $template->process( "@defaultHeader@", $templateVars);
845
      $template->process( "@searchResults@", $templateVars);
846
      $template->process( "@defaultFooter@", $templateVars);
847

  
848
    }
849

  
850
    exit();
851
}
852

  
853
#
854
# search the LDAP directory to see if a similar account already exists
855
#
856
sub searchDirectory {
857
    my $ldapurl = shift;
858
    my $base = shift;
859
    my $filter = shift;
860
    my $attref = shift;
861

  
862
    my $foundAccounts = 0;
863

  
864
    my $ldap = Net::LDAP->new($ldapurl) or die "$@";
865
    $ldap->bind( version => 3, anonymous => 1);
866
    my $mesg = $ldap->search (
867
        base   => $base,
868
        filter => $filter,
869
        attrs => @$attref,
870
    );
871

  
872
    if ($mesg->count() > 0) {
873
        $foundAccounts = "";
874
        my $entry;
875
        foreach $entry ($mesg->sorted(['sn'])) {
876
          $foundAccounts .= "<tr>\n<td class=\"main\">\n";
877
          $foundAccounts .= "<a href=\"" unless 
878
                    (!$entry->get_value('labeledURI'));
879
          $foundAccounts .= $entry->get_value('labeledURI') unless
880
                    (!$entry->get_value('labeledURI'));
881
          $foundAccounts .= "\">\n" unless 
882
                    (!$entry->get_value('labeledURI'));
883
          $foundAccounts .= $entry->get_value('givenName');
884
          $foundAccounts .= "</a>\n" unless 
885
                    (!$entry->get_value('labeledURI'));
886
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
887
          $foundAccounts .= "<a href=\"" unless 
888
                    (!$entry->get_value('labeledURI'));
889
          $foundAccounts .= $entry->get_value('labeledURI') unless
890
                    (!$entry->get_value('labeledURI'));
891
          $foundAccounts .= "\">\n" unless 
892
                    (!$entry->get_value('labeledURI'));
893
          $foundAccounts .= $entry->get_value('sn');
894
          $foundAccounts .= "</a>\n";
895
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
896
          $foundAccounts .= $entry->get_value('mail');
897
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
898
          $foundAccounts .= $entry->get_value('telephonenumber');
899
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
900
          $foundAccounts .= $entry->get_value('title');
901
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
902
          $foundAccounts .= $entry->get_value('ou');
903
          $foundAccounts .= "\n</td>\n";
904
          $foundAccounts .= "</tr>\n";
905
        }
906
    }
907
    $ldap->unbind;   # take down session
908
    return $foundAccounts;
909
}
910

  
911
sub debug {
912
    my $msg = shift;
913
    
914
    if ($debug) {
915
        print STDERR "$msg\n";
916
    }
917
}
0 918

  
src/perl/create-ldap-account.pl
1
#!/usr/bin/perl -w
2
 #
3
 #  '$RCSfile$'
4
 #  Copyright: 2004 Regents of the University of California 
5
 #
6
 #   '$Author$'
7
 #     '$Date$'
8
 # '$Revision$' 
9
 # 
10
 # This program is free software; you can redistribute it and/or modify
11
 # it under the terms of the GNU General Public License as published by
12
 # the Free Software Foundation; either version 2 of the License, or
13
 # (at your option) any later version.
14
 #
15
 # This program is distributed in the hope that it will be useful,
16
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 # GNU General Public License for more details.
19
 #
20
 # You should have received a copy of the GNU General Public License
21
 # along with this program; if not, write to the Free Software
22
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 #
24

  
25
#
26
# This is a simple script for creating a new LDAP record with a
27
# predefined format that is hardcoded in the script.  This could be generalized
28
# to support an externally-configured record format.
29
# Matt Jones
30
#
31
use strict;       # turn on strict syntax checking.
32
use Net::LDAP;    # load the LDAP net libraries
33
use Digest::SHA1; # for creating the password hash
34
use MIME::Base64; # for creating the password hash
35
use URI;          # for parsing URL syntax
36
use AppConfig qw(:expand :argcount);
37
use Term::ReadKey;
38

  
39
# Set up our default configuration
40
my $ldapurl = "";
41
my $root = "";
42
my $rootpw = "";
43
my $searchBase = "";
44
my $mailhost = "";
45
my $sender = "";
46

  
47
my $debug = 0;
48

  
49
#--------------------------------------------------------------------------80c->
50
# Read the ldapweb.cfg file
51
my $cfgfile = "ldapweb.cfg";
52
my $config = AppConfig->new({ 
53
    GLOBAL => { ARGCOUNT => ARGCOUNT_ONE, } });
54

  
55
$config->define("ldapurl", { ARGCOUNT => ARGCOUNT_HASH} );           
56
$config->define("ldapsearchbase", { ARGCOUNT => ARGCOUNT_HASH} );
57
$config->define("dn", { ARGCOUNT => ARGCOUNT_HASH} );
58
$config->define("filter", { ARGCOUNT => ARGCOUNT_HASH} );
59
$config->define("user", { ARGCOUNT => ARGCOUNT_HASH} );
60
$config->define("password", { ARGCOUNT => ARGCOUNT_HASH} );
61

  
62
$config->file($cfgfile);
63
my $config_ldapurl = $config->get('ldapurl');
64
my $config_ldapsearchbase = $config->get('ldapsearchbase');
65
my $config_dn = $config->get('dn');
66
my $config_filter = $config->get('filter');
67
my $config_user = $config->get('user');
68
my $config_password = $config->get('password');
69

  
70
my @orglist;
71
foreach my $neworg (keys %$config_dn) {
72
    push(@orglist, $neworg);
73
    debug($neworg);
74
}
75

  
76

  
77
#--------------------------------------------------------------------------80c->
78
# Define the main program logic that calls subroutines to do the work
79
#--------------------------------------------------------------------------80c->
80

  
81
my $allParams = getAccountInfo();
82
my $shouldContinue = checkForDuplicateAccounts($allParams);
83
createAccount($allParams);
84
exit(0);
85

  
86
#--------------------------------------------------------------------------80c->
87
# Define the subroutines to do the work
88
#--------------------------------------------------------------------------80c->
89

  
90
#
91
# Prompt the user for one piece of input information
92
#
93
sub getUserInput {
94
    my $prompt = shift;
95
    my $hideInput = shift;
96

  
97
    print $prompt, ": ";
98
    if ($hideInput) {
99
        ReadMode('noecho');
100
    }
101

  
102
    my $value = ReadLine(0);
103
    chomp($value);
104
    if ($hideInput) {
105
        ReadMode('normal');
106
        print "\n";
107
    }
108
    return $value;
109
}
110

  
111
#
112
# get input about the account to be created
113
#
114
sub getAccountInfo {
115
    
116
    my $uid = getUserInput("UserID", 0);
117
    my $userPassword = getUserInput("Password", 1);
118
    my $userPassword2 = getUserInput("Password Again", 1);
119
    my $givenName = getUserInput("GivenName", 0);
120
    my $sn = getUserInput("Surname", 0);
121
    my $o = getUserInput("Organization", 0);
122
    my $mail = getUserInput("Email", 0);
123
    my $title = getUserInput("Title", 0);
124
    my $telephoneNumber = getUserInput("Telephone", 0);
125
    print "\n";
126

  
127
    my $allParams = { 'givenName' => $givenName, 
128
                      'sn' => $sn,
129
                      'o' => $o, 
130
                      'mail' => $mail, 
131
                      'uid' => $uid, 
132
                      'userPassword' => $userPassword, 
133
                      'userPassword2' => $userPassword2, 
134
                      'title' => $title, 
135
                      'telephoneNumber' => $telephoneNumber };
136
    # Check that all required fields are provided and not null
137
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail', 
138
                           'uid', 'userPassword', 'userPassword2');
139
    if (! paramsAreValid($allParams, @requiredParams)) {
140
        my $errorMessage = "Required information is missing. " .
141
            "Please try again and provide all required fields.";
142
        print $errorMessage, "\n";
143
        exit(0);
144
    } else {
145
	    $ldapurl = $config_ldapurl->{$o};
146
	    $searchBase = $config_ldapsearchbase->{$o};  
147
    }
148

  
149
    return $allParams;
150
}
151

  
152
sub checkForDuplicateAccounts { 
153
    my $allParams = shift;
154

  
155
    # Search LDAP for matching entries that already exist
156
    # Some forms use a single text search box, whereas others search per
157
    # attribute.
158
    my $filter;
159
    $filter = "(|" . 
160
              "(uid=" . $allParams->{'uid'} . ") " .
161
              "(mail=" . $allParams->{'mail'} . ")" .
162
              "(&(sn=" . $allParams->{'sn'} . ") " . 
163
              "(givenName=" . $allParams->{'givenName'} . "))" . 
164
              ")";
165

  
166
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
167

  
168
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
169

  
170
    # If entries match, ask if the account should be created
171
    if ($found) {
172
        print $found, "\n";
173
        my $question  = "Similar accounts already exist.  Do you want to " .
174
            "create a\nnew account anyways? (y/n)";
175
        my $continue = getUserInput($question, 0);
176
        if ($continue =~ "y") {
177
            return 1;
178
        } else {
179
            return 0;
180
        }
181
    # Otherwise, create a new user in the LDAP directory
182
    } else {
183
        return 1;
184
    }
185
}
186

  
187
#
188
# generate a Seeded SHA1 hash of a plaintext password
189
#
190
sub createSeededPassHash {
191
    my $secret = shift;
192

  
193
    my $salt = "";
194
    for (my $i=0; $i < 4; $i++) {
195
        $salt .= int(rand(10));
196
    }
197

  
198
    my $ctx = Digest::SHA1->new;
199
    $ctx->add($secret);
200
    $ctx->add($salt);
201
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
202

  
203
    return $hashedPasswd;
204
}
205

  
206
#
207
# search the LDAP directory to see if a similar account already exists
208
#
209
sub findExistingAccounts {
210
    my $ldapurl = shift;
211
    my $base = shift;
212
    my $filter = shift;
213
    my $attref = shift;
214

  
215
    my $foundAccounts = 0;
216

  
217
    my $ldap = Net::LDAP->new($ldapurl) or die "$@";
218
    $ldap->bind( version => 3, anonymous => 1);
219
    my $mesg = $ldap->search (
220
        base   => $base,
221
        filter => $filter,
222
        attrs => @$attref,
223
    );
224

  
225
    if ($mesg->count() > 0) {
226
        $foundAccounts = "";
227
        my $entry;
228
        foreach $entry ($mesg->all_entries) { 
229
            $foundAccounts .= "\nAccount: ";
230
            $foundAccounts .= $entry->dn();
231
            $foundAccounts .= "\n";
232
            foreach my $attribute ($entry->attributes()) {
233
                $foundAccounts .= "    $attribute: ";
234
                $foundAccounts .= $entry->get_value($attribute);
235
                $foundAccounts .= "\n";
236
            }
237
            $foundAccounts .= "\n";
238
        }
239
    }
240
    $ldap->unbind;   # take down session
241

  
242
    # Follow references
243
    my @references = $mesg->references();
244
    for (my $i = 0; $i <= $#references; $i++) {
245
        my $uri = URI->new($references[$i]);
246
        my $host = $uri->host();
247
        my $path = $uri->path();
248
        $path =~ s/^\///;
249
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
250
        if ($refFound) {
251
            $foundAccounts .= $refFound;
252
        }
253
    }
254

  
255
    #print "<p>Checking referrals...</p>\n";
256
    #my @referrals = $mesg->referrals();
257
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
258
    #for (my $i = 0; $i <= $#referrals; $i++) {
259
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
260
    #}
261

  
262
    return $foundAccounts;
263
}
264

  
265
#
266
# Validate that we have the proper set of input parameters
267
#
268
sub paramsAreValid {
269
    my $allParams = shift;
270
    my @pnames = @_;
271

  
272
    my $allValid = 1;
273

  
274
    foreach my $parameter (@pnames) {
275
        if (!defined($allParams->{$parameter}) || 
276
            ! $allParams->{$parameter} ||
277
            $allParams->{$parameter} =~ /^\s+$/) {
278
            $allValid = 0;
279
        }
280
    }
281

  
282
    return $allValid;
283
}
284

  
285
#
286
# Bind to LDAP and create a new account using the information provided
287
# by the user
288
#
289
sub createAccount {
290
    my $allParams = shift;
291

  
292
    my $o = $allParams->{'o'};
293

  
294
    if ($o =~ "LTER") {
295
        # Handle LTER case and redirect them there
296
    } else {
297

  
298
        # Be sure the passwords match
299
        if ($allParams->{'userPassword'} !~ $allParams->{'userPassword2'}) {
300
            my $errorMessage = "The passwords do not match. Try again.";
301
            print $errorMessage, "\n";
302
            exit(0);
303
        }
304

  
305
	    my $ldapurl = $config_ldapurl->{$o};
306
	    my $root = $config_user->{$o};
307
	    my $rootpw = $config_password->{$o};
308
	    my $searchBase = $config_ldapsearchbase->{$o};
309
	    my $dnBase = $config_dn->{$o};
310

  
311
        my $ldap = Net::LDAP->new($ldapurl) or die "$@";
312
        $ldap->bind( version => 3, dn => $root, password => $rootpw );
313
        print "Inserting new entry for $allParams->{'uid'} ...\n";
314
        my $dn = 'uid=' . $allParams->{'uid'} . ',' . $dnBase;
315

  
316
        # Create a hashed version of the password
317
        my $shapass = createSeededPassHash($allParams->{'userPassword'});
318

  
319
        # Do the insertion
320
        my $additions = [ 
321
                'uid'   => $allParams->{'uid'},
322
                'o'   => $allParams->{'o'},
323
                'cn'   => join(" ", $allParams->{'givenName'}, 
324
                                    $allParams->{'sn'}),
325
                'sn'   => $allParams->{'sn'},
326
                'givenName'   => $allParams->{'givenName'},
327
                'mail' => $allParams->{'mail'},
328
                'userPassword' => $shapass,
329
                'objectclass' => ['top', 'person', 'organizationalPerson', 
330
                                'inetOrgPerson', 'uidObject' ]
331
            ];
332
        if (defined($allParams->{'telephoneNumber'}) && 
333
            $allParams->{'telephoneNumber'} &&
334
            ! $allParams->{'telephoneNumber'} =~ /^\s+$/) {
335
            $$additions[$#$additions + 1] = 'telephoneNumber';
336
            $$additions[$#$additions + 1] = $allParams->{'telephoneNumber'};
337
        }
338
        if (defined($allParams->{'title'}) && 
339
            $allParams->{'title'} &&
340
            ! $allParams->{'title'} =~ /^\s+$/) {
341
            $$additions[$#$additions + 1] = 'title';
342
            $$additions[$#$additions + 1] = $allParams->{'title'};
343
        }
344
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
345
    
346
        if ($result->code()) {
347
            # Post an error message
348
            print "Error while creating account:\n";
349
            print $result->code(), "\n";
350
        } else {
351
            print "Account created.\n";
352
        }
353

  
354
        $ldap->unbind;   # take down session
355
    }
356
}
357

  
358
sub debug {
359
    my $msg = shift;
360
    
361
    if ($debug) {
362
        print STDERR "$msg\n";
363
    }
364
}
0 365

  

Also available in: Unified diff