Project

General

Profile

1 2341 sgarg
#!/usr/bin/perl -w
2 4865 walbridge
#
3
#  '$RCSfile$'
4
#  Copyright: 2001 Regents of the University of California
5
#
6
#   '$Author$'
7
#     '$Date$'
8
# '$Revision$'
9
#
10
# This program is free software; you can redistribute it and/or modify
11
# it under the terms of the GNU General Public License as published by
12
# the Free Software Foundation; either version 2 of the License, or
13
# (at your option) any later version.
14
#
15
# This program is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
# GNU General Public License for more details.
19
#
20
# You should have received a copy of the GNU General Public License
21
# along with this program; if not, write to the Free Software
22
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
#
24 2341 sgarg
25
#
26
# This is a web-based application for allowing users to register a new
27
# account for Metacat access.  We currently only support LDAP even
28
# though metacat could potentially support other types of directories.
29 4394 walbridge
30
use lib '../WEB-INF/lib';
31 4080 daigle
use strict;             # turn on strict syntax checking
32
use Template;           # load the template-toolkit module
33 4394 walbridge
use CGI qw/:standard :html3/; # load the CGI module
34 4080 daigle
use Net::LDAP;          # load the LDAP net libraries
35
use Net::SMTP;          # load the SMTP net libraries
36
use Digest::SHA1;       # for creating the password hash
37
use MIME::Base64;       # for creating the password hash
38
use URI;                # for parsing URL syntax
39
use Config::Properties; # for parsing Java .properties files
40
use File::Basename;     # for path name parsing
41 8351 leinfelder
use DateTime;			# for parsing dates
42
use DateTime::Duration; # for substracting
43 8166 tao
use Captcha::reCAPTCHA; # for protection against spams
44 4394 walbridge
use Cwd 'abs_path';
45 8413 tao
use Scalar::Util qw(looks_like_number);
46 2341 sgarg
47 4080 daigle
# Global configuration paramters
48 4394 walbridge
# This entire block (including skin parsing) could be pushed out to a separate .pm file
49 4080 daigle
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 4394 walbridge
    print "Content-type: text/html\n\n";
55 4080 daigle
    print "Unable to locate Metacat properties. Working directory is set as " .
56
        $workingDirectory .", is this correct?";
57
    exit(0);
58
}
59 2341 sgarg
60 4080 daigle
$properties->load(*METACAT_PROPERTIES);
61 4010 tao
62 4394 walbridge
# 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 7199 leinfelder
my $protocol = 'http://';
70
if ( $properties->getProperty('server.httpPort') eq '443' ) {
71
	$protocol = 'https://';
72
}
73 8253 leinfelder
my $serverUrl = $protocol . $properties->getProperty('server.name');
74 4864 walbridge
if ($properties->getProperty('server.httpPort') ne '80') {
75 8253 leinfelder
        $serverUrl = $serverUrl . ':' . $properties->getProperty('server.httpPort');
76 4864 walbridge
}
77 8253 leinfelder
my $context = $properties->getProperty('application.context');
78
my $contextUrl = $serverUrl . '/' .  $context;
79 4394 walbridge
80
my $metacatUrl = $contextUrl . "/metacat";
81 8253 leinfelder
my $cgiPrefix = "/" . $context . "/cgi-bin";
82 4394 walbridge
my $styleSkinsPath = $contextUrl . "/style/skins";
83
my $styleCommonPath = $contextUrl . "/style/common";
84 9514 tao
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 4394 walbridge
94 9514 tao
95 8169 tao
#recaptcha key information
96
my $recaptchaPublicKey=$properties->getProperty('ldap.recaptcha.publickey');
97
my $recaptchaPrivateKey=$properties->getProperty('ldap.recaptcha.privatekey');
98
99 4394 walbridge
my @errorMessages;
100
my $error = 0;
101
102 8181 tao
my $emailVerification= 'emailverification';
103
104 8413 tao
 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 4394 walbridge
# 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 4747 walbridge
    debug("No configuration set.");
118 4394 walbridge
    print "Content-type: text/html\n\n";
119 4749 walbridge
    print 'LDAPweb Error: The registry requires a skin name to continue.';
120 4394 walbridge
    exit();
121
}
122
123
# Metacat isn't initialized, the registry will fail in strange ways.
124
if (!($metacatUrl)) {
125 4747 walbridge
    debug("No Metacat.");
126 4394 walbridge
    print "Content-type: text/html\n\n";
127
    'Registry Error: Metacat is not initialized! Make sure' .
128 5214 walbridge
        ' MetacatUrl is set correctly in ' .  $skinName . '.properties';
129 4394 walbridge
    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 4870 walbridge
# 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 4394 walbridge
my $searchBase;
168
my $ldapUsername;
169
my $ldapPassword;
170 4728 walbridge
# 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 4080 daigle
174
# Java uses miliseconds, Perl expects whole seconds
175 4728 walbridge
my $timeout = $properties->getProperty('ldap.connectTimeLimit') / 1000;
176 4080 daigle
177 2341 sgarg
# Get the CGI input variables
178
my $query = new CGI;
179 4747 walbridge
my $debug = 1;
180 2341 sgarg
181
#--------------------------------------------------------------------------80c->
182
# Set up the Template Toolkit to read html form templates
183
184 4080 daigle
# templates hash, imported from ldap.templates tree in metacat.properties
185
my $templates = $properties->splitToTree(qr/\./, 'ldap.templates');
186 4394 walbridge
$$templates{'header'} = $skinProperties->getProperty("registry.templates.header");
187
$$templates{'footer'} = $skinProperties->getProperty("registry.templates.footer");
188 2341 sgarg
189
# set some configuration options for the template object
190 4394 walbridge
my $ttConfig = {
191
             INCLUDE_PATH => $templatesDir,
192
             INTERPOLATE  => 0,
193
             POST_CHOMP   => 1,
194
             DEBUG        => 1,
195 2341 sgarg
             };
196
197
# create an instance of the template
198 4394 walbridge
my $template = Template->new($ttConfig) || handleGeneralServerFailure($Template::ERROR);
199 2341 sgarg
200 4080 daigle
# custom LDAP properties hash
201
my $ldapCustom = $properties->splitToTree(qr/\./, 'ldap');
202 2341 sgarg
203 8201 tao
# This is a hash which has the keys of the organization's properties 'name', 'base', 'organization'.
204 4394 walbridge
my $orgProps = $properties->splitToTree(qr/\./, 'organization');
205 8201 tao
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 4394 walbridge
my $orgNames = $properties->splitToTree(qr/\./, 'organization.name');
208
# pull out properties available e.g. 'name', 'base'
209
my @orgData = keys(%$orgProps);
210 4870 walbridge
211 8201 tao
my @orgList; #An array has the names (i.e, sub tree names, such as 'NCEAS', 'LTER' and 'KU')  of the all organizations in the metacat.properties.
212 4394 walbridge
while (my ($oKey, $oVal) = each(%$orgNames)) {
213
    push(@orgList, $oKey);
214
}
215
216 4866 walbridge
my $authBase = $properties->getProperty("auth.base");
217 4080 daigle
my $ldapConfig;
218
foreach my $o (@orgList) {
219 4394 walbridge
    foreach my $d (@orgData) {
220
        $ldapConfig->{$o}{$d} = $properties->getProperty("organization.$d.$o");
221 4080 daigle
    }
222 4866 walbridge
223 4870 walbridge
    # 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 4866 walbridge
    # 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 8201 tao
            #$ldapConfig->{$o}{'filter'} = $filter;
245
            $ldapConfig->{$o}{'filter'} = $ldapConfig->{$o}{'org'};
246 4866 walbridge
        }
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 4394 walbridge
    } else {
252
        $ldapConfig->{$o}{'dn'} = $ldapConfig->{$o}{'base'};
253
    }
254 4868 walbridge
255
    # set LDAP administrator user account
256 4866 walbridge
    if (!$ldapConfig->{$o}{'user'}) {
257
        $ldapConfig->{$o}{'user'} = $ldapConfig->{'unaffiliated'}{'user'};
258 4865 walbridge
    }
259 4868 walbridge
    # 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 4866 walbridge
265
    if (!$ldapConfig->{$o}{'password'}) {
266
        $ldapConfig->{$o}{'password'} = $ldapConfig->{'unaffiliated'}{'password'};
267
    }
