Project

General

Profile

1 2341 sgarg
#!/usr/bin/perl -w
2 4865 walbridge
#
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 2341 sgarg
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 8166 tao
use Captcha::reCAPTCHA; # for protection against spams
42 4394 walbridge
use Cwd 'abs_path';
43 2341 sgarg
44 4080 daigle
# Global configuration paramters
45 4394 walbridge
# This entire block (including skin parsing) could be pushed out to a separate .pm file
46 4080 daigle
my $cgiUrl = $ENV{'SCRIPT_FILENAME'};
47
my $workingDirectory = dirname($cgiUrl);
48
my $metacatProps = "${workingDirectory}/../WEB-INF/metacat.properties";
49
my $properties = new Config::Properties();
50
unless (open (METACAT_PROPERTIES, $metacatProps)) {
51 4394 walbridge
    print "Content-type: text/html\n\n";
52 4080 daigle
    print "Unable to locate Metacat properties. Working directory is set as " .
53
        $workingDirectory .", is this correct?";
54
    exit(0);
55
}
56 2341 sgarg
57 4080 daigle
$properties->load(*METACAT_PROPERTIES);
58 4010 tao
59 4394 walbridge
# local directory configuration
60
my $skinsDir = "${workingDirectory}/../style/skins";
61
my $templatesDir = abs_path("${workingDirectory}/../style/common/templates");
62
my $tempDir = $properties->getProperty('application.tempDir');
63
64
# url configuration
65
my $server = $properties->splitToTree(qr/\./, 'server');
66 7199 leinfelder
my $protocol = 'http://';
67
if ( $properties->getProperty('server.httpPort') eq '443' ) {
68
	$protocol = 'https://';
69
}
70
my $contextUrl = $protocol . $properties->getProperty('server.name');
71 4864 walbridge
if ($properties->getProperty('server.httpPort') ne '80') {
72
        $contextUrl = $contextUrl . ':' . $properties->getProperty('server.httpPort');
73
}
74
$contextUrl = $contextUrl . '/' .  $properties->getProperty('application.context');
75 4394 walbridge
76
my $metacatUrl = $contextUrl . "/metacat";
77
my $cgiPrefix = "/" . $properties->getProperty('application.context') . "/cgi-bin";
78
my $styleSkinsPath = $contextUrl . "/style/skins";
79
my $styleCommonPath = $contextUrl . "/style/common";
80
81 8169 tao
#recaptcha key information
82
my $recaptchaPublicKey=$properties->getProperty('ldap.recaptcha.publickey');
83
my $recaptchaPrivateKey=$properties->getProperty('ldap.recaptcha.privatekey');
84
85 4394 walbridge
my @errorMessages;
86
my $error = 0;
87
88
# Import all of the HTML form fields as variables
89
import_names('FORM');
90
91
# Must have a config to use Metacat
92
my $skinName = "";
93
if ($FORM::cfg) {
94
    $skinName = $FORM::cfg;
95
} elsif ($ARGV[0]) {
96
    $skinName = $ARGV[0];
97
} else {
98 4747 walbridge
    debug("No configuration set.");
99 4394 walbridge
    print "Content-type: text/html\n\n";
100 4749 walbridge
    print 'LDAPweb Error: The registry requires a skin name to continue.';
101 4394 walbridge
    exit();
102
}
103
104
# Metacat isn't initialized, the registry will fail in strange ways.
105
if (!($metacatUrl)) {
106 4747 walbridge
    debug("No Metacat.");
107 4394 walbridge
    print "Content-type: text/html\n\n";
108
    'Registry Error: Metacat is not initialized! Make sure' .
109 5214 walbridge
        ' MetacatUrl is set correctly in ' .  $skinName . '.properties';
110 4394 walbridge
    exit();
111
}
112
113
my $skinProperties = new Config::Properties();
114
if (!($skinName)) {
115
    $error = "Application misconfigured.  Please contact the administrator.";
116
    push(@errorMessages, $error);
117
} else {
118
    my $skinProps = "$skinsDir/$skinName/$skinName.properties";
119
    unless (open (SKIN_PROPERTIES, $skinProps)) {
120
        print "Content-type: text/html\n\n";
121
        print "Unable to locate skin properties at $skinProps.  Is this path correct?";
122
        exit(0);
123
    }
124
    $skinProperties->load(*SKIN_PROPERTIES);
125
}
126
127
my $config = $skinProperties->splitToTree(qr/\./, 'registry.config');
128
129 4870 walbridge
# XXX HACK: this is a temporary fix to pull out the UCNRS password property from the
130
#           NRS skin instead of metacat.properties. The intent is to prevent editing
131
#           of our core properties file, which is manipulated purely through the web.
132
#           Once organizations are editable, this section should be removed as should
133
#           the properties within nrs/nrs.properties.
134
my $nrsProperties = new Config::Properties();
135
my $nrsProps = "$skinsDir/nrs/nrs.properties";
136
unless (open (NRS_PROPERTIES, $nrsProps)) {
137
    print "Content-type: text/html\n\n";
138
    print "Unable to locate skin properties at $nrsProps.  Is this path correct?";
139
    exit(0);
140
}
141
$nrsProperties->load(*NRS_PROPERTIES);
142
143
my $nrsConfig = $nrsProperties->splitToTree(qr/\./, 'registry.config');
144
145
# XXX END HACK
146
147
148 4394 walbridge
my $searchBase;
149
my $ldapUsername;
150
my $ldapPassword;
151 4728 walbridge
# TODO: when should we use surl instead? Is there a setting promoting one over the other?
152
# TODO: the default tree for accounts should be exposed somewhere, defaulting to unaffiliated
153
my $ldapurl = $properties->getProperty('auth.url');
154 4080 daigle
155
# Java uses miliseconds, Perl expects whole seconds
156 4728 walbridge
my $timeout = $properties->getProperty('ldap.connectTimeLimit') / 1000;
157 4080 daigle
158 2341 sgarg
# Get the CGI input variables
159
my $query = new CGI;
160 4747 walbridge
my $debug = 1;
161 2341 sgarg
162
#--------------------------------------------------------------------------80c->
163
# Set up the Template Toolkit to read html form templates
164
165 4080 daigle
# templates hash, imported from ldap.templates tree in metacat.properties
166
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
167 4394 walbridge
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
168
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
169 2341 sgarg
170
# set some configuration options for the template object
171 4394 walbridge
my $ttConfig = {
172
             INCLUDE_PATH => $templatesDir,
173
             INTERPOLATE  => 0,
174
             POST_CHOMP   => 1,
175
             DEBUG        => 1,
176 2341 sgarg
             };
