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