Project

General

Profile

1 10143 cjones
#!/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 10010 cjones
     my $contact;
294 8206 tao
     $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
295 10010 cjones
     $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
296 8206 tao
    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 10010 cjones
     . $skinName . ".properties file) is invalid. Please send the information to ". $contact;
300 8206 tao
    exit(0);
301
}
302
303
304 2341 sgarg
#--------------------------------------------------------------------------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 4080 daigle
my $stage = $query->param('stage') || $templates->{'stage'};
310 2341 sgarg
311
my $cfg = $query->param('cfg');
312 4767 walbridge
debug("started with stage $stage, cfg $cfg");
313 2341 sgarg
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 2972 jones
              'changepass'        => \&handleChangePassword,
325
              'initchangepass'    => \&handleInitialChangePassword,
326 2341 sgarg
              'resetpass'         => \&handleResetPassword,
327 2414 sgarg
              'initresetpass'     => \&handleInitialResetPassword,
328 8185 tao
              'emailverification' => \&handleEmailVerification,
329 8229 tao
              'lookupname'        => \&handleLookupName,
330
              'searchnamesbyemail'=> \&handleSearchNameByEmail,
331 8818 tao
              #'getnextuid'        => \&getExistingHighestUidNum,
332 2341 sgarg
             );
333 4394 walbridge
334 2341 sgarg
# 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 8351 leinfelder
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 8354 leinfelder
	my $expirationDate = $dt->ymd("") . $dt->hms("") . "Z";
358 8356 leinfelder
    my $filter = "(&(objectClass=inetOrgPerson)(createTimestamp<=" . $expirationDate . "))";
359
    debug("Clearing expired accounts with filter: " . $filter . ", base: " . $tmpSearchBase);
360 8351 leinfelder
    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 8356 leinfelder
    debug("clearTemporaryAccounts: connecting to $ldapurl, $timeout");
369 8351 leinfelder
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
370
    if ($ldap) {
371 8403 tao
    	$ldap->start_tls( verify => 'require',
372
                      cafile => $ldapServerCACertFile);
373 8351 leinfelder
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
374
		$mesg = $ldap->search (
375 8356 leinfelder
			base   => $tmpSearchBase,
376 8351 leinfelder
			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 8357 leinfelder
   				debug("Removing expired account: " . $dn);
385
            	$ldap->delete($dn);
386 8351 leinfelder
			}
387
        }
388
    	$ldap->unbind;   # take down session
389
    }
390
391 8354 leinfelder
    return 0;
