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 4394 walbridge
30
use lib '../WEB-INF/lib';
31 4080 daigle
use strict;             # turn on strict syntax checking
32
use Template;           # load the template-toolkit module
33 4394 walbridge
use CGI qw/:standard :html3/; # load the CGI module
34 4080 daigle
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 4394 walbridge
use Cwd 'abs_path';
42 2341 sgarg
43 4080 daigle
# Global configuration paramters
44 4394 walbridge
# This entire block (including skin parsing) could be pushed out to a separate .pm file
45 4080 daigle
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 4394 walbridge
    print "Content-type: text/html\n\n";
51 4080 daigle
    print "Unable to locate Metacat properties. Working directory is set as " .
52
        $workingDirectory .", is this correct?";
53
    exit(0);
54
}
55 2341 sgarg
56 4080 daigle
$properties->load(*METACAT_PROPERTIES);
57 4010 tao
58 4394 walbridge
# 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("Registry: No configuration set.");
88
    print "Content-type: text/html\n\n";
89
    print 'Registry 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("Registry: 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 4728 walbridge
# 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 4080 daigle
125
# Java uses miliseconds, Perl expects whole seconds
126 4728 walbridge
my $timeout = $properties->getProperty('ldap.connectTimeLimit') / 1000;
127
my $ldapdownmessage = "The main ldap server $ldapurl is down!";
128 4080 daigle
129 2341 sgarg
# Get the CGI input variables
130
my $query = new CGI;
131
my $debug = 0;
132
133
#--------------------------------------------------------------------------80c->
134
# Set up the Template Toolkit to read html form templates
135
136 4080 daigle
# templates hash, imported from ldap.templates tree in metacat.properties
137
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
138 4394 walbridge
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
139
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
140 2341 sgarg
141
# set some configuration options for the template object
142 4394 walbridge
my $ttConfig = {
143
             INCLUDE_PATH => $templatesDir,
144
             INTERPOLATE  => 0,
145
             POST_CHOMP   => 1,
146
             DEBUG        => 1,
147 2341 sgarg
             };
148
149
# create an instance of the template
150 4394 walbridge
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
151 2341 sgarg
152 4080 daigle
# custom LDAP properties hash
153
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
154 2341 sgarg
155 4394 walbridge
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 4080 daigle
my $ldapConfig;
165
foreach my $o (@orgList) {
166 4394 walbridge
    foreach my $d (@orgData) {
167
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
168 4080 daigle
    }
169 4394 walbridge
    # 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 2341 sgarg
}
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 4080 daigle
my $stage = $query->param('stage') || $templates->{'stage'};
184 2341 sgarg
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 2972 jones
              'changepass'        => \&handleChangePassword,
198
              'initchangepass'    => \&handleInitialChangePassword,
199 2341 sgarg
              'resetpass'         => \&handleResetPassword,
200 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
201 2341 sgarg
             );
202 4394 walbridge
203 2341 sgarg
# 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 4728 walbridge
sub fullTemplate {
215
    my $templateList = shift;
216
    my $templateVars = setVars(shift);
217 2341 sgarg
218 4728 walbridge
    $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 2341 sgarg
#
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 4080 daigle
  fullTemplate(['register'], {stage => "register"});
234 2341 sgarg
  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 4080 daigle
        fullTemplate(['register'], { stage => "register",
261
                                     allParams => $allParams,
262
                                     errorMessage => $errorMessage });
263
        exit();
264 2341 sgarg
    } else {
265 2972 jones
        my $o = $query->param('o');
266 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
267 2341 sgarg
    }
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 4080 daigle
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
296
                                                     allParams => $allParams,
297
                                                     foundAccounts => $found });
298 2341 sgarg
    # Otherwise, create a new user in the LDAP directory
299
    } else {
300 3175 tao
        #print("ingore create account\n");
301 2341 sgarg
        createAccount($allParams);
302
    }
303
304
    exit();
