Project

General

Profile

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

    
25
#
26
# This is a simple script for creating a new LDAP record with a
27
# predefined format that is hardcoded in the script.  This could be generalized
28
# to support an externally-configured record format.
29
# Matt Jones
30
#
31
use strict;       # turn on strict syntax checking.
32
use Net::LDAP;    # load the LDAP net libraries
33
use Digest::SHA1; # for creating the password hash
34
use MIME::Base64; # for creating the password hash
35
use URI;          # for parsing URL syntax
36
use AppConfig qw(:expand :argcount);
37
use Term::ReadKey;
38

    
39
# Set up our default configuration
40
my $ldapurl = "";
41
my $root = "";
42
my $rootpw = "";
43
my $searchBase = "";
44
my $mailhost = "";
45
my $sender = "";
46

    
47
my $debug = 0;
48

    
49
#--------------------------------------------------------------------------80c->
50
# Read the ldapweb.cfg file
51
my $cfgfile = "ldapweb.cfg";
52
my $config = AppConfig->new({ 
53
    GLOBAL => { ARGCOUNT => ARGCOUNT_ONE, } });
54

    
55
$config->define("ldapurl", { ARGCOUNT => ARGCOUNT_HASH} );           
56
$config->define("ldapsearchbase", { ARGCOUNT => ARGCOUNT_HASH} );
57
$config->define("dn", { ARGCOUNT => ARGCOUNT_HASH} );
58
$config->define("filter", { ARGCOUNT => ARGCOUNT_HASH} );
59
$config->define("user", { ARGCOUNT => ARGCOUNT_HASH} );
60
$config->define("password", { ARGCOUNT => ARGCOUNT_HASH} );
61

    
62
$config->file($cfgfile);
63
my $config_ldapurl = $config->get('ldapurl');
64
my $config_ldapsearchbase = $config->get('ldapsearchbase');
65
my $config_dn = $config->get('dn');
66
my $config_filter = $config->get('filter');
67
my $config_user = $config->get('user');
68
my $config_password = $config->get('password');
69

    
70
my @orglist;
71
foreach my $neworg (keys %$config_dn) {
72
    push(@orglist, $neworg);
73
    debug($neworg);
74
}
75

    
76

    
77
#--------------------------------------------------------------------------80c->
78
# Define the main program logic that calls subroutines to do the work
79
#--------------------------------------------------------------------------80c->
80

    
81
my $allParams = getAccountInfo();
82
my $shouldContinue = checkForDuplicateAccounts($allParams);
83
createAccount($allParams);
84
exit(0);
85

    
86
#--------------------------------------------------------------------------80c->
87
# Define the subroutines to do the work
88
#--------------------------------------------------------------------------80c->
89

    
90
#
91
# Prompt the user for one piece of input information
92
#
93
sub getUserInput {
94
    my $prompt = shift;
95
    my $hideInput = shift;
96

    
97
    print $prompt, ": ";
98
    if ($hideInput) {
99
        ReadMode('noecho');
100
    }
101

    
102
    my $value = ReadLine(0);
103
    chomp($value);
104
    if ($hideInput) {
105
        ReadMode('normal');
106
        print "\n";
107
    }
108
    return $value;
109
}
110

    
111
#
112
# get input about the account to be created
113
#
114
sub getAccountInfo {
115
    
116
    my $uid = getUserInput("UserID", 0);
117
    my $userPassword = getUserInput("Password", 1);
118
    my $userPassword2 = getUserInput("Password Again", 1);
119
    my $givenName = getUserInput("GivenName", 0);
120
    my $sn = getUserInput("Surname", 0);
121
    my $o = getUserInput("Organization", 0);
122
    my $mail = getUserInput("Email", 0);
123
    my $title = getUserInput("Title", 0);
124
    my $telephoneNumber = getUserInput("Telephone", 0);
125
    print "\n";
126

    
127
    my $allParams = { 'givenName' => $givenName, 
128
                      'sn' => $sn,
129
                      'o' => $o, 
130
                      'mail' => $mail, 
131
                      'uid' => $uid, 
132
                      'userPassword' => $userPassword, 
133
                      'userPassword2' => $userPassword2, 
134
                      'title' => $title, 
135
                      'telephoneNumber' => $telephoneNumber };
136
    # Check that all required fields are provided and not null
137
    my @requiredParams = ( 'givenName', 'sn', 'o', 'mail', 
138
                           'uid', 'userPassword', 'userPassword2');
139
    if (! paramsAreValid($allParams, @requiredParams)) {
140
        my $errorMessage = "Required information is missing. " .
141
            "Please try again and provide all required fields.";
142
        print $errorMessage, "\n";
143
        exit(0);
144
    } else {
145
	    $ldapurl = $config_ldapurl->{$o};
146
	    $searchBase = $config_ldapsearchbase->{$o};  
147
    }
148

    
149
    return $allParams;
150
}
151

    
152
sub checkForDuplicateAccounts { 
153
    my $allParams = shift;
154

    
155
    # Search LDAP for matching entries that already exist
156
    # Some forms use a single text search box, whereas others search per
157
    # attribute.
158
    my $filter;
159
    $filter = "(|" . 
160
              "(uid=" . $allParams->{'uid'} . ") " .
161
              "(mail=" . $allParams->{'mail'} . ")" .
162
              "(&(sn=" . $allParams->{'sn'} . ") " . 
163
              "(givenName=" . $allParams->{'givenName'} . "))" . 
164
              ")";
165

    
166
    my @attrs = [ 'uid', 'o', 'cn', 'mail', 'telephoneNumber', 'title' ];
167

    
168
    my $found = findExistingAccounts($ldapurl, $searchBase, $filter, \@attrs);
169

    
170
    # If entries match, ask if the account should be created
171
    if ($found) {
172
        print $found, "\n";
173
        my $question  = "Similar accounts already exist.  Do you want to " .
174
            "create a\nnew account anyways? (y/n)";
175
        my $continue = getUserInput($question, 0);
176
        if ($continue =~ "y") {
177
            return 1;
178
        } else {
179
            return 0;
180
        }
181
    # Otherwise, create a new user in the LDAP directory
182
    } else {
183
        return 1;
184
    }
185
}
186

    
187
#
188
# generate a Seeded SHA1 hash of a plaintext password
189
#
190
sub createSeededPassHash {
191
    my $secret = shift;
192

    
193
    my $salt = "";
194
    for (my $i=0; $i < 4; $i++) {
195
        $salt .= int(rand(10));
196
    }
197

    
198
    my $ctx = Digest::SHA1->new;
199
    $ctx->add($secret);
200
    $ctx->add($salt);
201
    my $hashedPasswd = '{SSHA}' . encode_base64($ctx->digest . $salt ,'');
202

    
203
    return $hashedPasswd;
204
}
205

    
206
#
207
# search the LDAP directory to see if a similar account already exists
208
#
209
sub findExistingAccounts {
210
    my $ldapurl = shift;
211
    my $base = shift;
212
    my $filter = shift;
213
    my $attref = shift;
214

    
215
    my $foundAccounts = 0;
216

    
217
    my $ldap = Net::LDAP->new($ldapurl) or die "$@";
218
    $ldap->bind( version => 3, anonymous => 1);
219
    my $mesg = $ldap->search (
220
        base   => $base,
221
        filter => $filter,
222
        attrs => @$attref,
223
    );
224

    
225
    if ($mesg->count() > 0) {
226
        $foundAccounts = "";
227
        my $entry;
228
        foreach $entry ($mesg->all_entries) { 
229
            $foundAccounts .= "\nAccount: ";
230
            $foundAccounts .= $entry->dn();
231
            $foundAccounts .= "\n";
232
            foreach my $attribute ($entry->attributes()) {
233
                $foundAccounts .= "    $attribute: ";
234
                $foundAccounts .= $entry->get_value($attribute);
235
                $foundAccounts .= "\n";
236
            }
237
            $foundAccounts .= "\n";
238
        }
239
    }
240
    $ldap->unbind;   # take down session
241

    
242
    # Follow references
243
    my @references = $mesg->references();
244
    for (my $i = 0; $i <= $#references; $i++) {
245
        my $uri = URI->new($references[$i]);
246
        my $host = $uri->host();
247
        my $path = $uri->path();
248
        $path =~ s/^\///;
249
        my $refFound = &findExistingAccounts($host, $path, $filter, $attref);
250
        if ($refFound) {
251
            $foundAccounts .= $refFound;
252
        }
253
    }
254

    
255
    #print "<p>Checking referrals...</p>\n";
256
    #my @referrals = $mesg->referrals();
257
    #print "<p>Referrals count: ", scalar(@referrals), "</p>\n";
258
    #for (my $i = 0; $i <= $#referrals; $i++) {
259
        #print "<p>Referral: ", $referrals[$i], "</p>\n";
260
    #}
261

    
262
    return $foundAccounts;
263
}
264

    
265
#
266
# Validate that we have the proper set of input parameters
267
#
268
sub paramsAreValid {
269
    my $allParams = shift;
270
    my @pnames = @_;
271

    
272
    my $allValid = 1;
273

    
274
    foreach my $parameter (@pnames) {
275
        if (!defined($allParams->{$parameter}) || 
276
            ! $allParams->{$parameter} ||
277
            $allParams->{$parameter} =~ /^\s+$/) {
278
            $allValid = 0;
279
        }
280
    }
281

    
282
    return $allValid;
283
}
284

    
285
#
286
# Bind to LDAP and create a new account using the information provided
287
# by the user
288
#
289
sub createAccount {
290
    my $allParams = shift;
291

    
292
    my $o = $allParams->{'o'};
293

    
294
    if ($o =~ "LTER") {
295
        # Handle LTER case and redirect them there
296
    } else {
297

    
298
        # Be sure the passwords match
299
        if ($allParams->{'userPassword'} !~ $allParams->{'userPassword2'}) {
300
            my $errorMessage = "The passwords do not match. Try again.";
301
            print $errorMessage, "\n";
302
            exit(0);
303
        }
304

    
305
	    my $ldapurl = $config_ldapurl->{$o};
306
	    my $root = $config_user->{$o};
307
	    my $rootpw = $config_password->{$o};
308
	    my $searchBase = $config_ldapsearchbase->{$o};
309
	    my $dnBase = $config_dn->{$o};
310

    
311
        my $ldap = Net::LDAP->new($ldapurl) or die "$@";
312
        $ldap->bind( version => 3, dn => $root, password => $rootpw );
313
        print "Inserting new entry for $allParams->{'uid'} ...\n";
314
        my $dn = 'uid=' . $allParams->{'uid'} . ',' . $dnBase;
315

    
316
        # Create a hashed version of the password
317
        my $shapass = createSeededPassHash($allParams->{'userPassword'});
318

    
319
        # Do the insertion
320
        my $additions = [ 
321
                'uid'   => $allParams->{'uid'},
322
                'o'   => $allParams->{'o'},
323
                'cn'   => join(" ", $allParams->{'givenName'}, 
324
                                    $allParams->{'sn'}),
325
                'sn'   => $allParams->{'sn'},
326
                'givenName'   => $allParams->{'givenName'},
327
                'mail' => $allParams->{'mail'},
328
                'userPassword' => $shapass,
329
                'objectclass' => ['top', 'person', 'organizationalPerson', 
330
                                'inetOrgPerson', 'uidObject' ]
331
            ];
332
        if (defined($allParams->{'telephoneNumber'}) && 
333
            $allParams->{'telephoneNumber'} &&
334
            ! $allParams->{'telephoneNumber'} =~ /^\s+$/) {
335
            $$additions[$#$additions + 1] = 'telephoneNumber';
336
            $$additions[$#$additions + 1] = $allParams->{'telephoneNumber'};
337
        }
338
        if (defined($allParams->{'title'}) && 
339
            $allParams->{'title'} &&
340
            ! $allParams->{'title'} =~ /^\s+$/) {
341
            $$additions[$#$additions + 1] = 'title';
342
            $$additions[$#$additions + 1] = $allParams->{'title'};
343
        }
344
        my $result = $ldap->add ( 'dn' => $dn, 'attr' => [ @$additions ]);
345
    
346
        if ($result->code()) {
347
            # Post an error message
348
            print "Error while creating account:\n";
349
            print $result->code(), "\n";
350
        } else {
351
            print "Account created.\n";
352
        }
353

    
354
        $ldap->unbind;   # take down session
355
    }
356
}
357

    
358
sub debug {
359
    my $msg = shift;
360
    
361
    if ($debug) {
362
        print STDERR "$msg\n";
363
    }
364
}
(3-3/12)