268 2341 sgarg
}
269
270 8201 tao
### Determine the display organization list (such as NCEAS, Account ) in the ldap template files
271 8206 tao
my $displayOrgListStr;
272
$displayOrgListStr = $skinProperties->getProperty("ldap.templates.organizationList") or $displayOrgListStr = $properties->getProperty('ldap.templates.organizationList');
273 8207 tao
debug("the string of the org from properties : " . $displayOrgListStr);
274
my @displayOrgList = split(';', $displayOrgListStr);
275
276 8206 tao
my @validDisplayOrgList; #this array contains the org list which will be shown in the templates files.
277 8201 tao
278 8206 tao
my %orgNamesHash = %$orgNames;
279
foreach my $element (@displayOrgList) {
280
    if(exists $orgNamesHash{$element}) {
281 8540 tao
         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 8206 tao
         #if the name is found in the organization part of metacat.properties, put it into the valid array
286 8540 tao
         push(@validDisplayOrgList, \%displayHash);
287 8206 tao
    }
288
289
}
290 8201 tao
291 8206 tao
if(!@validDisplayOrgList) {
292
     my $sender;
293
     $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
294
    print "Content-type: text/html\n\n";
295
    print "The value of property ldap.templates.organizationList in "
296
     . $skinName . ".properties file or metacat.properties file (if the property doesn't exist in the "
297
     . $skinName . ".properties file) is invalid. Please send the information to ". $sender;
298
    exit(0);
299
}
300
301
302 2341 sgarg
#--------------------------------------------------------------------------80c->
303
# Define the main program logic that calls subroutines to do the work
304
#--------------------------------------------------------------------------80c->
305
306
# The processing step we are handling
307 4080 daigle
my $stage = $query->param('stage') || $templates->{'stage'};
308 2341 sgarg
309
my $cfg = $query->param('cfg');
310 4767 walbridge
debug("started with stage $stage, cfg $cfg");
311 2341 sgarg
312
# define the possible stages
313
my %stages = (
314
              'initregister'      => \&handleInitRegister,
315
              'register'          => \&handleRegister,
316
              'registerconfirmed' => \&handleRegisterConfirmed,
317
              'simplesearch'      => \&handleSimpleSearch,
318
              'initaddentry'      => \&handleInitAddEntry,
319
              'addentry'          => \&handleAddEntry,
320
              'initmodifyentry'   => \&handleInitModifyEntry,
321
              'modifyentry'       => \&handleModifyEntry,
322 2972 jones
              'changepass'        => \&handleChangePassword,
323
              'initchangepass'    => \&handleInitialChangePassword,
324 2341 sgarg
              'resetpass'         => \&handleResetPassword,
325 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
326 8185 tao
              'emailverification' => \&handleEmailVerification,
327 8229 tao
              'lookupname'        => \&handleLookupName,
328
              'searchnamesbyemail'=> \&handleSearchNameByEmail,
329 8818 tao
              #'getnextuid'        => \&getExistingHighestUidNum,
330 2341 sgarg
             );
331 4394 walbridge
332 2341 sgarg
# call the appropriate routine based on the stage
333
if ( $stages{$stage} ) {
334
  $stages{$stage}->();
335
} else {
336
  &handleResponseMessage();
337
}
338
339
#--------------------------------------------------------------------------80c->
340
# Define the subroutines to do the work
341
#--------------------------------------------------------------------------80c->
342
343 8351 leinfelder
sub clearTemporaryAccounts {
344
345
    #search accounts that have expired
346
	my $org = $query->param('o');
347
    my $ldapUsername = $ldapConfig->{$org}{'user'};
348
    my $ldapPassword = $ldapConfig->{$org}{'password'};
349
    my $orgAuthBase = $ldapConfig->{$org}{'base'};
350
    my $orgExpiration = $ldapConfig->{$org}{'expiration'};
351
    my $tmpSearchBase = 'dc=tmp,' . $orgAuthBase;
352
353
	my $dt = DateTime->now;
354
	$dt->subtract( hours => $orgExpiration );
355 8354 leinfelder
	my $expirationDate = $dt->ymd("") . $dt->hms("") . "Z";
356 8356 leinfelder
    my $filter = "(&(objectClass=inetOrgPerson)(createTimestamp<=" . $expirationDate . "))";
357
    debug("Clearing expired accounts with filter: " . $filter . ", base: " . $tmpSearchBase);
358 8351 leinfelder
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
359
360
    my $ldap;
361
    my $mesg;
362
363
    my $dn;
364
365
    #if main ldap server is down, a html file containing warning message will be returned
366 8356 leinfelder
    debug("clearTemporaryAccounts: connecting to $ldapurl, $timeout");
367 8351 leinfelder
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
368
    if ($ldap) {
369 8403 tao
    	$ldap->start_tls( verify => 'require',
370
                      cafile => $ldapServerCACertFile);
371 8351 leinfelder
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
372
		$mesg = $ldap->search (
373 8356 leinfelder
			base   => $tmpSearchBase,
374 8351 leinfelder
			filter => $filter,
375
			attrs => \@attrs,
376
		);
377
	    if ($mesg->count() > 0) {
378
			my $entry;
379
			foreach $entry ($mesg->all_entries) {
380
            	$dn = $entry->dn();
381
            	# remove the entry
382 8357 leinfelder
   				debug("Removing expired account: " . $dn);
383
            	$ldap->delete($dn);
384 8351 leinfelder
			}
385
        }
386
    	$ldap->unbind;   # take down session
387
    }
388
389 8354 leinfelder
    return 0;
390 8351 leinfelder
}
391
392 4728 walbridge
sub fullTemplate {
393
    my $templateList = shift;
394
    my $templateVars = setVars(shift);
395 8166 tao
    my $c = Captcha::reCAPTCHA->new;
396
    my $captcha = 'captcha';
397
    #my $error=null;
398
    my $use_ssl= 1;
399
    #my $options=null;
400 8250 leinfelder
    # use the AJAX style, only need to provide the public key to the template
401
    $templateVars->{'recaptchaPublicKey'} = $recaptchaPublicKey;
402
    #$templateVars->{$captcha} = $c->get_html($recaptchaPublicKey,undef, $use_ssl, undef);
403 4728 walbridge
    $template->process( $templates->{'header'}, $templateVars );
404
    foreach my $tmpl (@{$templateList}) {
405
        $template->process( $templates->{$tmpl}, $templateVars );
406
    }
407
    $template->process( $templates->{'footer'}, $templateVars );
408
}
409
410 8221 tao
411 8229 tao
#
412
# Initialize a form for a user to request the account name associated with an email address
413
#
414
sub handleLookupName {
415
416
    print "Content-type: text/html\n\n";
417
    # process the template files:
418
    fullTemplate(['lookupName']);
419
    exit();
420
}
421 8221 tao
422 2341 sgarg
#
423 8221 tao
# Handle the user's request to look up account names with a specified email address.
424
# This relates to "Forget your user name"
425
#
426 8229 tao
sub handleSearchNameByEmail{
427 8221 tao
428
    print "Content-type: text/html\n\n";
429
430
    my $allParams = {'mail' => $query->param('mail')};
431
    my @requiredParams = ('mail');
432
    if (! paramsAreValid(@requiredParams)) {
433
        my $errorMessage = "Required information is missing. " .
434
            "Please fill in all required fields and resubmit the form.";
435 8229 tao
        fullTemplate(['lookupName'], { allParams => $allParams,
436 8221 tao
                                     errorMessage => $errorMessage });
437
        exit();
438
    }
439
    my $mail = $query->param('mail');
440
441
    #search accounts with the specified emails
442
    $searchBase = $authBase;
443
    my $filter = "(mail=" . $mail . ")";
444
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
445
    my $notHtmlFormat = 1;
446
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs, $notHtmlFormat);
447
    my $accountInfo;
448 8254 leinfelder
    if ($found) {
449 8221 tao
        $accountInfo = $found;
450
    } else {
451 8254 leinfelder
        $accountInfo = "There are no accounts associated with the email " . $mail . ".\n";
452 8221 tao
    }
453 8254 leinfelder
454 8221 tao
    my $mailhost = $properties->getProperty('email.mailhost');
455
    my $sender;
456
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
457
    debug("the sender is " . $sender);
458
    my $recipient = $query->param('mail');
459
    # Send the email message to them
460
    my $smtp = Net::SMTP->new($mailhost) or do {
461 8229 tao
                                                  fullTemplate( ['lookupName'], {allParams => $allParams,
462
                                                                errorMessage => "Our mail server currently is experiencing some difficulties. Please contact " .
463
                                                                $skinProperties->getProperty("email.recipient") . "." });
464 8221 tao
                                                  exit(0);
465
                                               };
466
    $smtp->mail($sender);
467
    $smtp->to($recipient);
468
469
    my $message = <<"     ENDOFMESSAGE";
470
    To: $recipient
471
    From: $sender
472 8234 tao
    Subject: Your Account Information
473 8221 tao
474 8234 tao
    Somebody (hopefully you) looked up the account information associated with the email address.
475
    Here is the account information:
476 8221 tao
477
    $accountInfo
478
479
    Thanks,
480 8234 tao
        $sender
481 8221 tao
482
     ENDOFMESSAGE
483
     $message =~ s/^[ \t\r\f]+//gm;
484
485
     $smtp->data($message);
486
     $smtp->quit;
487
     fullTemplate( ['lookupNameSuccess'] );
