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: 2017-12-13 15:43:18 -0800 (Wed, 13 Dec 2017) $'
8
# '$Revision: 10478 $' 
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 DateTime;			# for parsing dates
42
use DateTime::Duration; # for substracting
43
use Captcha::reCAPTCHA; # for protection against spams
44
use Cwd 'abs_path';
45
use Scalar::Util qw(looks_like_number);
46

    
47
# Global configuration paramters
48
# This entire block (including skin parsing) could be pushed out to a separate .pm file
49
my $cgiUrl = $ENV{'SCRIPT_FILENAME'};
50
my $workingDirectory = dirname($cgiUrl);
51
my $metacatProps = "${workingDirectory}/../WEB-INF/metacat.properties";
52
my $properties = new Config::Properties();
53
unless (open (METACAT_PROPERTIES, $metacatProps)) {
54
    print "Content-type: text/html\n\n";
55
    print "Unable to locate Metacat properties. Working directory is set as " . 
56
        $workingDirectory .", is this correct?";
57
    exit(0);
58
}
59

    
60
$properties->load(*METACAT_PROPERTIES);
61

    
62
# local directory configuration
63
my $skinsDir = "${workingDirectory}/../style/skins";
64
my $templatesDir = abs_path("${workingDirectory}/../style/common/templates");
65
my $tempDir = $properties->getProperty('application.tempDir');
66

    
67
# url configuration
68
my $server = $properties->splitToTree(qr/\./, 'server');
69
my $protocol = 'http://';
70
if ( $properties->getProperty('server.httpPort') eq '443' ) {
71
	$protocol = 'https://';
72
}
73
my $serverUrl = $protocol . $properties->getProperty('server.name');
74
if ($properties->getProperty('server.httpPort') ne '80') {
75
        $serverUrl = $serverUrl . ':' . $properties->getProperty('server.httpPort');
76
}
77
my $context = $properties->getProperty('application.context');
78
my $contextUrl = $serverUrl . '/' .  $context;
79

    
80
my $metacatUrl = $contextUrl . "/metacat";
81
my $cgiPrefix = "/" . $context . "/cgi-bin";
82
my $styleSkinsPath = $contextUrl . "/style/skins";
83
my $styleCommonPath = $contextUrl . "/style/common";
84
my $caCertFileProp = $properties->getProperty('ldap.server.ca.certificate');
85
my $ldapServerCACertFile;
86
if ($caCertFileProp eq "") {
87
   $ldapServerCACertFile = "/etc/ssl/certs/ca-certificates.crt";
88
   debug("Metacat doesn't specify the ca file, we use the default one " . $ldapServerCACertFile);
89
} else {
90
   $ldapServerCACertFile = $workingDirectory. "/../" . $properties->getProperty('ldap.server.ca.certificate');
91
   debug("Metacat does specify the ca file, we will use it - " . $ldapServerCACertFile);
92
}
93

    
94

    
95
#recaptcha key information
96
my $recaptchaPublicKey=$properties->getProperty('ldap.recaptcha.publickey');
97
my $recaptchaPrivateKey=$properties->getProperty('ldap.recaptcha.privatekey');
98

    
99
my @errorMessages;
100
my $error = 0;
101

    
102
my $emailVerification= 'emailverification';
103

    
104
 my $dn_store_next_uid=$properties->getProperty('ldap.nextuid.storing.dn');
105
 my $attribute_name_store_next_uid = $properties->getProperty('ldap.nextuid.storing.attributename');
106

    
107
# Import all of the HTML form fields as variables
108
import_names('FORM');
109

    
110
# Must have a config to use Metacat
111
my $skinName = "";
112
if ($FORM::cfg) {
113
    $skinName = $FORM::cfg;
114
} elsif ($ARGV[0]) {
115
    $skinName = $ARGV[0];
116
} else {
117
    debug("No configuration set.");
118
    print "Content-type: text/html\n\n";
119
    print 'LDAPweb Error: The registry requires a skin name to continue.';
120
    exit();
121
}
122

    
123
# Metacat isn't initialized, the registry will fail in strange ways.
124
if (!($metacatUrl)) {
125
    debug("No Metacat.");
126
    print "Content-type: text/html\n\n";
127
    'Registry Error: Metacat is not initialized! Make sure' .
128
        ' MetacatUrl is set correctly in ' .  $skinName . '.properties';
129
    exit();
130
}
131

    
132
my $skinProperties = new Config::Properties();
133
if (!($skinName)) {
134
    $error = "Application misconfigured.  Please contact the administrator.";
135
    push(@errorMessages, $error);
136
} else {
137
    my $skinProps = "$skinsDir/$skinName/$skinName.properties";
138
    unless (open (SKIN_PROPERTIES, $skinProps)) {
139
        print "Content-type: text/html\n\n";
140
        print "Unable to locate skin properties at $skinProps.  Is this path correct?";
141
        exit(0);
142
    }
143
    $skinProperties->load(*SKIN_PROPERTIES);
144
}
145

    
146
my $config = $skinProperties->splitToTree(qr/\./, 'registry.config');
147

    
148
# XXX HACK: this is a temporary fix to pull out the UCNRS password property from the
149
#           NRS skin instead of metacat.properties. The intent is to prevent editing
150
#           of our core properties file, which is manipulated purely through the web.
151
#           Once organizations are editable, this section should be removed as should
152
#           the properties within nrs/nrs.properties.
153
my $nrsProperties = new Config::Properties();
154
my $nrsProps = "$skinsDir/nrs/nrs.properties";
155
unless (open (NRS_PROPERTIES, $nrsProps)) {
156
    print "Content-type: text/html\n\n";
157
    print "Unable to locate skin properties at $nrsProps.  Is this path correct?";
158
    exit(0);
159
}
160
$nrsProperties->load(*NRS_PROPERTIES);
161

    
162
my $nrsConfig = $nrsProperties->splitToTree(qr/\./, 'registry.config');
163

    
164
# XXX END HACK
165

    
166

    
167
my $searchBase;
168
my $ldapUsername;
169
my $ldapPassword;
170
# TODO: when should we use surl instead? Is there a setting promoting one over the other?
171
# TODO: the default tree for accounts should be exposed somewhere, defaulting to unaffiliated
172
my $ldapurl = $properties->getProperty('auth.url');
173

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

    
177
# Get the CGI input variables
178
my $query = new CGI;
179
my $debug = 1;
180

    
181
#--------------------------------------------------------------------------80c->
182
# Set up the Template Toolkit to read html form templates
183

    
184
# templates hash, imported from ldap.templates tree in metacat.properties
185
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
186
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
187
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
188

    
189
# set some configuration options for the template object
190
my $ttConfig = {
191
             INCLUDE_PATH => $templatesDir,
192
             INTERPOLATE  => 0,
193
             POST_CHOMP   => 1,
194
             DEBUG        => 1, 
195
             };
196

    
197
# create an instance of the template
198
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
199

    
200
# custom LDAP properties hash
201
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
202

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

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

    
211
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. 
212
while (my ($oKey, $oVal) = each(%$orgNames)) {
213
    push(@orgList, $oKey);
214
}
215

    
216
my $authBase = $properties->getProperty("auth.base");
217
my $ldapConfig;
218
foreach my $o (@orgList) {
219
    foreach my $d (@orgData) {
220
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
221
    }
222

    
223
    # XXX hack, remove after 1.9
224
    if ($o eq 'UCNRS') {
225
        $ldapConfig->{'UCNRS'}{'base'} = $nrsConfig->{'base'};
226
        $ldapConfig->{'UCNRS'}{'user'} = $nrsConfig->{'username'};
227
        $ldapConfig->{'UCNRS'}{'password'} = $nrsConfig->{'password'};
228
    }
229

    
230
    # set default base
231
    if (!$ldapConfig->{$o}{'base'}) {
232
        $ldapConfig->{$o}{'base'} = $authBase;
233
    }
234

    
235
    # include filter information. By default, our filters are 'o=$name', e.g. 'o=NAPIER'
236
    # these can be overridden by specifying them in metacat.properties. Non-default configs
237
    # such as UCNRS must specify all LDAP properties.
238
    if ($ldapConfig->{$o}{'base'} eq $authBase) {
239
        my $filter = "o=$o";
240
        if (!$ldapConfig->{$o}{'org'}) {
241
            $ldapConfig->{$o}{'org'} = $filter;
242
        }
243
        if (!$ldapConfig->{$o}{'filter'}) {
244
            #$ldapConfig->{$o}{'filter'} = $filter;
245
            $ldapConfig->{$o}{'filter'} = $ldapConfig->{$o}{'org'};
246
        }
247
        # also include DN, which is just org + base
248
        if ($ldapConfig->{$o}{'org'}) {
249
            $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'org'} . "," . $ldapConfig->{$o}{'base'};
250
        }
251
    } else {
252
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'base'};
253
    }