305
}
306
307
#
308
# process input from the registerconfirmed stage, which occurs when
309
# a user chooses to create an account despite similarities to other
310
# existing accounts
311
#
312
sub handleRegisterConfirmed {
313
314
    my $allParams = { 'givenName' => $query->param('givenName'),
315
                      'sn' => $query->param('sn'),
316 4080 daigle
                      'o' => 'unaffiliated', # only accept unaffiliated registration
317 2341 sgarg
                      'mail' => $query->param('mail'),
318
                      'uid' => $query->param('uid'),
319
                      'userPassword' => $query->param('userPassword'),
320
                      'userPassword2' => $query->param('userPassword2'),
321
                      'title' => $query->param('title'),
322
                      'telephoneNumber' => $query->param('telephoneNumber') };
323
    print "Content-type: text/html\n\n";
324
    createAccount($allParams);
325
    exit();
326
}
327
328
#
329
# change a user's password upon request
330
#
331
sub handleChangePassword {
332
333
    print "Content-type: text/html\n\n";
334
335
    my $allParams = { 'test' => "1", };
336
    if ($query->param('uid')) {
337
        $$allParams{'uid'} = $query->param('uid');
338
    }
339
    if ($query->param('o')) {
340
        $$allParams{'o'} = $query->param('o');
341 2972 jones
        my $o = $query->param('o');
342
343 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
344 2341 sgarg
    }
345
346
347
    # Check that all required fields are provided and not null
348
    my @requiredParams = ( 'uid', 'o', 'oldpass',
349
                           'userPassword', 'userPassword2');
350
    if (! paramsAreValid(@requiredParams)) {
351
        my $errorMessage = "Required information is missing. " .
352
            "Please fill in all required fields and submit the form.";
353 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
354
                                        allParams => $allParams,
355
                                        errorMessage => $errorMessage });
356
        exit();
357 2341 sgarg
    }
358
359
    # We have all of the info we need, so try to change the password
360
    if ($query->param('userPassword') =~ $query->param('userPassword2')) {
361
362 2972 jones
        my $o = $query->param('o');
363 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
364
        $ldapUsername = $ldapConfig->{$o}{'user'};
365
        $ldapPassword = $ldapConfig->{$o}{'password'};
366 2341 sgarg
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 2341 sgarg
        } else {
371
            my $errorMessage = changePassword(
372
                    $dn, $query->param('userPassword'),
373
                    $dn, $query->param('oldpass'), $query->param('o'));
374 2972 jones
            if ($errorMessage) {
375 4080 daigle
                fullTemplate( ['changePass'], { stage => "changepass",
376
                                                allParams => $allParams,
377
                                                errorMessage => $errorMessage });
378
                exit();
379 2341 sgarg
            } else {
380 4080 daigle
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
381
                                                       allParams => $allParams });
382
                exit();
383 2341 sgarg
            }
384
        }
385
    } else {
386
        my $errorMessage = "The passwords do not match. Try again.";
387 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
388
                                        allParams => $allParams,
389
                                        errorMessage => $errorMessage });
390
        exit();
391 2341 sgarg
    }
