Project

General

Profile

1
#!/usr/bin/perl -w
2
 #
3
 #  '$RCSfile$'
4
 #  Copyright: 2001 Regents of the University of California 
5
 #
6
 #   '$Author: walbridge $'
7
 #     '$Date: 2009-01-14 15:32:04 -0800 (Wed, 14 Jan 2009) $'
8
 # '$Revision: 4749 $' 
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 lib '../WEB-INF/lib';
31
use strict;             # turn on strict syntax checking
32
use Template;           # load the template-toolkit module
33
use CGI qw/:standard :html3/; # load the CGI module 
34
use Net::LDAP;          # load the LDAP net libraries
35
use Net::SMTP;          # load the SMTP net libraries
36
use Digest::SHA1;       # for creating the password hash
37
use MIME::Base64;       # for creating the password hash
38
use URI;                # for parsing URL syntax
39
use Config::Properties; # for parsing Java .properties files
40
use File::Basename;     # for path name parsing
41
use Cwd 'abs_path';
42

    
43
# Global configuration paramters
44
# This entire block (including skin parsing) could be pushed out to a separate .pm file
45
my $cgiUrl = $ENV{'SCRIPT_FILENAME'};
46
my $workingDirectory = dirname($cgiUrl);
47
my $metacatProps = "${workingDirectory}/../WEB-INF/metacat.properties";
48
my $properties = new Config::Properties();
49
unless (open (METACAT_PROPERTIES, $metacatProps)) {
50
    print "Content-type: text/html\n\n";
51
    print "Unable to locate Metacat properties. Working directory is set as " . 
52
        $workingDirectory .", is this correct?";
53
    exit(0);
54
}
55

    
56
$properties->load(*METACAT_PROPERTIES);
57

    
58
# local directory configuration
59
my $skinsDir = "${workingDirectory}/../style/skins";
60
my $templatesDir = abs_path("${workingDirectory}/../style/common/templates");
61
my $tempDir = $properties->getProperty('application.tempDir');
62

    
63
# url configuration
64
my $server = $properties->splitToTree(qr/\./, 'server');
65
my $contextUrl = 'http://' . $properties->getProperty('server.name') . ':' .
66
                 $properties->getProperty('server.httpPort') . '/' .
67
                 $properties->getProperty('application.context');
68

    
69
my $metacatUrl = $contextUrl . "/metacat";
70
my $cgiPrefix = "/" . $properties->getProperty('application.context') . "/cgi-bin";
71
my $styleSkinsPath = $contextUrl . "/style/skins";
72
my $styleCommonPath = $contextUrl . "/style/common";
73

    
74
my @errorMessages;
75
my $error = 0;
76

    
77
# Import all of the HTML form fields as variables
78
import_names('FORM');
79

    
80
# Must have a config to use Metacat
81
my $skinName = "";
82
if ($FORM::cfg) {
83
    $skinName = $FORM::cfg;
84
} elsif ($ARGV[0]) {
85
    $skinName = $ARGV[0];
86
} else {
87
    debug("No configuration set.");
88
    print "Content-type: text/html\n\n";
89
    print 'LDAPweb Error: The registry requires a skin name to continue.';
90
    exit();
91
}
92

    
93
# Metacat isn't initialized, the registry will fail in strange ways.
94
if (!($metacatUrl)) {
95
    debug("No Metacat.");
96
    print "Content-type: text/html\n\n";
97
    'Registry Error: Metacat is not initialized! Make sure' .
98
        ' MetacatUrl is set correctly in ' .  $skinName . '.cfg';
99
    exit();
100
}
101

    
102
my $skinProperties = new Config::Properties();
103
if (!($skinName)) {
104
    $error = "Application misconfigured.  Please contact the administrator.";
105
    push(@errorMessages, $error);
106
} else {
107
    my $skinProps = "$skinsDir/$skinName/$skinName.properties";
108
    unless (open (SKIN_PROPERTIES, $skinProps)) {
109
        print "Content-type: text/html\n\n";
110
        print "Unable to locate skin properties at $skinProps.  Is this path correct?";
111
        exit(0);
112
    }
113
    $skinProperties->load(*SKIN_PROPERTIES);
114
}
115

    
116
my $config = $skinProperties->splitToTree(qr/\./, 'registry.config');
117

    
118
my $searchBase;
119
my $ldapUsername;
120
my $ldapPassword;
121
# TODO: when should we use surl instead? Is there a setting promoting one over the other?
122
# TODO: the default tree for accounts should be exposed somewhere, defaulting to unaffiliated
123
my $ldapurl = $properties->getProperty('auth.url');
124

    
125
# Java uses miliseconds, Perl expects whole seconds
126
my $timeout = $properties->getProperty('ldap.connectTimeLimit') / 1000;
127
my $ldapdownmessage = "The main ldap server $ldapurl is down!";
128

    
129
# Get the CGI input variables
130
my $query = new CGI;
131
my $debug = 1;
132

    
133
#--------------------------------------------------------------------------80c->
134
# Set up the Template Toolkit to read html form templates
135

    
136
# templates hash, imported from ldap.templates tree in metacat.properties
137
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
138
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
139
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
140

    
141
# set some configuration options for the template object
142
my $ttConfig = {
143
             INCLUDE_PATH => $templatesDir,
144
             INTERPOLATE  => 0,
145
             POST_CHOMP   => 1,
146
             DEBUG        => 1, 
147
             };