488
489
}
490
491
492
#
493 2341 sgarg
# create the initial registration form
494
#
495
sub handleInitRegister {
496
  my $vars = shift;
497
  print "Content-type: text/html\n\n";
498
  # process the template files:
499 4080 daigle
  fullTemplate(['register'], {stage => "register"});
500 2341 sgarg
  exit();
501
}
502
503 8221 tao
504
505 2341 sgarg
#
506
# process input from the register stage, which occurs when
507
# a user submits form data to create a new account
508
#
509
sub handleRegister {
510
511 8258 tao
    #print "Content-type: text/html\n\n";
512 8220 tao
    if ($query->param('o') =~ "LTER") {
513 8258 tao
      print "Content-type: text/html\n\n";
514 8220 tao
      fullTemplate( ['registerLter'] );
515
      exit(0);
516
    }
517 8166 tao
518 2341 sgarg
    my $allParams = { 'givenName' => $query->param('givenName'),
519
                      'sn' => $query->param('sn'),
520
                      'o' => $query->param('o'),
521
                      'mail' => $query->param('mail'),
522
                      'uid' => $query->param('uid'),
523
                      'userPassword' => $query->param('userPassword'),
524
                      'userPassword2' => $query->param('userPassword2'),
525
                      'title' => $query->param('title'),
526
                      'telephoneNumber' => $query->param('telephoneNumber') };
527 8166 tao
528
    # Check the recaptcha
529
    my $c = Captcha::reCAPTCHA->new;
530
    my $challenge = $query->param('recaptcha_challenge_field');
531
    my $response = $query->param('recaptcha_response_field');
532
    # Verify submission
533
    my $result = $c->check_answer(
534 8169 tao
        $recaptchaPrivateKey, $ENV{'REMOTE_ADDR'},
535 8166 tao
        $challenge, $response
536
    );
537
538
    if ( $result->{is_valid} ) {
539
        #print "Yes!";
540
        #exit();
541
    }
542
    else {
543 8258 tao
        print "Content-type: text/html\n\n";
544 8166 tao
        my $errorMessage = "The verification code is wrong. Please input again.";
545
        fullTemplate(['register'], { stage => "register",
546
                                     allParams => $allParams,
547
                                     errorMessage => $errorMessage });
548
        exit();
549
    }
550
551
552 2341 sgarg
    # Check that all required fields are provided and not null
553
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail',
554
                           'uid', 'userPassword', 'userPassword2');
555
    if (! paramsAreValid(@requiredParams)) {
556 8258 tao
        print "Content-type: text/html\n\n";
557 2341 sgarg
        my $errorMessage = "Required information is missing. " .
558
            "Please fill in all required fields and resubmit the form.";
559 4080 daigle
        fullTemplate(['register'], { stage => "register",
560
                                     allParams => $allParams,
561
                                     errorMessage => $errorMessage });
562
        exit();
563 2341 sgarg
    } else {
564 8186 tao
         if ($query->param('userPassword') ne $query->param('userPassword2')) {
565 8258 tao
            print "Content-type: text/html\n\n";
566 8186 tao
            my $errorMessage = "The passwords do not match. Try again.";
567
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
568
                                                            allParams => $allParams,
569
                                                            errorMessage => $errorMessage });
570
            exit();
571
        }
572 2972 jones
        my $o = $query->param('o');
573 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
574 2341 sgarg
    }
575 8351 leinfelder
576
    # Remove any expired temporary accounts for this subtree before continuing
577
    clearTemporaryAccounts();
578 8877 tao
579
    # Check if the uid was taken in the production space
580
    my @attrs = [ 'uid', 'o', 'ou', 'cn', 'mail', 'telephoneNumber', 'title' ];
581
    my $uidExists;
582
    my $uid=$query->param('uid');
583
    my $uidFilter = "uid=" . $uid;
584
    my $newSearchBase = $ldapConfig->{$query->param('o')}{'org'} . "," .  $searchBase;
585
    debug("the new search base is $newSearchBase");
586
    $uidExists = uidExists($ldapurl, $newSearchBase, $uidFilter, \@attrs);
587
    debug("the result of uidExists $uidExists");
588
    if($uidExists) {
589
         print "Content-type: text/html\n\n";
590
            my $errorMessage = $uidExists;
591
            fullTemplate( ['registerFailed', 'register'], { stage => "register",
592
                                                            allParams => $allParams,
593
                                                            errorMessage => $errorMessage });
594
            exit();
595
    }
596 8880 tao
597 2341 sgarg
    # Search LDAP for matching entries that already exist
598
    # Some forms use a single text search box, whereas others search per
599
    # attribute.
600
    my $filter;
601
    if ($query->param('searchField')) {
602
603
      $filter = "(|" .
604
                "(uid=" . $query->param('searchField') . ") " .
605
                "(mail=" . $query->param('searchField') . ")" .
606
                "(&(sn=" . $query->param('searchField') . ") " .
607
                "(givenName=" . $query->param('searchField') . "))" .
608
                ")";
609
    } else {
610
      $filter = "(|" .
611
                "(uid=" . $query->param('uid') . ") " .
612
                "(mail=" . $query->param('mail') . ")" .
613
                "(&(sn=" . $query->param('sn') . ") " .
614
                "(givenName=" . $query->param('givenName') . "))" .
615
                ")";
616
    }
617
618 8880 tao
619 2341 sgarg
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
620
621
    # If entries match, send back a request to confirm new-user creation
622
    if ($found) {
623 8261 tao
      print "Content-type: text/html\n\n";
624 4080 daigle
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
625
                                                     allParams => $allParams,
626
                                                     foundAccounts => $found });
627 2341 sgarg
    # Otherwise, create a new user in the LDAP directory
628
    } else {
629 8180 tao
        createTemporaryAccount($allParams);
630 2341 sgarg
    }
631
632
    exit();
633
}
634
635
#
636
# process input from the registerconfirmed stage, which occurs when
637
# a user chooses to create an account despite similarities to other
638
# existing accounts
639
#
640
sub handleRegisterConfirmed {
641
642
    my $allParams = { 'givenName' => $query->param('givenName'),
643
                      'sn' => $query->param('sn'),
644 8207 tao
                      'o' => $query->param('o'),
645 2341 sgarg
                      'mail' => $query->param('mail'),
646
                      'uid' => $query->param('uid'),
647
                      'userPassword' => $query->param('userPassword'),
648
                      'userPassword2' => $query->param('userPassword2'),
649
                      'title' => $query->param('title'),
650
                      'telephoneNumber' => $query->param('telephoneNumber') };
651 8258 tao
    #print "Content-type: text/html\n\n";
652 8180 tao
    createTemporaryAccount($allParams);
653 2341 sgarg
    exit();
654
}
655
656
#
657
# change a user's password upon request
658
#
659
sub handleChangePassword {
660
661
    print "Content-type: text/html\n\n";
662
663
    my $allParams = { 'test' => "1", };
664
    if ($query->param('uid')) {
665
        $$allParams{'uid'} = $query->param('uid');
666
    }
667
    if ($query->param('o')) {
668
        $$allParams{'o'} = $query->param('o');
669 2972 jones
        my $o = $query->param('o');
670
671 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
672 2341 sgarg
    }
673
674
675
    # Check that all required fields are provided and not null
676
    my @requiredParams = ( 'uid', 'o', 'oldpass',
677
                           'userPassword', 'userPassword2');
678
    if (! paramsAreValid(@requiredParams)) {
679
        my $errorMessage = "Required information is missing. " .
680
            "Please fill in all required fields and submit the form.";
681 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
682
                                        allParams => $allParams,
683
                                        errorMessage => $errorMessage });
684
        exit();
685 2341 sgarg
    }
686
687
    # We have all of the info we need, so try to change the password
688 8880 tao
    if ($query->param('userPassword') eq $query->param('userPassword2')) {
689 2341 sgarg
690 2972 jones
        my $o = $query->param('o');
691 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
692
        $ldapUsername = $ldapConfig->{$o}{'user'};
693
        $ldapPassword = $ldapConfig->{$o}{'password'};
694 2341 sgarg
695 4080 daigle
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
696 2341 sgarg
        if ($query->param('o') =~ "LTER") {
697 4080 daigle
            fullTemplate( ['registerLter'] );
698 2341 sgarg
        } else {
699
            my $errorMessage = changePassword(
700
                    $dn, $query->param('userPassword'),
701
                    $dn, $query->param('oldpass'), $query->param('o'));
702 2972 jones
            if ($errorMessage) {
703 4080 daigle
                fullTemplate( ['changePass'], { stage => "changepass",
704
                                                allParams => $allParams,
705
                                                errorMessage => $errorMessage });
706
                exit();
707 2341 sgarg
            } else {
708 4080 daigle
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
709
                                                       allParams => $allParams });
710
                exit();
711 2341 sgarg
            }
712
        }
713
    } else {
714
        my $errorMessage = "The passwords do not match. Try again.";
715 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
716
                                        allParams => $allParams,
717
                                        errorMessage => $errorMessage });
