Project

General

Profile

1
#!/usr/bin/perl -w
2
#
3
#  '$RCSfile$'
4
#  Copyright: 2001 Regents of the University of California 
5
#
6
#   '$Author: tao $'
7
#     '$Date: 2013-09-16 14:31:18 -0700 (Mon, 16 Sep 2013) $'
8
# '$Revision: 8206 $' 
9
# 
10
# This program is free software; you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation; either version 2 of the License, or
13
# (at your option) any later version.
14
#
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
# GNU General Public License for more details.
19
#
20
# You should have received a copy of the GNU General Public License
21
# along with this program; if not, write to the Free Software
22
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
#
24

    
25
#
26
# This is a web-based application for allowing users to register a new
27
# account for Metacat access.  We currently only support LDAP even
28
# though metacat could potentially support other types of directories.
29

    
30
use lib '../WEB-INF/lib';
31
use strict;             # turn on strict syntax checking
32
use Template;           # load the template-toolkit module
33
use CGI qw/:standard :html3/; # load the CGI module 
34
use Net::LDAP;          # load the LDAP net libraries
35
use Net::SMTP;          # load the SMTP net libraries
36
use Digest::SHA1;       # for creating the password hash
37
use MIME::Base64;       # for creating the password hash
38
use URI;                # for parsing URL syntax
39
use Config::Properties; # for parsing Java .properties files
40
use File::Basename;     # for path name parsing
41
use Captcha::reCAPTCHA; # for protection against spams
42
use Cwd 'abs_path';
43

    
44
# Global configuration paramters
45
# This entire block (including skin parsing) could be pushed out to a separate .pm file
46
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
    print "Content-type: text/html\n\n";
52
    print "Unable to locate Metacat properties. Working directory is set as " . 
53
        $workingDirectory .", is this correct?";
54
    exit(0);
55
}
56

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

    
59
# 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
my $protocol = 'http://';
67
if ( $properties->getProperty('server.httpPort') eq '443' ) {
68
	$protocol = 'https://';
69
}
70
my $contextUrl = $protocol . $properties->getProperty('server.name');
71
if ($properties->getProperty('server.httpPort') ne '80') {
72
        $contextUrl = $contextUrl . ':' . $properties->getProperty('server.httpPort');
73
}
74
$contextUrl = $contextUrl . '/' .  $properties->getProperty('application.context');
75

    
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
#recaptcha key information
82
my $recaptchaPublicKey=$properties->getProperty('ldap.recaptcha.publickey');
83
my $recaptchaPrivateKey=$properties->getProperty('ldap.recaptcha.privatekey');
84

    
85
my @errorMessages;
86
my $error = 0;
87

    
88
my $emailVerification= 'emailverification';
89

    
90
# 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
    debug("No configuration set.");
101
    print "Content-type: text/html\n\n";
102
    print 'LDAPweb Error: The registry requires a skin name to continue.';
103
    exit();
104
}
105

    
106
# Metacat isn't initialized, the registry will fail in strange ways.
107
if (!($metacatUrl)) {
108
    debug("No Metacat.");
109
    print "Content-type: text/html\n\n";
110
    'Registry Error: Metacat is not initialized! Make sure' .
111
        ' MetacatUrl is set correctly in ' .  $skinName . '.properties';
112
    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
# 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
my $searchBase;
151
my $ldapUsername;
152
my $ldapPassword;
153
# 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

    
157
# Java uses miliseconds, Perl expects whole seconds
158
my $timeout = $properties->getProperty('ldap.connectTimeLimit') / 1000;
159

    
160
# Get the CGI input variables
161
my $query = new CGI;
162
my $debug = 1;
163

    
164
#--------------------------------------------------------------------------80c->
165
# Set up the Template Toolkit to read html form templates
166

    
167
# templates hash, imported from ldap.templates tree in metacat.properties
168
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
169
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
170
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
171

    
172
# set some configuration options for the template object
173
my $ttConfig = {
174
             INCLUDE_PATH => $templatesDir,
175
             INTERPOLATE  => 0,
176
             POST_CHOMP   => 1,
177
             DEBUG        => 1, 
178
             };
179

    
180
# create an instance of the template
181
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
182

    
183
# custom LDAP properties hash
184
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
185

    
186
# This is a hash which has the keys of the organization's properties 'name', 'base', 'organization'.
187
my $orgProps = $properties->splitToTree(qr/\./, 'organization');
188

    
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
my $orgNames = $properties->splitToTree(qr/\./, 'organization.name');
191
# pull out properties available e.g. 'name', 'base'
192
my @orgData = keys(%$orgProps);
193

    
194
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
while (my ($oKey, $oVal) = each(%$orgNames)) {
196
    push(@orgList, $oKey);
197
}
198

    
199
my $authBase = $properties->getProperty("auth.base");
200
my $ldapConfig;
201
foreach my $o (@orgList) {
202
    foreach my $d (@orgData) {
203
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
204
    }
205

    
206
    # 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
    # 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
            #$ldapConfig->{$o}{'filter'} = $filter;
228
            $ldapConfig->{$o}{'filter'} = $ldapConfig->{$o}{'org'};
229
        }
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
    } else {
235
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'base'};
236
    }
