Project

General

Profile

1 2341 sgarg
#!/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 4080 daigle
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 Config::Properties; # for parsing Java .properties files
39
use File::Basename;     # for path name parsing
40 2341 sgarg
41 4080 daigle
# Global configuration paramters
42
my $cgiUrl = $ENV{'SCRIPT_FILENAME'};
43
my $workingDirectory = dirname($cgiUrl);
44
my $metacatProps = "${workingDirectory}/../WEB-INF/metacat.properties";
45
my $properties = new Config::Properties();
46
unless (open (METACAT_PROPERTIES, $metacatProps)) {
47
    #print "Content-type: text/html\n\n";
48
    print "Unable to locate Metacat properties. Working directory is set as " .
49
        $workingDirectory .", is this correct?";
50
    exit(0);
51
}
52 2341 sgarg
53 4080 daigle
$properties->load(*METACAT_PROPERTIES);
54 4010 tao
55 4080 daigle
## Set up our default configuration
56
my $ldapProps = $properties->splitToTree(qr/\./, 'ldap');
57
my $ldapurl = $ldapProps->{'url'};
58
my $mainldapurl = $ldapProps->{'mainurl'};
59
my $ldapUsername = $ldapProps->{'user'};
60
my $ldapPassword = $ldapProps->{'password'};
61
my $searchBase = $ldapProps->{'searchbase'};
62
my $mailhost = $properties->getProperty('email.mailhost');
63
my $sender = $properties->getProperty('email.sender');
64
65
# Java uses miliseconds, Perl expects whole seconds
66
my $TIMEOUT = $ldapProps->{'connectTimeLimit'} / 1000;
67
my $mainldapdownmessage = "The main ldap server $mainldapurl is down!";
68
69 2341 sgarg
# Get the CGI input variables
70
my $query = new CGI;
71
72
my $debug = 0;
73
74
#--------------------------------------------------------------------------80c->
75
# Set up the Template Toolkit to read html form templates
76
77 4080 daigle
# templates hash, imported from ldap.templates tree in metacat.properties
78
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
79 2341 sgarg
80
# set some configuration options for the template object
81
my $config_templates = {
82 4080 daigle
             INCLUDE_PATH => $properties->getProperty('templates-dir'),
83 2341 sgarg
             INTERPOLATE  => 0,
84
             POST_CHOMP   => 1,
85
             };
86
87
# create an instance of the template
88 3177 tao
my $template = Template->new($config_templates) || handleGeneralServerFailure($Template::ERROR);
89 2341 sgarg
90 4080 daigle
# custom LDAP properties hash
91
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
92 2341 sgarg
93 4080 daigle
my @orgList = split(/,/, $properties->getProperty('ldap.organizations'));
94
my $ldapConfig;
95 2341 sgarg
96 4080 daigle
foreach my $o (@orgList) {
97
    debug($o);
98
    # pull the raw tree in to prevent Perl pass-by-value shenanigans
99
    $ldapConfig->{$o} = $properties->splitToTree(qr/\./, 'ldap');
100 2341 sgarg
101 4080 daigle
    # override the defaults set in ldap with the custom values
102
    if (defined $ldapCustom->{$o}) {
103
        my $custom = $ldapCustom->{$o};
104
        while (my ($key, $value) = each(%$custom)) {
105
            $ldapConfig->{$o}{$key} = $value;
106
        }
107
    }
108 2341 sgarg
}
109
110
#--------------------------------------------------------------------------80c->
111
# Define the main program logic that calls subroutines to do the work
112
#--------------------------------------------------------------------------80c->
113
114
115
# The processing step we are handling
116 4080 daigle
my $stage = $query->param('stage') || $templates->{'stage'};
117 2341 sgarg
118
my $cfg = $query->param('cfg');
119
120
# define the possible stages
121
my %stages = (
122
              'initregister'      => \&handleInitRegister,
123
              'register'          => \&handleRegister,
124
              'registerconfirmed' => \&handleRegisterConfirmed,
125
              'simplesearch'      => \&handleSimpleSearch,
126
              'initaddentry'      => \&handleInitAddEntry,
127
              'addentry'          => \&handleAddEntry,
128
              'initmodifyentry'   => \&handleInitModifyEntry,
129
              'modifyentry'       => \&handleModifyEntry,
130 2972 jones
              'changepass'        => \&handleChangePassword,
131
              'initchangepass'    => \&handleInitialChangePassword,
132 2341 sgarg
              'resetpass'         => \&handleResetPassword,
133 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
134 2341 sgarg
             );