148

    
149
# create an instance of the template
150
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
151

    
152
# custom LDAP properties hash
153
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
154

    
155
my $orgProps = $properties->splitToTree(qr/\./, 'organization');
156
my $orgNames = $properties->splitToTree(qr/\./, 'organization.name');
157
# pull out properties available e.g. 'name', 'base'
158
my @orgData = keys(%$orgProps);
159
my @orgList;
160
while (my ($oKey, $oVal) = each(%$orgNames)) {
161
    push(@orgList, $oKey);
162
}
163

    
164
my $ldapConfig;
165
foreach my $o (@orgList) {
166
    foreach my $d (@orgData) {
167
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
168
    }
169
    # also include DN, which is just org + base
170
    if ($ldapConfig->{$o}{'org'}) {
171
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'org'} . "," . $ldapConfig->{$o}{'base'};
172
    } else {
173
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'base'};
174
    }
175
}
176

    
177
#--------------------------------------------------------------------------80c->
178
# Define the main program logic that calls subroutines to do the work
179
#--------------------------------------------------------------------------80c->
180

    
181

    
182
# The processing step we are handling
183
my $stage = $query->param('stage') || $templates->{'stage'};
184

    
185
my $cfg = $query->param('cfg');
186

    
187
# define the possible stages
188
my %stages = (
189
              'initregister'      => \&handleInitRegister,
190
              'register'          => \&handleRegister,
191
              'registerconfirmed' => \&handleRegisterConfirmed,
192
              'simplesearch'      => \&handleSimpleSearch,
193
              'initaddentry'      => \&handleInitAddEntry,
194
              'addentry'          => \&handleAddEntry,
195
              'initmodifyentry'   => \&handleInitModifyEntry,
196
              'modifyentry'       => \&handleModifyEntry,
197
              'changepass'        => \&handleChangePassword,
198
              'initchangepass'    => \&handleInitialChangePassword,
199
              'resetpass'         => \&handleResetPassword,
200
              'initresetpass'     => \&handleInitialResetPassword,
201
             );
202

    
203
# call the appropriate routine based on the stage
204
if ( $stages{$stage} ) {
205
  $stages{$stage}->();
206
} else {
207
  &handleResponseMessage();
208
}
209

    
210
#--------------------------------------------------------------------------80c->
211
# Define the subroutines to do the work
212
#--------------------------------------------------------------------------80c->
213

    
214
sub fullTemplate {
215
    my $templateList = shift;
216
    my $templateVars = setVars(shift);
217

    
218
    $template->process( $templates->{'header'}, $templateVars );
219
    foreach my $tmpl (@{$templateList}) {
220
        $template->process( $templates->{$tmpl}, $templateVars );
221
    }
222
    $template->process( $templates->{'footer'}, $templateVars );
223
}
224

    
225
#
226
# create the initial registration form 
227
#
228
sub handleInitRegister {
229
  my $vars = shift;
230

    
231
  print "Content-type: text/html\n\n";
232
  # process the template files:
233
  fullTemplate(['register'], {stage => "register"}); 
234
  exit();
235
}
236

    
237
#
238
# process input from the register stage, which occurs when
239
# a user submits form data to create a new account
240
#
241
sub handleRegister {
242
    
243
    print "Content-type: text/html\n\n";
244

    
245
    my $allParams = { 'givenName' => $query->param('givenName'), 
246
                      'sn' => $query->param('sn'),
247
                      'o' => $query->param('o'), 
248
                      'mail' => $query->param('mail'), 
249
                      'uid' => $query->param('uid'), 
250
                      'userPassword' => $query->param('userPassword'), 
251
                      'userPassword2' => $query->param('userPassword2'), 
252
                      'title' => $query->param('title'), 
253
                      'telephoneNumber' => $query->param('telephoneNumber') };
254
    # Check that all required fields are provided and not null
255
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail', 
256
                           'uid', 'userPassword', 'userPassword2');