392
}
393
394
#
395 2414 sgarg
# change a user's password upon request - no input params
396
# only display chagepass template without any error
397
#
398
sub handleInitialChangePassword {
399
    print "Content-type: text/html\n\n";
400
401
    my $allParams = { 'test' => "1", };
402
    my $errorMessage = "";
403 4080 daigle
    fullTemplate( ['changePass'], { stage => "changepass",
404
                                    errorMessage => $errorMessage });
405
    exit();
406 2414 sgarg
}
407
408
#
409 2341 sgarg
# reset a user's password upon request
410
#
411
sub handleResetPassword {
412
413
    print "Content-type: text/html\n\n";
414
415
    my $allParams = { 'test' => "1", };
416
    if ($query->param('uid')) {
417
        $$allParams{'uid'} = $query->param('uid');
418
    }
419
    if ($query->param('o')) {
420
        $$allParams{'o'} = $query->param('o');
421 2972 jones
        my $o = $query->param('o');
422
423 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
424
        $ldapUsername = $ldapConfig->{$o}{'user'};
425
        $ldapPassword = $ldapConfig->{$o}{'password'};
426 2341 sgarg
    }
427
428
    # Check that all required fields are provided and not null
429
    my @requiredParams = ( 'uid', 'o' );
430
    if (! paramsAreValid(@requiredParams)) {
431
        my $errorMessage = "Required information is missing. " .
432
            "Please fill in all required fields and submit the form.";
433 4080 daigle
        fullTemplate( ['resetPass'],  { stage => "resetpass",
434
                                        allParams => $allParams,
435
                                        errorMessage => $errorMessage });
436
        exit();
437 2341 sgarg
    }
438
439
    # We have all of the info we need, so try to change the password
440
    my $o = $query->param('o');
441 4080 daigle
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
442 2341 sgarg
    if ($query->param('o') =~ "LTER") {
443 4080 daigle
        fullTemplate( ['registerLter'] );
444
        exit();
445 2341 sgarg
    } else {
446
        my $errorMessage = "";
447
        my $recipient;
448
        my $userPass;
449
        my $entry = getLdapEntry($ldapurl, $searchBase,
450
                $query->param('uid'), $query->param('o'));
451
452
        if ($entry) {
453
            $recipient = $entry->get_value('mail');
454
            $userPass = getRandomPassword();
455 4080 daigle
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
456 2341 sgarg
        } else {
457
            $errorMessage = "User not found in database.  Please try again.";
458
        }
459
460
        if ($errorMessage) {
461 4080 daigle
            fullTemplate( ['resetPass'], { stage => "resetpass",
462
                                           allParams => $allParams,
463
                                           errorMessage => $errorMessage });
464
            exit();
465 2341 sgarg
        } else {
466
            my $errorMessage = sendPasswordNotification($query->param('uid'),
467 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
468 4080 daigle
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
469
                                                  allParams => $allParams,
470
                                                  errorMessage => $errorMessage });
471
            exit();
472 2341 sgarg
        }
473
    }