392 8351 leinfelder
}
393
394 4728 walbridge
sub fullTemplate {
395
    my $templateList = shift;
396
    my $templateVars = setVars(shift);
397 8166 tao
    my $c = Captcha::reCAPTCHA->new;
398
    my $captcha = 'captcha';
399
    #my $error=null;
400
    my $use_ssl= 1;
401
    #my $options=null;
402 8250 leinfelder
    # 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 4728 walbridge
    $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 8221 tao
413 8229 tao
#
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 8221 tao
424 2341 sgarg
#
425 8221 tao
# 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 8229 tao
sub handleSearchNameByEmail{
429 8221 tao
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 8229 tao
        fullTemplate(['lookupName'], { allParams => $allParams,
438 8221 tao
                                     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 8254 leinfelder
    if ($found) {
451 8221 tao
        $accountInfo = $found;
452
    } else {
453 8254 leinfelder
        $accountInfo = "There are no accounts associated with the email " . $mail . ".\n";
454 8221 tao
    }
455 8254 leinfelder
456 8221 tao
    my $mailhost = $properties->getProperty('email.mailhost');
457
    my $sender;
458 10010 cjones
    my $contact;
459 8221 tao
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
460 10010 cjones
    $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
461 8221 tao
    debug("the sender is " . $sender);
462 10010 cjones
    debug("the contact is " . $contact);
463 8221 tao
    my $recipient = $query->param('mail');
464
    # Send the email message to them
465
    my $smtp = Net::SMTP->new($mailhost) or do {
466 8229 tao
                                                  fullTemplate( ['lookupName'], {allParams => $allParams,
467
                                                                errorMessage => "Our mail server currently is experiencing some difficulties. Please contact " .
468
                                                                $skinProperties->getProperty("email.recipient") . "." });
469 8221 tao
                                                  exit(0);
470
                                               };
471
    $smtp->mail($sender);
472
    $smtp->to($recipient);
473
474
    my $message = <<"     ENDOFMESSAGE";
475
    To: $recipient
476
    From: $sender
477 8234 tao
    Subject: Your Account Information
478 8221 tao
479 8234 tao
    Somebody (hopefully you) looked up the account information associated with the email address.
480
    Here is the account information:
481 8221 tao
482
    $accountInfo
483
484
    Thanks,
485 8234 tao
        $sender
486 8221 tao
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 2341 sgarg
# 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 4080 daigle
  fullTemplate(['register'], {stage => "register"});
505 2341 sgarg
  exit();
506
}
507
508 8221 tao
509
510 2341 sgarg
#
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 8258 tao
    #print "Content-type: text/html\n\n";
517 8220 tao
    if ($query->param('o') =~ "LTER") {
518 8258 tao
      print "Content-type: text/html\n\n";
519 8220 tao
      fullTemplate( ['registerLter'] );
520
      exit(0);
521
    }
522 8166 tao
523 2341 sgarg
    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 8166 tao
533
    # Check the recaptcha
534
    my $c = Captcha::reCAPTCHA->new;
535 10478 tao
    #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 8166 tao
548
    if ( $result->{is_valid} ) {
549
        #print "Yes!";
550
        #exit();
551
    }
552
    else {
553 8258 tao
        print "Content-type: text/html\n\n";
554 8166 tao
        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 2341 sgarg
    # 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 8258 tao
        print "Content-type: text/html\n\n";
567 2341 sgarg
        my $errorMessage = "Required information is missing. " .
568
            "Please fill in all required fields and resubmit the form.";
569 4080 daigle
        fullTemplate(['register'], { stage => "register",
570
                                     allParams => $allParams,
571
                                     errorMessage => $errorMessage });
572
        exit();
573 2341 sgarg
    } else {
574 8186 tao
         if ($query->param('userPassword') ne $query->param('userPassword2')) {
575 8258 tao
            print "Content-type: text/html\n\n";
576 8186 tao
            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 2972 jones
        my $o = $query->param('o');
583 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
584 2341 sgarg
    }
585 8351 leinfelder
586
    # Remove any expired temporary accounts for this subtree before continuing
587
    clearTemporaryAccounts();
588 8877 tao
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 8880 tao
607 2341 sgarg
    # 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 8880 tao
629 2341 sgarg
    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 8261 tao
      print "Content-type: text/html\n\n";
634 4080 daigle
      fullTemplate( ['registerMatch', 'register'], { stage => "registerconfirmed",
635
                                                     allParams => $allParams,
636
                                                     foundAccounts => $found });
637 2341 sgarg
    # Otherwise, create a new user in the LDAP directory
638
    } else {
639 8180 tao
        createTemporaryAccount($allParams);
640 2341 sgarg
    }
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 8207 tao
                      'o' => $query->param('o'),
655 2341 sgarg
                      '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 8258 tao
    #print "Content-type: text/html\n\n";
662 8180 tao
    createTemporaryAccount($allParams);
663 2341 sgarg
    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 2972 jones
        my $o = $query->param('o');
680
681 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
682 2341 sgarg
    }
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 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
692
                                        allParams => $allParams,
693
                                        errorMessage => $errorMessage });
694
        exit();
695 2341 sgarg
    }
696
697
    # We have all of the info we need, so try to change the password
698 8880 tao
    if ($query->param('userPassword') eq $query->param('userPassword2')) {
699 2341 sgarg
700 2972 jones
        my $o = $query->param('o');
701 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
702
        $ldapUsername = $ldapConfig->{$o}{'user'};
703
        $ldapPassword = $ldapConfig->{$o}{'password'};
704 2341 sgarg
705 4080 daigle
        my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};;
