Project

General

Profile

1 2341 sgarg
#!/usr/bin/perl -w
2
 #
3
 #  '$RCSfile$'
4
 #  Copyright: 2004 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
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
}