135
# call the appropriate routine based on the stage
136
if ( $stages{$stage} ) {
137
  $stages{$stage}->();
138
} else {
139
  &handleResponseMessage();
140
}
141
142
#--------------------------------------------------------------------------80c->
143
# Define the subroutines to do the work
144
#--------------------------------------------------------------------------80c->
145
146
147
#
148
# create the initial registration form
149
#
150
sub handleInitRegister {
151
  my $vars = shift;
152
153
  print "Content-type: text/html\n\n";
154
  # process the template files:
155 4080 daigle
  fullTemplate(['register'], {stage => "register"});
156 2341 sgarg
  exit();
157
}
158
159
#
160
# process input from the register stage, which occurs when
161
# a user submits form data to create a new account
162
#
163
sub handleRegister {
164
165
    print "Content-type: text/html\n\n";
166
167
    my $allParams = { 'givenName' => $query->param('givenName'),
168
                      'sn' => $query->param('sn'),
169
                      'o' => $query->param('o'),
170
                      'mail' => $query->param('mail'),
171
                      'uid' => $query->param('uid'),
172
                      'userPassword' => $query->param('userPassword'),
173
                      'userPassword2' => $query->param('userPassword2'),
174
                      'title' => $query->param('title'),
175
                      'telephoneNumber' => $query->param('telephoneNumber') };
176
    # Check that all required fields are provided and not null
177
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail',
178
                           'uid', 'userPassword', 'userPassword2');
179
    if (! paramsAreValid(@requiredParams)) {
180
        my $errorMessage = "Required information is missing. " .
181
            "Please fill in all required fields and resubmit the form.";
182 4080 daigle
        fullTemplate(['register'], { stage => "register",
183
                                     allParams => $allParams,
184
                                     errorMessage => $errorMessage });
185
        exit();
186 2341 sgarg
    } else {
187 2972 jones
        my $o = $query->param('o');
188 4080 daigle
        $ldapurl = $ldapConfig->{$o}{'url'};
189
        $searchBase = $ldapConfig->{$o}{'base'};
190 2341 sgarg
    }
191
192
    # Search LDAP for matching entries that already exist
193
    # Some forms use a single text search box, whereas others search per
194
    # attribute.
195
    my $filter;
196
    if ($query->param('searchField')) {
197
198
      $filter = "(|" .
199
                "(uid=" . $query->param('searchField') . ") " .
200
                "(mail=" . $query->param('searchField') . ")" .
201
                "(&(sn=" . $query->param('searchField') . ") " .
202
                "(givenName=" . $query->param('searchField') . "))" .
203
                ")";
204
    } else {
205
      $filter = "(|" .
206
                "(uid=" . $query->param('uid') . ") " .
207
                "(mail=" . $query->param('mail') . ")" .
208
                "(&(sn=" . $query->param('sn') . ") " .
209
                "(givenName=" . $query->param('givenName') . "))" .
210
                ")";
211
    }
212
213
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
214
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
215
216
    # If entries match, send back a request to confirm new-user creation
217
    if ($found) {
218 4080 daigle
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
219
                                                     allParams => $allParams,
220
                                                     foundAccounts => $found });
221 2341 sgarg
    # Otherwise, create a new user in the LDAP directory
222
    } else {
223 3175 tao
        #print("ingore create account\n");
224 2341 sgarg
        createAccount($allParams);
225
    }
226
227
    exit();