177
178
# create an instance of the template
179 4394 walbridge
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
180 2341 sgarg
181 4080 daigle
# custom LDAP properties hash
182
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
183 2341 sgarg
184 4394 walbridge
my $orgProps = $properties->splitToTree(qr/\./, 'organization');
185
my $orgNames = $properties->splitToTree(qr/\./, 'organization.name');
186
# pull out properties available e.g. 'name', 'base'
187
my @orgData = keys(%$orgProps);
188 4870 walbridge
189 4394 walbridge
my @orgList;
190
while (my ($oKey, $oVal) = each(%$orgNames)) {
191
    push(@orgList, $oKey);
192
}
193
194 4866 walbridge
my $authBase = $properties->getProperty("auth.base");
195 4080 daigle
my $ldapConfig;
196
foreach my $o (@orgList) {
197 4394 walbridge
    foreach my $d (@orgData) {
198
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
199 4080 daigle
    }
200 4866 walbridge
201 4870 walbridge
    # XXX hack, remove after 1.9
202
    if ($o eq 'UCNRS') {
203
        $ldapConfig->{'UCNRS'}{'base'} = $nrsConfig->{'base'};
204
        $ldapConfig->{'UCNRS'}{'user'} = $nrsConfig->{'username'};
205
        $ldapConfig->{'UCNRS'}{'password'} = $nrsConfig->{'password'};
206
    }
207
208 4866 walbridge
    # set default base
209
    if (!$ldapConfig->{$o}{'base'}) {
210
        $ldapConfig->{$o}{'base'} = $authBase;
211
    }
212
213
    # include filter information. By default, our filters are 'o=$name', e.g. 'o=NAPIER'
214
    # these can be overridden by specifying them in metacat.properties. Non-default configs
215
    # such as UCNRS must specify all LDAP properties.
216
    if ($ldapConfig->{$o}{'base'} eq $authBase) {
217
        my $filter = "o=$o";
218
        if (!$ldapConfig->{$o}{'org'}) {
219
            $ldapConfig->{$o}{'org'} = $filter;
220
        }
221
        if (!$ldapConfig->{$o}{'filter'}) {
222
            $ldapConfig->{$o}{'filter'} = $filter;
223
        }
224
        # also include DN, which is just org + base
225
        if ($ldapConfig->{$o}{'org'}) {
226
            $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'org'} . "," . $ldapConfig->{$o}{'base'};
227
        }
228 4394 walbridge
    } else {
229
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'base'};
230
    }
231 4868 walbridge
232
    # set LDAP administrator user account
233 4866 walbridge
    if (!$ldapConfig->{$o}{'user'}) {
234
        $ldapConfig->{$o}{'user'} = $ldapConfig->{'unaffiliated'}{'user'};
235 4865 walbridge
    }
236 4868 walbridge
    # check for a fully qualified LDAP name. If it doesn't exist, append base.
237
    my @userParts = split(',', $ldapConfig->{$o}{'user'});
238
    if (scalar(@userParts) == 1) {
239
        $ldapConfig->{$o}{'user'} = $ldapConfig->{$o}{'user'} . "," . $ldapConfig->{$o}{'base'};
240
    }
241 4866 walbridge
242
    if (!$ldapConfig->{$o}{'password'}) {
243
        $ldapConfig->{$o}{'password'} = $ldapConfig->{'unaffiliated'}{'password'};
244
    }
245 2341 sgarg
}
246
247
#--------------------------------------------------------------------------80c->
248
# Define the main program logic that calls subroutines to do the work
249
#--------------------------------------------------------------------------80c->
250
251
# The processing step we are handling
252 4080 daigle
my $stage = $query->param('stage') || $templates->{'stage'};
253 2341 sgarg
254
my $cfg = $query->param('cfg');
255 4767 walbridge
debug("started with stage $stage, cfg $cfg");
256 2341 sgarg
257
# define the possible stages
258
my %stages = (
259
              'initregister'      => \&handleInitRegister,
260
              'register'          => \&handleRegister,
261
              'registerconfirmed' => \&handleRegisterConfirmed,
262
              'simplesearch'      => \&handleSimpleSearch,
263
              'initaddentry'      => \&handleInitAddEntry,
264
              'addentry'          => \&handleAddEntry,
265
              'initmodifyentry'   => \&handleInitModifyEntry,
266
              'modifyentry'       => \&handleModifyEntry,
267 2972 jones
              'changepass'        => \&handleChangePassword,
268
              'initchangepass'    => \&handleInitialChangePassword,
269 2341 sgarg
              'resetpass'         => \&handleResetPassword,
270 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
271 8175 tao
              'createtemppass'    => \&createTemporaryAccount,
272 2341 sgarg
             );