474
}
475
476
#
477 2414 sgarg
# reset a user's password upon request- no initial params
478
# only display resetpass template without any error
479
#
480
sub handleInitialResetPassword {
481
    print "Content-type: text/html\n\n";
482
    my $errorMessage = "";
483 4080 daigle
    fullTemplate( ['resetPass'], { stage => "resetpass",
484
                                   errorMessage => $errorMessage });
485
    exit();
486 2414 sgarg
}
487
488
#
489 2341 sgarg
# Construct a random string to use for a newly reset password
490
#
491
sub getRandomPassword {
492
    my $length = shift;
493
    if (!$length) {
494
        $length = 8;
495
    }
496
    my $newPass = "";
497
498
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
499
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
500
    return $newPass;
501
}
502
503
#
504
# Change a password to a new value, binding as the provided user
505
#
506
sub changePassword {
507
    my $userDN = shift;
508
    my $userPass = shift;
509
    my $bindDN = shift;
510
    my $bindPass = shift;
511
    my $o = shift;
512
513 4080 daigle
    my $searchBase = $ldapConfig->{$o}{'base'};
514 2341 sgarg
515
    my $errorMessage = 0;
516 3177 tao
    my $ldap;
517 4394 walbridge
518
		#if main ldap server is down, a html file containing warning message will be returned
519
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
520
521
		#$ldap->start_tls( verify => 'require',
522 2972 jones
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
523
    $ldap->start_tls( verify => 'none');
524 2341 sgarg
    my $bindresult = $ldap->bind( version => 3, dn => $bindDN,
525
                                  password => $bindPass );
526
    if ($bindresult->code) {
527
        $errorMessage = "Failed to log in. Are you sure your old " .
528
                        "password is correct? Try again...";
529
        return $errorMessage;
530
    }
531
532
    # Find the user here and change their entry
533
    my $newpass = createSeededPassHash($userPass);
534
    my $modifications = { userPassword => $newpass };
535
    my $result = $ldap->modify( $userDN, replace => { %$modifications });
536
537
    if ($result->code()) {
538
        my $errorMessage = "There was an error changing the password." .
539
                           "<br />\n" . $result->error;
540
    }
541
    $ldap->unbind;   # take down session
542
543
    return $errorMessage;
544
}
545
546
#
547
# generate a Seeded SHA1 hash of a plaintext password
548
#
549
sub createSeededPassHash {
550
    my $secret = shift;
551
552
    my $salt = "";
553
    for (my $i=0; $i < 4; $i++) {
554
        $salt .= int(rand(10));
555
    }
556
557
    my $ctx = Digest::SHA1->new;
558
    $ctx->add($secret);
559
    $ctx->add($salt);
560
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
561
562
    return $hashedPasswd;
563
}
564
565
#
566
# Look up an ldap entry for a user
567
#
568
sub getLdapEntry {
569
    my $ldapurl = shift;
570
    my $base = shift;
571
    my $username = shift;
572
    my $org = shift;
573
574
    my $entry = "";
575
    my $mesg;
576 3177 tao
    my $ldap;
577
    print("ldap server ", $ldapurl, "\n");
578 4394 walbridge
579
    #if main ldap server is down, a html file containing warning message will be returned
580
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
581 2972 jones
    $ldap->start_tls( verify => 'none');
582 2341 sgarg
    my $bindresult = $ldap->bind;
583
    if ($bindresult->code) {
584
        return $entry;
585
    }
586
587 4080 daigle
    if($ldapConfig->{$org}{'filter'}){
588 2972 jones
        $mesg = $ldap->search ( base   => $base,
589 4080 daigle
                filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
590 2341 sgarg
    } else {
591 2972 jones
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
592 2341 sgarg
    }
593 3177 tao
594 2341 sgarg
    if ($mesg->count > 0) {
595
        $entry = $mesg->pop_entry;
596
        $ldap->unbind;   # take down session
597
    } else {
598
        $ldap->unbind;   # take down session
599
        # Follow references by recursive call to self
600
        my @references = $mesg->references();
601
        for (my $i = 0; $i <= $#references; $i++) {
602
            my $uri = URI->new($references[$i]);
603
            my $host = $uri->host();
604
            my $path = $uri->path();
605
            $path =~ s/^\///;
606
            $entry = &getLdapEntry($host, $path, $username, $org);
607
            if ($entry) {
608
                return $entry;
609
            }
610
        }
611
    }
612
    return $entry;
613
}
614
615
#
616
# send an email message notifying the user of the pw change
617
#
618
sub sendPasswordNotification {
619
    my $username = shift;
620
    my $org = shift;
621
    my $newPass = shift;
622
    my $recipient = shift;
623 2972 jones
    my $cfg = shift;
624 2341 sgarg
625
    my $errorMessage = "";
626
    if ($recipient) {
627 4394 walbridge
				my $mailhost = $properties->getProperty('email.mailhost');
628
				my $sender =  $properties->getProperty('email.sender');
629 2341 sgarg
        # Send the email message to them
630
        my $smtp = Net::SMTP->new($mailhost);
631
        $smtp->mail($sender);
632
        $smtp->to($recipient);
633
634
        my $message = <<"        ENDOFMESSAGE";
635
        To: $recipient
636
        From: $sender
637
        Subject: KNB Password Reset
638
639
        Somebody (hopefully you) requested that your KNB password be reset.
640
        This is generally done when somebody forgets their password.  Your
641
        password can be changed by visiting the following URL:
642
643 4080 daigle
        $cgiUrl?stage=changepass&cfg=$cfg
644 2341 sgarg
645
            Username: $username
646
        Organization: $org
647
        New Password: $newPass
648
649
        Thanks,
650
            The KNB Development Team
651
652
        ENDOFMESSAGE
653
        $message =~ s/^[ \t\r\f]+//gm;
654
655
        $smtp->data($message);
656
        $smtp->quit;
657
    } else {
658
        $errorMessage = "Failed to send password because I " .
659
                        "couldn't find a valid email address.";
660
    }
661
    return $errorMessage;
662
}
663
664
#
665
# search the LDAP directory to see if a similar account already exists
666
#
667
sub findExistingAccounts {
668
    my $ldapurl = shift;
669
    my $base = shift;
670
    my $filter = shift;
671
    my $attref = shift;
672 3175 tao
    my $ldap;
673 2341 sgarg
674
    my $foundAccounts = 0;
675 3175 tao
    #print("the ldapurl in findExstingAccounts is ", $ldapurl, "\n");
676 4394 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
677
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure($ldapdownmessage);
678 2972 jones
    $ldap->start_tls( verify => 'none');
679 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
680
    my $mesg = $ldap->search (
681
        base   => $base,
682
        filter => $filter,
683
        attrs => @$attref,
684
    );
685
686
    if ($mesg->count() > 0) {
687
        $foundAccounts = "";
688
        my $entry;
689
        foreach $entry ($mesg->all_entries) {
690
            $foundAccounts .= "<p>\n<b><u>Account:</u> ";
691
            $foundAccounts .= $entry->dn();
692
            $foundAccounts .= "</b><br />\n";
693
            foreach my $attribute ($entry->attributes()) {
694
                $foundAccounts .= "$attribute: ";
695
                $foundAccounts .= $entry->get_value($attribute);
696
                $foundAccounts .= "<br />\n";
697
            }
698
            $foundAccounts .= "</p>\n";
699
        }
700
    }
701
    $ldap->unbind;   # take down session
702
703
    # Follow references
704
    my @references = $mesg->references();
705
    for (my $i = 0; $i <= $#references; $i++) {
706
        my $uri = URI->new($references[$i]);
707
        my $host = $uri->host();
708
        my $path = $uri->path();
709
        $path =~ s/^\///;
710
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
711
        if ($refFound) {
712
            $foundAccounts .= $refFound;
713
        }
714
    }
715
716
    #print "<p>Checking referrals...</p>\n";
717
    #my @referrals = $mesg->referrals();
718
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
719
    #for (my $i = 0; $i <= $#referrals; $i++) {
720
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
721
    #}
722
723
    return $foundAccounts;
724
}
725
726
#
727
# Validate that we have the proper set of input parameters
728
#
729
sub paramsAreValid {
730
    my @pnames = @_;
731
732
    my $allValid = 1;
733
    foreach my $parameter (@pnames) {
734
        if (!defined($query->param($parameter)) ||
735
            ! $query->param($parameter) ||
736
            $query->param($parameter) =~ /^\s+$/) {
737
            $allValid = 0;
738
        }
739
    }
740
741
    return $allValid;
742
}
743
744
#
745
# Bind to LDAP and create a new account using the information provided
746
# by the user
747
#
748
sub createAccount {
749
    my $allParams = shift;
750
751
    if ($query->param('o') =~ "LTER") {
752 4080 daigle
        fullTemplate( ['registerLter'] );
753 2341 sgarg
    } else {
754
755
        # Be sure the passwords match
756
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
757
            my $errorMessage = "The passwords do not match. Try again.";
758 4080 daigle
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
759
                                                            allParams => $allParams,
760
                                                            errorMessage => $errorMessage });
761
            exit();
762 2341 sgarg
        }