228
}
229
230
#
231
# process input from the registerconfirmed stage, which occurs when
232
# a user chooses to create an account despite similarities to other
233
# existing accounts
234
#
235
sub handleRegisterConfirmed {
236
237
    my $allParams = { 'givenName' => $query->param('givenName'),
238
                      'sn' => $query->param('sn'),
239 4080 daigle
                      'o' => 'unaffiliated', # only accept unaffiliated registration
240 2341 sgarg
                      'mail' => $query->param('mail'),
241
                      'uid' => $query->param('uid'),
242
                      'userPassword' => $query->param('userPassword'),
243
                      'userPassword2' => $query->param('userPassword2'),
244
                      'title' => $query->param('title'),
245
                      'telephoneNumber' => $query->param('telephoneNumber') };
246
    print "Content-type: text/html\n\n";
247
    createAccount($allParams);
248
    exit();
249
}
250
251
#
252
# change a user's password upon request
253
#
254
sub handleChangePassword {
255
256
    print "Content-type: text/html\n\n";
257
258
    my $allParams = { 'test' => "1", };
259
    if ($query->param('uid')) {
260
        $$allParams{'uid'} = $query->param('uid');
261
    }
262
    if ($query->param('o')) {
263
        $$allParams{'o'} = $query->param('o');
264 2972 jones
        my $o = $query->param('o');
265
266 4080 daigle
        $ldapurl = $ldapConfig->{$o}{'url'};
267
        $searchBase = $ldapConfig->{$o}{'base'};
268 2341 sgarg
    }
269
270
271
    # Check that all required fields are provided and not null
272
    my @requiredParams = ( 'uid', 'o', 'oldpass',
273
                           'userPassword', 'userPassword2');
274
    if (! paramsAreValid(@requiredParams)) {
275
        my $errorMessage = "Required information is missing. " .
276
            "Please fill in all required fields and submit the form.";
277 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
278
                                        allParams => $allParams,
279
                                        errorMessage => $errorMessage });
280
        exit();
281 2341 sgarg
    }
282
283
    # We have all of the info we need, so try to change the password
284
    if ($query->param('userPassword') =~ $query->param('userPassword2')) {
285
286 2972 jones
        my $o = $query->param('o');
287 4080 daigle
        $ldapurl = $ldapConfig->{$o}{'url'};
288
        $searchBase = $ldapConfig->{$o}{'base'};
289
        $ldapUsername = $ldapConfig->{$o}{'user'};
290
        $ldapPassword = $ldapConfig->{$o}{'password'};
291 2341 sgarg
292 4080 daigle
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
293 2341 sgarg
        if ($query->param('o') =~ "LTER") {
294 4080 daigle
            fullTemplate( ['registerLter'] );
295 2341 sgarg
        } else {
296
            my $errorMessage = changePassword(
297
                    $dn, $query->param('userPassword'),
298
                    $dn, $query->param('oldpass'), $query->param('o'));
299 2972 jones
            if ($errorMessage) {
300 4080 daigle
                fullTemplate( ['changePass'], { stage => "changepass",
301
                                                allParams => $allParams,
302
                                                errorMessage => $errorMessage });
303
                exit();
304 2341 sgarg
            } else {
305 4080 daigle
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
306
                                                       allParams => $allParams });
307
                exit();
308 2341 sgarg
            }
309
        }
310
    } else {
311
        my $errorMessage = "The passwords do not match. Try again.";
312 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
313
                                        allParams => $allParams,
314
                                        errorMessage => $errorMessage });
315
        exit();
316 2341 sgarg
    }