706 2341 sgarg
        if ($query->param('o') =~ "LTER") {
707 4080 daigle
            fullTemplate( ['registerLter'] );
708 2341 sgarg
        } else {
709
            my $errorMessage = changePassword(
710
                    $dn, $query->param('userPassword'),
711
                    $dn, $query->param('oldpass'), $query->param('o'));
712 2972 jones
            if ($errorMessage) {
713 4080 daigle
                fullTemplate( ['changePass'], { stage => "changepass",
714
                                                allParams => $allParams,
715
                                                errorMessage => $errorMessage });
716
                exit();
717 2341 sgarg
            } else {
718 4080 daigle
                fullTemplate( ['changePassSuccess'], { stage => "changepass",
719
                                                       allParams => $allParams });
720
                exit();
721 2341 sgarg
            }
722
        }
723
    } else {
724
        my $errorMessage = "The passwords do not match. Try again.";
725 4080 daigle
        fullTemplate( ['changePass'], { stage => "changepass",
726
                                        allParams => $allParams,
727
                                        errorMessage => $errorMessage });
728
        exit();
729 2341 sgarg
    }
730
}
731
732
#
733 2414 sgarg
# 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 4080 daigle
    fullTemplate( ['changePass'], { stage => "changepass",
742
                                    errorMessage => $errorMessage });
743
    exit();
744 2414 sgarg
}
745
746
#
747 2341 sgarg
# 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 2972 jones
        my $o = $query->param('o');
760
761 4080 daigle
        $searchBase = $ldapConfig->{$o}{'base'};
762 4868 walbridge
        $ldapUsername = $ldapConfig->{$o}{'user'};
763 4080 daigle
        $ldapPassword = $ldapConfig->{$o}{'password'};
764 2341 sgarg
    }
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 4080 daigle
        fullTemplate( ['resetPass'],  { stage => "resetpass",
772
                                        allParams => $allParams,
773
                                        errorMessage => $errorMessage });
774
        exit();
775 2341 sgarg
    }
776
777
    # We have all of the info we need, so try to change the password
778
    my $o = $query->param('o');
779 4080 daigle
    my $dn = "uid=" . $query->param('uid') . "," . $ldapConfig->{$o}{'dn'};
780 4866 walbridge
    debug("handleResetPassword: dn: $dn");
781 2341 sgarg
    if ($query->param('o') =~ "LTER") {
782 4080 daigle
        fullTemplate( ['registerLter'] );
783
        exit();
784 2341 sgarg
    } 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 4080 daigle
            $errorMessage = changePassword($dn, $userPass, $ldapUsername, $ldapPassword, $query->param('o'));
795 2341 sgarg
        } else {
796
            $errorMessage = "User not found in database.  Please try again.";
797
        }
798
799
        if ($errorMessage) {
800 4080 daigle
            fullTemplate( ['resetPass'], { stage => "resetpass",
801
                                           allParams => $allParams,
802
                                           errorMessage => $errorMessage });
803
            exit();
804 2341 sgarg
        } else {
805
            my $errorMessage = sendPasswordNotification($query->param('uid'),
806 2972 jones
                    $query->param('o'), $userPass, $recipient, $cfg);
807 4080 daigle
            fullTemplate( ['resetPassSuccess'], { stage => "resetpass",
808
                                                  allParams => $allParams,
809
                                                  errorMessage => $errorMessage });
810
            exit();
811 2341 sgarg
        }
812
    }
813
}
814
815
#
816 2414 sgarg
# 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 4080 daigle
    fullTemplate( ['resetPass'], { stage => "resetpass",
823
                                   errorMessage => $errorMessage });
824
    exit();
825 2414 sgarg
}
826
827
#
828 2341 sgarg
# 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 4080 daigle
    my $searchBase = $ldapConfig->{$o}{'base'};
853 4868 walbridge
854 2341 sgarg
    my $errorMessage = 0;
855 3177 tao
    my $ldap;
856 4868 walbridge
857 4771 walbridge
    #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 4394 walbridge