763
764 2972 jones
        my $o = $query->param('o');
765 2341 sgarg
766 4080 daigle
        my $ldapUsername = $ldapConfig->{$o}{'user'};
767
        my $ldapPassword = $ldapConfig->{$o}{'password'};
768
        my $searchBase = $ldapConfig->{$o}{'base'};
769
        my $dnBase = $ldapConfig->{$o}{'dn'};
770 3177 tao
771
        #if main ldap server is down, a html file containing warning message will be returned
772 4394 walbridge
        my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
773 3177 tao
774
775 2972 jones
        $ldap->start_tls( verify => 'none');
776 4080 daigle
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
777 2341 sgarg
        #print "Inserting new entry...\n";
778
        my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
779
780
        # Create a hashed version of the password
781
        my $shapass = createSeededPassHash($query->param('userPassword'));
782
783
        # Do the insertion
784
        my $additions = [
785
                'uid'   => $query->param('uid'),
786
                'o'   => $query->param('o'),
787
                'cn'   => join(" ", $query->param('givenName'),
788
                                    $query->param('sn')),
789
                'sn'   => $query->param('sn'),
790
                'givenName'   => $query->param('givenName'),
791
                'mail' => $query->param('mail'),
792
                'userPassword' => $shapass,
793
                'objectclass' => ['top', 'person', 'organizationalPerson',
794
                                'inetOrgPerson', 'uidObject' ]
795
            ];