257
    if (! paramsAreValid(@requiredParams)) {
258
        my $errorMessage = "Required information is missing. " .
259
            "Please fill in all required fields and resubmit the form.";
260
        fullTemplate(['register'], { stage => "register",
261
                                     allParams => $allParams,
262
                                     errorMessage => $errorMessage });
263
        exit();
264
    } else {
265
        my $o = $query->param('o');    
266
        $searchBase = $ldapConfig->{$o}{'base'};  
267
    }
268

    
269
    # Search LDAP for matching entries that already exist
270
    # Some forms use a single text search box, whereas others search per
271
    # attribute.
272
    my $filter;
273
    if ($query->param('searchField')) {
274

    
275
      $filter = "(|" . 
276
                "(uid=" . $query->param('searchField') . ") " .
277
                "(mail=" . $query->param('searchField') . ")" .
278
                "(&(sn=" . $query->param('searchField') . ") " . 
279
                "(givenName=" . $query->param('searchField') . "))" . 
280
                ")";
281
    } else {
282
      $filter = "(|" . 
283
                "(uid=" . $query->param('uid') . ") " .
284
                "(mail=" . $query->param('mail') . ")" .
285
                "(&(sn=" . $query->param('sn') . ") " . 
286
                "(givenName=" . $query->param('givenName') . "))" . 
287
                ")";
288
    }
289

    
290
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
291
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
292

    
293
    # If entries match, send back a request to confirm new-user creation
294
    if ($found) {
295
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
296
                                                     allParams => $allParams,
297
                                                     foundAccounts => $found });
298
    # Otherwise, create a new user in the LDAP directory
299
    } else {
300
        createAccount($allParams);
301
    }
302

    
303
    exit();
304
}
305

    
306
#
307
# process input from the registerconfirmed stage, which occurs when
308
# a user chooses to create an account despite similarities to other
309
# existing accounts
310
#
311
sub handleRegisterConfirmed {
312
  
313
    my $allParams = { 'givenName' => $query->param('givenName'), 
314
                      'sn' => $query->param('sn'),
315
                      'o' => 'unaffiliated', # only accept unaffiliated registration
316
                      'mail' => $query->param('mail'), 
317
                      'uid' => $query->param('uid'), 
318
                      'userPassword' => $query->param('userPassword'), 
319
                      'userPassword2' => $query->param('userPassword2'), 
320
                      'title' => $query->param('title'), 
321
                      'telephoneNumber' => $query->param('telephoneNumber') };
322
    print "Content-type: text/html\n\n";
323
    createAccount($allParams);
324
    exit();
325
}
326

    
327
#
328
# change a user's password upon request
329
#
330
sub handleChangePassword {
331

    
332
    print "Content-type: text/html\n\n";
333

    
334
    my $allParams = { 'test' => "1", };
335
    if ($query->param('uid')) {
336
        $$allParams{'uid'} = $query->param('uid');
337
    }
338
    if ($query->param('o')) {
339
        $$allParams{'o'} = $query->param('o');
340
        my $o = $query->param('o');
341
        
342
        $searchBase = $ldapConfig->{$o}{'base'};
343
    }
344

    
345

    
346
    # Check that all required fields are provided and not null
347
    my @requiredParams = ( 'uid', 'o', 'oldpass', 
348
                           'userPassword', 'userPassword2');
349
    if (! paramsAreValid(@requiredParams)) {
350
        my $errorMessage = "Required information is missing. " .
351
            "Please fill in all required fields and submit the form.";
352
        fullTemplate( ['changePass'], { stage => "changepass",
353
                                        allParams => $allParams,
354
                                        errorMessage => $errorMessage });
355
        exit();
356
    }
357

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

    
361
        my $o = $query->param('o');
362
        $searchBase = $ldapConfig->{$o}{'base'};
363
        $ldapUsername = $ldapConfig->{$o}{'user'};
364
        $ldapPassword = $ldapConfig->{$o}{'password'};
365

    
366
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
367
        if ($query->param('o') =~ "LTER") {
368
            fullTemplate( ['registerLter'] );
369
        } else {
370
            my $errorMessage = changePassword(
371
                    $dn, $query->param('userPassword'), 
372
                    $dn, $query->param('oldpass'), $query->param('o'));
373
            if ($errorMessage) {
374
                fullTemplate( ['changePass'], { stage => "changepass",
375
                                                allParams => $allParams,
376
                                                errorMessage => $errorMessage });
377
                exit();
378
            } else {
379
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
380
                                                       allParams => $allParams });
381
                exit();
382
            }