317
}
318
319
#
320 2414 sgarg
# change a user's password upon request - no input params
321
# only display chagepass template without any error
322
#
323
sub handleInitialChangePassword {
324
    print "Content-type: text/html\n\n";
325
326
    my $allParams = { 'test' => "1", };
327
    my $errorMessage = "";
328 4080 daigle
    fullTemplate( ['changePass'], { stage => "changepass",
329
                                    errorMessage => $errorMessage });
330
    exit();
331 2414 sgarg
}
332
333
#
334 2341 sgarg
# reset a user's password upon request
335
#
336
sub handleResetPassword {
337
338
    print "Content-type: text/html\n\n";
339
340
    my $allParams = { 'test' => "1", };
341
    if ($query->param('uid')) {
342
        $$allParams{'uid'} = $query->param('uid');
343
    }
344
    if ($query->param('o')) {
345
        $$allParams{'o'} = $query->param('o');
346 2972 jones
        my $o = $query->param('o');
347
348 4080 daigle
        $ldapurl = $ldapConfig->{$o}{'url'};
349
        $searchBase = $ldapConfig->{$o}{'base'};
350
        $ldapUsername = $ldapConfig->{$o}{'user'};
351
        $ldapPassword = $ldapConfig->{$o}{'password'};
352 2341 sgarg
    }
353
354
    # Check that all required fields are provided and not null
355
    my @requiredParams = ( 'uid', 'o' );
356
    if (! paramsAreValid(@requiredParams)) {
357
        my $errorMessage = "Required information is missing. " .
358
            "Please fill in all required fields and submit the form.";
359 4080 daigle
        fullTemplate( ['resetPass'],  { stage => "resetpass",
360
                                        allParams => $allParams,
361
                                        errorMessage => $errorMessage });
362
        exit();
363 2341 sgarg
    }
364
365
    # We have all of the info we need, so try to change the password
366
    my $o = $query->param('o');
367 4080 daigle
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
368 2341 sgarg
    if ($query->param('o') =~ "LTER") {
369 4080 daigle
        fullTemplate( ['registerLter'] );
370
        exit();
371 2341 sgarg
    } else {
372
        my $errorMessage = "";
373
        my $recipient;
374
        my $userPass;
375
        my $entry = getLdapEntry($ldapurl, $searchBase,
376
                $query->param('uid'), $query->param('o'));
377
378
        if ($entry) {
379
            $recipient = $entry->get_value('mail');
380
            $userPass = getRandomPassword();
381 4080 daigle
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
382 2341 sgarg
        } else {
383
            $errorMessage = "User not found in database.  Please try again.";
384
        }
385
386
        if ($errorMessage) {
387 4080 daigle
            fullTemplate( ['resetPass'], { stage => "resetpass",
388
                                           allParams => $allParams,
389
                                           errorMessage => $errorMessage });
390
            exit();
391 2341 sgarg
        } else {
392
            my $errorMessage = sendPasswordNotification($query->param('uid'),
393 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
394 4080 daigle
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
395
                                                  allParams => $allParams,
396
                                                  errorMessage => $errorMessage });
397
            exit();
398 2341 sgarg
        }
399
    }