860 4849 daigle
    if ($ldap) {
861 8403 tao
        $ldap->start_tls( verify => 'require',
862
                      cafile => $ldapServerCACertFile);
863 4868 walbridge
        debug("changePassword: attempting to bind to $bindDN");
864
        my $bindresult = $ldap->bind( version => 3, dn => $bindDN,
865 2341 sgarg
                                  password => $bindPass );
866 4868 walbridge
        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 2341 sgarg
872 4849 daigle
    	# Find the user here and change their entry
873
    	my $newpass = createSeededPassHash($userPass);
874
    	my $modifications = { userPassword => $newpass };
875 4868 walbridge
      debug("changePass: setting password for $userDN to $newpass");
876 4849 daigle
    	my $result = $ldap->modify( $userDN, replace => { %$modifications });
877 2341 sgarg
878 4849 daigle
    	if ($result->code()) {
879 4866 walbridge
            debug("changePass: error changing password: " . $result->error);
880
        	$errorMessage = "There was an error changing the password:" .
881 2341 sgarg
                           "<br />\n" . $result->error;
882 4849 daigle
    	}
883
    	$ldap->unbind;   # take down session
884
    }
885 2341 sgarg
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 3177 tao
    my $ldap;
920 4749 walbridge
    debug("ldap server: $ldapurl");
921 4394 walbridge
922
    #if main ldap server is down, a html file containing warning message will be returned
923 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
924 4849 daigle
925
    if ($ldap) {
926 8501 tao
        $ldap->start_tls( verify => 'none');
927
        #$ldap->start_tls( verify => 'require',
928
        #              cafile => $ldapServerCACertFile);
929 4849 daigle
    	my $bindresult = $ldap->bind;
930
    	if ($bindresult->code) {
931
        	return $entry;
932
    	}
933 2341 sgarg
934 8415 tao
        $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 3177 tao
947 4849 daigle
    	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 4865 walbridge
                    debug("getLdapEntry: recursion found $host, $path, $username, $org");
962 4849 daigle
                	return $entry;
963
            	}
964
        	}
965
    	}
966 2341 sgarg
    }
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 2972 jones
    my $cfg = shift;
979 2341 sgarg
980
    my $errorMessage = "";
981
    if ($recipient) {
982 8254 leinfelder
983 4771 walbridge
        my $mailhost = $properties->getProperty('email.mailhost');
984 8197 tao
        my $sender;
985 10143 cjones
        my $contact;
986 8197 tao
        $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
987 2341 sgarg
        # 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 8234 tao
        Subject: Your Account Password Reset
996 2341 sgarg
997 8234 tao
        Somebody (hopefully you) requested that your account password be reset.
998 8259 leinfelder
        Your temporary password is below. Please change it as soon as possible
999 8413 tao
        at: $contextUrl/style/skins/account/.
1000 2341 sgarg
1001
            Username: $username
1002
        Organization: $org
1003
        New Password: $newPass
1004
1005
        Thanks,
1006 8234 tao
            $sender
1007 10010 cjones
            $contact
1008 2341 sgarg
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 8877 tao
# 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 2341 sgarg
# 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 8221 tao
    my $notHtmlFormat = shift;
1072 3175 tao
    my $ldap;
1073 4847 daigle
    my $mesg;
1074 2341 sgarg
1075
    my $foundAccounts = 0;
1076 4749 walbridge
1077 4394 walbridge
    #if main ldap server is down, a html file containing warning message will be returned
1078 4868 walbridge
    debug("findExistingAccounts: connecting to $ldapurl, $timeout");
1079 4771 walbridge
    $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1080 4845 daigle
    if ($ldap) {
1081 8501 tao
    	$ldap->start_tls( verify => 'none');
1082
    	#$ldap->start_tls( verify => 'require',
1083
        #              cafile => $ldapServerCACertFile);
1084 4845 daigle
    	$ldap->bind( version => 3, anonymous => 1);
1085 4848 daigle
		$mesg = $ldap->search (
1086 4845 daigle
			base   => $base,
1087
			filter => $filter,
1088
			attrs => @$attref,
1089
		);
1090 2341 sgarg
1091 4845 daigle
	    if ($mesg->count() > 0) {
1092
			$foundAccounts = "";
1093
			my $entry;
1094
			foreach $entry ($mesg->all_entries) {
1095 5650 walbridge
                # 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 8217 tao
                #if ($entry->dn !~ /ou=Account/) {
1098 8221 tao
                    if($notHtmlFormat) {
1099
                        $foundAccounts .= "\nAccount: ";
1100
                    } else {
1101
                        $foundAccounts .= "<p>\n<b><u>Account:</u> ";
1102
                    }
1103 5650 walbridge
                    $foundAccounts .= $entry->dn();
1104 8221 tao
                    if($notHtmlFormat) {
1105
                        $foundAccounts .= "\n";
1106
                    } else {
1107
                        $foundAccounts .= "</b><br />\n";
1108
                    }
1109 5650 walbridge
                    foreach my $attribute ($entry->attributes()) {
1110
                        my $value = $entry->get_value($attribute);
1111
                        $foundAccounts .= "$attribute: ";
1112
                        $foundAccounts .= $value;
1113 8221 tao
                         if($notHtmlFormat) {
1114
                            $foundAccounts .= "\n";
1115
                        } else {
1116
                            $foundAccounts .= "<br />\n";
1117
                        }
1118 5650 walbridge
                    }
1119 8221 tao
                    if($notHtmlFormat) {
1120
                        $foundAccounts .= "\n";
1121
                    } else {
1122
                        $foundAccounts .= "</p>\n";
1123
                    }
1124
1125 8217 tao
                #}
1126 4845 daigle
			}
1127 2341 sgarg
        }
1128 4845 daigle
    	$ldap->unbind;   # take down session
1129 2341 sgarg
1130 4848 daigle
    	# 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 8254 leinfelder
        	my $refFound = &findExistingAccounts($host, $path, $filter, $attref, $notHtmlFormat);
1138 4848 daigle
        	if ($refFound) {
1139
            	$foundAccounts .= $refFound;
1140
        	}
1141
    	}
1142 2341 sgarg
    }
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 8175 tao
# 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 8180 tao
    my $org = $query->param('o');