383
        }
384
    } else {
385
        my $errorMessage = "The passwords do not match. Try again.";
386
        fullTemplate( ['changePass'], { stage => "changepass",
387
                                        allParams => $allParams,
388
                                        errorMessage => $errorMessage });
389
        exit();
390
    }
391
}
392

    
393
#
394
# change a user's password upon request - no input params
395
# only display chagepass template without any error
396
#
397
sub handleInitialChangePassword {
398
    print "Content-type: text/html\n\n";
399

    
400
    my $allParams = { 'test' => "1", };
401
    my $errorMessage = "";
402
    fullTemplate( ['changePass'], { stage => "changepass",
403
                                    errorMessage => $errorMessage });
404
    exit();
405
}
406

    
407
#
408
# reset a user's password upon request
409
#
410
sub handleResetPassword {
411

    
412
    print "Content-type: text/html\n\n";
413

    
414
    my $allParams = { 'test' => "1", };
415
    if ($query->param('uid')) {
416
        $$allParams{'uid'} = $query->param('uid');
417
    }
418
    if ($query->param('o')) {
419
        $$allParams{'o'} = $query->param('o');
420
        my $o = $query->param('o');
421
        
422
        $searchBase = $ldapConfig->{$o}{'base'};
423
        $ldapUsername = $ldapConfig->{$o}{'user'};
424
        $ldapPassword = $ldapConfig->{$o}{'password'};
425
    }
426

    
427
    # Check that all required fields are provided and not null
428
    my @requiredParams = ( 'uid', 'o' );
429
    if (! paramsAreValid(@requiredParams)) {
430
        my $errorMessage = "Required information is missing. " .
431
            "Please fill in all required fields and submit the form.";
432
        fullTemplate( ['resetPass'],  { stage => "resetpass",
433
                                        allParams => $allParams,
434
                                        errorMessage => $errorMessage });
435
        exit();
436
    }
437

    
438
    # We have all of the info we need, so try to change the password
439
    my $o = $query->param('o');
440
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
441
    if ($query->param('o') =~ "LTER") {
442
        fullTemplate( ['registerLter'] );
443
        exit();
444
    } else {
445
        my $errorMessage = "";
446
        my $recipient;
447
        my $userPass;
448
        my $entry = getLdapEntry($ldapurl, $searchBase, 
449
                $query->param('uid'), $query->param('o'));
450

    
451
        if ($entry) {
452
            $recipient = $entry->get_value('mail');
453
            $userPass = getRandomPassword();
454
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
455
        } else {
456
            $errorMessage = "User not found in database.  Please try again.";
457
        }
458

    
459
        if ($errorMessage) {
460
            fullTemplate( ['resetPass'], { stage => "resetpass",
461
                                           allParams => $allParams,
462
                                           errorMessage => $errorMessage });
463
            exit();
464
        } else {
465
            my $errorMessage = sendPasswordNotification($query->param('uid'),
466
                    $query->param('o'), $userPass, $recipient, $cfg);
467
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
468
                                                  allParams => $allParams,
469
                                                  errorMessage => $errorMessage });
470
            exit();
471
        }
472
    }