237
    
238
    # set LDAP administrator user account
239
    if (!$ldapConfig->{$o}{'user'}) {
240
        $ldapConfig->{$o}{'user'} = $ldapConfig->{'unaffiliated'}{'user'};
241
    }
242
    # 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

    
248
    if (!$ldapConfig->{$o}{'password'}) {
249
        $ldapConfig->{$o}{'password'} = $ldapConfig->{'unaffiliated'}{'password'};
250
    }
251
}
252

    
253
### Determine the display organization list (such as NCEAS, Account ) in the ldap template files
254
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

    
259
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

    
268
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
#--------------------------------------------------------------------------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
my $stage = $query->param('stage') || $templates->{'stage'};
285

    
286
my $cfg = $query->param('cfg');
287
debug("started with stage $stage, cfg $cfg");
288

    
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
              'changepass'        => \&handleChangePassword,
300
              'initchangepass'    => \&handleInitialChangePassword,
301
              'resetpass'         => \&handleResetPassword,
302
              'initresetpass'     => \&handleInitialResetPassword,
303
              'emailverification' => \&handleEmailVerification,
304
             );
305

    
306
# 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
sub fullTemplate {
318
    my $templateList = shift;
319
    my $templateVars = setVars(shift);
320
    my $c = Captcha::reCAPTCHA->new;
321
    my $captcha = 'captcha';
322
    #my $error=null;
323
    my $use_ssl= 1;
324
    #my $options=null;
325
    $templateVars->{$captcha} = $c->get_html($recaptchaPublicKey,undef, $use_ssl, undef);
326
    $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
#
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
  fullTemplate(['register'], {stage => "register"}); 
341
  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
    
352
    
353
    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
    
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
        $recaptchaPrivateKey, $ENV{'REMOTE_ADDR'},
370
        $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
    # 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
        fullTemplate(['register'], { stage => "register",
393
                                     allParams => $allParams,
394
                                     errorMessage => $errorMessage });
395
        exit();
396
    } else {
397
         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
        my $o = $query->param('o');    
405
        $searchBase = $ldapConfig->{$o}{'base'};  
406
    }
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
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
435
                                                     allParams => $allParams,
436
                                                     foundAccounts => $found });
437
    # Otherwise, create a new user in the LDAP directory
438
    } else {
439
        createTemporaryAccount($allParams);
440
    }
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
                      'o' => 'unaffiliated', # only accept unaffiliated registration
455
                      '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
    createTemporaryAccount($allParams);
463
    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
        my $o = $query->param('o');
480
        
481
        $searchBase = $ldapConfig->{$o}{'base'};
482
    }
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
        fullTemplate( ['changePass'], { stage => "changepass",
492
                                        allParams => $allParams,
493
                                        errorMessage => $errorMessage });
494
        exit();
495
    }
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
        my $o = $query->param('o');
501
        $searchBase = $ldapConfig->{$o}{'base'};
502
        $ldapUsername = $ldapConfig->{$o}{'user'};
503
        $ldapPassword = $ldapConfig->{$o}{'password'};
504

    
505
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
506
        if ($query->param('o') =~ "LTER") {
507
            fullTemplate( ['registerLter'] );
508
        } else {
509
            my $errorMessage = changePassword(
510
                    $dn, $query->param('userPassword'), 
511
                    $dn, $query->param('oldpass'), $query->param('o'));
512
            if ($errorMessage) {
513
                fullTemplate( ['changePass'], { stage => "changepass",
514
                                                allParams => $allParams,
515
                                                errorMessage => $errorMessage });
516
                exit();
517
            } else {
518
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
519
                                                       allParams => $allParams });
520
                exit();
521
            }
522
        }
523
    } else {
524
        my $errorMessage = "The passwords do not match. Try again.";
525
        fullTemplate( ['changePass'], { stage => "changepass",
526
                                        allParams => $allParams,
527
                                        errorMessage => $errorMessage });
528
        exit();
529
    }