1179 8220 tao
    my $ldapUsername = $ldapConfig->{$org}{'user'};
1180
    my $ldapPassword = $ldapConfig->{$org}{'password'};
1181
    my $tmp = 1;
1182 8185 tao
1183 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
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 8175 tao
1201 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
1202 8201 tao
    my $filter = $ldapConfig->{$org}{'filter'};
1203 8220 tao
1204 8176 tao
    debug("search filer " . $filter);
1205
    debug("ldap server ". $ldapurl);
1206
    debug("sesarch base " . $tmpSearchBase);
1207 8262 tao
    #print "Content-type: text/html\n\n";
1208 8175 tao
    my @attrs = ['o', 'ou' ];
1209
    my $found = searchDirectory($ldapurl, $tmpSearchBase, $filter, \@attrs);
1210 8220 tao
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 8180 tao
1215 8176 tao
    if(!$found) {
1216 8180 tao
        debug("generate the subtree in the dc=tmp===========================");
1217 8176 tao
        #need to generate the subtree o or ou
1218 8220 tao
        my $additions;
1219 8207 tao
            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 8220 tao
        my $dn=$ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1233
        createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1234 8176 tao
    }
1235 8175 tao
1236 8180 tao
    ################create an account under tmp subtree
1237 8176 tao
1238 8413 tao
     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 8411 tao
    #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 10010 cjones
         my $contact;
1246 8411 tao
        $sender = $skinProperties->getProperty("email.recipient") or $sender = $properties->getProperty('email.recipient');
1247 10010 cjones
        $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 8413 tao
                           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 8411 tao
        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 8180 tao
    #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 8411 tao
                'cn'   => $cn,
1264 8180 tao
                'sn'   => $query->param('sn'),
1265
                'givenName'   => $query->param('givenName'),
1266
                'mail' => $query->param('mail'),
1267
                'userPassword' => $shapass,
1268
                'employeeNumber' => $randomStr,
1269 8411 tao
                'uidNumber' => $nextUidNumber,
1270
                'gidNumber' => $nextUidNumber,
1271
                'loginShell' => '/sbin/nologin',
1272
                'homeDirectory' => '/dev/null',
1273 8180 tao
                'objectclass' => ['top', 'person', 'organizationalPerson',
1274 8411 tao
                                'inetOrgPerson', 'posixAccount', 'shadowAccount' ],
1275 8201 tao
                $organization   => $organizationName
1276 8180 tao
                ];
1277 8411 tao
    my $gecos;
1278 8180 tao
    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 8411 tao
                $gecos = $cn . ',,'. $query->param('telephoneNumber'). ',';