796
        if (defined($query->param('telephoneNumber')) &&
797
            $query->param('telephoneNumber') &&
798
            ! $query->param('telephoneNumber') =~ /^\s+$/) {
799
            $$additions[$#$additions + 1] = 'telephoneNumber';
800
            $$additions[$#$additions + 1] = $query->param('telephoneNumber');
801
        }
802
        if (defined($query->param('title')) &&
803
            $query->param('title') &&
804
            ! $query->param('title') =~ /^\s+$/) {
805
            $$additions[$#$additions + 1] = 'title';
806
            $$additions[$#$additions + 1] = $query->param('title');
807
        }
808
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
809
810
        if ($result->code()) {
811 4080 daigle
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
812
                                                            allParams => $allParams,
813
                                                            errorMessage => $result->error });
814
            # TODO SCW was included as separate errors, test this
815
            #$templateVars    = setVars({ stage => "register",
816
            #                     allParams => $allParams });
817
            #$template->process( $templates->{'register'}, $templateVars);
818 2341 sgarg
        } else {
819 4080 daigle
            fullTemplate( ['success'] );
820 2341 sgarg
        }
821
822
        $ldap->unbind;   # take down session
823
    }
824
}
825
826
sub handleResponseMessage {
827
828
  print "Content-type: text/html\n\n";
829
  my $errorMessage = "You provided invalid input to the script. " .
830
                     "Try again please.";
831 4080 daigle
  fullTemplate( [], { stage => $templates->{'stage'},
832
                      errorMessage => $errorMessage });
833
  exit();
834 2341 sgarg
}
835
836
#
837
# perform a simple search against the LDAP database using
838
# a small subset of attributes of each dn and return it
839
# as a table to the calling browser.
840
#
841
sub handleSimpleSearch {
842
843
    my $o = $query->param('o');
844
845 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
846
    my $searchBase = $ldapConfig->{$o}{'base'};
847 2341 sgarg
848
    print "Content-type: text/html\n\n";
849
850
    my $allParams = {
851
                      'cn' => $query->param('cn'),
852
                      'sn' => $query->param('sn'),
853
                      'gn' => $query->param('gn'),
854
                      'o'  => $query->param('o'),
855
                      'facsimiletelephonenumber'
856
                      => $query->param('facsimiletelephonenumber'),
857
                      'mail' => $query->param('cmail'),
858
                      'telephonenumber' => $query->param('telephonenumber'),
859
                      'title' => $query->param('title'),
860
                      'uid' => $query->param('uid'),
861
                      'ou' => $query->param('ou'),
862
                    };
863
864
    # Search LDAP for matching entries that already exist
865
    my $filter = "(" .
866
                 $query->param('searchField') . "=" .
867
                 "*" .
868
                 $query->param('searchValue') .
869
                 "*" .
870
                 ")";
871
872
    my @attrs = [ 'sn',
873
                  'gn',
874
                  'cn',
875
                  'o',
876
                  'facsimiletelephonenumber',
877
                  'mail',
878
                  'telephoneNumber',
879
                  'title',
880
                  'uid',
881
                  'labeledURI',
882
                  'ou' ];
883
884
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
885
886
    # Send back the search results
887
    if ($found) {
888 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
889
                                         allParams => $allParams,
890
                                         foundAccounts => $found });