473
}
474

    
475
#
476
# reset a user's password upon request- no initial params
477
# only display resetpass template without any error
478
#
479
sub handleInitialResetPassword {
480
    print "Content-type: text/html\n\n";
481
    my $errorMessage = "";
482
    fullTemplate( ['resetPass'], { stage => "resetpass",
483
                                   errorMessage => $errorMessage });
484
    exit();
485
}
486

    
487
#
488
# Construct a random string to use for a newly reset password
489
#
490
sub getRandomPassword {
491
    my $length = shift;
492
    if (!$length) {
493
        $length = 8;
494
    }
495
    my $newPass = "";
496

    
497
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
498
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
499
    return $newPass;
500
}
501

    
502
#
503
# Change a password to a new value, binding as the provided user
504
#
505
sub changePassword {
506
    my $userDN = shift;
507
    my $userPass = shift;
508
    my $bindDN = shift;
509
    my $bindPass = shift;
510
    my $o = shift;
511

    
512
    my $searchBase = $ldapConfig->{$o}{'base'};
513
    
514
    my $errorMessage = 0;
515
    my $ldap;
516
    
517
		#if main ldap server is down, a html file containing warning message will be returned
518
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
519
    
520
		#$ldap->start_tls( verify => 'require',
521
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
522
    $ldap->start_tls( verify => 'none');
523
    my $bindresult = $ldap->bind( version => 3, dn => $bindDN, 
524
                                  password => $bindPass );
525
    if ($bindresult->code) {
526
        $errorMessage = "Failed to log in. Are you sure your old " .
527
                        "password is correct? Try again...";
528
        return $errorMessage;
529
    }
530

    
531
    # Find the user here and change their entry
532
    my $newpass = createSeededPassHash($userPass);
533
    my $modifications = { userPassword => $newpass };
534
    my $result = $ldap->modify( $userDN, replace => { %$modifications });
535
    
536
    if ($result->code()) {
537
        my $errorMessage = "There was an error changing the password." .
538
                           "<br />\n" . $result->error;
539
    } 
540
    $ldap->unbind;   # take down session
541

    
542
    return $errorMessage;
543
}
544

    
545
#
546
# generate a Seeded SHA1 hash of a plaintext password
547
#
548
sub createSeededPassHash {
549
    my $secret = shift;
550

    
551
    my $salt = "";
552
    for (my $i=0; $i < 4; $i++) {
553
        $salt .= int(rand(10));
554
    }
555

    
556
    my $ctx = Digest::SHA1->new;
557
    $ctx->add($secret);
558
    $ctx->add($salt);
559
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
560

    
561
    return $hashedPasswd;
562
}
563

    
564
#
565
# Look up an ldap entry for a user
566
#
567
sub getLdapEntry {
568
    my $ldapurl = shift;
569
    my $base = shift;
570
    my $username = shift;
571
    my $org = shift;
572

    
573
    my $entry = "";
574
    my $mesg;
575
    my $ldap;
576
    debug("ldap server: $ldapurl");
577

    
578
    #if main ldap server is down, a html file containing warning message will be returned
579
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
580
    $ldap->start_tls( verify => 'none');
581
    my $bindresult = $ldap->bind;
582
    if ($bindresult->code) {
583
        return $entry;
584
    }
585

    
586
    if($ldapConfig->{$org}{'filter'}){
587
        $mesg = $ldap->search ( base   => $base,
588
                filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
589
    } else {
590
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
591
    }
592
    
593
    if ($mesg->count > 0) {
594
        $entry = $mesg->pop_entry;
595
        $ldap->unbind;   # take down session
596
    } else {
597
        $ldap->unbind;   # take down session
598
        # Follow references by recursive call to self
599
        my @references = $mesg->references();
600
        for (my $i = 0; $i <= $#references; $i++) {
601
            my $uri = URI->new($references[$i]);
602
            my $host = $uri->host();
603
            my $path = $uri->path();
604
            $path =~ s/^\///;
605
            $entry = &getLdapEntry($host, $path, $username, $org);
606
            if ($entry) {
607
                return $entry;
608
            }
609
        }
610
    }
611
    return $entry;
612
}
613

    
614
# 
615
# send an email message notifying the user of the pw change
616
#
617
sub sendPasswordNotification {
618
    my $username = shift;
619
    my $org = shift;
620
    my $newPass = shift;
621
    my $recipient = shift;
622
    my $cfg = shift;
623

    
624
    my $errorMessage = "";
625
    if ($recipient) {
626
				my $mailhost = $properties->getProperty('email.mailhost');
627
				my $sender =  $properties->getProperty('email.sender');
628
        # Send the email message to them
629
        my $smtp = Net::SMTP->new($mailhost);
630
        $smtp->mail($sender);
631
        $smtp->to($recipient);
632

    
633
        my $message = <<"        ENDOFMESSAGE";
634
        To: $recipient
635
        From: $sender
636
        Subject: KNB Password Reset
637
        
638
        Somebody (hopefully you) requested that your KNB password be reset.  
639
        This is generally done when somebody forgets their password.  Your 
640
        password can be changed by visiting the following URL:
641

    
642
        $cgiUrl?stage=changepass&cfg=$cfg
643

    
644
            Username: $username
645
        Organization: $org
646
        New Password: $newPass
647

    
648
        Thanks,
649
            The KNB Development Team
650
    
651
        ENDOFMESSAGE
652
        $message =~ s/^[ \t\r\f]+//gm;
653
    
654
        $smtp->data($message);
655
        $smtp->quit;
656
    } else {
657
        $errorMessage = "Failed to send password because I " .
658
                        "couldn't find a valid email address.";
659
    }
660
    return $errorMessage;
661
}
662

    
663
#
664
# search the LDAP directory to see if a similar account already exists
665
#
666
sub findExistingAccounts {
667
    my $ldapurl = shift;
668
    my $base = shift;
669
    my $filter = shift;
670
    my $attref = shift;
671
    my $ldap;
672

    
673
    my $foundAccounts = 0;
674

    
675
    #if main ldap server is down, a html file containing warning message will be returned
676
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
677
    $ldap->start_tls( verify => 'none');
678
    $ldap->bind( version => 3, anonymous => 1);
679
    my $mesg = $ldap->search (
680
        base   => $base,
681
        filter => $filter,
682
        attrs => @$attref,
683
    );
684

    
685
    if ($mesg->count() > 0) {
686
        $foundAccounts = "";
687
        my $entry;
688
        foreach $entry ($mesg->all_entries) { 
689
            $foundAccounts .= "<p>\n<b><u>Account:</u> ";
690
            $foundAccounts .= $entry->dn();
691
            $foundAccounts .= "</b><br />\n";
692
            foreach my $attribute ($entry->attributes()) {
693
                $foundAccounts .= "$attribute: ";
694
                $foundAccounts .= $entry->get_value($attribute);
695
                $foundAccounts .= "<br />\n";
696
            }
697
            $foundAccounts .= "</p>\n";
698
        }
699
    }
700
    $ldap->unbind;   # take down session
701

    
702
    # Follow references
703
    my @references = $mesg->references();
704
    for (my $i = 0; $i <= $#references; $i++) {
705
        my $uri = URI->new($references[$i]);
706
        my $host = $uri->host();
707
        my $path = $uri->path();
708
        $path =~ s/^\///;
709
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
710
        if ($refFound) {
711
            $foundAccounts .= $refFound;
712
        }
713
    }
714

    
715
    #print "<p>Checking referrals...</p>\n";
716
    #my @referrals = $mesg->referrals();
717
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
718
    #for (my $i = 0; $i <= $#referrals; $i++) {
719
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
720
    #}
721

    
722
    return $foundAccounts;
723
}
724

    
725
#
726
# Validate that we have the proper set of input parameters
727
#
728
sub paramsAreValid {
729
    my @pnames = @_;
730

    
731
    my $allValid = 1;
732
    foreach my $parameter (@pnames) {
733
        if (!defined($query->param($parameter)) || 
734
            ! $query->param($parameter) ||
735
            $query->param($parameter) =~ /^\s+$/) {
736
            $allValid = 0;
737
        }
738
    }
739

    
740
    return $allValid;
741
}
742

    
743
#
744
# Bind to LDAP and create a new account using the information provided
745
# by the user
746
#
747
sub createAccount {
748
    my $allParams = shift;
749

    
750
    if ($query->param('o') =~ "LTER") {
751
        fullTemplate( ['registerLter'] );
752
    } else {
753

    
754
        # Be sure the passwords match
755
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
756
            my $errorMessage = "The passwords do not match. Try again.";
757
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
758
                                                            allParams => $allParams,
759
                                                            errorMessage => $errorMessage });
760
            exit();
761
        }