718
        exit();
719 2341 sgarg
    }
720
}
721
722
#
723 2414 sgarg
# change a user's password upon request - no input params
724
# only display chagepass template without any error
725
#
726
sub handleInitialChangePassword {
727
    print "Content-type: text/html\n\n";
728
729
    my $allParams = { 'test' => "1", };
730
    my $errorMessage = "";
731 4080 daigle
    fullTemplate( ['changePass'], { stage => "changepass",
732
                                    errorMessage => $errorMessage });
733
    exit();
734 2414 sgarg
}
735
736
#
737 2341 sgarg
# reset a user's password upon request
738
#
739
sub handleResetPassword {
740
741
    print "Content-type: text/html\n\n";
742
743
    my $allParams = { 'test' => "1", };
744
    if ($query->param('uid')) {
745
        $$allParams{'uid'} = $query->param('uid');
746
    }
747
    if ($query->param('o')) {
748
        $$allParams{'o'} = $query->param('o');
749 2972 jones
        my $o = $query->param('o');
750
751 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
752 4868 walbridge
        $ldapUsername = $ldapConfig->{$o}{'user'};
753 4080 daigle
        $ldapPassword = $ldapConfig->{$o}{'password'};
754 2341 sgarg
    }
755
756
    # Check that all required fields are provided and not null
757
    my @requiredParams = ( 'uid', 'o' );
758
    if (! paramsAreValid(@requiredParams)) {
759
        my $errorMessage = "Required information is missing. " .
760
            "Please fill in all required fields and submit the form.";
761 4080 daigle
        fullTemplate( ['resetPass'],  { stage => "resetpass",
762
                                        allParams => $allParams,
763
                                        errorMessage => $errorMessage });
764
        exit();
765 2341 sgarg
    }
766
767
    # We have all of the info we need, so try to change the password
768
    my $o = $query->param('o');
769 4080 daigle
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
770 4866 walbridge
    debug("handleResetPassword: dn: $dn");
771 2341 sgarg
    if ($query->param('o') =~ "LTER") {
772 4080 daigle
        fullTemplate( ['registerLter'] );
773
        exit();
774 2341 sgarg
    } else {
775
        my $errorMessage = "";
776
        my $recipient;
777
        my $userPass;
778
        my $entry = getLdapEntry($ldapurl, $searchBase,
779
                $query->param('uid'), $query->param('o'));
780
781
        if ($entry) {
782
            $recipient = $entry->get_value('mail');
783
            $userPass = getRandomPassword();
784 4080 daigle
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
785 2341 sgarg
        } else {
786
            $errorMessage = "User not found in database.  Please try again.";
787
        }
788
789
        if ($errorMessage) {
790 4080 daigle
            fullTemplate( ['resetPass'], { stage => "resetpass",
791
                                           allParams => $allParams,
792
                                           errorMessage => $errorMessage });
793
            exit();
794 2341 sgarg
        } else {
795
            my $errorMessage = sendPasswordNotification($query->param('uid'),
796 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
797 4080 daigle
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
798
                                                  allParams => $allParams,
799
                                                  errorMessage => $errorMessage });
800
            exit();
801 2341 sgarg
        }
802
    }