254
    
255
    # set LDAP administrator user account
256
    if (!$ldapConfig->{$o}{'user'}) {
257
        $ldapConfig->{$o}{'user'} = $ldapConfig->{'unaffiliated'}{'user'};
258
    }
259
    # check for a fully qualified LDAP name. If it doesn't exist, append base.
260
    my @userParts = split(',', $ldapConfig->{$o}{'user'});
261
    if (scalar(@userParts) == 1) {
262
        $ldapConfig->{$o}{'user'} = $ldapConfig->{$o}{'user'} . "," . $ldapConfig->{$o}{'base'};
263
    }
264

    
265
    if (!$ldapConfig->{$o}{'password'}) {
266
        $ldapConfig->{$o}{'password'} = $ldapConfig->{'unaffiliated'}{'password'};
267
    }
268
}
269

    
270
### Determine the display organization list (such as NCEAS, Account ) in the ldap template files
271
my $displayOrgListStr;
272
$displayOrgListStr = $skinProperties->getProperty("ldap.templates.organizationList") or $displayOrgListStr = $properties->getProperty('ldap.templates.organizationList');
273
debug("the string of the org from properties : " . $displayOrgListStr);
274
my @displayOrgList = split(';', $displayOrgListStr);
275

    
276
my @validDisplayOrgList; #this array contains the org list which will be shown in the templates files.
277

    
278
my %orgNamesHash = %$orgNames;
279
foreach my $element (@displayOrgList) {
280
    if(exists $orgNamesHash{$element}) {
281
         my $label = $ldapConfig->{$element}{'label'};
282
         my %displayHash;
283
         $displayHash{$element} = $label;
284
         debug("push a hash containing the key " . $element . "with the value label" . $label . " into the display array");
285
         #if the name is found in the organization part of metacat.properties, put it into the valid array
286
         push(@validDisplayOrgList, \%displayHash);
287
    } 
288
    
289
}
290

    
291
if(!@validDisplayOrgList) {
292
     my $sender;
293
     my $contact;
294
     $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
295
     $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
296
    print "Content-type: text/html\n\n";
297
    print "The value of property ldap.templates.organizationList in " 
298
     . $skinName . ".properties file or metacat.properties file (if the property doesn't exist in the " 
299
     . $skinName . ".properties file) is invalid. Please send the information to ". $contact;
300
    exit(0);
301
}
302

    
303

    
304
#--------------------------------------------------------------------------80c->
305
# Define the main program logic that calls subroutines to do the work
306
#--------------------------------------------------------------------------80c->
307

    
308
# The processing step we are handling
309
my $stage = $query->param('stage') || $templates->{'stage'};
310

    
311
my $cfg = $query->param('cfg');
312
debug("started with stage $stage, cfg $cfg");
313

    
314
# define the possible stages
315
my %stages = (
316
              'initregister'      => \&handleInitRegister,
317
              'register'          => \&handleRegister,
318
              'registerconfirmed' => \&handleRegisterConfirmed,
319
              'simplesearch'      => \&handleSimpleSearch,
320
              'initaddentry'      => \&handleInitAddEntry,
321
              'addentry'          => \&handleAddEntry,
322
              'initmodifyentry'   => \&handleInitModifyEntry,
323
              'modifyentry'       => \&handleModifyEntry,
324
              'changepass'        => \&handleChangePassword,
325
              'initchangepass'    => \&handleInitialChangePassword,
326
              'resetpass'         => \&handleResetPassword,
327
              'initresetpass'     => \&handleInitialResetPassword,
328
              'emailverification' => \&handleEmailVerification,
329
              'lookupname'        => \&handleLookupName,
330
              'searchnamesbyemail'=> \&handleSearchNameByEmail,
331
              #'getnextuid'        => \&getExistingHighestUidNum,
332
             );
333

    
334
# call the appropriate routine based on the stage
335
if ( $stages{$stage} ) {
336
  $stages{$stage}->();
337
} else {
338
  &handleResponseMessage();
339
}
340

    
341
#--------------------------------------------------------------------------80c->
342
# Define the subroutines to do the work
343
#--------------------------------------------------------------------------80c->
344

    
345
sub clearTemporaryAccounts {
346
	
347
    #search accounts that have expired
348
	my $org = $query->param('o'); 
349
    my $ldapUsername = $ldapConfig->{$org}{'user'};
350
    my $ldapPassword = $ldapConfig->{$org}{'password'};
351
    my $orgAuthBase = $ldapConfig->{$org}{'base'};
352
    my $orgExpiration = $ldapConfig->{$org}{'expiration'};
353
    my $tmpSearchBase = 'dc=tmp,' . $orgAuthBase; 
354
	
355
	my $dt = DateTime->now;
356
	$dt->subtract( hours => $orgExpiration );
357
	my $expirationDate = $dt->ymd("") . $dt->hms("") . "Z";
358
    my $filter = "(&(objectClass=inetOrgPerson)(createTimestamp<=" . $expirationDate . "))";
359
    debug("Clearing expired accounts with filter: " . $filter . ", base: " . $tmpSearchBase);    
360
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
361

    
362
    my $ldap;
363
    my $mesg;
364
    
365
    my $dn;
366

    
367
    #if main ldap server is down, a html file containing warning message will be returned
368
    debug("clearTemporaryAccounts: connecting to $ldapurl, $timeout");
369
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
370
    if ($ldap) {
371
    	$ldap->start_tls( verify => 'require',
372
                      cafile => $ldapServerCACertFile);
373
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword ); 
374
		$mesg = $ldap->search (
375
			base   => $tmpSearchBase,
376
			filter => $filter,
377
			attrs => \@attrs,
378
		);
379
	    if ($mesg->count() > 0) {
380
			my $entry;
381
			foreach $entry ($mesg->all_entries) { 
382
            	$dn = $entry->dn();
383
            	# remove the entry
384
   				debug("Removing expired account: " . $dn);
385
            	$ldap->delete($dn);
386
			}
387
        }
388
    	$ldap->unbind;   # take down session
389
    }
390

    
391
    return 0;
392
}
393

    
394
sub fullTemplate {
395
    my $templateList = shift;
396
    my $templateVars = setVars(shift);
397
    my $c = Captcha::reCAPTCHA->new;
398
    my $captcha = 'captcha';
399
    #my $error=null;
400
    my $use_ssl= 1;
401
    #my $options=null;
402
    # use the AJAX style, only need to provide the public key to the template
403
    $templateVars->{'recaptchaPublicKey'} = $recaptchaPublicKey;
404
    #$templateVars->{$captcha} = $c->get_html($recaptchaPublicKey,undef, $use_ssl, undef);
405
    $template->process( $templates->{'header'}, $templateVars );
406
    foreach my $tmpl (@{$templateList}) {
407
        $template->process( $templates->{$tmpl}, $templateVars );
408
    }
409
    $template->process( $templates->{'footer'}, $templateVars );
410
}
411

    
412

    
413
#
414
# Initialize a form for a user to request the account name associated with an email address
415
#
416
sub handleLookupName {
417
    
418
    print "Content-type: text/html\n\n";
419
    # process the template files:
420
    fullTemplate(['lookupName']); 
421
    exit();
422
}
423

    
424
#
425
# Handle the user's request to look up account names with a specified email address.
426
# This relates to "Forget your user name"
427
#
428
sub handleSearchNameByEmail{
429

    
430
    print "Content-type: text/html\n\n";
431
   
432
    my $allParams = {'mail' => $query->param('mail')};
433
    my @requiredParams = ('mail');
434
    if (! paramsAreValid(@requiredParams)) {
435
        my $errorMessage = "Required information is missing. " .
436
            "Please fill in all required fields and resubmit the form.";
437
        fullTemplate(['lookupName'], { allParams => $allParams,
438
                                     errorMessage => $errorMessage });
439
        exit();
440
    }
441
    my $mail = $query->param('mail');
442
    
443
    #search accounts with the specified emails 
444
    $searchBase = $authBase; 
445
    my $filter = "(mail=" . $mail . ")";
446
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
447
    my $notHtmlFormat = 1;
448
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs, $notHtmlFormat);
449
    my $accountInfo;
450
    if ($found) {
451
        $accountInfo = $found;
452
    } else {
453
        $accountInfo = "There are no accounts associated with the email " . $mail . ".\n";
454
    }
455

    
456
    my $mailhost = $properties->getProperty('email.mailhost');
457
    my $sender;
458
    my $contact;