273 4394 walbridge
274 2341 sgarg
# call the appropriate routine based on the stage
275
if ( $stages{$stage} ) {
276
  $stages{$stage}->();
277
} else {
278
  &handleResponseMessage();
279
}
280
281
#--------------------------------------------------------------------------80c->
282
# Define the subroutines to do the work
283
#--------------------------------------------------------------------------80c->
284
285 4728 walbridge
sub fullTemplate {
286
    my $templateList = shift;
287
    my $templateVars = setVars(shift);
288 8166 tao
    my $c = Captcha::reCAPTCHA->new;
289
    my $captcha = 'captcha';
290
    #my $error=null;
291
    my $use_ssl= 1;
292
    #my $options=null;
293 8169 tao
    $templateVars->{$captcha} = $c->get_html($recaptchaPublicKey,undef, $use_ssl, undef);
294 4728 walbridge
    $template->process( $templates->{'header'}, $templateVars );
295
    foreach my $tmpl (@{$templateList}) {
296
        $template->process( $templates->{$tmpl}, $templateVars );
297
    }
298
    $template->process( $templates->{'footer'}, $templateVars );
299
}
300
301 2341 sgarg
#
302
# create the initial registration form
303
#
304
sub handleInitRegister {
305
  my $vars = shift;
306
  print "Content-type: text/html\n\n";
307
  # process the template files:
308 4080 daigle
  fullTemplate(['register'], {stage => "register"});
309 2341 sgarg
  exit();
310
}
311
312
#
313
# process input from the register stage, which occurs when
314
# a user submits form data to create a new account
315
#
316
sub handleRegister {
317
318
    print "Content-type: text/html\n\n";
319 8166 tao
320
321 2341 sgarg
    my $allParams = { 'givenName' => $query->param('givenName'),
322
                      'sn' => $query->param('sn'),
323
                      'o' => $query->param('o'),
324
                      'mail' => $query->param('mail'),
325
                      'uid' => $query->param('uid'),
326
                      'userPassword' => $query->param('userPassword'),
327
                      'userPassword2' => $query->param('userPassword2'),
328
                      'title' => $query->param('title'),
329
                      'telephoneNumber' => $query->param('telephoneNumber') };
330 8166 tao
331
    # Check the recaptcha
332
    my $c = Captcha::reCAPTCHA->new;
333
    my $challenge = $query->param('recaptcha_challenge_field');
334
    my $response = $query->param('recaptcha_response_field');
335
    # Verify submission
336
    my $result = $c->check_answer(
337 8169 tao
        $recaptchaPrivateKey, $ENV{'REMOTE_ADDR'},
338 8166 tao
        $challenge, $response
339
    );
340
341
    if ( $result->{is_valid} ) {
342
        #print "Yes!";
343
        #exit();
344
    }
345
    else {
346
        my $errorMessage = "The verification code is wrong. Please input again.";
347
        fullTemplate(['register'], { stage => "register",
348
                                     allParams => $allParams,
349
                                     errorMessage => $errorMessage });
350
        exit();
351
    }
352
353
354 2341 sgarg
    # Check that all required fields are provided and not null
355
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail',
356
                           'uid', 'userPassword', 'userPassword2');
357
    if (! paramsAreValid(@requiredParams)) {
358
        my $errorMessage = "Required information is missing. " .
359
            "Please fill in all required fields and resubmit the form.";
360 4080 daigle
        fullTemplate(['register'], { stage => "register",
361
                                     allParams => $allParams,
362
                                     errorMessage => $errorMessage });
363
        exit();
364 2341 sgarg
    } else {
365 2972 jones
        my $o = $query->param('o');
366 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
367 2341 sgarg
    }
368
369
    # Search LDAP for matching entries that already exist
370
    # Some forms use a single text search box, whereas others search per
371
    # attribute.
372
    my $filter;
373
    if ($query->param('searchField')) {
374
375
      $filter = "(|" .
376
                "(uid=" . $query->param('searchField') . ") " .
377
                "(mail=" . $query->param('searchField') . ")" .
378
                "(&(sn=" . $query->param('searchField') . ") " .
379
                "(givenName=" . $query->param('searchField') . "))" .
380
                ")";
381
    } else {
382
      $filter = "(|" .
383
                "(uid=" . $query->param('uid') . ") " .
384
                "(mail=" . $query->param('mail') . ")" .
385
                "(&(sn=" . $query->param('sn') . ") " .
386
                "(givenName=" . $query->param('givenName') . "))" .
387
                ")";
388
    }
389
390
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
391
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
392
393
    # If entries match, send back a request to confirm new-user creation
394
    if ($found) {
395 4080 daigle
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
396
                                                     allParams => $allParams,
397
                                                     foundAccounts => $found });
398 2341 sgarg
    # Otherwise, create a new user in the LDAP directory
399
    } else {
400
        createAccount($allParams);
401
    }
402
403
    exit();