803
}
804
805
#
806 2414 sgarg
# reset a user's password upon request- no initial params
807
# only display resetpass template without any error
808
#
809
sub handleInitialResetPassword {
810
    print "Content-type: text/html\n\n";
811
    my $errorMessage = "";
812 4080 daigle
    fullTemplate( ['resetPass'], { stage => "resetpass",
813
                                   errorMessage => $errorMessage });
814
    exit();
815 2414 sgarg
}
816
817
#
818 2341 sgarg
# Construct a random string to use for a newly reset password
819
#
820
sub getRandomPassword {
821
    my $length = shift;
822
    if (!$length) {
823
        $length = 8;
824
    }
825
    my $newPass = "";
826
827
    my @chars = ( "A" .. "Z", "a" .. "z", 0 .. 9, qw(! @ $ ^) );
828
    $newPass = join("", @chars[ map { rand @chars } ( 1 .. $length ) ]);
829
    return $newPass;
830
}
831
832
#
833
# Change a password to a new value, binding as the provided user
834
#
835
sub changePassword {
836
    my $userDN = shift;
837
    my $userPass = shift;
838
    my $bindDN = shift;
839
    my $bindPass = shift;
840
    my $o = shift;
841
842 4080 daigle
    my $searchBase = $ldapConfig->{$o}{'base'};
843 4868 walbridge
844 2341 sgarg
    my $errorMessage = 0;
845 3177 tao
    my $ldap;
846 4868 walbridge
847 4771 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
848
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
849 4394 walbridge
850 4849 daigle
    if ($ldap) {
851 8403 tao
        $ldap->start_tls( verify => 'require',
852
                      cafile => $ldapServerCACertFile);
853 4868 walbridge
        debug("changePassword: attempting to bind to $bindDN");
854
        my $bindresult = $ldap->bind( version => 3, dn => $bindDN,
855 2341 sgarg
                                  password => $bindPass );
856 4868 walbridge
        if ($bindresult->code) {
857
            $errorMessage = "Failed to log in. Are you sure your connection credentails are " .
858
                            "correct? Please correct and try again...";
859
            return $errorMessage;
860
        }
861 2341 sgarg
862 4849 daigle
    	# Find the user here and change their entry
863
    	my $newpass = createSeededPassHash($userPass);
864
    	my $modifications = { userPassword => $newpass };
865 4868 walbridge
      debug("changePass: setting password for $userDN to $newpass");
866 4849 daigle
    	my $result = $ldap->modify( $userDN, replace => { %$modifications });
867 2341 sgarg
868 4849 daigle
    	if ($result->code()) {
869 4866 walbridge
            debug("changePass: error changing password: " . $result->error);
870
        	$errorMessage = "There was an error changing the password:" .
871 2341 sgarg
                           "<br />\n" . $result->error;
872 4849 daigle
    	}
873
    	$ldap->unbind;   # take down session
874
    }
875 2341 sgarg
876
    return $errorMessage;
877
}
878
879
#
880
# generate a Seeded SHA1 hash of a plaintext password
881
#
882
sub createSeededPassHash {
883
    my $secret = shift;
884
885
    my $salt = "";
886
    for (my $i=0; $i < 4; $i++) {
887
        $salt .= int(rand(10));
888
    }
889
890
    my $ctx = Digest::SHA1->new;
891
    $ctx->add($secret);
892
    $ctx->add($salt);
893
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
894
895
    return $hashedPasswd;
896
}
897
898
#
899
# Look up an ldap entry for a user
900
#
901
sub getLdapEntry {
902
    my $ldapurl = shift;
903
    my $base = shift;
904
    my $username = shift;
905
    my $org = shift;
906
907
    my $entry = "";
908
    my $mesg;
909 3177 tao
    my $ldap;
910 4749 walbridge
    debug("ldap server: $ldapurl");
911 4394 walbridge
912
    #if main ldap server is down, a html file containing warning message will be returned
913 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
914 4849 daigle
915
    if ($ldap) {
916 8501 tao
        $ldap->start_tls( verify => 'none');
917
        #$ldap->start_tls( verify => 'require',
918
        #              cafile => $ldapServerCACertFile);
919 4849 daigle
    	my $bindresult = $ldap->bind;
920
    	if ($bindresult->code) {
921
        	return $entry;
922
    	}
923 2341 sgarg
924 8415 tao
        $base = $ldapConfig->{$org}{'org'} . ',' . $base;
925
        debug("getLdapEntry, searching for $base, (uid=$username)");
926
        $mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
927
    	#if($ldapConfig->{$org}{'filter'}){
928
            #debug("getLdapEntry: filter set, searching for base=$base, " .
929
                  #"(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
930
        	#$mesg = $ldap->search ( base   => $base,
931
                #filter => "(&(uid=$username)($ldapConfig->{$org}{'filter'}))");
932
    	#} else {
933
            #debug("getLdapEntry: no filter, searching for $base, (uid=$username)");
934
        	#$mesg = $ldap->search ( base   => $base, filter => "(uid=$username)");
935
    	#}
936 3177 tao
937 4849 daigle
    	if ($mesg->count > 0) {
938
        	$entry = $mesg->pop_entry;
939
        	$ldap->unbind;   # take down session
940
    	} else {
941
        	$ldap->unbind;   # take down session
942
        	# Follow references by recursive call to self
943
        	my @references = $mesg->references();
944
        	for (my $i = 0; $i <= $#references; $i++) {
945
            	my $uri = URI->new($references[$i]);
946
            	my $host = $uri->host();
947
            	my $path = $uri->path();
948
            	$path =~ s/^\///;
949
            	$entry = &getLdapEntry($host, $path, $username, $org);
950
            	if ($entry) {
951 4865 walbridge
                    debug("getLdapEntry: recursion found $host, $path, $username, $org");
952 4849 daigle
                	return $entry;
953
            	}
954
        	}
955
    	}
956 2341 sgarg
    }
957
    return $entry;
958
}
959
960
#
961
# send an email message notifying the user of the pw change
962
#
963
sub sendPasswordNotification {
964
    my $username = shift;
965
    my $org = shift;
966
    my $newPass = shift;
967
    my $recipient = shift;
968 2972 jones
    my $cfg = shift;
969 2341 sgarg
970
    my $errorMessage = "";
971
    if ($recipient) {
972 8254 leinfelder
973 4771 walbridge
        my $mailhost = $properties->getProperty('email.mailhost');
974 8197 tao
        my $sender;
975
        $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
976 2341 sgarg
        # Send the email message to them
977
        my $smtp = Net::SMTP->new($mailhost);
978
        $smtp->mail($sender);
979
        $smtp->to($recipient);
980
981
        my $message = <<"        ENDOFMESSAGE";
982
        To: $recipient
983
        From: $sender
984 8234 tao
        Subject: Your Account Password Reset
985 2341 sgarg
986 8234 tao
        Somebody (hopefully you) requested that your account password be reset.
987 8259 leinfelder
        Your temporary password is below. Please change it as soon as possible
988 8413 tao
        at: $contextUrl/style/skins/account/.
989 2341 sgarg
990
            Username: $username
991
        Organization: $org
992
        New Password: $newPass
993
994
        Thanks,
995 8234 tao
            $sender
996 2341 sgarg
997
        ENDOFMESSAGE
998
        $message =~ s/^[ \t\r\f]+//gm;
999
1000
        $smtp->data($message);
1001
        $smtp->quit;
1002
    } else {
1003
        $errorMessage = "Failed to send password because I " .
1004
                        "couldn't find a valid email address.";
1005
    }
1006
    return $errorMessage;
1007
}
1008
1009
#
1010 8877 tao
# search the LDAP production space to see if a uid already exists
1011
#
1012
sub uidExists {
1013
    my $ldapurl = shift;
1014
    debug("the ldap ulr is $ldapurl");
1015
    my $base = shift;
1016
    debug("the base is $base");
1017
    my $filter = shift;
1018
    debug("the filter is $filter");
1019
    my $attref = shift;
1020
1021
    my $ldap;
1022
    my $mesg;
1023
1024
    my $foundAccounts = 0;
1025
1026
    #if main ldap server is down, a html file containing warning message will be returned
1027
    debug("uidExists: connecting to $ldapurl, $timeout");
1028
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1029
    if ($ldap) {
1030
        $ldap->start_tls( verify => 'none');
1031
        #$ldap->start_tls( verify => 'require',
1032
        #              cafile => $ldapServerCACertFile);
1033
        $ldap->bind( version => 3, anonymous => 1);
1034
        $mesg = $ldap->search (
1035
            base   => $base,
1036
            filter => $filter,
1037
            attrs => @$attref,
1038
        );
1039
        debug("the message count is " . $mesg->count());
1040
        if ($mesg->count() > 0) {
1041
            $foundAccounts = "The username has been taken already by another user. Please choose a different one.";
1042
1043
        }
1044
        $ldap->unbind;   # take down session
1045
    } else {
1046
        $foundAccounts = "The ldap server is not running";
1047
    }
1048
    return $foundAccounts;
1049
}
1050
1051
#
1052 2341 sgarg
# search the LDAP directory to see if a similar account already exists
1053
#
1054
sub findExistingAccounts {
1055
    my $ldapurl = shift;
1056
    my $base = shift;
1057
    my $filter = shift;
1058
    my $attref = shift;
1059 8221 tao
    my $notHtmlFormat = shift;
1060 3175 tao
    my $ldap;
1061 4847 daigle
    my $mesg;
1062 2341 sgarg
1063
    my $foundAccounts = 0;
1064 4749 walbridge
1065 4394 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
1066 4868 walbridge
    debug("findExistingAccounts: connecting to $ldapurl, $timeout");
1067 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1068 4845 daigle
    if ($ldap) {
1069 8501 tao
    	$ldap->start_tls( verify => 'none');
1070
    	#$ldap->start_tls( verify => 'require',
1071
        #              cafile => $ldapServerCACertFile);
1072 4845 daigle
    	$ldap->bind( version => 3, anonymous => 1);
1073 4848 daigle
		$mesg = $ldap->search (
1074 4845 daigle
			base   => $base,
1075
			filter => $filter,
1076
			attrs => @$attref,
1077
		);
1078 2341 sgarg
1079 4845 daigle
	    if ($mesg->count() > 0) {
1080
			$foundAccounts = "";
1081
			my $entry;
1082
			foreach $entry ($mesg->all_entries) {
1083 5650 walbridge
                # a fix to ignore 'ou=Account' properties which are not usable accounts within Metacat.
1084
                # this could be done directly with filters on the LDAP connection, instead.
1085 8217 tao
                #if ($entry->dn !~ /ou=Account/) {
1086 8221 tao
                    if($notHtmlFormat) {
1087
                        $foundAccounts .= "\nAccount: ";
1088
                    } else {
1089
                        $foundAccounts .= "<p>\n<b><u>Account:</u> ";
1090
                    }
1091 5650 walbridge
                    $foundAccounts .= $entry->dn();
1092 8221 tao
                    if($notHtmlFormat) {
1093
                        $foundAccounts .= "\n";
1094
                    } else {
1095
                        $foundAccounts .= "</b><br />\n";
1096
                    }
1097 5650 walbridge
                    foreach my $attribute ($entry->attributes()) {
1098
                        my $value = $entry->get_value($attribute);
1099
                        $foundAccounts .= "$attribute: ";
1100
                        $foundAccounts .= $value;
1101 8221 tao
                         if($notHtmlFormat) {
1102
                            $foundAccounts .= "\n";
1103
                        } else {
1104
                            $foundAccounts .= "<br />\n";
1105
                        }
1106 5650 walbridge
                    }
1107 8221 tao
                    if($notHtmlFormat) {
1108
                        $foundAccounts .= "\n";
1109
                    } else {
1110
                        $foundAccounts .= "</p>\n";
1111
                    }
1112
1113 8217 tao
                #}
1114 4845 daigle
			}
1115 2341 sgarg
        }
1116 4845 daigle
    	$ldap->unbind;   # take down session
1117 2341 sgarg
1118 4848 daigle
    	# Follow references
1119
    	my @references = $mesg->references();
1120
    	for (my $i = 0; $i <= $#references; $i++) {
1121
        	my $uri = URI->new($references[$i]);
1122
        	my $host = $uri->host();
1123
        	my $path = $uri->path();
1124
        	$path =~ s/^\///;
1125 8254 leinfelder
        	my $refFound = &findExistingAccounts($host, $path, $filter, $attref, $notHtmlFormat);
1126 4848 daigle
        	if ($refFound) {
1127
            	$foundAccounts .= $refFound;
1128
        	}
1129
    	}
1130 2341 sgarg
    }
1131
1132
    #print "<p>Checking referrals...</p>\n";
1133
    #my @referrals = $mesg->referrals();
1134
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
1135
    #for (my $i = 0; $i <= $#referrals; $i++) {
1136
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
1137
    #}
1138
1139
    return $foundAccounts;
1140
}
1141
1142
#
1143
# Validate that we have the proper set of input parameters
1144
#
1145
sub paramsAreValid {
1146
    my @pnames = @_;
1147
1148
    my $allValid = 1;
1149
    foreach my $parameter (@pnames) {
1150
        if (!defined($query->param($parameter)) ||
1151
            ! $query->param($parameter) ||
1152
            $query->param($parameter) =~ /^\s+$/) {
1153
            $allValid = 0;
1154
        }
1155
    }
1156
1157
    return $allValid;
1158
}
1159
1160
#
1161 8175 tao
# Create a temporary account for a user and send an email with a link which can click for the
1162
# verification. This is used to protect the ldap server against spams.
1163
#
1164
sub createTemporaryAccount {
1165
    my $allParams = shift;
1166 8180 tao
    my $org = $query->param('o');
1167 8220 tao
    my $ldapUsername = $ldapConfig->{$org}{'user'};
1168
    my $ldapPassword = $ldapConfig->{$org}{'password'};
1169
    my $tmp = 1;
1170 8185 tao
1171 8220 tao
    ################## 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
1172
    my $orgAuthBase = $ldapConfig->{$org}{'base'};
1173
    my $tmpSearchBase = 'dc=tmp,' . $orgAuthBase;
1174
    my $tmpFilter = "dc=tmp";
1175
    my @attributes=['dc'];
1176
    my $foundTmp = searchDirectory($ldapurl, $orgAuthBase, $tmpFilter, \@attributes);
1177
    if (!$foundTmp) {
1178
        my $dn = $tmpSearchBase;
1179
        my $additions = [
1180
                    'dc' => 'tmp',
1181
                    'o'  => 'tmp',
1182
                    'objectclass' => ['top', 'dcObject', 'organization']
1183
                    ];
1184
        createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1185
    } else {
1186
     debug("found the tmp space");
1187
    }
1188 8175 tao
1189 8220 tao
    ################## Search LDAP for matching o or ou under the dc=tmp that already exist. If it doesn't exist, it will be generated
1190 8201 tao
    my $filter = $ldapConfig->{$org}{'filter'};
1191 8220 tao
1192 8176 tao
    debug("search filer " . $filter);
1193
    debug("ldap server ". $ldapurl);
1194
    debug("sesarch base " . $tmpSearchBase);
1195 8262 tao
    #print "Content-type: text/html\n\n";
1196 8175 tao
    my @attrs = ['o', 'ou' ];
1197
    my $found = searchDirectory($ldapurl, $tmpSearchBase, $filter, \@attrs);
1198 8220 tao
1199
    my @organizationInfo = split('=', $ldapConfig->{$org}{'org'}); #split 'o=NCEAS' or something like that
1200
    my $organization = $organizationInfo[0]; # This will be 'o' or 'ou'
1201
    my $organizationName = $organizationInfo[1]; # This will be 'NCEAS' or 'Account'
1202 8180 tao
1203 8176 tao
    if(!$found) {
1204 8180 tao
        debug("generate the subtree in the dc=tmp===========================");
1205 8176 tao
        #need to generate the subtree o or ou
1206 8220 tao
        my $additions;
1207 8207 tao
            if($organization eq 'ou') {
1208
                $additions = [
1209
                    $organization   => $organizationName,
1210
                    'objectclass' => ['top', 'organizationalUnit']
1211
                    ];
1212
1213
            } else {
1214
                $additions = [
1215
                    $organization   => $organizationName,
1216
                    'objectclass' => ['top', 'organization']
1217
                    ];
1218
1219
            }
1220 8220 tao
        my $dn=$ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1221
        createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1222 8176 tao
    }
1223 8175 tao
1224 8180 tao
    ################create an account under tmp subtree
1225 8176 tao
1226 8413 tao
     my $dn_store_next_uid=$properties->getProperty('ldap.nextuid.storing.dn');
1227
    my $attribute_name_store_next_uid = $properties->getProperty('ldap.nextuid.storing.attributename');
1228 8411 tao
    #get the next avaliable uid number. If it fails, the program will exist.
1229
    my $nextUidNumber = getNextUidNumber($ldapUsername, $ldapPassword);
1230
    if(!$nextUidNumber) {
1231
        print "Content-type: text/html\n\n";
1232
         my $sender;
1233
        $sender = $skinProperties->getProperty("email.recipient") or $sender = $properties->getProperty('email.recipient');
1234 8413 tao
        my $errorMessage = "The Identity Service can't get the next avaliable uid number. Please try again.  If the issue persists, please contact the administrator - $sender.
1235
                           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
1236
                           is not a number; or lots of users were registering and you couldn't get a lock on the dn - $dn_store_next_uid.";
1237 8411 tao
        fullTemplate(['register'], { stage => "register",
1238
                                     allParams => $allParams,
1239
                                     errorMessage => $errorMessage });
1240
        exit(0);
1241
    }
1242
    my $cn = join(" ", $query->param('givenName'), $query->param('sn'));
1243 8180 tao
    #generate a randomstr for matching the email.
1244
    my $randomStr = getRandomPassword(16);
1245
    # Create a hashed version of the password
1246
    my $shapass = createSeededPassHash($query->param('userPassword'));
1247
    my $additions = [
1248
                'uid'   => $query->param('uid'),
1249 8411 tao
                'cn'   => $cn,
1250 8180 tao
                'sn'   => $query->param('sn'),
1251
                'givenName'   => $query->param('givenName'),
1252
                'mail' => $query->param('mail'),
1253
                'userPassword' => $shapass,
1254
                'employeeNumber' => $randomStr,
1255 8411 tao
                'uidNumber' => $nextUidNumber,
1256
                'gidNumber' => $nextUidNumber,
1257
                'loginShell' => '/sbin/nologin',
1258
                'homeDirectory' => '/dev/null',
1259 8180 tao
                'objectclass' => ['top', 'person', 'organizationalPerson',
1260 8411 tao
                                'inetOrgPerson', 'posixAccount', 'shadowAccount' ],
1261 8201 tao
                $organization   => $organizationName
1262 8180 tao
                ];
1263 8411 tao
    my $gecos;
1264 8180 tao
    if (defined($query->param('telephoneNumber')) &&
1265
                $query->param('telephoneNumber') &&
1266
                ! $query->param('telephoneNumber') =~ /^\s+$/) {
1267
                $$additions[$#$additions + 1] = 'telephoneNumber';
1268
                $$additions[$#$additions + 1] = $query->param('telephoneNumber');
1269 8411 tao
                $gecos = $cn . ',,'. $query->param('telephoneNumber'). ',';
1270
    } else {
1271
        $gecos = $cn . ',,,';
1272 8180 tao
    }
1273 8411 tao
1274
    $$additions[$#$additions + 1] = 'gecos';
1275
    $$additions[$#$additions + 1] = $gecos;
1276
1277 8180 tao
    if (defined($query->param('title')) &&
1278
                $query->param('title') &&
1279
                ! $query->param('title') =~ /^\s+$/) {
1280
                $$additions[$#$additions + 1] = 'title';
1281
                $$additions[$#$additions + 1] = $query->param('title');
1282
    }
1283 8201 tao
1284
1285
    #$$additions[$#$additions + 1] = 'o';
1286
    #$$additions[$#$additions + 1] = $org;
1287
    my $dn='uid=' . $query->param('uid') . ',' . $ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1288 8220 tao
    createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1289 8176 tao
1290 8180 tao
1291
    ####################send the verification email to the user
1292 8253 leinfelder
    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.
1293 8180 tao
1294 8253 leinfelder
    my $overrideURL;
1295
    $overrideURL = $skinProperties->getProperty("email.overrideURL");
1296 8411 tao
    debug("the overrideURL is $overrideURL");
1297 8253 leinfelder
    if (defined($overrideURL) && !($overrideURL eq '')) {
1298
    	$link = $serverUrl . $overrideURL . $link;
1299
    } else {
1300
    	$link = $serverUrl . $link;
1301
    }
1302
1303 8181 tao
    my $mailhost = $properties->getProperty('email.mailhost');
1304 8197 tao
    my $sender;
1305
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
1306
    debug("the sender is " . $sender);
1307 8181 tao
    my $recipient = $query->param('mail');
1308
    # Send the email message to them
1309 8191 tao
    my $smtp = Net::SMTP->new($mailhost) or do {
1310
                                                  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 " .
1311
                                                  $skinProperties->getProperty("email.recipient") . "." });
1312
                                                  exit(0);
1313
                                               };
1314 8181 tao
    $smtp->mail($sender);
1315
    $smtp->to($recipient);
1316
1317
    my $message = <<"     ENDOFMESSAGE";
1318
    To: $recipient
1319
    From: $sender
1320 8239 leinfelder
    Subject: New Account Activation
1321 8181 tao
1322 8413 tao
    Somebody (hopefully you) registered an account on $contextUrl/style/skins/account/.
1323 8181 tao
    Please click the following link to activate your account.
1324
    If the link doesn't work, please copy the link to your browser:
1325
1326
    $link
1327
1328
    Thanks,
1329 8234 tao
        $sender
1330 8181 tao
1331
     ENDOFMESSAGE
1332
     $message =~ s/^[ \t\r\f]+//gm;
1333
1334
     $smtp->data($message);
1335
     $smtp->quit;
1336 8182 tao
    debug("the link is " . $link);
1337 8181 tao
    fullTemplate( ['success'] );
1338
1339 8175 tao
}
1340
1341
#
1342 8220 tao
# Bind to LDAP and create a new item (a user or subtree) using the information provided
1343 2341 sgarg
# by the user
1344
#
1345 8220 tao
sub createItem {
1346 8180 tao
    my $dn = shift;
1347
    my $ldapUsername = shift;
1348
    my $ldapPassword = shift;
1349
    my $additions = shift;
1350
    my $temp = shift; #if it is for a temporary account.
1351
    my $allParams = shift;
1352
1353
    my @failureTemplate;
1354
    if($temp){
1355
        @failureTemplate = ['registerFailed', 'register'];
1356
    } else {
1357
        @failureTemplate = ['registerFailed'];
1358
    }
1359
    print "Content-type: text/html\n\n";
1360
    debug("the dn is " . $dn);
1361
    debug("LDAP connection to $ldapurl...");
1362 9514 tao
    debug("the ldap ca certificate is " . $ldapServerCACertFile);
1363 8180 tao
    #if main ldap server is down, a html file containing warning message will be returned
1364
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1365
    if ($ldap) {
1366 8403 tao
            $ldap->start_tls( verify => 'require',
1367
                      cafile => $ldapServerCACertFile);
1368 8180 tao
            debug("Attempting to bind to LDAP server with dn = $ldapUsername, pwd = $ldapPassword");
1369 8185 tao
            $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
1370 8180 tao
            my $result = $ldap->add ( 'dn' => $dn, 'attr' => [@$additions ]);
1371
            if ($result->code()) {
1372
                fullTemplate(@failureTemplate, { stage => "register",
1373
                                                            allParams => $allParams,
1374
                                                            errorMessage => $result->error });
1375 8220 tao
                exist(0);
1376 8180 tao
                # TODO SCW was included as separate errors, test this
1377
                #$templateVars    = setVars({ stage => "register",
1378
                #                     allParams => $allParams });
1379
                #$template->process( $templates->{'register'}, $templateVars);
1380
            } else {
1381 8181 tao
                #fullTemplate( ['success'] );
1382 8180 tao
            }
1383
            $ldap->unbind;   # take down session
1384
1385
    } else {
1386
         fullTemplate(@failureTemplate, { stage => "register",
1387
                                                            allParams => $allParams,
1388
                                                            errorMessage => "The ldap server is not available now. Please try it later"});
1389
         exit(0);
1390
    }
1391
1392
}
1393
1394 2341 sgarg
1395
1396
1397
1398
1399 8185 tao
#
1400
# This subroutine will handle a email verification:
1401
# If the hash string matches the one store in the ldap, the account will be
1402
# copied from the temporary space to the permanent tree and the account in
1403
# the temporary space will be removed.
1404
sub handleEmailVerification {
1405
1406
    my $cfg = $query->param('cfg');
1407
    my $dn = $query->param('dn');
1408
    my $hash = $query->param('hash');
1409
    my $org = $query->param('o');
1410
    my $uid = $query->param('uid');
1411
1412
    my $ldapUsername;
1413
    my $ldapPassword;
1414 8211 tao
    #my $orgAuthBase;
1415
1416
    $ldapUsername = $ldapConfig->{$org}{'user'};
1417
    $ldapPassword = $ldapConfig->{$org}{'password'};
1418
    #$orgAuthBase = $ldapConfig->{$org}{'base'};
1419
1420 8185 tao
    debug("LDAP connection to $ldapurl...");
1421
1422
1423
   print "Content-type: text/html\n\n";
1424
   #if main ldap server is down, a html file containing warning message will be returned
1425
   my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1426
   if ($ldap) {
1427 8403 tao
        $ldap->start_tls( verify => 'require',
1428
                      cafile => $ldapServerCACertFile);
1429 8185 tao
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
1430 8211 tao
        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.
1431 8185 tao
        my $max = $mesg->count;
1432
        debug("the count is " . $max);
1433
        if($max < 1) {
1434
            $ldap->unbind;   # take down session
1435 8216 tao
            fullTemplate( ['verificationFailed'], {errorMessage => "No record matched the dn " . $dn . " for the activation. You probably already activated the account."});
1436 8185 tao
            #handleLDAPBindFailure($ldapurl);
1437
            exit(0);
1438
        } else {
1439
            #check if the hash string match
1440
            my $entry = $mesg->entry (0);
1441
            my $hashStrFromLdap = $entry->get_value('employeeNumber');
1442
            if( $hashStrFromLdap eq $hash) {
1443
                #my $additions = [ ];
1444
                #foreach my $attr ( $entry->attributes ) {
1445
                    #if($attr ne 'employeeNumber') {
1446
                        #$$additions[$#$additions + 1] = $attr;
1447
                        #$$additions[$#$additions + 1] = $entry->get_value( $attr );
1448
                    #}
1449
                #}
1450 8211 tao
1451
1452
                my $orgDn = $ldapConfig->{$org}{'dn'}; #the DN for the organization.
1453 8185 tao
                $mesg = $ldap->moddn(
1454
                            dn => $dn,
1455
                            deleteoldrdn => 1,
1456
                            newrdn => "uid=" . $uid,
1457 8211 tao
                            newsuperior  =>  $orgDn);
1458 8185 tao
                $ldap->unbind;   # take down session
1459 8186 tao
                if($mesg->code()) {
1460 8216 tao
                    fullTemplate( ['verificationFailed'], {errorMessage => "Cannot move the account from the inactive area to the ative area since " . $mesg->error()});
1461 8185 tao
                    exit(0);
1462
                } else {
1463 8216 tao
                    fullTemplate( ['verificationSuccess'] );
1464 8185 tao
                }
1465
                #createAccount2($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1466
            } else {
1467
                $ldap->unbind;   # take down session
1468 8216 tao
                fullTemplate( ['verificationFailed'], {errorMessage => "The hash string " . $hash . " from your link doesn't match our record."});
1469 8185 tao
                exit(0);
1470
            }
1471
1472
        }
1473
    } else {
1474
        handleLDAPBindFailure($ldapurl);
1475
        exit(0);
1476
    }
1477
1478
}
1479
1480 2341 sgarg
sub handleResponseMessage {
1481
1482
  print "Content-type: text/html\n\n";
1483
  my $errorMessage = "You provided invalid input to the script. " .
1484
                     "Try again please.";
1485 4080 daigle
  fullTemplate( [], { stage => $templates->{'stage'},
1486
                      errorMessage => $errorMessage });
1487
  exit();
1488 2341 sgarg
}
1489
1490
#
1491
# perform a simple search against the LDAP database using
1492
# a small subset of attributes of each dn and return it
1493
# as a table to the calling browser.
1494
#
1495
sub handleSimpleSearch {
1496
1497
    my $o = $query->param('o');
1498
1499 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
1500
    my $searchBase = $ldapConfig->{$o}{'base'};
1501 2341 sgarg
1502
    print "Content-type: text/html\n\n";
1503
1504
    my $allParams = {
1505
                      'cn' => $query->param('cn'),
1506
                      'sn' => $query->param('sn'),
1507
                      'gn' => $query->param('gn'),
1508
                      'o'  => $query->param('o'),
1509
                      'facsimiletelephonenumber'
1510
                      => $query->param('facsimiletelephonenumber'),
1511
                      'mail' => $query->param('cmail'),
1512
                      'telephonenumber' => $query->param('telephonenumber'),
1513
                      'title' => $query->param('title'),
1514
                      'uid' => $query->param('uid'),
1515
                      'ou' => $query->param('ou'),
1516
                    };
1517
1518
    # Search LDAP for matching entries that already exist
1519
    my $filter = "(" .
1520
                 $query->param('searchField') . "=" .
1521
                 "*" .
1522
                 $query->param('searchValue') .
1523
                 "*" .
1524
                 ")";
1525
1526
    my @attrs = [ 'sn',
1527
                  'gn',
1528
                  'cn',
1529
                  'o',
1530
                  'facsimiletelephonenumber',
1531
                  'mail',
1532
                  'telephoneNumber',
1533
                  'title',
1534
                  'uid',
1535
                  'labeledURI',
1536
                  'ou' ];
1537
1538
    my $found = searchDirectory($ldapurl, $searchBase, $filter, \@attrs);
1539
1540
    # Send back the search results
1541
    if ($found) {
1542 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1543
                                         allParams => $allParams,
1544
                                         foundAccounts => $found });
1545 2341 sgarg
    } else {
1546
      $found = "No entries matched your criteria.  Please try again\n";
1547
1548 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1549
                                         allParams => $allParams,
1550
                                         foundAccounts => $found });
1551 2341 sgarg
    }
1552
1553
    exit();
1554
}
1555
1556
#
1557
# search the LDAP directory to see if a similar account already exists
1558
#
1559
sub searchDirectory {
1560
    my $ldapurl = shift;
1561
    my $base = shift;
1562
    my $filter = shift;
1563
    my $attref = shift;
1564
1565 4849 daigle
	my $mesg;
1566 2341 sgarg
    my $foundAccounts = 0;
1567 3177 tao
1568
    #if ldap server is down, a html file containing warning message will be returned
1569 4771 walbridge
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1570 3177 tao
1571 4849 daigle
    if ($ldap) {
1572 8403 tao
    	$ldap->start_tls( verify => 'require',
1573
                      cafile => $ldapServerCACertFile);
1574 4849 daigle
    	$ldap->bind( version => 3, anonymous => 1);
1575
    	my $mesg = $ldap->search (
1576
        	base   => $base,
1577
        	filter => $filter,
1578
        	attrs => @$attref,
1579
    	);
1580 2341 sgarg
1581 4849 daigle
    	if ($mesg->count() > 0) {
1582
        	$foundAccounts = "";
1583
        	my $entry;
1584
        	foreach $entry ($mesg->sorted(['sn'])) {
1585
          		$foundAccounts .= "<tr>\n<td class=\"main\">\n";
1586
          		$foundAccounts .= "<a href=\"" unless
1587 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1588 4849 daigle
         		 $foundAccounts .= $entry->get_value('labeledURI') unless
1589 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1590 4849 daigle
          		$foundAccounts .= "\">\n" unless
1591 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1592 4849 daigle
          		$foundAccounts .= $entry->get_value('givenName');
1593
          		$foundAccounts .= "</a>\n" unless
1594 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1595 4849 daigle
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1596
          		$foundAccounts .= "<a href=\"" unless
1597 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1598 4849 daigle
          		$foundAccounts .= $entry->get_value('labeledURI') unless
1599 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1600 4849 daigle
          		$foundAccounts .= "\">\n" unless
1601 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1602 4849 daigle
          		$foundAccounts .= $entry->get_value('sn');
1603
          		$foundAccounts .= "</a>\n";
1604
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1605
          		$foundAccounts .= $entry->get_value('mail');
1606
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1607
          		$foundAccounts .= $entry->get_value('telephonenumber');
1608
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1609
          		$foundAccounts .= $entry->get_value('title');
1610
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1611
          		$foundAccounts .= $entry->get_value('ou');
1612
          		$foundAccounts .= "\n</td>\n";
1613
          		$foundAccounts .= "</tr>\n";
1614
        	}
1615
    	}
1616
    	$ldap->unbind;   # take down session
1617 2341 sgarg
    }
1618
    return $foundAccounts;
1619
}
1620
1621
sub debug {
1622
    my $msg = shift;
1623
1624
    if ($debug) {
1625 4747 walbridge
        print STDERR "LDAPweb: $msg\n";
1626 2341 sgarg
    }
1627
}
1628 3175 tao
1629 4771 walbridge
sub handleLDAPBindFailure {
1630
    my $ldapAttemptUrl = shift;
1631
    my $primaryLdap =  $properties->getProperty('auth.url');
1632
1633
    if ($ldapAttemptUrl eq  $primaryLdap) {
1634
        handleGeneralServerFailure("The main LDAP server $ldapurl is down!");
1635
    } else {
1636
        debug("attempted to bind to nonresponsive LDAP server $ldapAttemptUrl, skipped.");
1637
    }
1638
}
1639
1640 3177 tao
sub handleGeneralServerFailure {
1641
    my $errorMessage = shift;
1642 4728 walbridge
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
1643 3175 tao
    exit(0);
1644
   }