530
}
531

    
532
#
533
# 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
    fullTemplate( ['changePass'], { stage => "changepass",
542
                                    errorMessage => $errorMessage });
543
    exit();
544
}
545

    
546
#
547
# 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
        my $o = $query->param('o');
560
        
561
        $searchBase = $ldapConfig->{$o}{'base'};
562
        $ldapUsername = $ldapConfig->{$o}{'user'};
563
        $ldapPassword = $ldapConfig->{$o}{'password'};
564
    }
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
        fullTemplate( ['resetPass'],  { stage => "resetpass",
572
                                        allParams => $allParams,
573
                                        errorMessage => $errorMessage });
574
        exit();
575
    }
576

    
577
    # We have all of the info we need, so try to change the password
578
    my $o = $query->param('o');
579
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
580
    debug("handleResetPassword: dn: $dn");
581
    if ($query->param('o') =~ "LTER") {
582
        fullTemplate( ['registerLter'] );
583
        exit();
584
    } 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
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
595
        } else {
596
            $errorMessage = "User not found in database.  Please try again.";
597
        }
598

    
599
        if ($errorMessage) {
600
            fullTemplate( ['resetPass'], { stage => "resetpass",
601
                                           allParams => $allParams,
602
                                           errorMessage => $errorMessage });
603
            exit();
604
        } else {
605
            my $errorMessage = sendPasswordNotification($query->param('uid'),
606
                    $query->param('o'), $userPass, $recipient, $cfg);
607
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
608
                                                  allParams => $allParams,
609
                                                  errorMessage => $errorMessage });
610
            exit();
611
        }
612
    }
613
}
614

    
615
#
616
# 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
    fullTemplate( ['resetPass'], { stage => "resetpass",
623
                                   errorMessage => $errorMessage });
624
    exit();
625
}
626

    
627
#
628
# 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
    my $searchBase = $ldapConfig->{$o}{'base'};
653

    
654
    my $errorMessage = 0;
655
    my $ldap;
656

    
657
    #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
    
660
    if ($ldap) {
661
        #$ldap->start_tls( verify => 'require',
662
                      #cafile => '/usr/share/ssl/ldapcerts/cacert.pem');
663
        $ldap->start_tls( verify => 'none');
664
        debug("changePassword: attempting to bind to $bindDN");
665
        my $bindresult = $ldap->bind( version => 3, dn => $bindDN, 
666
                                  password => $bindPass );
667
        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

    
673
    	# Find the user here and change their entry
674
    	my $newpass = createSeededPassHash($userPass);
675
    	my $modifications = { userPassword => $newpass };
676
      debug("changePass: setting password for $userDN to $newpass");
677
    	my $result = $ldap->modify( $userDN, replace => { %$modifications });
678
    
679
    	if ($result->code()) {
680
            debug("changePass: error changing password: " . $result->error);
681
        	$errorMessage = "There was an error changing the password:" .
682
                           "<br />\n" . $result->error;
683
    	} 
684
    	$ldap->unbind;   # take down session
685
    }
686

    
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
    my $ldap;
721
    debug("ldap server: $ldapurl");
722

    
723
    #if main ldap server is down, a html file containing warning message will be returned
724
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
725
    
726
    if ($ldap) {
727
    	$ldap->start_tls( verify => 'none');
728
    	my $bindresult = $ldap->bind;
729
    	if ($bindresult->code) {
730
        	return $entry;
731
    	}
732

    
733
    	if($ldapConfig->{$org}{'filter'}){
734
            debug("getLdapEntry: filter set, searching for base=$base, " .
735
                  "(&(uid=$username)($ldapConfig->{$org}{'filter'})");
736
        	$mesg = $ldap->search ( base   => $base,
737
                filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
738
    	} else {
739
            debug("getLdapEntry: no filter, searching for $base, (uid=$username)");
740
        	$mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
741
    	}
742
    
743
    	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
                    debug("getLdapEntry: recursion found $host, $path, $username, $org");
758
                	return $entry;
759
            	}
760
        	}
761
    	}
762
    }
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
    my $cfg = shift;
775

    
776
    my $errorMessage = "";