762

    
763
        my $o = $query->param('o');
764

    
765
        my $searchBase = $ldapConfig->{$o}{'base'};
766
        my $dnBase = $ldapConfig->{$o}{'dn'};
767
        my $ldapUsername = $ldapConfig->{$o}{'user'} . ',' . $searchBase;
768
        my $ldapPassword = $ldapConfig->{$o}{'password'};
769
    		debug("LDAP connection to $ldapurl...");    
770
        #if main ldap server is down, a html file containing warning message will be returned
771
        my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
772
        
773
        
774
        $ldap->start_tls( verify => 'none');
775
				debug("Attempting to bind to LDAP server with dn = $ldapUsername, pwd = $ldapPassword");
776
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
777
        
778
        my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
779
				debug("Inserting new entry for: $dn");
780

    
781
        # Create a hashed version of the password
782
        my $shapass = createSeededPassHash($query->param('userPassword'));
783

    
784
        # Do the insertion
785
        my $additions = [ 
786
                'uid'   => $query->param('uid'),
787
                'o'   => $query->param('o'),
788
                'cn'   => join(" ", $query->param('givenName'), 
789
                                    $query->param('sn')),
790
                'sn'   => $query->param('sn'),
791
                'givenName'   => $query->param('givenName'),
792
                'mail' => $query->param('mail'),
793
                'userPassword' => $shapass,
794
                'objectclass' => ['top', 'person', 'organizationalPerson', 
795
                                'inetOrgPerson', 'uidObject' ]
796
            ];