1645
1646 4080 daigle
sub setVars {
1647
    my $paramVars = shift;
1648
    # initialize default parameters
1649
    my $templateVars = { cfg => $cfg,
1650 4394 walbridge
                         styleSkinsPath => $contextUrl . "/style/skins",
1651
                         styleCommonPath => $contextUrl . "/style/common",
1652
                         contextUrl => $contextUrl,
1653 4770 daigle
                         cgiPrefix => $cgiPrefix,
1654 8206 tao
                         orgList => \@validDisplayOrgList,
1655 4394 walbridge
                         config  => $config,
1656 4080 daigle
    };
1657
1658
    # append customized params
1659
    while (my ($k, $v) = each (%$paramVars)) {
1660
        $templateVars->{$k} = $v;
1661
    }
1662
1663
    return $templateVars;
1664
}
1665 8180 tao
1666 8408 tao
#Method to get the next avaliable uid number. We use the mechanism - http://www.rexconsulting.net/ldap-protocol-uidNumber.html
1667
sub getNextUidNumber {
1668 8413 tao
1669 8410 tao
    my $maxAttempt = $properties->getProperty('ldap.nextuid.maxattempt');
1670 8408 tao
1671 8411 tao
    my $ldapUsername = shift;
1672
    my $ldapPassword = shift;
1673 8408 tao
1674 8411 tao
    my $realUidNumber;
1675
    my $uidNumber;
1676 8408 tao
    my $entry;
1677
    my $mesg;
1678
    my $ldap;
1679
1680
    debug("ldap server: $ldapurl");
1681
1682
    #if main ldap server is down, a html file containing warning message will be returned
1683
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1684
1685
    if ($ldap) {
1686 8818 tao
    	my $existingHighUid=getExistingHighestUidNum($ldapUsername, $ldapPassword);
1687 8408 tao
        $ldap->start_tls( verify => 'require',
1688
                      cafile => $ldapServerCACertFile);
1689
        my $bindresult = $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword);
1690
        #read the uid value stored in uidObject class
1691
        for(my $index=0; $index<$maxAttempt; $index++) {
1692 8413 tao
            $mesg = $ldap->search(base  => $dn_store_next_uid, filter => '(objectClass=*)');
1693 8408 tao
            if ($mesg->count() > 0) {
1694 8413 tao
                debug("Find the cn - $dn_store_next_uid");
1695 8408 tao
                $entry = $mesg->pop_entry;
1696 8413 tao
                $uidNumber = $entry->get_value($attribute_name_store_next_uid);
1697 8408 tao
                if($uidNumber) {
1698 8413 tao
                    if (looks_like_number($uidNumber)) {
1699
                        debug("uid number is $uidNumber");
1700
                        #remove the uid attribute with the read value
1701
                        my $delMesg = $ldap->modify($dn_store_next_uid, delete => { $attribute_name_store_next_uid => $uidNumber});
1702
                        if($delMesg->is_error()) {
1703
                            my $error=$delMesg->error();
1704
                            my $errorName = $delMesg->error_name();
1705
                            debug("can't remove the attribute - $error");
1706
                            debug("can't remove the attribute and the error name - $errorName");
1707
                            #can't remove the attribute with the specified value - that means somebody modify the value in another route, so try it again
1708
                        } else {
1709
                            debug("Remove the attribute successfully and write a new increased value back");
1710 8819 tao
                            if($existingHighUid) {
1711 8821 tao
                            	debug("exiting high uid exists =======================================");
1712 8819 tao
                            	if($uidNumber <= $existingHighUid ) {
1713
                            		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");
1714
                            		$uidNumber = $existingHighUid +1;
1715
                            	}
1716
                            }
1717 8413 tao
                            my $newValue = $uidNumber +1;
1718
                            $delMesg = $ldap->modify($dn_store_next_uid, add => {$attribute_name_store_next_uid => $newValue});
1719
                            $realUidNumber = $uidNumber;
1720
                            last;
1721
                        }
1722 8408 tao
                    }
1723 8413 tao
1724 8408 tao
               } else {
1725 8413 tao
                 debug("can't find the attribute - $attribute_name_store_next_uid in the $dn_store_next_uid and we will try again");
1726 8408 tao
               }
1727
            }
1728
        }
1729
        $ldap->unbind;   # take down session
1730
    }