404
}
405
406
#
407
# process input from the registerconfirmed stage, which occurs when
408
# a user chooses to create an account despite similarities to other
409
# existing accounts
410
#
411
sub handleRegisterConfirmed {
412
413
    my $allParams = { 'givenName' => $query->param('givenName'),
414
                      'sn' => $query->param('sn'),
415 4080 daigle
                      'o' => 'unaffiliated', # only accept unaffiliated registration
416 2341 sgarg
                      'mail' => $query->param('mail'),
417
                      'uid' => $query->param('uid'),
418
                      'userPassword' => $query->param('userPassword'),
419
                      'userPassword2' => $query->param('userPassword2'),
420
                      'title' => $query->param('title'),
421
                      'telephoneNumber' => $query->param('telephoneNumber') };
422
    print "Content-type: text/html\n\n";
423
    createAccount($allParams);
424
    exit();
425
}
426
427
#
428
# change a user's password upon request
429
#
430
sub handleChangePassword {
431
432
    print "Content-type: text/html\n\n";
433
434
    my $allParams = { 'test' => "1", };
435
    if ($query->param('uid')) {
436
        $$allParams{'uid'} = $query->param('uid');
437
    }
438
    if ($query->param('o')) {
439
        $$allParams{'o'} = $query->param('o');
440 2972 jones
        my $o = $query->param('o');
441
442 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
443 2341 sgarg
    }
444
445
446
    # Check that all required fields are provided and not null
447
    my @requiredParams = ( 'uid', 'o', 'oldpass',
448
                           'userPassword', 'userPassword2');
449
    if (! paramsAreValid(@requiredParams)) {
450
        my $errorMessage = "Required information is missing. " .
451
            "Please fill in all required fields and submit the form.";
452 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
453
                                        allParams => $allParams,
454
                                        errorMessage => $errorMessage });
455
        exit();
456 2341 sgarg
    }
457
458
    # We have all of the info we need, so try to change the password
459
    if ($query->param('userPassword') =~ $query->param('userPassword2')) {
460
461 2972 jones
        my $o = $query->param('o');
462 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
463
        $ldapUsername = $ldapConfig->{$o}{'user'};
464
        $ldapPassword = $ldapConfig->{$o}{'password'};
465 2341 sgarg
466 4080 daigle
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
467 2341 sgarg
        if ($query->param('o') =~ "LTER") {
468 4080 daigle
            fullTemplate( ['registerLter'] );
469 2341 sgarg
        } else {
470
            my $errorMessage = changePassword(
471
                    $dn, $query->param('userPassword'),
472
                    $dn, $query->param('oldpass'), $query->param('o'));
473 2972 jones
            if ($errorMessage) {
474 4080 daigle
                fullTemplate( ['changePass'], { stage => "changepass",
475
                                                allParams => $allParams,
476
                                                errorMessage => $errorMessage });
477
                exit();
478 2341 sgarg
            } else {
479 4080 daigle
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
480
                                                       allParams => $allParams });
481
                exit();
482 2341 sgarg
            }
483
        }
484
    } else {
485
        my $errorMessage = "The passwords do not match. Try again.";
486 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
487
                                        allParams => $allParams,
488
                                        errorMessage => $errorMessage });
489
        exit();
490 2341 sgarg
    }
491
}
492
493
#
494 2414 sgarg
# change a user's password upon request - no input params
495
# only display chagepass template without any error
496
#
497
sub handleInitialChangePassword {
498
    print "Content-type: text/html\n\n";
499
500
    my $allParams = { 'test' => "1", };
501
    my $errorMessage = "";
502 4080 daigle
    fullTemplate( ['changePass'], { stage => "changepass",
503
                                    errorMessage => $errorMessage });
504
    exit();
505 2414 sgarg
}
506
507
#
508 2341 sgarg
# reset a user's password upon request
509
#
510
sub handleResetPassword {
511
512
    print "Content-type: text/html\n\n";
513
514
    my $allParams = { 'test' => "1", };
515
    if ($query->param('uid')) {
516
        $$allParams{'uid'} = $query->param('uid');
517
    }
518
    if ($query->param('o')) {
519
        $$allParams{'o'} = $query->param('o');
520 2972 jones
        my $o = $query->param('o');
521
522 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
523 4868 walbridge
        $ldapUsername = $ldapConfig->{$o}{'user'};
524 4080 daigle
        $ldapPassword = $ldapConfig->{$o}{'password'};
525 2341 sgarg
    }
526
527
    # Check that all required fields are provided and not null
528
    my @requiredParams = ( 'uid', 'o' );
529
    if (! paramsAreValid(@requiredParams)) {
530
        my $errorMessage = "Required information is missing. " .
531
            "Please fill in all required fields and submit the form.";
532 4080 daigle
        fullTemplate( ['resetPass'],  { stage => "resetpass",
533
                                        allParams => $allParams,
534
                                        errorMessage => $errorMessage });
535
        exit();
536 2341 sgarg
    }
537
538
    # We have all of the info we need, so try to change the password
539
    my $o = $query->param('o');
540 4080 daigle
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
541 4866 walbridge
    debug("handleResetPassword: dn: $dn");
542 2341 sgarg
    if ($query->param('o') =~ "LTER") {
543 4080 daigle
        fullTemplate( ['registerLter'] );
544
        exit();
545 2341 sgarg
    } else {
546
        my $errorMessage = "";
547
        my $recipient;
548
        my $userPass;
549
        my $entry = getLdapEntry($ldapurl, $searchBase,
550
                $query->param('uid'), $query->param('o'));
551
552
        if ($entry) {
553
            $recipient = $entry->get_value('mail');
554
            $userPass = getRandomPassword();
555 4080 daigle
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
556 2341 sgarg
        } else {
557
            $errorMessage = "User not found in database.  Please try again.";
558
        }
559
560
        if ($errorMessage) {
561 4080 daigle
            fullTemplate( ['resetPass'], { stage => "resetpass",
562
                                           allParams => $allParams,
563
                                           errorMessage => $errorMessage });
564
            exit();
565 2341 sgarg
        } else {
566
            my $errorMessage = sendPasswordNotification($query->param('uid'),
567 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
568 4080 daigle
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
569
                                                  allParams => $allParams,
570
                                                  errorMessage => $errorMessage });
571
            exit();
572 2341 sgarg
        }
573
    }