1284
    } else {
1285
        $gecos = $cn . ',,,';
1286 8180 tao
    }
1287 8411 tao
1288
    $$additions[$#$additions + 1] = 'gecos';
1289
    $$additions[$#$additions + 1] = $gecos;
1290
1291 8180 tao
    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 8201 tao
1298
1299
    #$$additions[$#$additions + 1] = 'o';
1300
    #$$additions[$#$additions + 1] = $org;
1301
    my $dn='uid=' . $query->param('uid') . ',' . $ldapConfig->{$org}{'org'} . ',' . $tmpSearchBase;
1302 8220 tao
    createItem($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1303 8176 tao
1304 8180 tao
1305
    ####################send the verification email to the user
1306 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.
1307 8180 tao
1308 8253 leinfelder
    my $overrideURL;
1309
    $overrideURL = $skinProperties->getProperty("email.overrideURL");
1310 8411 tao
    debug("the overrideURL is $overrideURL");
1311 8253 leinfelder
    if (defined($overrideURL) && !($overrideURL eq '')) {
1312
    	$link = $serverUrl . $overrideURL . $link;
1313
    } else {
1314
    	$link = $serverUrl . $link;
1315
    }
1316
1317 8181 tao
    my $mailhost = $properties->getProperty('email.mailhost');
1318 8197 tao
    my $sender;
1319 10010 cjones
    my $contact;
1320 8197 tao
    $sender = $skinProperties->getProperty("email.sender") or $sender = $properties->getProperty('email.sender');
1321 10010 cjones
    $contact = $skinProperties->getProperty("email.contact") or $contact = $properties->getProperty('email.contact');
1322 8197 tao
    debug("the sender is " . $sender);
1323 10010 cjones
    debug("the contact is :" . $contact);
1324 8181 tao
    my $recipient = $query->param('mail');
1325
    # Send the email message to them
1326 8191 tao
    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 8181 tao
    $smtp->mail($sender);
1332
    $smtp->to($recipient);
1333
1334
    my $message = <<"     ENDOFMESSAGE";
1335
    To: $recipient
1336
    From: $sender
1337 8239 leinfelder
    Subject: New Account Activation
1338 8181 tao
1339 8413 tao
    Somebody (hopefully you) registered an account on $contextUrl/style/skins/account/.
1340 8181 tao
    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 8234 tao
        $sender
1347 10010 cjones
        $contact
1348 8181 tao
1349
     ENDOFMESSAGE
1350
     $message =~ s/^[ \t\r\f]+//gm;
1351
1352
     $smtp->data($message);
1353
     $smtp->quit;
1354 8182 tao
    debug("the link is " . $link);
1355 8181 tao
    fullTemplate( ['success'] );
1356
1357 8175 tao
}
1358
1359
#
1360 8220 tao
# Bind to LDAP and create a new item (a user or subtree) using the information provided
1361 2341 sgarg
# by the user
1362
#
1363 8220 tao
sub createItem {
1364 8180 tao
    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 9514 tao
    debug("the ldap ca certificate is " . $ldapServerCACertFile);
1381 8180 tao
    #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 8403 tao
            $ldap->start_tls( verify => 'require',
1385
                      cafile => $ldapServerCACertFile);
1386 8180 tao
            debug("Attempting to bind to LDAP server with dn = $ldapUsername, pwd = $ldapPassword");
1387 8185 tao
            $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
1388 8180 tao
            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 8220 tao
                exist(0);
1394 8180 tao
                # 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 8181 tao
                #fullTemplate( ['success'] );
1400 8180 tao
            }
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 2341 sgarg
1413
1414
1415
1416
1417 8185 tao
#
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 8211 tao
    #my $orgAuthBase;
1433
1434
    $ldapUsername = $ldapConfig->{$org}{'user'};
1435
    $ldapPassword = $ldapConfig->{$org}{'password'};
1436
    #$orgAuthBase = $ldapConfig->{$org}{'base'};
1437
1438 8185 tao
    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 8403 tao
        $ldap->start_tls( verify => 'require',
1446
                      cafile => $ldapServerCACertFile);
1447 8185 tao
        $ldap->bind( version => 3, dn => $ldapUsername, password => $ldapPassword );
1448 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.
1449 8185 tao
        my $max = $mesg->count;
1450
        debug("the count is " . $max);
1451
        if($max < 1) {
1452
            $ldap->unbind;   # take down session
1453 8216 tao
            fullTemplate( ['verificationFailed'], {errorMessage => "No record matched the dn " . $dn . " for the activation. You probably already activated the account."});
1454 8185 tao
            #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 8211 tao
1469
1470
                my $orgDn = $ldapConfig->{$org}{'dn'}; #the DN for the organization.
1471 8185 tao
                $mesg = $ldap->moddn(
1472
                            dn => $dn,
1473
                            deleteoldrdn => 1,
1474
                            newrdn => "uid=" . $uid,
1475 8211 tao
                            newsuperior  =>  $orgDn);
1476 8185 tao
                $ldap->unbind;   # take down session
1477 8186 tao
                if($mesg->code()) {
1478 8216 tao
                    fullTemplate( ['verificationFailed'], {errorMessage => "Cannot move the account from the inactive area to the ative area since " . $mesg->error()});
1479 8185 tao
                    exit(0);
1480
                } else {
1481 8216 tao
                    fullTemplate( ['verificationSuccess'] );
1482 8185 tao
                }
1483
                #createAccount2($dn, $ldapUsername, $ldapPassword, $additions, $tmp, $allParams);
1484
            } else {
1485
                $ldap->unbind;   # take down session
1486 8216 tao
                fullTemplate( ['verificationFailed'], {errorMessage => "The hash string " . $hash . " from your link doesn't match our record."});
1487 8185 tao
                exit(0);
1488
            }
1489
1490
        }
1491
    } else {
1492
        handleLDAPBindFailure($ldapurl);
1493
        exit(0);
1494
    }