400
}
401
402
#
403 2414 sgarg
# reset a user's password upon request- no initial params
404
# only display resetpass template without any error
405
#
406
sub handleInitialResetPassword {
407
    print "Content-type: text/html\n\n";
408
    my $errorMessage = "";
409 4080 daigle
    fullTemplate( ['resetPass'], { stage => "resetpass",
410
                                   errorMessage => $errorMessage });
411
    exit();
412 2414 sgarg
}
413
414
#
415 2341 sgarg
# Construct a random string to use for a newly reset password
416
#
417
sub getRandomPassword {
418
    my $length = shift;
419
    if (!$length) {
420
        $length = 8;
421
    }
422
    my $newPass = "";
423
424
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
425
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
426
    return $newPass;
427
}
428
429
#
430
# Change a password to a new value, binding as the provided user
431
#
432
sub changePassword {
433
    my $userDN = shift;
434
    my $userPass = shift;
435
    my $bindDN = shift;
436
    my $bindPass = shift;
437
    my $o = shift;
438
439 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
440
    my $searchBase = $ldapConfig->{$o}{'base'};
441 2341 sgarg
442
    my $errorMessage = 0;
443 3177 tao
    my $ldap;
444
    if ($ldapurl =~ $mainldapurl){
445
        #if main ldap server is down, a html file containing warning message will be returned
446
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
447
    }
448
    else{
449
        #if a referral ldap server is down, we will ignore it silently
450
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
451
    }
452 2972 jones
    #$ldap->start_tls( verify => 'require',
453
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
454
    $ldap->start_tls( verify => 'none');
455 2341 sgarg
    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 3177 tao
    my $ldap;
508
    print("ldap server ", $ldapurl, "\n");
509
    if ($ldapurl =~ $mainldapurl){
510
        #if main ldap server is down, a html file containing warning message will be returned
511
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
512
    }
513
    else{
514
        #if a referral ldap server is down, we will ignore it silently
515
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
516
    }
517 2972 jones
    $ldap->start_tls( verify => 'none');
518 2341 sgarg
    my $bindresult = $ldap->bind;
519
    if ($bindresult->code) {
520
        return $entry;
521
    }
522
523 4080 daigle
    if($ldapConfig->{$org}{'filter'}){
524 2972 jones
        $mesg = $ldap->search ( base   => $base,
525 4080 daigle
                filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
526 2341 sgarg
    } else {
527 2972 jones
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
528 2341 sgarg
    }
529 3177 tao
530 2341 sgarg
    if ($mesg->count > 0) {
531
        $entry = $mesg->pop_entry;
532
        $ldap->unbind;   # take down session
533
    } else {
534
        $ldap->unbind;   # take down session
535
        # Follow references by recursive call to self
536
        my @references = $mesg->references();
537
        for (my $i = 0; $i <= $#references; $i++) {
538
            my $uri = URI->new($references[$i]);
539
            my $host = $uri->host();
540
            my $path = $uri->path();
541
            $path =~ s/^\///;
542
            $entry = &getLdapEntry($host, $path, $username, $org);
543
            if ($entry) {
544
                return $entry;
545
            }
546
        }
547
    }
548
    return $entry;
549
}
550
551
#
552
# send an email message notifying the user of the pw change
553
#
554
sub sendPasswordNotification {
555
    my $username = shift;
556
    my $org = shift;
557
    my $newPass = shift;
558
    my $recipient = shift;
559 2972 jones
    my $cfg = shift;
560 2341 sgarg
561
    my $errorMessage = "";
562
    if ($recipient) {
563
        # Send the email message to them
564
        my $smtp = Net::SMTP->new($mailhost);
565
        $smtp->mail($sender);
566
        $smtp->to($recipient);
567
568
        my $message = <<"        ENDOFMESSAGE";
569
        To: $recipient
570
        From: $sender
571
        Subject: KNB Password Reset
572
573
        Somebody (hopefully you) requested that your KNB password be reset.
574
        This is generally done when somebody forgets their password.  Your
575
        password can be changed by visiting the following URL:
576
577 4080 daigle
        $cgiUrl?stage=changepass&cfg=$cfg
578 2341 sgarg
579
            Username: $username
580
        Organization: $org
581
        New Password: $newPass
582
583
        Thanks,
584
            The KNB Development Team
585
586
        ENDOFMESSAGE
587
        $message =~ s/^[ \t\r\f]+//gm;
588
589
        $smtp->data($message);
590
        $smtp->quit;
591
    } else {
592
        $errorMessage = "Failed to send password because I " .
593
                        "couldn't find a valid email address.";
594
    }
595
    return $errorMessage;
596
}
597
598
#
599
# search the LDAP directory to see if a similar account already exists
600
#
601
sub findExistingAccounts {
602
    my $ldapurl = shift;
603
    my $base = shift;
604
    my $filter = shift;
605
    my $attref = shift;
606 3175 tao
    my $ldap;
607 2341 sgarg
608
    my $foundAccounts = 0;
609 3175 tao
    #print("the ldapurl in findExstingAccounts is ", $ldapurl, "\n");
610
    if ($ldapurl =~ $mainldapurl){
611
        #if main ldap server is down, a html file containing warning message will be returned
612 3177 tao
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure($mainldapdownmessage);
613 3175 tao
    }
614
    else{
615
        #if a referral ldap server is down, we will ignore it silently
616
        $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or return;
617
    }
618 2972 jones
    $ldap->start_tls( verify => 'none');
619 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
620
    my $mesg = $ldap->search (
621
        base   => $base,
622
        filter => $filter,
623
        attrs => @$attref,
624
    );
625
626
    if ($mesg->count() > 0) {
627
        $foundAccounts = "";
628
        my $entry;
629
        foreach $entry ($mesg->all_entries) {
630
            $foundAccounts .= "<p>\n<b><u>Account:</u> ";
631
            $foundAccounts .= $entry->dn();
632
            $foundAccounts .= "</b><br />\n";
633
            foreach my $attribute ($entry->attributes()) {
634
                $foundAccounts .= "$attribute: ";
635
                $foundAccounts .= $entry->get_value($attribute);
636
                $foundAccounts .= "<br />\n";
637
            }
638
            $foundAccounts .= "</p>\n";
639
        }
640
    }
641
    $ldap->unbind;   # take down session
642
643
    # Follow references
644
    my @references = $mesg->references();
645
    for (my $i = 0; $i <= $#references; $i++) {
646
        my $uri = URI->new($references[$i]);
647
        my $host = $uri->host();
648
        my $path = $uri->path();
649
        $path =~ s/^\///;
650
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
651
        if ($refFound) {
652
            $foundAccounts .= $refFound;
653
        }
654
    }
655
656
    #print "<p>Checking referrals...</p>\n";
657
    #my @referrals = $mesg->referrals();
658
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
659
    #for (my $i = 0; $i <= $#referrals; $i++) {
660
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
661
    #}
662
663
    return $foundAccounts;
664
}
665
666
#
667
# Validate that we have the proper set of input parameters
668
#
669
sub paramsAreValid {
670
    my @pnames = @_;
671
672
    my $allValid = 1;
673
    foreach my $parameter (@pnames) {
674
        if (!defined($query->param($parameter)) ||
675
            ! $query->param($parameter) ||
676
            $query->param($parameter) =~ /^\s+$/) {
677
            $allValid = 0;
678
        }
679
    }
680
681
    return $allValid;
682
}
683
684
#
685
# Bind to LDAP and create a new account using the information provided
686
# by the user
687
#
688
sub createAccount {
689
    my $allParams = shift;
690
691
    if ($query->param('o') =~ "LTER") {
692 4080 daigle
        fullTemplate( ['registerLter'] );
693 2341 sgarg
    } else {
694
695
        # Be sure the passwords match
696
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
697
            my $errorMessage = "The passwords do not match. Try again.";
698 4080 daigle
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
699
                                                            allParams => $allParams,
700
                                                            errorMessage => $errorMessage });
701
            exit();
702 2341 sgarg
        }