574
}
575
576
#
577 2414 sgarg
# reset a user's password upon request- no initial params
578
# only display resetpass template without any error
579
#
580
sub handleInitialResetPassword {
581
    print "Content-type: text/html\n\n";
582
    my $errorMessage = "";
583 4080 daigle
    fullTemplate( ['resetPass'], { stage => "resetpass",
584
                                   errorMessage => $errorMessage });
585
    exit();
586 2414 sgarg
}
587
588
#
589 2341 sgarg
# Construct a random string to use for a newly reset password
590
#
591
sub getRandomPassword {
592
    my $length = shift;
593
    if (!$length) {
594
        $length = 8;
595
    }
596
    my $newPass = "";
597
598
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
599
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
600
    return $newPass;
601
}
602
603
#
604
# Change a password to a new value, binding as the provided user
605
#
606
sub changePassword {
607
    my $userDN = shift;
608
    my $userPass = shift;
609
    my $bindDN = shift;
610
    my $bindPass = shift;
611
    my $o = shift;
612
613 4080 daigle
    my $searchBase = $ldapConfig->{$o}{'base'};
614 4868 walbridge
615 2341 sgarg
    my $errorMessage = 0;
616 3177 tao
    my $ldap;
617 4868 walbridge
618 4771 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
619
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
620 4394 walbridge
621 4849 daigle
    if ($ldap) {
622 4868 walbridge
        #$ldap->start_tls( verify => 'require',
623 2972 jones
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
624 4868 walbridge
        $ldap->start_tls( verify => 'none');
625
        debug("changePassword: attempting to bind to $bindDN");
626
        my $bindresult = $ldap->bind( version => 3, dn => $bindDN,
627 2341 sgarg
                                  password => $bindPass );
628 4868 walbridge
        if ($bindresult->code) {
629
            $errorMessage = "Failed to log in. Are you sure your connection credentails are " .
630
                            "correct? Please correct and try again...";
631
            return $errorMessage;
632
        }
633 2341 sgarg
634 4849 daigle
    	# Find the user here and change their entry
635
    	my $newpass = createSeededPassHash($userPass);
636
    	my $modifications = { userPassword => $newpass };
637 4868 walbridge
      debug("changePass: setting password for $userDN to $newpass");
638 4849 daigle
    	my $result = $ldap->modify( $userDN, replace => { %$modifications });
639 2341 sgarg
640 4849 daigle
    	if ($result->code()) {
641 4866 walbridge
            debug("changePass: error changing password: " . $result->error);
642
        	$errorMessage = "There was an error changing the password:" .
643 2341 sgarg
                           "<br />\n" . $result->error;
644 4849 daigle
    	}
645
    	$ldap->unbind;   # take down session
646
    }
647 2341 sgarg
648
    return $errorMessage;
649
}
650
651
#
652
# generate a Seeded SHA1 hash of a plaintext password
653
#
654
sub createSeededPassHash {
655
    my $secret = shift;
656
657
    my $salt = "";
658
    for (my $i=0; $i < 4; $i++) {
659
        $salt .= int(rand(10));
660
    }
661
662
    my $ctx = Digest::SHA1->new;
663
    $ctx->add($secret);
664
    $ctx->add($salt);
665
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
666
667
    return $hashedPasswd;
668
}
669
670
#
671
# Look up an ldap entry for a user
672
#
673
sub getLdapEntry {
674
    my $ldapurl = shift;
675
    my $base = shift;
676
    my $username = shift;
677
    my $org = shift;
678
679
    my $entry = "";
680
    my $mesg;
681 3177 tao
    my $ldap;
682 4749 walbridge
    debug("ldap server: $ldapurl");
683 4394 walbridge
684
    #if main ldap server is down, a html file containing warning message will be returned
685 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
686 4849 daigle
687
    if ($ldap) {
688
    	$ldap->start_tls( verify => 'none');
689
    	my $bindresult = $ldap->bind;
690
    	if ($bindresult->code) {
691
        	return $entry;
692
    	}
693 2341 sgarg
694 4849 daigle
    	if($ldapConfig->{$org}{'filter'}){
695 4865 walbridge
            debug("getLdapEntry: filter set, searching for base=$base, " .
696
                  "(&(uid=$username)($ldapConfig->{$org}{'filter'})");
697 4849 daigle
        	$mesg = $ldap->search ( base   => $base,
698 4080 daigle
                filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
699 4849 daigle
    	} else {
700 4865 walbridge
            debug("getLdapEntry: no filter, searching for $base, (uid=$username)");
701 4849 daigle
        	$mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
702
    	}
703 3177 tao
704 4849 daigle
    	if ($mesg->count > 0) {
705
        	$entry = $mesg->pop_entry;
706
        	$ldap->unbind;   # take down session
707
    	} else {
708
        	$ldap->unbind;   # take down session
709
        	# Follow references by recursive call to self
710
        	my @references = $mesg->references();
711
        	for (my $i = 0; $i <= $#references; $i++) {
712
            	my $uri = URI->new($references[$i]);
713
            	my $host = $uri->host();
714
            	my $path = $uri->path();
715
            	$path =~ s/^\///;
716
            	$entry = &getLdapEntry($host, $path, $username, $org);
717
            	if ($entry) {
718 4865 walbridge
                    debug("getLdapEntry: recursion found $host, $path, $username, $org");
719 4849 daigle
                	return $entry;
720
            	}
721
        	}
722
    	}
723 2341 sgarg
    }
724
    return $entry;
725
}
726
727
#
728
# send an email message notifying the user of the pw change
729
#
730
sub sendPasswordNotification {
731
    my $username = shift;
732
    my $org = shift;
733
    my $newPass = shift;
734
    my $recipient = shift;
735 2972 jones
    my $cfg = shift;
736 2341 sgarg
737
    my $errorMessage = "";
738
    if ($recipient) {
739 4771 walbridge
        my $mailhost = $properties->getProperty('email.mailhost');
740
        my $sender =  $properties->getProperty('email.sender');
741 2341 sgarg
        # Send the email message to them
742
        my $smtp = Net::SMTP->new($mailhost);
743
        $smtp->mail($sender);
744
        $smtp->to($recipient);
745
746
        my $message = <<"        ENDOFMESSAGE";
747
        To: $recipient
748
        From: $sender
749
        Subject: KNB Password Reset
750
751
        Somebody (hopefully you) requested that your KNB password be reset.
752
        This is generally done when somebody forgets their password.  Your
753
        password can be changed by visiting the following URL:
754
755 4864 walbridge
        $contextUrl/cgi-bin/ldapweb.cgi?stage=changepass&cfg=$cfg
756 2341 sgarg
757
            Username: $username
758
        Organization: $org
759
        New Password: $newPass
760
761
        Thanks,
762
            The KNB Development Team
763
764
        ENDOFMESSAGE
765
        $message =~ s/^[ \t\r\f]+//gm;
766
767
        $smtp->data($message);
768
        $smtp->quit;
769
    } else {
770
        $errorMessage = "Failed to send password because I " .
771
                        "couldn't find a valid email address.";
772
    }
773
    return $errorMessage;
774
}
775
776
#
777
# search the LDAP directory to see if a similar account already exists
778
#
779
sub findExistingAccounts {
780
    my $ldapurl = shift;
781
    my $base = shift;
782
    my $filter = shift;
783
    my $attref = shift;
784 3175 tao
    my $ldap;
785 4847 daigle
    my $mesg;
786 2341 sgarg
787
    my $foundAccounts = 0;
788 4749 walbridge
789 4394 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
790 4868 walbridge
    debug("findExistingAccounts: connecting to $ldapurl, $timeout");
791 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
792 4845 daigle
    if ($ldap) {
793
    	$ldap->start_tls( verify => 'none');
794
    	$ldap->bind( version => 3, anonymous => 1);
795 4848 daigle
		$mesg = $ldap->search (
796 4845 daigle
			base   => $base,
797
			filter => $filter,
798
			attrs => @$attref,
799
		);
800 2341 sgarg
801 4845 daigle
	    if ($mesg->count() > 0) {
802
			$foundAccounts = "";
803
			my $entry;
804
			foreach $entry ($mesg->all_entries) {
805 5650 walbridge
                # a fix to ignore 'ou=Account' properties which are not usable accounts within Metacat.
806
                # this could be done directly with filters on the LDAP connection, instead.
807
                if ($entry->dn !~ /ou=Account/) {
808
                    $foundAccounts .= "<p>\n<b><u>Account:</u> ";
809
                    $foundAccounts .= $entry->dn();
810
                    $foundAccounts .= "</b><br />\n";
811
                    foreach my $attribute ($entry->attributes()) {
812
                        my $value = $entry->get_value($attribute);
813
                        $foundAccounts .= "$attribute: ";
814
                        $foundAccounts .= $value;
815
                        $foundAccounts .= "<br />\n";
816
                    }
817
                    $foundAccounts .= "</p>\n";
818
                }
819 4845 daigle
			}
820 2341 sgarg
        }
821 4845 daigle
    	$ldap->unbind;   # take down session
822 2341 sgarg
823 4848 daigle
    	# Follow references
824
    	my @references = $mesg->references();
825
    	for (my $i = 0; $i <= $#references; $i++) {
826
        	my $uri = URI->new($references[$i]);
827
        	my $host = $uri->host();
828
        	my $path = $uri->path();
829
        	$path =~ s/^\///;
830
        	my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
831
        	if ($refFound) {
832
            	$foundAccounts .= $refFound;
833
        	}
834
    	}
835 2341 sgarg
    }
836
837
    #print "<p>Checking referrals...</p>\n";
838
    #my @referrals = $mesg->referrals();
839
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
840
    #for (my $i = 0; $i <= $#referrals; $i++) {
841
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
842
    #}
843
844
    return $foundAccounts;
845
}
846
847
#
848
# Validate that we have the proper set of input parameters
849
#
850
sub paramsAreValid {
851
    my @pnames = @_;
852
853
    my $allValid = 1;
854
    foreach my $parameter (@pnames) {
855
        if (!defined($query->param($parameter)) ||
856
            ! $query->param($parameter) ||
857
            $query->param($parameter) =~ /^\s+$/) {
858
            $allValid = 0;
859
        }
860
    }
861
862
    return $allValid;
863
}
864
865
#
866 8175 tao
# Create a temporary account for a user and send an email with a link which can click for the
867
# verification. This is used to protect the ldap server against spams.
868
#
869
sub createTemporaryAccount {
870
    my $allParams = shift;
871
    #my $org = $query->param('o');
872
    my $org = 'LTER';
873
    my $ou = $query-param('ou');
874
    my $uid = $query->param('uid');
875
876
    #to see if the organizaton exist
877
    my $tmpSearchBase = 'dc=tmp,' . $authBase;
878
879
    print "Content-type: text/html\n\n";
880
881
882
    # Search LDAP for matching entries that already exist
883
    my $filter = "(o"
884
                  . "=" . $org .
885
                 ")";
886
887
    my @attrs = ['o', 'ou' ];
888
    my $found = searchDirectory($ldapurl, $tmpSearchBase, $filter, \@attrs);
889
890
    if($found) {
891
      print "ldap server ". $ldapurl;
892
      print "sesarch base" . $tmpSearchBase;
893
      print "find the organization " . $org;
894
    } else {
895
      print "ldap server ". $ldapurl;
896
      print "sesarch base " . $tmpSearchBase;
897
      print "not find the organization " . $org;
898
    }
899
900
    #$query->param('o','tmp');
901
    #createAccount($allParams);
902
    #$query->param('o',$org);
903
    #constrct url
904
    #my $link =
905
    #print "Content-type: text/html\n\n";
906
    #print $query->param('o');
907
}
908
909
#
910 2341 sgarg
# Bind to LDAP and create a new account using the information provided
911
# by the user
912
#
913
sub createAccount {
914
    my $allParams = shift;
915
916
    if ($query->param('o') =~ "LTER") {
917 4080 daigle
        fullTemplate( ['registerLter'] );
918 2341 sgarg
    } else {
919
920
        # Be sure the passwords match
921
        if ($query->param('userPassword') !~ $query->param('userPassword2')) {
922
            my $errorMessage = "The passwords do not match. Try again.";
923 4080 daigle
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
924
                                                            allParams => $allParams,
925
                                                            errorMessage => $errorMessage });
926
            exit();
927 2341 sgarg
        }
928
929 2972 jones
        my $o = $query->param('o');
930 2341 sgarg
931 4080 daigle
        my $searchBase = $ldapConfig->{$o}{'base'};
932
        my $dnBase = $ldapConfig->{$o}{'dn'};
933 4868 walbridge
        my $ldapUsername = $ldapConfig->{$o}{'user'};
934 4749 walbridge
        my $ldapPassword = $ldapConfig->{$o}{'password'};
935 4771 walbridge
        debug("LDAP connection to $ldapurl...");
936 3177 tao
        #if main ldap server is down, a html file containing warning message will be returned
937 4771 walbridge
        my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
938 3177 tao
939 4849 daigle
        if ($ldap) {
940
        	$ldap->start_tls( verify => 'none');
941
        	debug("Attempting to bind to LDAP server with dn = $ldapUsername, pwd = $ldapPassword");
942
        	$ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
943 3177 tao
944 4849 daigle
        	my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
945
        	debug("Inserting new entry for: $dn");
946 2341 sgarg
947 4849 daigle
        	# Create a hashed version of the password
948
        	my $shapass = createSeededPassHash($query->param('userPassword'));
949 2341 sgarg
950 4849 daigle
        	# Do the insertion
951
        	my $additions = [
952 2341 sgarg
                'uid'   => $query->param('uid'),
953
                'o'   => $query->param('o'),
954
                'cn'   => join(" ", $query->param('givenName'),
955
                                    $query->param('sn')),
956
                'sn'   => $query->param('sn'),
957
                'givenName'   => $query->param('givenName'),
958
                'mail' => $query->param('mail'),
959
                'userPassword' => $shapass,
960
                'objectclass' => ['top', 'person', 'organizationalPerson',
961
                                'inetOrgPerson', 'uidObject' ]
962 4849 daigle
            	];
963
        	if (defined($query->param('telephoneNumber')) &&
964
            	$query->param('telephoneNumber') &&
965
            	! $query->param('telephoneNumber') =~ /^\s+$/) {
966
            	$$additions[$#$additions + 1] = 'telephoneNumber';
967
            	$$additions[$#$additions + 1] = $query->param('telephoneNumber');
968
        	}
969
        	if (defined($query->param('title')) &&
970
            	$query->param('title') &&
971
            	! $query->param('title') =~ /^\s+$/) {
972
            	$$additions[$#$additions + 1] = 'title';
973
            	$$additions[$#$additions + 1] = $query->param('title');
974
        	}
975
        	my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
976 2341 sgarg
977 4849 daigle
        	if ($result->code()) {
978
            	fullTemplate( ['registerFailed', 'register'], { stage => "register",
979 4080 daigle
                                                            allParams => $allParams,
980
                                                            errorMessage => $result->error });
981 4849 daigle
            	# TODO SCW was included as separate errors, test this
982
           	 	#$templateVars    = setVars({ stage => "register",
983
           	 	#                     allParams => $allParams });
984
            	#$template->process( $templates->{'register'}, $templateVars);
985
        	} else {
986
            	fullTemplate( ['success'] );
987
        	}
988
989
        	$ldap->unbind;   # take down session
990 2341 sgarg
        }
991
    }
992
}
993
994
sub handleResponseMessage {
995
996
  print "Content-type: text/html\n\n";
997
  my $errorMessage = "You provided invalid input to the script. " .
998
                     "Try again please.";
999 4080 daigle
  fullTemplate( [], { stage => $templates->{'stage'},
1000
                      errorMessage => $errorMessage });
1001
  exit();
1002 2341 sgarg
}
1003
1004
#
1005
# perform a simple search against the LDAP database using
1006
# a small subset of attributes of each dn and return it
1007
# as a table to the calling browser.
1008
#
1009
sub handleSimpleSearch {
1010
1011
    my $o = $query->param('o');
1012
1013 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
1014
    my $searchBase = $ldapConfig->{$o}{'base'};
1015 2341 sgarg
1016
    print "Content-type: text/html\n\n";
1017
1018
    my $allParams = {
1019
                      'cn' => $query->param('cn'),
1020
                      'sn' => $query->param('sn'),
1021
                      'gn' => $query->param('gn'),
1022
                      'o'  => $query->param('o'),
1023
                      'facsimiletelephonenumber'
1024
                      => $query->param('facsimiletelephonenumber'),
1025
                      'mail' => $query->param('cmail'),
1026
                      'telephonenumber' => $query->param('telephonenumber'),
1027
                      'title' => $query->param('title'),
1028
                      'uid' => $query->param('uid'),
1029
                      'ou' => $query->param('ou'),
1030
                    };
1031
1032
    # Search LDAP for matching entries that already exist
1033
    my $filter = "(" .
1034
                 $query->param('searchField') . "=" .
1035
                 "*" .
1036
                 $query->param('searchValue') .
1037
                 "*" .
1038
                 ")";
1039
1040
    my @attrs = [ 'sn',
1041
                  'gn',
1042
                  'cn',
1043
                  'o',
1044
                  'facsimiletelephonenumber',
1045
                  'mail',
1046
                  'telephoneNumber',
1047
                  'title',
1048
                  'uid',
1049
                  'labeledURI',
1050
                  'ou' ];
1051
1052
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
1053
1054
    # Send back the search results
1055
    if ($found) {
1056 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1057
                                         allParams => $allParams,
1058
                                         foundAccounts => $found });
1059 2341 sgarg
    } else {
1060
      $found = "No entries matched your criteria.  Please try again\n";
1061
1062 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1063
                                         allParams => $allParams,
1064
                                         foundAccounts => $found });
1065 2341 sgarg
    }