1495
1496
}
1497
1498 2341 sgarg
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 4080 daigle
  fullTemplate( [], { stage => $templates->{'stage'},
1504
                      errorMessage => $errorMessage });
1505
  exit();
1506 2341 sgarg
}
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 4080 daigle
    my $ldapurl = $ldapConfig->{$o}{'url'};
1518
    my $searchBase = $ldapConfig->{$o}{'base'};
1519 2341 sgarg
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 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1561
                                         allParams => $allParams,
1562
                                         foundAccounts => $found });
1563 2341 sgarg
    } else {
1564
      $found = "No entries matched your criteria.  Please try again\n";
1565
1566 4080 daigle
      fullTemplate( ('searchResults'), { stage => "searchresults",
1567
                                         allParams => $allParams,
1568
                                         foundAccounts => $found });
1569 2341 sgarg
    }
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 4849 daigle
	my $mesg;
1584 2341 sgarg
    my $foundAccounts = 0;
1585 3177 tao
1586
    #if ldap server is down, a html file containing warning message will be returned
1587 4771 walbridge
    my $ldap = Net::LDAP->new($ldapurl, timeout => $timeout) or handleLDAPBindFailure($ldapurl);
1588 3177 tao
1589 4849 daigle
    if ($ldap) {
1590 8403 tao
    	$ldap->start_tls( verify => 'require',
1591
                      cafile => $ldapServerCACertFile);
1592 4849 daigle
    	$ldap->bind( version => 3, anonymous => 1);
1593
    	my $mesg = $ldap->search (
1594
        	base   => $base,
1595
        	filter => $filter,
1596
        	attrs => @$attref,
1597
    	);
1598 2341 sgarg
1599 4849 daigle
    	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 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1606 4849 daigle
         		 $foundAccounts .= $entry->get_value('labeledURI') unless
1607 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1608 4849 daigle
          		$foundAccounts .= "\">\n" unless
1609 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1610 4849 daigle
          		$foundAccounts .= $entry->get_value('givenName');
1611
          		$foundAccounts .= "</a>\n" unless
1612 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1613 4849 daigle
          		$foundAccounts .= "\n</td>\n<td class=\"main\">\n";
1614
          		$foundAccounts .= "<a href=\"" unless
1615 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1616 4849 daigle
          		$foundAccounts .= $entry->get_value('labeledURI') unless
1617 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1618 4849 daigle
          		$foundAccounts .= "\">\n" unless
1619 2341 sgarg
                    (!$entry->get_value('labeledURI'));
1620 4849 daigle
          		$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 2341 sgarg
    }