777
    if ($recipient) {
778
        my $mailhost = $properties->getProperty('email.mailhost');
779
        my $sender;
780
        $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
781
        # 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
        $contextUrl/cgi-bin/ldapweb.cgi?stage=changepass&cfg=$cfg
796

    
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
    my $ldap;
825
    my $mesg;
826

    
827
    my $foundAccounts = 0;
828

    
829
    #if main ldap server is down, a html file containing warning message will be returned
830
    debug("findExistingAccounts: connecting to $ldapurl, $timeout");
831
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
832
    if ($ldap) {
833
    	$ldap->start_tls( verify => 'none');
834
    	$ldap->bind( version => 3, anonymous => 1);
835
		$mesg = $ldap->search (
836
			base   => $base,
837
			filter => $filter,
838
			attrs => @$attref,
839
		);
840

    
841
	    if ($mesg->count() > 0) {
842
			$foundAccounts = "";
843
			my $entry;
844
			foreach $entry ($mesg->all_entries) { 
845
                # 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
			}
860
        }
861
    	$ldap->unbind;   # take down session
862

    
863
    	# 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
    }
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
# 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
    my $org = $query->param('o'); 
912
 
913

    
914
    
915
    ################## Search LDAP for matching o or ou that already exist
916
    my $orgAuthBase = $ldapConfig->{$org}{'base'};; 
917
    my $filter = $ldapConfig->{$org}{'filter'};   
918
    my $tmpSearchBase = 'dc=tmp,' . $orgAuthBase; 
919
    debug("search filer " . $filter);
920
    debug("ldap server ". $ldapurl);
921
    debug("sesarch base " . $tmpSearchBase);
922
    print "Content-type: text/html\n\n";
923
    my @attrs = ['o', 'ou' ];
924
    my $found = searchDirectory($ldapurl, $tmpSearchBase, $filter, \@attrs);
925
    
926
    my $ldapUsername = $ldapConfig->{$org}{'user'};
927
    my $ldapPassword = $ldapConfig->{$org}{'password'};
928
    debug("LDAP connection to $ldapurl...");    
929
    
930
     my @organizationInfo = split('=', $ldapConfig->{$org}{'org'}); #split 'o=NCEAS' or something like that
931
     my $organization = $organizationInfo[0]; # This will be 'o' or 'ou'
932
     my $organizationName = $organizationInfo[1]; # This will be 'NCEAS' or 'Account'
933
        
934
    if(!$found) {
935
        debug("generate the subtree in the dc=tmp===========================");
936
        #need to generate the subtree o or ou
937
        my $dn;
938
        #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
            my $additions; 
945
            $additions = [ 
946
                $organization   => $organizationName,
947
                'objectclass' => ['top', 'organization']
948
                ];
949
            $dn=$ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
950
            # Do the insertion
951
            my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
952
            if ($result->code()) {
953
                fullTemplate( ['registerFailed', 'register'], { stage => "register",
954
                                                            allParams => $allParams,
955
                                                            errorMessage => $result->error });
956
                $ldap->unbind;   # take down session
957
                exist(0)
958
                # 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
        } else {
965
            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
        }
970

    
971
    } 
972
    
973
    ################create an account under tmp subtree 
974
    
975
    #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
                                'inetOrgPerson', 'uidObject' ],
990
                $organization   => $organizationName
991
                ];
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

    
1005
    
1006
    #$$additions[$#$additions + 1] = 'o';
1007
    #$$additions[$#$additions + 1] = $org;
1008
    my $dn='uid=' . $query->param('uid') . ',' . $ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1009
    my $tmp = 1;
1010
    createAccount2($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1011
    
1012
    
1013
    ####################send the verification email to the user
1014
    my $link = $contextUrl. '/cgi-bin/ldapweb.cgi?cfg=' . $skinName . '&' . 'stage=' . $emailVerification . '&' . 'dn=' . $dn . '&' . 'hash=' . $randomStr . '&' . $ldapConfig->{$org}{'org'} . '&uid=' . $query->param('uid');
1015
    
1016
    my $mailhost = $properties->getProperty('email.mailhost');
1017
    my $sender;
1018
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
1019
    debug("the sender is " . $sender);
1020
    my $recipient = $query->param('mail');
1021
    # Send the email message to them
1022
    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
    $smtp->mail($sender);
1028
    $smtp->to($recipient);
1029

    
1030
    my $message = <<"     ENDOFMESSAGE";
1031
    To: $recipient
1032
    From: $sender
1033
    Subject: Activate the New KNB Account
1034
        
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
    debug("the link is " . $link);
1050
    fullTemplate( ['success'] );
1051
    
1052
}
1053

    
1054
#
1055
# Bind to LDAP and create a new account using the information provided
1056
# by the user
1057
#
1058
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
            $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword ); 
1081
            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
                #fullTemplate( ['success'] );
1092
            }
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
sub createAccount {
1109
    my $allParams = shift;
1110

    
1111
    if ($query->param('o') =~ "LTER") {
1112
        fullTemplate( ['registerLter'] );
1113
    } 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
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
1119
                                                            allParams => $allParams,
1120
                                                            errorMessage => $errorMessage });