1066
1067
    exit();
1068
}
1069
1070
#
1071
# search the LDAP directory to see if a similar account already exists
1072
#
1073
sub searchDirectory {
1074
    my $ldapurl = shift;
1075
    my $base = shift;
1076
    my $filter = shift;
1077
    my $attref = shift;
1078
1079 4849 daigle
	my $mesg;
1080 2341 sgarg
    my $foundAccounts = 0;
1081 3177 tao
1082
    #if ldap server is down, a html file containing warning message will be returned
1083 4771 walbridge
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1084 3177 tao
1085 4849 daigle
    if ($ldap) {
1086
    	$ldap->start_tls( verify => 'none');
1087
    	$ldap->bind( version => 3, anonymous => 1);
1088
    	my $mesg = $ldap->search (
1089
        	base   => $base,
1090
        	filter => $filter,
1091
        	attrs => @$attref,
1092
    	);
1093 2341 sgarg
1094 4849 daigle
    	if ($mesg->count() > 0) {
1095
        	$foundAccounts = "";
1096
        	my $entry;
1097
        	foreach $entry ($mesg->sorted(['sn'])) {
1098
          		$foundAccounts .= "<tr>\n<td class=\"main\">\n";
1099
          		$foundAccounts .= "<a href=\"" unless
1100 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1101 4849 daigle
         		 $foundAccounts .= $entry->get_value('labeledURI') unless
1102 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1103 4849 daigle
          		$foundAccounts .= "\">\n" unless
1104 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1105 4849 daigle
          		$foundAccounts .= $entry->get_value('givenName');
1106
          		$foundAccounts .= "</a>\n" unless
1107 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1108 4849 daigle
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1109
          		$foundAccounts .= "<a href=\"" unless
1110 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1111 4849 daigle
          		$foundAccounts .= $entry->get_value('labeledURI') unless
1112 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1113 4849 daigle
          		$foundAccounts .= "\">\n" unless
1114 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1115 4849 daigle
          		$foundAccounts .= $entry->get_value('sn');
1116
          		$foundAccounts .= "</a>\n";
1117
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1118
          		$foundAccounts .= $entry->get_value('mail');
1119
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1120
          		$foundAccounts .= $entry->get_value('telephonenumber');
1121
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1122
          		$foundAccounts .= $entry->get_value('title');
1123
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1124
          		$foundAccounts .= $entry->get_value('ou');
1125
          		$foundAccounts .= "\n</td>\n";
1126
          		$foundAccounts .= "</tr>\n";
1127
        	}
1128
    	}
1129
    	$ldap->unbind;   # take down session
1130 2341 sgarg
    }
1131
    return $foundAccounts;
1132
}
1133
1134
sub debug {
1135
    my $msg = shift;
1136
1137
    if ($debug) {
1138 4747 walbridge
        print STDERR "LDAPweb: $msg\n";
1139 2341 sgarg
    }
1140
}
1141 3175 tao
1142 4771 walbridge
sub handleLDAPBindFailure {
1143
    my $ldapAttemptUrl = shift;
1144
    my $primaryLdap =  $properties->getProperty('auth.url');
1145
1146
    if ($ldapAttemptUrl eq  $primaryLdap) {
1147
        handleGeneralServerFailure("The main LDAP server $ldapurl is down!");
1148
    } else {
1149
        debug("attempted to bind to nonresponsive LDAP server $ldapAttemptUrl, skipped.");
1150
    }
1151
}
1152
1153 3177 tao
sub handleGeneralServerFailure {
1154
    my $errorMessage = shift;
1155 4728 walbridge
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
1156 3175 tao
    exit(0);
1157
   }
1158
1159 4080 daigle
sub setVars {
1160
    my $paramVars = shift;
1161
    # initialize default parameters
1162
    my $templateVars = { cfg => $cfg,
1163 4394 walbridge
                         styleSkinsPath => $contextUrl . "/style/skins",
1164
                         styleCommonPath => $contextUrl . "/style/common",
1165
                         contextUrl => $contextUrl,
1166 4770 daigle
                         cgiPrefix => $cgiPrefix,
1167 4080 daigle
                         orgList => \@orgList,
1168 4394 walbridge
                         config  => $config,
1169 4080 daigle
    };
1170
1171
    # append customized params
1172
    while (my ($k, $v) = each (%$paramVars)) {
1173
        $templateVars->{$k} = $v;
1174
    }
1175
1176
    return $templateVars;
1177
}