703
704 2972 jones
        my $o = $query->param('o');
705 2341 sgarg
706 4080 daigle
        my $ldapurl = $ldapConfig->{$o}{'url'};
707
        my $ldapUsername = $ldapConfig->{$o}{'user'};
708
        my $ldapPassword = $ldapConfig->{$o}{'password'};
709
        my $searchBase = $ldapConfig->{$o}{'base'};
710
        my $dnBase = $ldapConfig->{$o}{'dn'};
711 2341 sgarg
712 3177 tao
713
        #if main ldap server is down, a html file containing warning message will be returned
714
        my $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
715
716
717 2972 jones
        $ldap->start_tls( verify => 'none');
718 4080 daigle
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
719 2341 sgarg
        #print "Inserting new entry...\n";
720
        my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
721
722
        # Create a hashed version of the password
723
        my $shapass = createSeededPassHash($query->param('userPassword'));
724
725
        # Do the insertion
726
        my $additions = [
727
                'uid'   => $query->param('uid'),
728
                'o'   => $query->param('o'),
729
                'cn'   => join(" ", $query->param('givenName'),
730
                                    $query->param('sn')),
731
                'sn'   => $query->param('sn'),
732
                'givenName'   => $query->param('givenName'),
733
                'mail' => $query->param('mail'),
734
                'userPassword' => $shapass,
735
                'objectclass' => ['top', 'person', 'organizationalPerson',
736
                                'inetOrgPerson', 'uidObject' ]
737
            ];