1121
            exit();
1122
        }
1123

    
1124
        my $o = $query->param('o');
1125

    
1126
        my $searchBase = $ldapConfig->{$o}{'base'};
1127
        my $dnBase = $ldapConfig->{$o}{'dn'};
1128
        debug("the dn is " . $dnBase);
1129
        my $ldapUsername = $ldapConfig->{$o}{'user'};
1130
        my $ldapPassword = $ldapConfig->{$o}{'password'};
1131
        debug("LDAP connection to $ldapurl...");    
1132
        #if main ldap server is down, a html file containing warning message will be returned
1133
        my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1134
        
1135
        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
        
1140
        	my $dn = 'uid=' . $query->param('uid') . ',' . $dnBase;
1141
        	debug("Inserting new entry for: $dn");
1142

    
1143
        	# Create a hashed version of the password
1144
        	my $shapass = createSeededPassHash($query->param('userPassword'));
1145

    
1146
        	# Do the insertion
1147
        	my $additions = [ 
1148
                '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
            	];
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
    
1173
        	if ($result->code()) {
1174
            	fullTemplate( ['registerFailed', 'register'], { stage => "register",
1175
                                                            allParams => $allParams,
1176
                                                            errorMessage => $result->error });
1177
            	# 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
        }
1187
    }
1188
}
1189

    
1190
#
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
            fullTemplate( ['registerFailed'], {errorMessage => "No record matched the dn " . $dn . " for the verification. You probably already verified the account."});
1237
            #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
                if($mesg->code()) {
1260
                    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
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
  fullTemplate( [], { stage => $templates->{'stage'},
1286
                      errorMessage => $errorMessage });
1287
  exit();
1288
}
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
    my $ldapurl = $ldapConfig->{$o}{'url'};
1300
    my $searchBase = $ldapConfig->{$o}{'base'};
1301

    
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
      fullTemplate( ('searchResults'), { stage => "searchresults",
1343
                                         allParams => $allParams,
1344
                                         foundAccounts => $found });
1345
    } else {
1346
      $found = "No entries matched your criteria.  Please try again\n";
1347

    
1348
      fullTemplate( ('searchResults'), { stage => "searchresults",
1349
                                         allParams => $allParams,
1350
                                         foundAccounts => $found });
1351
    }
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
	my $mesg;
1366
    my $foundAccounts = 0;
1367
    
1368
    #if ldap server is down, a html file containing warning message will be returned
1369
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1370
    
1371
    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

    
1380
    	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
                    (!$entry->get_value('labeledURI'));
1387
         		 $foundAccounts .= $entry->get_value('labeledURI') unless
1388
                    (!$entry->get_value('labeledURI'));
1389
          		$foundAccounts .= "\">\n" unless 
1390
                    (!$entry->get_value('labeledURI'));
1391
          		$foundAccounts .= $entry->get_value('givenName');
1392
          		$foundAccounts .= "</a>\n" unless 
1393
                    (!$entry->get_value('labeledURI'));
1394
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1395
          		$foundAccounts .= "<a href=\"" unless 
1396
                    (!$entry->get_value('labeledURI'));
1397
          		$foundAccounts .= $entry->get_value('labeledURI') unless
1398
                    (!$entry->get_value('labeledURI'));
1399
          		$foundAccounts .= "\">\n" unless 
1400
                    (!$entry->get_value('labeledURI'));
1401
          		$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
    }
1417
    return $foundAccounts;
1418
}
1419

    
1420
sub debug {
1421
    my $msg = shift;
1422
    
1423
    if ($debug) {
1424
        print STDERR "LDAPweb: $msg\n";
1425
    }
1426
}
1427

    
1428
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
sub handleGeneralServerFailure {
1440
    my $errorMessage = shift;
1441
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
1442
    exit(0);   
1443
   }
1444
    
1445
sub setVars {
1446
    my $paramVars = shift;
1447
    # initialize default parameters 
1448
    my $templateVars = { cfg => $cfg,
1449
                         styleSkinsPath => $contextUrl . "/style/skins",
1450
                         styleCommonPath => $contextUrl . "/style/common",
1451
                         contextUrl => $contextUrl,
1452
                         cgiPrefix => $cgiPrefix,
1453
                         orgList => \@validDisplayOrgList,
1454
                         config  => $config,
1455
    };
1456
    
1457
    # append customized params
1458
    while (my ($k, $v) = each (%$paramVars)) {
1459
        $templateVars->{$k} = $v;
1460
    }
1461
    
1462
    return $templateVars;
1463
} 
1464

    
(10-10/14)