459
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
460
    $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
461
    debug("the sender is " . $sender);
462
    debug("the contact is " . $contact);
463
    my $recipient = $query->param('mail');
464
    # Send the email message to them
465
    my $smtp = Net::SMTP->new($mailhost) or do {  
466
                                                  fullTemplate( ['lookupName'], {allParams => $allParams, 
467
                                                                errorMessage => "Our mail server currently is experiencing some difficulties. Please contact " . 
468
                                                                $skinProperties->getProperty("email.recipient") . "." });  
469
                                                  exit(0);
470
                                               };
471
    $smtp->mail($sender);
472
    $smtp->to($recipient);
473

    
474
    my $message = <<"     ENDOFMESSAGE";
475
    To: $recipient
476
    From: $sender
477
    Subject: Your Account Information
478
        
479
    Somebody (hopefully you) looked up the account information associated with the email address.  
480
    Here is the account information:
481
    
482
    $accountInfo
483

    
484
    Thanks,
485
        $sender
486
    
487
     ENDOFMESSAGE
488
     $message =~ s/^[ \t\r\f]+//gm;
489
    
490
     $smtp->data($message);
491
     $smtp->quit;
492
     fullTemplate( ['lookupNameSuccess'] );
493
    
494
}
495

    
496

    
497
#
498
# create the initial registration form 
499
#
500
sub handleInitRegister {
501
  my $vars = shift;
502
  print "Content-type: text/html\n\n";
503
  # process the template files:
504
  fullTemplate(['register'], {stage => "register"}); 
505
  exit();
506
}
507

    
508

    
509

    
510
#
511
# process input from the register stage, which occurs when
512
# a user submits form data to create a new account
513
#
514
sub handleRegister {
515
    
516
    #print "Content-type: text/html\n\n";
517
    if ($query->param('o') =~ "LTER") {
518
      print "Content-type: text/html\n\n";
519
      fullTemplate( ['registerLter'] );
520
      exit(0);
521
    } 
522
    
523
    my $allParams = { 'givenName' => $query->param('givenName'), 
524
                      'sn' => $query->param('sn'),
525
                      'o' => $query->param('o'), 
526
                      'mail' => $query->param('mail'), 
527
                      'uid' => $query->param('uid'), 
528
                      'userPassword' => $query->param('userPassword'), 
529
                      'userPassword2' => $query->param('userPassword2'), 
530
                      'title' => $query->param('title'), 
531
                      'telephoneNumber' => $query->param('telephoneNumber') };
532
    
533
    # Check the recaptcha
534
    my $c = Captcha::reCAPTCHA->new;
535
    #my $challenge = $query->param('recaptcha_challenge_field');
536
    my $response = $query->param('g-recaptcha-response');
537
    if ($response) {
538
       #do nothing
539
       debug("users passed the test");
540
    } else {
541
       debug("users didn't pass the test and reset the reponse to error");
542
       $response="error";
543
    }
544
    #debug("the reponse of recaptcha is $response");
545
    # Verify submission (v2 version)
546
    my $result = $c->check_answer_v2($recaptchaPrivateKey, $response, $ENV{REMOTE_ADDR});
547

    
548
    if ( $result->{is_valid} ) {
549
        #print "Yes!";
550
        #exit();
551
    }
552
    else {
553
        print "Content-type: text/html\n\n";
554
        my $errorMessage = "The verification code is wrong. Please input again.";
555
        fullTemplate(['register'], { stage => "register",
556
                                     allParams => $allParams,
557
                                     errorMessage => $errorMessage });
558
        exit();
559
    }
560
    
561
    
562
    # Check that all required fields are provided and not null
563
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail', 
564
                           'uid', 'userPassword', 'userPassword2');
565
    if (! paramsAreValid(@requiredParams)) {
566
        print "Content-type: text/html\n\n";
567
        my $errorMessage = "Required information is missing. " .
568
            "Please fill in all required fields and resubmit the form.";
569
        fullTemplate(['register'], { stage => "register",
570
                                     allParams => $allParams,
571
                                     errorMessage => $errorMessage });
572
        exit();
573
    } else {
574
         if ($query->param('userPassword') ne $query->param('userPassword2')) {
575
            print "Content-type: text/html\n\n";
576
            my $errorMessage = "The passwords do not match. Try again.";
577
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
578
                                                            allParams => $allParams,
579
                                                            errorMessage => $errorMessage });
580
            exit();
581
        }
582
        my $o = $query->param('o');    
583
        $searchBase = $ldapConfig->{$o}{'base'};  
584
    }
585
    
586
    # Remove any expired temporary accounts for this subtree before continuing
587
    clearTemporaryAccounts();
588
    
589
    # Check if the uid was taken in the production space
590
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
591
    my $uidExists;
592
    my $uid=$query->param('uid');
593
    my $uidFilter = "uid=" . $uid;
594
    my $newSearchBase = $ldapConfig->{$query->param('o')}{'org'} . "," .  $searchBase;
595
    debug("the new search base is $newSearchBase");
596
    $uidExists = uidExists($ldapurl, $newSearchBase, $uidFilter, \@attrs);
597
    debug("the result of uidExists $uidExists");
598
    if($uidExists) {
599
         print "Content-type: text/html\n\n";
600
            my $errorMessage = $uidExists;
601
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
602
                                                            allParams => $allParams,
603
                                                            errorMessage => $errorMessage });
604
            exit();
605
    }
606

    
607
    # Search LDAP for matching entries that already exist
608
    # Some forms use a single text search box, whereas others search per
609
    # attribute.
610
    my $filter;
611
    if ($query->param('searchField')) {
612

    
613
      $filter = "(|" . 
614
                "(uid=" . $query->param('searchField') . ") " .
615
                "(mail=" . $query->param('searchField') . ")" .
616
                "(&(sn=" . $query->param('searchField') . ") " . 
617
                "(givenName=" . $query->param('searchField') . "))" . 
618
                ")";
619
    } else {
620
      $filter = "(|" . 
621
                "(uid=" . $query->param('uid') . ") " .
622
                "(mail=" . $query->param('mail') . ")" .
623
                "(&(sn=" . $query->param('sn') . ") " . 
624
                "(givenName=" . $query->param('givenName') . "))" . 
625
                ")";
626
    }
627

    
628
    
629
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
630

    
631
    # If entries match, send back a request to confirm new-user creation
632
    if ($found) {
633
      print "Content-type: text/html\n\n";
634
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
635
                                                     allParams => $allParams,
636
                                                     foundAccounts => $found });
637
    # Otherwise, create a new user in the LDAP directory
638
    } else {
639
        createTemporaryAccount($allParams);
640
    }
641

    
642
    exit();
643
}
644

    
645
#
646
# process input from the registerconfirmed stage, which occurs when
647
# a user chooses to create an account despite similarities to other
648
# existing accounts
649
#
650
sub handleRegisterConfirmed {
651
  
652
    my $allParams = { 'givenName' => $query->param('givenName'), 
653
                      'sn' => $query->param('sn'),
654
                      'o' => $query->param('o'), 
655
                      'mail' => $query->param('mail'), 
656
                      'uid' => $query->param('uid'), 
657
                      'userPassword' => $query->param('userPassword'), 
658
                      'userPassword2' => $query->param('userPassword2'), 
659
                      'title' => $query->param('title'), 
660
                      'telephoneNumber' => $query->param('telephoneNumber') };
661
    #print "Content-type: text/html\n\n";
662
    createTemporaryAccount($allParams);
663
    exit();
664
}
665

    
666
#
667
# change a user's password upon request
668
#
669
sub handleChangePassword {
670

    
671
    print "Content-type: text/html\n\n";
672

    
673
    my $allParams = { 'test' => "1", };
674
    if ($query->param('uid')) {
675
        $$allParams{'uid'} = $query->param('uid');
676
    }
677
    if ($query->param('o')) {
678
        $$allParams{'o'} = $query->param('o');
679
        my $o = $query->param('o');
680
        
681
        $searchBase = $ldapConfig->{$o}{'base'};
682
    }
683

    
684

    
685
    # Check that all required fields are provided and not null
686
    my @requiredParams = ( 'uid', 'o', 'oldpass', 
687
                           'userPassword', 'userPassword2');
688
    if (! paramsAreValid(@requiredParams)) {
689
        my $errorMessage = "Required information is missing. " .
690
            "Please fill in all required fields and submit the form.";
691
        fullTemplate( ['changePass'], { stage => "changepass",
692
                                        allParams => $allParams,
693
                                        errorMessage => $errorMessage });
694
        exit();
695
    }
696

    
697
    # We have all of the info we need, so try to change the password
698
    if ($query->param('userPassword') eq $query->param('userPassword2')) {
699

    
700
        my $o = $query->param('o');
701
        $searchBase = $ldapConfig->{$o}{'base'};
702
        $ldapUsername = $ldapConfig->{$o}{'user'};
703
        $ldapPassword = $ldapConfig->{$o}{'password'};
704

    
705
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
706
        if ($query->param('o') =~ "LTER") {
707
            fullTemplate( ['registerLter'] );
708
        } else {
709
            my $errorMessage = changePassword(
710
                    $dn, $query->param('userPassword'), 
711
                    $dn, $query->param('oldpass'), $query->param('o'));
712
            if ($errorMessage) {
713
                fullTemplate( ['changePass'], { stage => "changepass",
714
                                                allParams => $allParams,
715
                                                errorMessage => $errorMessage });
716
                exit();
717
            } else {
718
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
719
                                                       allParams => $allParams });
720
                exit();
721
            }
722
        }