738
        if (defined($query->param('telephoneNumber')) &&
739
            $query->param('telephoneNumber') &&
740
            ! $query->param('telephoneNumber') =~ /^\s+$/) {
741
            $$additions[$#$additions + 1] = 'telephoneNumber';
742
            $$additions[$#$additions + 1] = $query->param('telephoneNumber');
743
        }
744
        if (defined($query->param('title')) &&
745
            $query->param('title') &&
746
            ! $query->param('title') =~ /^\s+$/) {
747
            $$additions[$#$additions + 1] = 'title';
748
            $$additions[$#$additions + 1] = $query->param('title');
749
        }
750
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
751
752
        if ($result->code()) {
753 4080 daigle
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
754
                                                            allParams => $allParams,
755
                                                            errorMessage => $result->error });
756
            # TODO SCW was included as separate errors, test this
757
            #$templateVars    = setVars({ stage => "register",
758
            #                     allParams => $allParams });
759
            #$template->process( $templates->{'register'}, $templateVars);
760 2341 sgarg
        } else {
761 4080 daigle
            fullTemplate( ['success'] );
762 2341 sgarg
        }
763
764
        $ldap->unbind;   # take down session
765
    }
766
}
767
768
sub handleResponseMessage {
769
770
  print "Content-type: text/html\n\n";
771
  my $errorMessage = "You provided invalid input to the script. " .
772
                     "Try again please.";
773 4080 daigle
  fullTemplate( [], { stage => $templates->{'stage'},
774
                      errorMessage => $errorMessage });
775
  exit();
776 2341 sgarg
}
777
778
#
779
# perform a simple search against the LDAP database using
780
# a small subset of attributes of each dn and return it
781
# as a table to the calling browser.
782
#
783
sub handleSimpleSearch {
784
785
    my $o = $query->param('o');
786
787 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
788
    my $searchBase = $ldapConfig->{$o}{'base'};
789 2341 sgarg
790
    print "Content-type: text/html\n\n";
791
792
    my $allParams = {
793
                      'cn' => $query->param('cn'),
794
                      'sn' => $query->param('sn'),
795
                      'gn' => $query->param('gn'),
796
                      'o'  => $query->param('o'),
797
                      'facsimiletelephonenumber'
798
                      => $query->param('facsimiletelephonenumber'),
799
                      'mail' => $query->param('cmail'),
800
                      'telephonenumber' => $query->param('telephonenumber'),
801
                      'title' => $query->param('title'),
802
                      'uid' => $query->param('uid'),
803
                      'ou' => $query->param('ou'),
804
                    };
805
806
    # Search LDAP for matching entries that already exist
807
    my $filter = "(" .
808
                 $query->param('searchField') . "=" .
809
                 "*" .
810
                 $query->param('searchValue') .
811
                 "*" .
812
                 ")";
813
814
    my @attrs = [ 'sn',
815
                  'gn',
816
                  'cn',
817
                  'o',
818
                  'facsimiletelephonenumber',
819
                  'mail',
820
                  'telephoneNumber',
821
                  'title',
822
                  'uid',
823
                  'labeledURI',
824
                  'ou' ];
825
826
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
827
828
    # Send back the search results
829
    if ($found) {
830 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
831
                                         allParams => $allParams,
832
                                         foundAccounts => $found });
833 2341 sgarg
    } else {
834
      $found = "No entries matched your criteria.  Please try again\n";
835
836 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
837
                                         allParams => $allParams,
838
                                         foundAccounts => $found });
839 2341 sgarg
    }
840
841
    exit();