891 2341 sgarg
    } else {
892
      $found = "No entries matched your criteria.  Please try again\n";
893
894 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
895
                                         allParams => $allParams,
896
                                         foundAccounts => $found });
897 2341 sgarg
    }
898
899
    exit();
900
}
901
902
#
903
# search the LDAP directory to see if a similar account already exists
904
#
905
sub searchDirectory {
906
    my $ldapurl = shift;
907
    my $base = shift;
908
    my $filter = shift;
909
    my $attref = shift;
910
911
    my $foundAccounts = 0;
912 3177 tao
913
    #if ldap server is down, a html file containing warning message will be returned
914 4394 walbridge
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleGeneralServerFailure("The ldap server " . $ldapurl . " is down!");
915 3177 tao
916 2972 jones
    $ldap->start_tls( verify => 'none');
917 2341 sgarg
    $ldap->bind( version => 3, anonymous => 1);
918
    my $mesg = $ldap->search (
919
        base   => $base,
920
        filter => $filter,
921
        attrs => @$attref,
922
    );
923
924
    if ($mesg->count() > 0) {
925
        $foundAccounts = "";
926
        my $entry;
927
        foreach $entry ($mesg->sorted(['sn'])) {
928
          $foundAccounts .= "<tr>\n<td class=\"main\">\n";
929
          $foundAccounts .= "<a href=\"" unless
930
                    (!$entry->get_value('labeledURI'));
931
          $foundAccounts .= $entry->get_value('labeledURI') unless
932
                    (!$entry->get_value('labeledURI'));
933
          $foundAccounts .= "\">\n" unless
934
                    (!$entry->get_value('labeledURI'));
935
          $foundAccounts .= $entry->get_value('givenName');
936
          $foundAccounts .= "</a>\n" unless
937
                    (!$entry->get_value('labeledURI'));
938
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
939
          $foundAccounts .= "<a href=\"" unless
940
                    (!$entry->get_value('labeledURI'));
941
          $foundAccounts .= $entry->get_value('labeledURI') unless
942
                    (!$entry->get_value('labeledURI'));
943
          $foundAccounts .= "\">\n" unless
944
                    (!$entry->get_value('labeledURI'));
945
          $foundAccounts .= $entry->get_value('sn');
946
          $foundAccounts .= "</a>\n";
947
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
948
          $foundAccounts .= $entry->get_value('mail');
949
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
950
          $foundAccounts .= $entry->get_value('telephonenumber');
951
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
952
          $foundAccounts .= $entry->get_value('title');
953
          $foundAccounts .= "\n</td>\n<td class=\"main\">\n";
954
          $foundAccounts .= $entry->get_value('ou');
955
          $foundAccounts .= "\n</td>\n";
956
          $foundAccounts .= "</tr>\n";
957
        }
958
    }
959
    $ldap->unbind;   # take down session
960
    return $foundAccounts;
961
}
962
963
sub debug {
964
    my $msg = shift;
965
966
    if ($debug) {
967
        print STDERR "$msg\n";
968
    }
969
}
970 3175 tao
971 3177 tao
sub handleGeneralServerFailure {
972
    my $errorMessage = shift;
973 4728 walbridge
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
974 3175 tao
    exit(0);
975
   }
976
977 4080 daigle
sub setVars {
978
    my $paramVars = shift;
979
    # initialize default parameters
980
    my $templateVars = { cfg => $cfg,
981 4394 walbridge
                         styleSkinsPath => $contextUrl . "/style/skins",
982
                         styleCommonPath => $contextUrl . "/style/common",
983
                         contextUrl => $contextUrl,
984 4080 daigle
                         orgList => \@orgList,
985 4394 walbridge
                         config  => $config,
986 4080 daigle
    };
987
988
    # append customized params
989
    while (my ($k, $v) = each (%$paramVars)) {
990
        $templateVars->{$k} = $v;
991
    }
992
993
    return $templateVars;
994
}