723
    } else {
724
        my $errorMessage = "The passwords do not match. Try again.";
725
        fullTemplate( ['changePass'], { stage => "changepass",
726
                                        allParams => $allParams,
727
                                        errorMessage => $errorMessage });
728
        exit();
729
    }
730
}
731

    
732
#
733
# change a user's password upon request - no input params
734
# only display chagepass template without any error
735
#
736
sub handleInitialChangePassword {
737
    print "Content-type: text/html\n\n";
738

    
739
    my $allParams = { 'test' => "1", };
740
    my $errorMessage = "";
741
    fullTemplate( ['changePass'], { stage => "changepass",
742
                                    errorMessage => $errorMessage });
743
    exit();
744
}
745

    
746
#
747
# reset a user's password upon request
748
#
749
sub handleResetPassword {
750

    
751
    print "Content-type: text/html\n\n";
752

    
753
    my $allParams = { 'test' => "1", };
754
    if ($query->param('uid')) {
755
        $$allParams{'uid'} = $query->param('uid');
756
    }
757
    if ($query->param('o')) {
758
        $$allParams{'o'} = $query->param('o');
759
        my $o = $query->param('o');
760
        
761
        $searchBase = $ldapConfig->{$o}{'base'};
762
        $ldapUsername = $ldapConfig->{$o}{'user'};
763
        $ldapPassword = $ldapConfig->{$o}{'password'};
764
    }
765

    
766
    # Check that all required fields are provided and not null
767
    my @requiredParams = ( 'uid', 'o' );
768
    if (! paramsAreValid(@requiredParams)) {
769
        my $errorMessage = "Required information is missing. " .
770
            "Please fill in all required fields and submit the form.";
771
        fullTemplate( ['resetPass'],  { stage => "resetpass",
772
                                        allParams => $allParams,
773
                                        errorMessage => $errorMessage });
774
        exit();
775
    }
776

    
777
    # We have all of the info we need, so try to change the password
778
    my $o = $query->param('o');
779
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
780
    debug("handleResetPassword: dn: $dn");
781
    if ($query->param('o') =~ "LTER") {
782
        fullTemplate( ['registerLter'] );
783
        exit();
784
    } else {
785
        my $errorMessage = "";
786
        my $recipient;
787
        my $userPass;
788
        my $entry = getLdapEntry($ldapurl, $searchBase, 
789
                $query->param('uid'), $query->param('o'));
790

    
791
        if ($entry) {
792
            $recipient = $entry->get_value('mail');
793
            $userPass = getRandomPassword();
794
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
795
        } else {
796
            $errorMessage = "User not found in database.  Please try again.";
797
        }
798

    
799
        if ($errorMessage) {
800
            fullTemplate( ['resetPass'], { stage => "resetpass",
801
                                           allParams => $allParams,
802
                                           errorMessage => $errorMessage });
803
            exit();
804
        } else {
805
            my $errorMessage = sendPasswordNotification($query->param('uid'),
806
                    $query->param('o'), $userPass, $recipient, $cfg);
807
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
808
                                                  allParams => $allParams,
809
                                                  errorMessage => $errorMessage });
810
            exit();
811
        }
812
    }