842
}
843
844
#
845
# search the LDAP directory to see if a similar account already exists
846
#
847
sub searchDirectory {
848
    my $ldapurl = shift;
849
    my $base = shift;
850
    my $filter = shift;
851
    my $attref = shift;
852
853
    my $foundAccounts = 0;
854
855 3177 tao
856
857
    #if ldap server is down, a html file containing warning message will be returned
858
    my $ldap = Net::LDAP->new($ldapurl, timeout => $TIMEOUT) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
859
860 2972 jones
    $ldap->start_tls( verify => 'none');
861 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
862
    my $mesg = $ldap->search (
863
        base   => $base,
864
        filter => $filter,
865
        attrs => @$attref,
866
    );
867
868
    if ($mesg->count() > 0) {
869
        $foundAccounts = "";
870
        my $entry;
871
        foreach $entry ($mesg->sorted(['sn'])) {
872
          $foundAccounts .= "<tr>\n<td class=\"main\">\n";
873
          $foundAccounts .= "<a href=\"" unless
874
                    (!$entry->get_value('labeledURI'));
875
          $foundAccounts .= $entry->get_value('labeledURI') unless
876
                    (!$entry->get_value('labeledURI'));
877
          $foundAccounts .= "\">\n" unless
878
                    (!$entry->get_value('labeledURI'));
879
          $foundAccounts .= $entry->get_value('givenName');
880
          $foundAccounts .= "</a>\n" unless
881
                    (!$entry->get_value('labeledURI'));
882
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
883
          $foundAccounts .= "<a href=\"" unless
884
                    (!$entry->get_value('labeledURI'));
885
          $foundAccounts .= $entry->get_value('labeledURI') unless
886
                    (!$entry->get_value('labeledURI'));
887
          $foundAccounts .= "\">\n" unless
888
                    (!$entry->get_value('labeledURI'));
889
          $foundAccounts .= $entry->get_value('sn');
890
          $foundAccounts .= "</a>\n";
891
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
892
          $foundAccounts .= $entry->get_value('mail');
893
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
894
          $foundAccounts .= $entry->get_value('telephonenumber');
895
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
896
          $foundAccounts .= $entry->get_value('title');
897
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
898
          $foundAccounts .= $entry->get_value('ou');
899
          $foundAccounts .= "\n</td>\n";
900
          $foundAccounts .= "</tr>\n";
901
        }
902
    }
903
    $ldap->unbind;   # take down session
904
    return $foundAccounts;
905
}
906
907
sub debug {
908
    my $msg = shift;
909
910
    if ($debug) {
911
        print STDERR "$msg\n";
912
    }
913
}
914 3175 tao
915 3177 tao
sub handleGeneralServerFailure {
916
    my $errorMessage = shift;
917 4080 daigle
    fullTemplate( ('mainServerFailure'), { errorMessage => $errorMessage });
918 3175 tao
    exit(0);
919
   }
920
921 4080 daigle
sub setVars {
922
    my $paramVars = shift;
923
    # initialize default parameters
924
    my $templateVars = { cfg => $cfg,
925
                         styleSkinsPath => $properties->getProperty('style-skins-path'),
926
                         styleCommonPath => $properties->getProperty('style-common-path'),
927
                         baseUrl => $properties->getProperty('baseUrl'),
928
                         orgList => \@orgList,
929
    };
930
931
    # append customized params
932
    while (my ($k, $v) = each (%$paramVars)) {
933
        $templateVars->{$k} = $v;
934
    }
935
936
    return $templateVars;
937
}
938
939
sub fullTemplate {
940
    my $templateList = shift;
941
    my $templateVars = setVars(shift);
942 3175 tao
943 4080 daigle
    $template->process( $templates->{'header'}, $templateVars );
944
945
    foreach my $tmpl (@{$templateList}) {
946
        $template->process( $templates->{$tmpl}, $templateVars );
947
    }
948
    $template->process( $templates->{'footer'}, $templateVars );
949
}