797
        if (defined($query->param('telephoneNumber')) && 
798
            $query->param('telephoneNumber') &&
799
            ! $query->param('telephoneNumber') =~ /^\s+$/) {
800
            $$additions[$#$additions + 1] = 'telephoneNumber';
801
            $$additions[$#$additions + 1] = $query->param('telephoneNumber');
802
        }
803
        if (defined($query->param('title')) && 
804
            $query->param('title') &&
805
            ! $query->param('title') =~ /^\s+$/) {
806
            $$additions[$#$additions + 1] = 'title';
807
            $$additions[$#$additions + 1] = $query->param('title');
808
        }
809
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
810
    
811
        if ($result->code()) {
812
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
813
                                                            allParams => $allParams,
814
                                                            errorMessage => $result->error });
815
            # TODO SCW was included as separate errors, test this
816
            #$templateVars    = setVars({ stage => "register",
817
            #                     allParams => $allParams });
818
            #$template->process( $templates->{'register'}, $templateVars);
819
        } else {
820
            fullTemplate( ['success'] );
821
        }
822

    
823
        $ldap->unbind;   # take down session
824
    }
825
}
826

    
827
sub handleResponseMessage {
828

    
829
  print "Content-type: text/html\n\n";
830
  my $errorMessage = "You provided invalid input to the script. " .
831
                     "Try again please.";
832
  fullTemplate( [], { stage => $templates->{'stage'},
833
                      errorMessage => $errorMessage });
834
  exit();
835
}
836

    
837
#
838
# perform a simple search against the LDAP database using 
839
# a small subset of attributes of each dn and return it
840
# as a table to the calling browser.
841
#
842
sub handleSimpleSearch {
843

    
844
    my $o = $query->param('o');
845

    
846
    my $ldapurl = $ldapConfig->{$o}{'url'};
847
    my $searchBase = $ldapConfig->{$o}{'base'};
848

    
849
    print "Content-type: text/html\n\n";
850

    
851
    my $allParams = { 
852
                      'cn' => $query->param('cn'),
853
                      'sn' => $query->param('sn'),
854
                      'gn' => $query->param('gn'),
855
                      'o'  => $query->param('o'),
856
                      'facsimiletelephonenumber' 
857
                      => $query->param('facsimiletelephonenumber'),
858
                      'mail' => $query->param('cmail'),
859
                      'telephonenumber' => $query->param('telephonenumber'),
860
                      'title' => $query->param('title'),
861
                      'uid' => $query->param('uid'),
862
                      'ou' => $query->param('ou'),
863
                    };
864

    
865
    # Search LDAP for matching entries that already exist
866
    my $filter = "(" . 
867
                 $query->param('searchField') . "=" .
868
                 "*" .
869
                 $query->param('searchValue') .
870
                 "*" .
871
                 ")";
872

    
873
    my @attrs = [ 'sn', 
874
                  'gn', 
875
                  'cn', 
876
                  'o', 
877
                  'facsimiletelephonenumber', 
878
                  'mail', 
879
                  'telephoneNumber', 
880
                  'title', 
881
                  'uid', 
882
                  'labeledURI', 
883
                  'ou' ];
884

    
885
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
886

    
887
    # Send back the search results
888
    if ($found) {
889
      fullTemplate( ('searchResults'), { stage => "searchresults",
890
                                         allParams => $allParams,
891
                                         foundAccounts => $found });
892
    } else {
893
      $found = "No entries matched your criteria.  Please try again\n";
894

    
895
      fullTemplate( ('searchResults'), { stage => "searchresults",
896
                                         allParams => $allParams,
897
                                         foundAccounts => $found });
898
    }
899

    
900
    exit();
901
}
902

    
903
#
904
# search the LDAP directory to see if a similar account already exists
905
#
906
sub searchDirectory {
907
    my $ldapurl = shift;
908
    my $base = shift;
909
    my $filter = shift;
910
    my $attref = shift;
911

    
912
    my $foundAccounts = 0;
913
    
914
    #if ldap server is down, a html file containing warning message will be returned
915
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
916
    
917
    $ldap->start_tls( verify => 'none');
918
    $ldap->bind( version => 3, anonymous => 1);
919
    my $mesg = $ldap->search (
920
        base   => $base,
921
        filter => $filter,
922
        attrs => @$attref,
923
    );
924

    
925
    if ($mesg->count() > 0) {
926
        $foundAccounts = "";
927
        my $entry;
928
        foreach $entry ($mesg->sorted(['sn'])) {
929
          $foundAccounts .= "<tr>\n<td class=\"main\">\n";
930
          $foundAccounts .= "<a href=\"" unless 
931
                    (!$entry->get_value('labeledURI'));
932
          $foundAccounts .= $entry->get_value('labeledURI') unless
933
                    (!$entry->get_value('labeledURI'));
934
          $foundAccounts .= "\">\n" unless 
935
                    (!$entry->get_value('labeledURI'));
936
          $foundAccounts .= $entry->get_value('givenName');
937
          $foundAccounts .= "</a>\n" unless 
938
                    (!$entry->get_value('labeledURI'));
939
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
940
          $foundAccounts .= "<a href=\"" unless 
941
                    (!$entry->get_value('labeledURI'));
942
          $foundAccounts .= $entry->get_value('labeledURI') unless
943
                    (!$entry->get_value('labeledURI'));
944
          $foundAccounts .= "\">\n" unless 
945
                    (!$entry->get_value('labeledURI'));
946
          $foundAccounts .= $entry->get_value('sn');
947
          $foundAccounts .= "</a>\n";
948
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
949
          $foundAccounts .= $entry->get_value('mail');
950
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
951
          $foundAccounts .= $entry->get_value('telephonenumber');
952
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
953
          $foundAccounts .= $entry->get_value('title');
954
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
955
          $foundAccounts .= $entry->get_value('ou');
956
          $foundAccounts .= "\n</td>\n";
957
          $foundAccounts .= "</tr>\n";
958
        }
959
    }
960
    $ldap->unbind;   # take down session
961
    return $foundAccounts;
962
}
963

    
964
sub debug {
965
    my $msg = shift;
966
    
967
    if ($debug) {
968
        print STDERR "LDAPweb: $msg\n";
969
    }
970
}
971

    
972
sub handleGeneralServerFailure {
973
    my $errorMessage = shift;
974
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
975
    exit(0);   
976
   }
977
    
978
sub setVars {
979
    my $paramVars = shift;
980
    # initialize default parameters 
981
    my $templateVars = { cfg => $cfg,
982
                         styleSkinsPath => $contextUrl . "/style/skins",
983
                         styleCommonPath => $contextUrl . "/style/common",
984
                         contextUrl => $contextUrl,
985
                         orgList => \@orgList,
986
                         config  => $config,
987
    };
988
    
989
    # append customized params
990
    while (my ($k, $v) = each (%$paramVars)) {
991
        $templateVars->{$k} = $v;
992
    }
993
    
994
    return $templateVars;
995
} 
(10-10/14)