1731
    return $realUidNumber;
1732
}
1733
1734 8818 tao
#Method to get the existing high uidNumber in the account tree.
1735
sub getExistingHighestUidNum {
1736
    my $ldapUsername = shift;
1737
    my $ldapPassword = shift;
1738
1739
    my $high;
1740
    my $ldap;
1741 8821 tao
    my $storedUidNumber;
1742 8818 tao
1743 8819 tao
1744 8818 tao
    #if main ldap server is down, a html file containing warning message will be returned
1745
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1746
    if ($ldap) {
1747
        $ldap->start_tls( verify => 'require',
1748
                      cafile => $ldapServerCACertFile);
1749
        my $bindresult = $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword);
1750 8821 tao
        my $mesg = $ldap->search(base  => $dn_store_next_uid, filter => '(objectClass=*)');
1751
         if ($mesg->count() > 0) {
1752
                debug("Find the cn - $dn_store_next_uid");
1753
                my  $entry = $mesg->pop_entry;
1754
                $storedUidNumber = $entry->get_value($attribute_name_store_next_uid);
1755
        }
1756 8877 tao
        my $authBase = $properties->getProperty("auth.base");
1757 8818 tao
        my $uids = $ldap->search(
1758 8844 leinfelder
                        base => $authBase,
1759 8818 tao
                        scope => "sub",
1760
                        filter => "uidNumber=*",
1761
                        attrs   => [ 'uidNumber' ],
1762
                        );
1763
       return unless $uids->count;
1764
  	    my @uids;
1765
        if ($uids->count > 0) {
1766
                foreach my $uid ($uids->all_entries) {
1767 8821 tao
                		if($storedUidNumber) {
1768
                			if( $uid->get_value('uidNumber') >= $storedUidNumber) {
1769
                				push @uids, $uid->get_value('uidNumber');
1770
                			}
1771
                		} else {
1772
                        	push @uids, $uid->get_value('uidNumber');
1773
                        }
1774 8818 tao
                }
1775
        }
1776
1777 8821 tao
        if(@uids) {
1778
        	@uids = sort { $b <=> $a } @uids;
1779
        	$high = $uids[0];
1780
        }
1781 8818 tao
        debug("the highest exiting uidnumber is $high");
1782
        $ldap->unbind;   # take down session
1783
    }
1784
    return $high;
1785 8408 tao
1786 8818 tao
}
1787