813
}
814

    
815
#
816
# reset a user's password upon request- no initial params
817
# only display resetpass template without any error
818
#
819
sub handleInitialResetPassword {
820
    print "Content-type: text/html\n\n";
821
    my $errorMessage = "";
822
    fullTemplate( ['resetPass'], { stage => "resetpass",
823
                                   errorMessage => $errorMessage });
824
    exit();
825
}
826

    
827
#
828
# Construct a random string to use for a newly reset password
829
#
830
sub getRandomPassword {
831
    my $length = shift;
832
    if (!$length) {
833
        $length = 8;
834
    }
835
    my $newPass = "";
836

    
837
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
838
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
839
    return $newPass;
840
}
841

    
842
#
843
# Change a password to a new value, binding as the provided user
844
#
845
sub changePassword {
846
    my $userDN = shift;
847
    my $userPass = shift;
848
    my $bindDN = shift;
849
    my $bindPass = shift;
850
    my $o = shift;
851

    
852
    my $searchBase = $ldapConfig->{$o}{'base'};
853

    
854
    my $errorMessage = 0;
855
    my $ldap;
856

    
857
    #if main ldap server is down, a html file containing warning message will be returned
858
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
859
    
860
    if ($ldap) {
861
        $ldap->start_tls( verify => 'require',
862
                      cafile => $ldapServerCACertFile);
863
        debug("changePassword: attempting to bind to $bindDN");
864
        my $bindresult = $ldap->bind( version => 3, dn => $bindDN, 
865
                                  password => $bindPass );
866
        if ($bindresult->code) {
867
            $errorMessage = "Failed to log in. Are you sure your connection credentails are " .
868
                            "correct? Please correct and try again...";
869
            return $errorMessage;
870
        }
871

    
872
    	# Find the user here and change their entry
873
    	my $newpass = createSeededPassHash($userPass);
874
    	my $modifications = { userPassword => $newpass };
875
      debug("changePass: setting password for $userDN to $newpass");
876
    	my $result = $ldap->modify( $userDN, replace => { %$modifications });
877
    
878
    	if ($result->code()) {
879
            debug("changePass: error changing password: " . $result->error);
880
        	$errorMessage = "There was an error changing the password:" .
881
                           "<br />\n" . $result->error;
882
    	} 
883
    	$ldap->unbind;   # take down session
884
    }
885

    
886
    return $errorMessage;
887
}
888

    
889
#
890
# generate a Seeded SHA1 hash of a plaintext password
891
#
892
sub createSeededPassHash {
893
    my $secret = shift;
894

    
895
    my $salt = "";
896
    for (my $i=0; $i < 4; $i++) {
897
        $salt .= int(rand(10));
898
    }
899

    
900
    my $ctx = Digest::SHA1->new;
901
    $ctx->add($secret);
902
    $ctx->add($salt);
903
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
904

    
905
    return $hashedPasswd;
906
}
907

    
908
#
909
# Look up an ldap entry for a user
910
#
911
sub getLdapEntry {
912
    my $ldapurl = shift;
913
    my $base = shift;
914
    my $username = shift;
915
    my $org = shift;
916

    
917
    my $entry = "";
918
    my $mesg;
919
    my $ldap;
920
    debug("ldap server: $ldapurl");
921

    
922
    #if main ldap server is down, a html file containing warning message will be returned
923
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
924
    
925
    if ($ldap) {
926
        $ldap->start_tls( verify => 'none');
927
        #$ldap->start_tls( verify => 'require',
928
        #              cafile => $ldapServerCACertFile);
929
    	my $bindresult = $ldap->bind;
930
    	if ($bindresult->code) {
931
        	return $entry;
932
    	}
933

    
934
        $base = $ldapConfig->{$org}{'org'} . ',' . $base;
935
        debug("getLdapEntry, searching for $base, (uid=$username)");
936
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
937
    	#if($ldapConfig->{$org}{'filter'}){
938
            #debug("getLdapEntry: filter set, searching for base=$base, " .
939
                  #"(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
940
        	#$mesg = $ldap->search ( base   => $base,
941
                #filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
942
    	#} else {
943
            #debug("getLdapEntry: no filter, searching for $base, (uid=$username)");
944
        	#$mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
945
    	#}
946
    
947
    	if ($mesg->count > 0) {
948
        	$entry = $mesg->pop_entry;
949
        	$ldap->unbind;   # take down session
950
    	} else {
951
        	$ldap->unbind;   # take down session
952
        	# Follow references by recursive call to self
953
        	my @references = $mesg->references();
954
        	for (my $i = 0; $i <= $#references; $i++) {
955
            	my $uri = URI->new($references[$i]);
956
            	my $host = $uri->host();
957
            	my $path = $uri->path();
958
            	$path =~ s/^\///;
959
            	$entry = &getLdapEntry($host, $path, $username, $org);
960
            	if ($entry) {
961
                    debug("getLdapEntry: recursion found $host, $path, $username, $org");
962
                	return $entry;
963
            	}
964
        	}
965
    	}
966
    }
967
    return $entry;
968
}
969

    
970
# 
971
# send an email message notifying the user of the pw change
972
#
973
sub sendPasswordNotification {
974
    my $username = shift;
975
    my $org = shift;
976
    my $newPass = shift;
977
    my $recipient = shift;
978
    my $cfg = shift;
979

    
980
    my $errorMessage = "";
981
    if ($recipient) {
982
    
983
        my $mailhost = $properties->getProperty('email.mailhost');
984
        my $sender;
985
        my $contact;
986
        $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
987
        # Send the email message to them
988
        my $smtp = Net::SMTP->new($mailhost);
989
        $smtp->mail($sender);
990
        $smtp->to($recipient);
991

    
992
        my $message = <<"        ENDOFMESSAGE";
993
        To: $recipient
994
        From: $sender
995
        Subject: Your Account Password Reset
996
        
997
        Somebody (hopefully you) requested that your account password be reset.  
998
        Your temporary password is below. Please change it as soon as possible 
999
        at: $contextUrl/style/skins/account/.
1000

    
1001
            Username: $username
1002
        Organization: $org
1003
        New Password: $newPass
1004

    
1005
        Thanks,
1006
            $sender
1007
            $contact
1008
    
1009
        ENDOFMESSAGE
1010
        $message =~ s/^[ \t\r\f]+//gm;
1011
    
1012
        $smtp->data($message);
1013
        $smtp->quit;
1014
    } else {
1015
        $errorMessage = "Failed to send password because I " .
1016
                        "couldn't find a valid email address.";
1017
    }
1018
    return $errorMessage;
1019
}
1020

    
1021
#
1022
# search the LDAP production space to see if a uid already exists
1023
#
1024
sub uidExists {
1025
    my $ldapurl = shift;
1026
    debug("the ldap ulr is $ldapurl");
1027
    my $base = shift;
1028
    debug("the base is $base");
1029
    my $filter = shift;
1030
    debug("the filter is $filter");
1031
    my $attref = shift;
1032
  
1033
    my $ldap;
1034
    my $mesg;
1035

    
1036
    my $foundAccounts = 0;
1037

    
1038
    #if main ldap server is down, a html file containing warning message will be returned
1039
    debug("uidExists: connecting to $ldapurl, $timeout");
1040
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1041
    if ($ldap) {
1042
        $ldap->start_tls( verify => 'none');
1043
        #$ldap->start_tls( verify => 'require',
1044
        #              cafile => $ldapServerCACertFile);
1045
        $ldap->bind( version => 3, anonymous => 1);
1046
        $mesg = $ldap->search (
1047
            base   => $base,
1048
            filter => $filter,
1049
            attrs => @$attref,
1050
        );
1051
        debug("the message count is " . $mesg->count());
1052
        if ($mesg->count() > 0) {
1053
            $foundAccounts = "The username has been taken already by another user. Please choose a different one.";
1054
           
1055
        }
1056
        $ldap->unbind;   # take down session
1057
    } else {
1058
        $foundAccounts = "The ldap server is not running";
1059
    }
1060
    return $foundAccounts;
1061
}
1062

    
1063
#
1064
# search the LDAP directory to see if a similar account already exists
1065
#
1066
sub findExistingAccounts {
1067
    my $ldapurl = shift;
1068
    my $base = shift;
1069
    my $filter = shift;
1070
    my $attref = shift;
1071
    my $notHtmlFormat = shift;
1072
    my $ldap;
1073
    my $mesg;
1074

    
1075
    my $foundAccounts = 0;
1076

    
1077
    #if main ldap server is down, a html file containing warning message will be returned
1078
    debug("findExistingAccounts: connecting to $ldapurl, $timeout");
1079
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1080
    if ($ldap) {
1081
    	$ldap->start_tls( verify => 'none');
1082
    	#$ldap->start_tls( verify => 'require',
1083
        #              cafile => $ldapServerCACertFile);
1084
    	$ldap->bind( version => 3, anonymous => 1);
1085
		$mesg = $ldap->search (
1086
			base   => $base,
1087
			filter => $filter,
1088
			attrs => @$attref,
1089
		);
1090

    
1091
	    if ($mesg->count() > 0) {
1092
			$foundAccounts = "";
1093
			my $entry;
1094
			foreach $entry ($mesg->all_entries) { 
1095
                # a fix to ignore 'ou=Account' properties which are not usable accounts within Metacat.
1096
                # this could be done directly with filters on the LDAP connection, instead.
1097
                #if ($entry->dn !~ /ou=Account/) {
1098
                    if($notHtmlFormat) {
1099
                        $foundAccounts .= "\nAccount: ";
1100
                    } else {
1101
                        $foundAccounts .= "<p>\n<b><u>Account:</u> ";
1102
                    }
1103
                    $foundAccounts .= $entry->dn();
1104
                    if($notHtmlFormat) {
1105
                        $foundAccounts .= "\n";
1106
                    } else {
1107
                        $foundAccounts .= "</b><br />\n";
1108
                    }
1109
                    foreach my $attribute ($entry->attributes()) {
1110
                        my $value = $entry->get_value($attribute);
1111
                        $foundAccounts .= "$attribute: ";
1112
                        $foundAccounts .= $value;
1113
                         if($notHtmlFormat) {
1114
                            $foundAccounts .= "\n";
1115
                        } else {
1116
                            $foundAccounts .= "<br />\n";
1117
                        }
1118
                    }
1119
                    if($notHtmlFormat) {
1120
                        $foundAccounts .= "\n";
1121
                    } else {
1122
                        $foundAccounts .= "</p>\n";
1123
                    }
1124
                    
1125
                #}
1126
			}
1127
        }
1128
    	$ldap->unbind;   # take down session
1129

    
1130
    	# Follow references
1131
    	my @references = $mesg->references();
1132
    	for (my $i = 0; $i <= $#references; $i++) {
1133
        	my $uri = URI->new($references[$i]);
1134
        	my $host = $uri->host();
1135
        	my $path = $uri->path();
1136
        	$path =~ s/^\///;
1137
        	my $refFound = &findExistingAccounts($host, $path, $filter, $attref, $notHtmlFormat);
1138
        	if ($refFound) {
1139
            	$foundAccounts .= $refFound;
1140
        	}
1141
    	}
1142
    }
1143

    
1144
    #print "<p>Checking referrals...</p>\n";
1145
    #my @referrals = $mesg->referrals();
1146
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
1147
    #for (my $i = 0; $i <= $#referrals; $i++) {
1148
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
1149
    #}
1150

    
1151
    return $foundAccounts;
1152
}
1153

    
1154
#
1155
# Validate that we have the proper set of input parameters
1156
#
1157
sub paramsAreValid {
1158
    my @pnames = @_;
1159

    
1160
    my $allValid = 1;
1161
    foreach my $parameter (@pnames) {
1162
        if (!defined($query->param($parameter)) || 
1163
            ! $query->param($parameter) ||
1164
            $query->param($parameter) =~ /^\s+$/) {
1165
            $allValid = 0;
1166
        }
1167
    }
1168

    
1169
    return $allValid;
1170
}
1171

    
1172
#
1173
# Create a temporary account for a user and send an email with a link which can click for the
1174
# verification. This is used to protect the ldap server against spams.
1175
#
1176
sub createTemporaryAccount {
1177
    my $allParams = shift;
1178
    my $org = $query->param('o'); 
1179
    my $ldapUsername = $ldapConfig->{$org}{'user'};
1180
    my $ldapPassword = $ldapConfig->{$org}{'password'};
1181
    my $tmp = 1;
1182

    
1183
    ################## Search LDAP to see if the dc=tmp which stores the inactive accounts exist or not. If it doesn't exist, it will be generated
1184
    my $orgAuthBase = $ldapConfig->{$org}{'base'};
1185
    my $tmpSearchBase = 'dc=tmp,' . $orgAuthBase; 
1186
    my $tmpFilter = "dc=tmp";
1187
    my @attributes=['dc'];
1188
    my $foundTmp = searchDirectory($ldapurl, $orgAuthBase, $tmpFilter, \@attributes);
1189
    if (!$foundTmp) {
1190
        my $dn = $tmpSearchBase;
1191
        my $additions = [ 
1192
                    'dc' => 'tmp',
1193
                    'o'  => 'tmp',
1194
                    'objectclass' => ['top', 'dcObject', 'organization']
1195
                    ];
1196
        createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1197
    } else {
1198
     debug("found the tmp space");
1199
    }
1200
    
1201
    ################## Search LDAP for matching o or ou under the dc=tmp that already exist. If it doesn't exist, it will be generated
1202
    my $filter = $ldapConfig->{$org}{'filter'};   
1203
    
1204
    debug("search filer " . $filter);
1205
    debug("ldap server ". $ldapurl);
1206
    debug("sesarch base " . $tmpSearchBase);
1207
    #print "Content-type: text/html\n\n";
1208
    my @attrs = ['o', 'ou' ];
1209
    my $found = searchDirectory($ldapurl, $tmpSearchBase, $filter, \@attrs);
1210

    
1211
    my @organizationInfo = split('=', $ldapConfig->{$org}{'org'}); #split 'o=NCEAS' or something like that
1212
    my $organization = $organizationInfo[0]; # This will be 'o' or 'ou'
1213
    my $organizationName = $organizationInfo[1]; # This will be 'NCEAS' or 'Account'
1214
        
1215
    if(!$found) {
1216
        debug("generate the subtree in the dc=tmp===========================");
1217
        #need to generate the subtree o or ou
1218
        my $additions;
1219
            if($organization eq 'ou') {
1220
                $additions = [ 
1221
                    $organization   => $organizationName,
1222
                    'objectclass' => ['top', 'organizationalUnit']
1223
                    ];
1224
            
1225
            } else {
1226
                $additions = [ 
1227
                    $organization   => $organizationName,
1228
                    'objectclass' => ['top', 'organization']
1229
                    ];
1230
            
1231
            } 
1232
        my $dn=$ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1233
        createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1234
    } 
1235
    
1236
    ################create an account under tmp subtree 
1237
    
1238
     my $dn_store_next_uid=$properties->getProperty('ldap.nextuid.storing.dn');
1239
    my $attribute_name_store_next_uid = $properties->getProperty('ldap.nextuid.storing.attributename');
1240
    #get the next avaliable uid number. If it fails, the program will exist.
1241
    my $nextUidNumber = getNextUidNumber($ldapUsername, $ldapPassword);
1242
    if(!$nextUidNumber) {
1243
        print "Content-type: text/html\n\n";
1244
         my $sender;
1245
         my $contact;
1246
        $sender = $skinProperties->getProperty("email.recipient") or $sender = $properties->getProperty('email.recipient');
1247
        $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
1248
        my $errorMessage = "The Identity Service can't get the next avaliable uid number. Please try again.  If the issue persists, please contact the administrator - $contact.
1249
                           The possible reasons are: the dn - $dn_store_next_uid or its attribute - $attribute_name_store_next_uid don't exist; the value of the attribute - $attribute_name_store_next_uid
1250
                           is not a number; or lots of users were registering and you couldn't get a lock on the dn - $dn_store_next_uid.";
1251
        fullTemplate(['register'], { stage => "register",
1252
                                     allParams => $allParams,
1253
                                     errorMessage => $errorMessage });
1254
        exit(0);
1255
    }
1256
    my $cn = join(" ", $query->param('givenName'), $query->param('sn')); 
1257
    #generate a randomstr for matching the email.
1258
    my $randomStr = getRandomPassword(16);
1259
    # Create a hashed version of the password
1260
    my $shapass = createSeededPassHash($query->param('userPassword'));
1261
    my $additions = [ 
1262
                'uid'   => $query->param('uid'),
1263
                'cn'   => $cn,
1264
                'sn'   => $query->param('sn'),
1265
                'givenName'   => $query->param('givenName'),
1266
                'mail' => $query->param('mail'),
1267
                'userPassword' => $shapass,
1268
                'employeeNumber' => $randomStr,
1269
                'uidNumber' => $nextUidNumber,
1270
                'gidNumber' => $nextUidNumber,
1271
                'loginShell' => '/sbin/nologin',
1272
                'homeDirectory' => '/dev/null',
1273
                'objectclass' => ['top', 'person', 'organizationalPerson', 
1274
                                'inetOrgPerson', 'posixAccount', 'shadowAccount' ],
1275
                $organization   => $organizationName
1276
                ];
1277
    my $gecos;
1278
    if (defined($query->param('telephoneNumber')) && 
1279
                $query->param('telephoneNumber') &&
1280
                ! $query->param('telephoneNumber') =~ /^\s+$/) {
1281
                $$additions[$#$additions + 1] = 'telephoneNumber';
1282
                $$additions[$#$additions + 1] = $query->param('telephoneNumber');
1283
                $gecos = $cn . ',,'. $query->param('telephoneNumber'). ',';
1284
    } else {
1285
        $gecos = $cn . ',,,';
1286
    }
1287
    
1288
    $$additions[$#$additions + 1] = 'gecos';
1289
    $$additions[$#$additions + 1] = $gecos;
1290
    
1291
    if (defined($query->param('title')) && 
1292
                $query->param('title') &&
1293
                ! $query->param('title') =~ /^\s+$/) {
1294
                $$additions[$#$additions + 1] = 'title';
1295
                $$additions[$#$additions + 1] = $query->param('title');
1296
    }
1297

    
1298
    
1299
    #$$additions[$#$additions + 1] = 'o';
1300
    #$$additions[$#$additions + 1] = $org;
1301
    my $dn='uid=' . $query->param('uid') . ',' . $ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1302
    createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1303
    
1304
    
1305
    ####################send the verification email to the user
1306
    my $link = '/' . $context . '/cgi-bin/ldapweb.cgi?cfg=' . $skinName . '&' . 'stage=' . $emailVerification . '&' . 'dn=' . $dn . '&' . 'hash=' . $randomStr . '&o=' . $org . '&uid=' . $query->param('uid'); #even though we use o=something. The emailVerification will figure the real o= or ou=something.
1307
    
1308
    my $overrideURL;
1309
    $overrideURL = $skinProperties->getProperty("email.overrideURL");
1310
    debug("the overrideURL is $overrideURL");
1311
    if (defined($overrideURL) && !($overrideURL eq '')) {
1312
    	$link = $serverUrl . $overrideURL . $link;
1313
    } else {
1314
    	$link = $serverUrl . $link;
1315
    }
1316
    
1317
    my $mailhost = $properties->getProperty('email.mailhost');
1318
    my $sender;
1319
    my $contact;
1320
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
1321
    $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
1322
    debug("the sender is " . $sender);
1323
    debug("the contact is :" . $contact);
1324
    my $recipient = $query->param('mail');
1325
    # Send the email message to them
1326
    my $smtp = Net::SMTP->new($mailhost) or do {  
1327
                                                  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 " . 
1328
                                                  $skinProperties->getProperty("email.recipient") . "." });  
1329
                                                  exit(0);
1330
                                               };
1331
    $smtp->mail($sender);
1332
    $smtp->to($recipient);
1333

    
1334
    my $message = <<"     ENDOFMESSAGE";
1335
    To: $recipient
1336
    From: $sender
1337
    Subject: New Account Activation
1338
        
1339
    Somebody (hopefully you) registered an account on $contextUrl/style/skins/account/.  
1340
    Please click the following link to activate your account.
1341
    If the link doesn't work, please copy the link to your browser:
1342
    
1343
    $link
1344

    
1345
    Thanks,
1346
        $sender
1347
        $contact
1348
    
1349
     ENDOFMESSAGE
1350
     $message =~ s/^[ \t\r\f]+//gm;
1351
    
1352
     $smtp->data($message);
1353
     $smtp->quit;
1354
    debug("the link is " . $link);
1355
    fullTemplate( ['success'] );
1356
    
1357
}
1358

    
1359
#
1360
# Bind to LDAP and create a new item (a user or subtree) using the information provided
1361
# by the user
1362
#
1363
sub createItem {
1364
    my $dn = shift;
1365
    my $ldapUsername = shift;
1366
    my $ldapPassword = shift;
1367
    my $additions = shift;
1368
    my $temp = shift; #if it is for a temporary account.
1369
    my $allParams = shift;
1370
    
1371
    my @failureTemplate;
1372
    if($temp){
1373
        @failureTemplate = ['registerFailed', 'register'];
1374
    } else {
1375
        @failureTemplate = ['registerFailed'];
1376
    }
1377
    print "Content-type: text/html\n\n";
1378
    debug("the dn is " . $dn);
1379
    debug("LDAP connection to $ldapurl...");    
1380
    debug("the ldap ca certificate is " . $ldapServerCACertFile);
1381
    #if main ldap server is down, a html file containing warning message will be returned
1382
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1383
    if ($ldap) {
1384
            $ldap->start_tls( verify => 'require',
1385
                      cafile => $ldapServerCACertFile);
1386
            debug("Attempting to bind to LDAP server with dn = $ldapUsername, pwd = $ldapPassword");
1387
            $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword ); 
1388
            my $result = $ldap->add ( 'dn' => $dn, 'attr' => [@$additions ]);
1389
            if ($result->code()) {
1390
                fullTemplate(@failureTemplate, { stage => "register",
1391
                                                            allParams => $allParams,
1392
                                                            errorMessage => $result->error });
1393
                exist(0);
1394
                # TODO SCW was included as separate errors, test this
1395
                #$templateVars    = setVars({ stage => "register",
1396
                #                     allParams => $allParams });
1397
                #$template->process( $templates->{'register'}, $templateVars);
1398
            } else {
1399
                #fullTemplate( ['success'] );
1400
            }
1401
            $ldap->unbind;   # take down session
1402
            
1403
    } else {   
1404
         fullTemplate(@failureTemplate, { stage => "register",
1405
                                                            allParams => $allParams,
1406
                                                            errorMessage => "The ldap server is not available now. Please try it later"});
1407
         exit(0);
1408
    }
1409
  
1410
}
1411

    
1412

    
1413

    
1414

    
1415

    
1416

    
1417
#
1418
# This subroutine will handle a email verification:
1419
# If the hash string matches the one store in the ldap, the account will be
1420
# copied from the temporary space to the permanent tree and the account in 
1421
# the temporary space will be removed.
1422
sub handleEmailVerification {
1423

    
1424
    my $cfg = $query->param('cfg');
1425
    my $dn = $query->param('dn');
1426
    my $hash = $query->param('hash');
1427
    my $org = $query->param('o');
1428
    my $uid = $query->param('uid');
1429
    
1430
    my $ldapUsername;
1431
    my $ldapPassword;
1432
    #my $orgAuthBase;
1433

    
1434
    $ldapUsername = $ldapConfig->{$org}{'user'};
1435
    $ldapPassword = $ldapConfig->{$org}{'password'};
1436
    #$orgAuthBase = $ldapConfig->{$org}{'base'};
1437
    
1438
    debug("LDAP connection to $ldapurl...");    
1439
    
1440

    
1441
   print "Content-type: text/html\n\n";
1442
   #if main ldap server is down, a html file containing warning message will be returned
1443
   my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1444
   if ($ldap) {
1445
        $ldap->start_tls( verify => 'require',
1446
                      cafile => $ldapServerCACertFile);
1447
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
1448
        my $mesg = $ldap->search(base => $dn, scope => 'base', filter => '(objectClass=*)'); #This dn is with the dc=tmp. So it will find out the temporary account registered in registration step.
1449
        my $max = $mesg->count;
1450
        debug("the count is " . $max);
1451
        if($max < 1) {
1452
            $ldap->unbind;   # take down session
1453
            fullTemplate( ['verificationFailed'], {errorMessage => "No record matched the dn " . $dn . " for the activation. You probably already activated the account."});
1454
            #handleLDAPBindFailure($ldapurl);
1455
            exit(0);
1456
        } else {
1457
            #check if the hash string match
1458
            my $entry = $mesg->entry (0);
1459
            my $hashStrFromLdap = $entry->get_value('employeeNumber');
1460
            if( $hashStrFromLdap eq $hash) {
1461
                #my $additions = [ ];
1462
                #foreach my $attr ( $entry->attributes ) {
1463
                    #if($attr ne 'employeeNumber') {
1464
                        #$$additions[$#$additions + 1] = $attr;
1465
                        #$$additions[$#$additions + 1] = $entry->get_value( $attr );
1466
                    #}
1467
                #}
1468

    
1469
                
1470
                my $orgDn = $ldapConfig->{$org}{'dn'}; #the DN for the organization.
1471
                $mesg = $ldap->moddn(
1472
                            dn => $dn,
1473
                            deleteoldrdn => 1,
1474
                            newrdn => "uid=" . $uid,
1475
                            newsuperior  =>  $orgDn);
1476
                $ldap->unbind;   # take down session
1477
                if($mesg->code()) {
1478
                    fullTemplate( ['verificationFailed'], {errorMessage => "Cannot move the account from the inactive area to the ative area since " . $mesg->error()});
1479
                    exit(0);
1480
                } else {
1481
                    fullTemplate( ['verificationSuccess'] );
1482
                }
1483
                #createAccount2($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1484
            } else {
1485
                $ldap->unbind;   # take down session
1486
                fullTemplate( ['verificationFailed'], {errorMessage => "The hash string " . $hash . " from your link doesn't match our record."});
1487
                exit(0);
1488
            }
1489
            
1490
        }
1491
    } else {   
1492
        handleLDAPBindFailure($ldapurl);
1493
        exit(0);
1494
    }
1495

    
1496
}
1497

    
1498
sub handleResponseMessage {
1499

    
1500
  print "Content-type: text/html\n\n";
1501
  my $errorMessage = "You provided invalid input to the script. " .
1502
                     "Try again please.";
1503
  fullTemplate( [], { stage => $templates->{'stage'},
1504
                      errorMessage => $errorMessage });
1505
  exit();
1506
}
1507

    
1508
#
1509
# perform a simple search against the LDAP database using 
1510
# a small subset of attributes of each dn and return it
1511
# as a table to the calling browser.
1512
#
1513
sub handleSimpleSearch {
1514

    
1515
    my $o = $query->param('o');
1516

    
1517
    my $ldapurl = $ldapConfig->{$o}{'url'};
1518
    my $searchBase = $ldapConfig->{$o}{'base'};
1519

    
1520
    print "Content-type: text/html\n\n";
1521

    
1522
    my $allParams = { 
1523
                      'cn' => $query->param('cn'),
1524
                      'sn' => $query->param('sn'),
1525
                      'gn' => $query->param('gn'),
1526
                      'o'  => $query->param('o'),
1527
                      'facsimiletelephonenumber' 
1528
                      => $query->param('facsimiletelephonenumber'),
1529
                      'mail' => $query->param('cmail'),
1530
                      'telephonenumber' => $query->param('telephonenumber'),
1531
                      'title' => $query->param('title'),
1532
                      'uid' => $query->param('uid'),
1533
                      'ou' => $query->param('ou'),
1534
                    };
1535

    
1536
    # Search LDAP for matching entries that already exist
1537
    my $filter = "(" . 
1538
                 $query->param('searchField') . "=" .
1539
                 "*" .
1540
                 $query->param('searchValue') .
1541
                 "*" .
1542
                 ")";
1543

    
1544
    my @attrs = [ 'sn', 
1545
                  'gn', 
1546
                  'cn', 
1547
                  'o', 
1548
                  'facsimiletelephonenumber', 
1549
                  'mail', 
1550
                  'telephoneNumber', 
1551
                  'title', 
1552
                  'uid', 
1553
                  'labeledURI', 
1554
                  'ou' ];
1555

    
1556
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
1557

    
1558
    # Send back the search results
1559
    if ($found) {
1560
      fullTemplate( ('searchResults'), { stage => "searchresults",
1561
                                         allParams => $allParams,
1562
                                         foundAccounts => $found });
1563
    } else {
1564
      $found = "No entries matched your criteria.  Please try again\n";
1565

    
1566
      fullTemplate( ('searchResults'), { stage => "searchresults",
1567
                                         allParams => $allParams,
1568
                                         foundAccounts => $found });
1569
    }
1570

    
1571
    exit();
1572
}
1573

    
1574
#
1575
# search the LDAP directory to see if a similar account already exists
1576
#
1577
sub searchDirectory {
1578
    my $ldapurl = shift;
1579
    my $base = shift;
1580
    my $filter = shift;
1581
    my $attref = shift;
1582

    
1583
	my $mesg;
1584
    my $foundAccounts = 0;
1585
    
1586
    #if ldap server is down, a html file containing warning message will be returned
1587
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1588
    
1589
    if ($ldap) {
1590
    	$ldap->start_tls( verify => 'require',
1591
                      cafile => $ldapServerCACertFile);
1592
    	$ldap->bind( version => 3, anonymous => 1);
1593
    	my $mesg = $ldap->search (
1594
        	base   => $base,
1595
        	filter => $filter,
1596
        	attrs => @$attref,
1597
    	);
1598

    
1599
    	if ($mesg->count() > 0) {
1600
        	$foundAccounts = "";
1601
        	my $entry;
1602
        	foreach $entry ($mesg->sorted(['sn'])) {
1603
          		$foundAccounts .= "<tr>\n<td class=\"main\">\n";
1604
          		$foundAccounts .= "<a href=\"" unless 
1605
                    (!$entry->get_value('labeledURI'));
1606
         		 $foundAccounts .= $entry->get_value('labeledURI') unless
1607
                    (!$entry->get_value('labeledURI'));
1608
          		$foundAccounts .= "\">\n" unless 
1609
                    (!$entry->get_value('labeledURI'));
1610
          		$foundAccounts .= $entry->get_value('givenName');
1611
          		$foundAccounts .= "</a>\n" unless 
1612
                    (!$entry->get_value('labeledURI'));
1613
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1614
          		$foundAccounts .= "<a href=\"" unless 
1615
                    (!$entry->get_value('labeledURI'));
1616
          		$foundAccounts .= $entry->get_value('labeledURI') unless
1617
                    (!$entry->get_value('labeledURI'));
1618
          		$foundAccounts .= "\">\n" unless 
1619
                    (!$entry->get_value('labeledURI'));
1620
          		$foundAccounts .= $entry->get_value('sn');
1621
          		$foundAccounts .= "</a>\n";
1622
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1623
          		$foundAccounts .= $entry->get_value('mail');
1624
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1625
          		$foundAccounts .= $entry->get_value('telephonenumber');
1626
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1627
          		$foundAccounts .= $entry->get_value('title');
1628
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1629
          		$foundAccounts .= $entry->get_value('ou');
1630
          		$foundAccounts .= "\n</td>\n";
1631
          		$foundAccounts .= "</tr>\n";
1632
        	}
1633
    	}
1634
    	$ldap->unbind;   # take down session
1635
    }
1636
    return $foundAccounts;
1637
}
1638

    
1639
sub debug {
1640
    my $msg = shift;
1641
    
1642
    if ($debug) {
1643
        print STDERR "LDAPweb: $msg\n";
1644
    }
1645
}
1646

    
1647
sub handleLDAPBindFailure {
1648
    my $ldapAttemptUrl = shift;
1649
    my $primaryLdap =  $properties->getProperty('auth.url');
1650

    
1651
    if ($ldapAttemptUrl eq  $primaryLdap) {
1652
        handleGeneralServerFailure("The main LDAP server $ldapurl is down!");
1653
    } else {
1654
        debug("attempted to bind to nonresponsive LDAP server $ldapAttemptUrl, skipped.");
1655
    }
1656
}
1657

    
1658
sub handleGeneralServerFailure {
1659
    my $errorMessage = shift;
1660
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
1661
    exit(0);   
1662
   }
1663
    
1664
sub setVars {
1665
    my $paramVars = shift;
1666
    # initialize default parameters 
1667
    my $templateVars = { cfg => $cfg,
1668
                         styleSkinsPath => $contextUrl . "/style/skins",
1669
                         styleCommonPath => $contextUrl . "/style/common",
1670
                         contextUrl => $contextUrl,
1671
                         cgiPrefix => $cgiPrefix,
1672
                         orgList => \@validDisplayOrgList,
1673
                         config  => $config,
1674
    };
1675
    
1676
    # append customized params
1677
    while (my ($k, $v) = each (%$paramVars)) {
1678
        $templateVars->{$k} = $v;
1679
    }
1680
    
1681
    return $templateVars;
1682
} 
1683

    
1684
#Method to get the next avaliable uid number. We use the mechanism - http://www.rexconsulting.net/ldap-protocol-uidNumber.html
1685
sub getNextUidNumber {
1686

    
1687
    my $maxAttempt = $properties->getProperty('ldap.nextuid.maxattempt');
1688
    
1689
    my $ldapUsername = shift;
1690
    my $ldapPassword = shift;
1691
    
1692
    my $realUidNumber;
1693
    my $uidNumber;
1694
    my $entry;
1695
    my $mesg;
1696
    my $ldap;
1697
    
1698
    debug("ldap server: $ldapurl");
1699
    
1700
    #if main ldap server is down, a html file containing warning message will be returned
1701
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1702
    
1703
    if ($ldap) {
1704
    	my $existingHighUid=getExistingHighestUidNum($ldapUsername, $ldapPassword);
1705
        $ldap->start_tls( verify => 'require',
1706
                      cafile => $ldapServerCACertFile);
1707
        my $bindresult = $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword);
1708
        #read the uid value stored in uidObject class
1709
        for(my $index=0; $index<$maxAttempt; $index++) {
1710
            $mesg = $ldap->search(base  => $dn_store_next_uid, filter => '(objectClass=*)');
1711
            if ($mesg->count() > 0) {
1712
                debug("Find the cn - $dn_store_next_uid");
1713
                $entry = $mesg->pop_entry;
1714
                $uidNumber = $entry->get_value($attribute_name_store_next_uid);
1715
                if($uidNumber) {
1716
                    if (looks_like_number($uidNumber)) {
1717
                        debug("uid number is $uidNumber");
1718
                        #remove the uid attribute with the read value
1719
                        my $delMesg = $ldap->modify($dn_store_next_uid, delete => { $attribute_name_store_next_uid => $uidNumber});
1720
                        if($delMesg->is_error()) {
1721
                            my $error=$delMesg->error();
1722
                            my $errorName = $delMesg->error_name();
1723
                            debug("can't remove the attribute - $error");
1724
                            debug("can't remove the attribute and the error name - $errorName");
1725
                            #can't remove the attribute with the specified value - that means somebody modify the value in another route, so try it again
1726
                        } else {
1727
                            debug("Remove the attribute successfully and write a new increased value back");
1728
                            if($existingHighUid) {
1729
                            	debug("exiting high uid exists =======================================");
1730
                            	if($uidNumber <= $existingHighUid ) {
1731
                            		debug("The stored uidNumber $uidNumber is less than or equals the used uidNumber $existingHighUid, so we will use the new number which is $existingHighUid+1");
1732
                            		$uidNumber = $existingHighUid +1;
1733
                            	} 
1734
                            }                  
1735
                            my $newValue = $uidNumber +1;
1736
                            $delMesg = $ldap->modify($dn_store_next_uid, add => {$attribute_name_store_next_uid => $newValue});
1737
                            $realUidNumber = $uidNumber;
1738
                            last;
1739
                        }
1740
                    }
1741
                    
1742
               } else {
1743
                 debug("can't find the attribute - $attribute_name_store_next_uid in the $dn_store_next_uid and we will try again");
1744
               }
1745
            } 
1746
        }
1747
        $ldap->unbind;   # take down session
1748
    }
1749
    return $realUidNumber;
1750
}
1751

    
1752
#Method to get the existing high uidNumber in the account tree.
1753
sub getExistingHighestUidNum {
1754
    my $ldapUsername = shift;
1755
    my $ldapPassword = shift;
1756
   
1757
    my $high;
1758
    my $ldap;
1759
    my $storedUidNumber;
1760
    
1761
    
1762
    #if main ldap server is down, a html file containing warning message will be returned
1763
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1764
    if ($ldap) {
1765
        $ldap->start_tls( verify => 'require',
1766
                      cafile => $ldapServerCACertFile);
1767
        my $bindresult = $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword);
1768
        my $mesg = $ldap->search(base  => $dn_store_next_uid, filter => '(objectClass=*)');
1769
         if ($mesg->count() > 0) {
1770
                debug("Find the cn - $dn_store_next_uid");
1771
                my  $entry = $mesg->pop_entry;
1772
                $storedUidNumber = $entry->get_value($attribute_name_store_next_uid);
1773
        }
1774
        my $authBase = $properties->getProperty("auth.base");
1775
        my $uids = $ldap->search(
1776
                        base => $authBase,
1777
                        scope => "sub",
1778
                        filter => "uidNumber=*", 
1779
                        attrs   => [ 'uidNumber' ],
1780
                        );
1781
       return unless $uids->count;
1782
  	    my @uids;
1783
        if ($uids->count > 0) {
1784
                foreach my $uid ($uids->all_entries) {
1785
                		if($storedUidNumber) {
1786
                			if( $uid->get_value('uidNumber') >= $storedUidNumber) {
1787
                				push @uids, $uid->get_value('uidNumber');
1788
                			}
1789
                		} else {
1790
                        	push @uids, $uid->get_value('uidNumber');
1791
                        }
1792
                }
1793
        }       
1794
        
1795
        if(@uids) {
1796
        	@uids = sort { $b <=> $a } @uids;
1797
        	$high = $uids[0];   
1798
        }    
1799
        debug("the highest exiting uidnumber is $high");
1800
        $ldap->unbind;   # take down session
1801
    }
1802
    return $high;
1803

    
1804
}
1805

    
1806

    
(10-10/14)