1636
    return $foundAccounts;
1637
}
1638
1639
sub debug {
1640
    my $msg = shift;
1641
1642
    if ($debug) {
1643 4747 walbridge
        print STDERR "LDAPweb: $msg\n";
1644 2341 sgarg
    }
1645
}
1646 3175 tao
1647 4771 walbridge
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 3177 tao
sub handleGeneralServerFailure {
1659
    my $errorMessage = shift;
1660 4728 walbridge
    fullTemplate( ['mainServerFailure'], { errorMessage => $errorMessage });
1661 3175 tao
    exit(0);
1662
   }
1663
1664 4080 daigle
sub setVars {
1665
    my $paramVars = shift;
1666
    # initialize default parameters
1667
    my $templateVars = { cfg => $cfg,
1668 4394 walbridge
                         styleSkinsPath => $contextUrl . "/style/skins",
1669
                         styleCommonPath => $contextUrl . "/style/common",
1670
                         contextUrl => $contextUrl,
1671 4770 daigle
                         cgiPrefix => $cgiPrefix,
1672 8206 tao
                         orgList => \@validDisplayOrgList,
1673 4394 walbridge
                         config  => $config,
1674 4080 daigle
    };
1675
1676
    # append customized params
1677
    while (my ($k, $v) = each (%$paramVars)) {
1678
        $templateVars->{$k} = $v;
1679
    }
1680
1681
    return $templateVars;
1682
}
1683 8180 tao
1684 8408 tao
#Method to get the next avaliable uid number. We use the mechanism - http://www.rexconsulting.net/ldap-protocol-uidNumber.html
1685
sub getNextUidNumber {
1686 8413 tao
1687 8410 tao
    my $maxAttempt = $properties->getProperty('ldap.nextuid.maxattempt');
1688 8408 tao
1689 8411 tao
    my $ldapUsername = shift;
1690
    my $ldapPassword = shift;
1691 8408 tao
1692 8411 tao
    my $realUidNumber;
1693
    my $uidNumber;
1694 8408 tao
    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 8818 tao
    	my $existingHighUid=getExistingHighestUidNum($ldapUsername, $ldapPassword);
1705 8408 tao
        $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 8413 tao
            $mesg = $ldap->search(base  => $dn_store_next_uid, filter => '(objectClass=*)');
1711 8408 tao
            if ($mesg->count() > 0) {
1712 8413 tao
                debug("Find the cn - $dn_store_next_uid");
1713 8408 tao
                $entry = $mesg->pop_entry;
1714 8413 tao
                $uidNumber = $entry->get_value($attribute_name_store_next_uid);
1715 8408 tao
                if($uidNumber) {
1716 8413 tao
                    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 8819 tao
                            if($existingHighUid) {
1729 8821 tao
                            	debug("exiting high uid exists =======================================");
1730 8819 tao
                            	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 8413 tao
                            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 8408 tao
                    }
1741 8413 tao
1742 8408 tao
               } else {
1743 8413 tao
                 debug("can't find the attribute - $attribute_name_store_next_uid in the $dn_store_next_uid and we will try again");
1744 8408 tao
               }
1745
            }
1746
        }
1747
        $ldap->unbind;   # take down session
1748
    }
1749
    return $realUidNumber;
1750
}
1751
1752 8818 tao
#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 8821 tao
    my $storedUidNumber;
1760 8818 tao
1761 8819 tao
1762 8818 tao
    #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 8821 tao
        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 8877 tao
        my $authBase = $properties->getProperty("auth.base");
1775 8818 tao
        my $uids = $ldap->search(
1776 8844 leinfelder
                        base => $authBase,
1777 8818 tao
                        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 8821 tao
                		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 8818 tao
                }
1793
        }
1794
1795 8821 tao
        if(@uids) {
1796
        	@uids = sort { $b <=> $a } @uids;
1797
        	$high = $uids[0];
1798
        }
1799 8818 tao
        debug("the highest exiting uidnumber is $high");
1800
        $ldap->unbind;   # take down session
1801
    }
1802
    return $high;
1803 8408 tao
1804 8818 tao
}
1805