Change default group from 1 (daemon) to something more sensible: 100
[mspang/plceo2.git] / modules / Accounts.pm
1
2 # Accounts.pm
3 #
4 # Defines methods for retrieving and modifying account entries
5
6 # the common representation for an account is a reference to a hash
7 # with the appropriate key-value pairs defined
8
9 package Accounts;
10
11 use strict;
12 use Net::LDAP;
13 use User::pwent;
14 use English;
15 use Authen::Krb5;
16 use Authen::Krb5::Admin qw(:constants);
17
18 require Common;
19
20 # function: Add($memberid, $userid)
21 #           adds an account to the system
22 #
23 #      arg: $account - the account hashref
24 #
25 #  returns: truth on success, false on failure
26 sub Add($) {
27         my ($memberid, $userid) = @_;
28
29         # * Check that the member doesn't already have an account
30         # * Check that that account name isn't already taken
31         # * If not, add the account to LDAP and add the appropriate
32         #   account name to the member entry
33         # * Change the password
34
35         my $shell = "/bin/bash";
36
37         my $member = Members::Get($memberid);
38         if (!$member) {
39             # No such member!
40             return 0;
41         }
42         my $currentid = $member->{"userid"};
43         if ($currentid) {
44             UI::MsgWait("Member $memberid already has an account: $currentid\n"
45                         . "Contact the sysadmin if there are still problems.");
46               return 0;
47         }
48         # TODO: Check that member is a member this term
49         # TODO: Check format of userid (alphanumeric, etc.)
50         
51         my $realname = $member->{"name"};
52
53         # Determine what UID number to assign
54         my $maxid = 0;
55         my $uidnum;
56         while (1) {
57             my $pw = getpwent() || last;
58             my $idnum = $pw->uid;
59             if ($idnum > $maxid && $idnum < 50000) {
60                 $maxid = $idnum;
61             }
62         }
63         if( $maxid < 100 ) {
64             # This is bad.  getpwent() failed miserably.
65             # TODO: Really fix this sometime
66             die 1;
67         }
68         $uidnum = $maxid + 2;
69
70         open LDAPSECRET, "</etc/ldap.ceo";
71
72         my $secret = <LDAPSECRET>;
73         chomp $secret;
74         close LDAPSECRET;
75         
76         my $ldap = Net::LDAP->new('localhost');
77         if (!$ldap) {
78             # TODO: Print error in $@
79             return 0;
80         }
81
82         my $result = $ldap->bind('cn=ceo,dc=csclub,dc=uwaterloo,dc=ca',
83                       password => $secret);
84
85         if ($result->code) {
86             # TODO: Print $result->error
87             die "Could not bind to LDAP! , ". $result->error ."\n";
88             return 0;
89         }
90
91         my $userpass;
92         my $repeat;
93         # TODO: Use some UI Password prompting function
94         do {
95             $userpass = &UI::Prompt("User password: ", 'custom', \&prompt_password, 1);
96             $repeat = &UI::Prompt("Enter the password again: ", 'custom', \&prompt_password, 1);
97         } while ($userpass ne $repeat);
98         
99         $result =
100             $ldap->add("uid=$userid,ou=People,dc=csclub,dc=uwaterloo,dc=ca",
101                        attr => ['uid' => "$userid",
102                                 'cn'  => "$realname",
103                                 'objectClass' => ['account',
104                                                   'posixAccount',
105                                                   'top'],
106                                 'userPassword' => "x",
107                                 'loginShell'   => "$shell",
108                                 'uidNumber'    => "$uidnum",
109                                 'gidNumber'    => "100",
110                                 'homeDirectory'=> "/users/$userid",
111                                 'gecos'        => "$realname"]
112                );
113
114         if ($result->code) {
115             # TODO: Print $result->error
116             die "Could not do stuff , ". $result->error ." ";
117             return 0;
118         }
119
120         $ldap->unbind();
121
122         #Add them to kerberose
123         my $krb5context;
124         eval {
125                 $krb5context = Authen::Krb5::init_context();
126                 Authen::Krb5::init_ets();
127         };
128         if ( $@ ) {
129                 #TODO: Print error
130                 #return 0;
131                 warn $@;
132                 die $@;
133         }
134
135
136         my $kadm5_config =  Authen::Krb5::Admin::Config->new(); 
137
138         my $kadm5 =
139           Authen::Krb5::Admin->init_with_skey( "ceo/admin\@CSCLUB.UWATERLOO.CA" , "/etc/ceo.keytab" , "kadmin/admin" , $kadm5_config )
140           or die Authen::Krb5::Admin::error;
141         
142  
143
144         my $krb5_princ;
145  
146         # get valid kerb5 principal from uid
147         $krb5_princ = Authen::Krb5::parse_name($userid)
148           or die Authen::Krb5::error;
149  
150         # get a new principal object
151         my $kadm5_princ = Authen::Krb5::Admin::Principal->new
152           or die Authen::Krb5::error;
153  
154         # set the value of the new principal's principal name
155         $kadm5_princ->principal($krb5_princ)
156           or die Authen::Krb5::error;
157  
158         # if principal does not exist, ok to create...
159         #if ( !$kadm5->get_principal($krb5_princ) ) {
160             # set the value of the principals policy
161            #$kadm5_princ->policy( "default" )
162            #     or die Authen::Krb5::Admin::error;
163         
164             # modify principal's pw expiration
165             #$kadm5_princ->pw_expiration( time() )
166             #    or die Authen::Krb5::Admin::error;
167                 
168             # create princ
169             $kadm5->create_principal( $kadm5_princ, $userpass )
170                 or die Authen::Krb5::Admin::error;
171         
172             #$kadm5->chpass($kadm5_princ , "pants");
173
174         #}
175         #else { 
176         #       warn "WARNING: Principal $userid already existed in Kerberos\n";
177         #}
178
179         # Make the home directory
180         #system("/usr/local/bin/addhomedir_ceo", "$userid");
181         
182         # Add userid to database
183         my $statement = $Common::CEODB->prepare("UPDATE members " .
184                                                 "SET userid = ? WHERE memberid = ?");
185
186         $result = $statement->execute($userid, $memberid);
187
188         if (!$result) {
189                 &Common::Log(
190                         10,
191                         "Accounts::Add",
192                         "members table",
193                         sprintf("Error: set account for member %s uid %s failed",
194                                 $memberid,
195                                 $userid));
196                 $statement->finish();
197                 &UI::MsgWait("The user was created but adding the user ID to the database failed. Please contact the sysadmin.");
198                 return 0;
199         }
200
201         &UI::MsgWait("Please run \'addhomedir $userid\'.");
202
203 #       MailSysadmin("Account $userid created", "For member $memberid.");
204         return 1;
205 }
206
207 # function: Add($memberid, $userid)
208 #           adds an account to the system
209 #
210 #      arg: $account - the account hashref
211 #
212 #  returns: truth on success, false on failure
213 sub ReAdd($) {
214         my ($memberid, $userid) = @_;
215
216         # * Check that the member doesn't already have an account
217         # * Check that that account name isn't already taken
218         # * If not, add the account to LDAP and add the appropriate
219         #   account name to the member entry
220         # * Change the password
221
222         my $shell = "/bin/bash";
223
224         my $member = Members::Get($memberid);
225         if (!$member) {
226             # No such member!
227             return 0;
228         }
229         my $currentid = $member->{"userid"};
230         #if ($currentid) {
231         #    UI::MsgWait("Member $memberid already has an account: $currentid\n"
232         #                . "Contact the sysadmin if there are still problems.");
233         #      return 0;
234         #}
235
236         # TODO: Check that member is a member this term
237         # TODO: Check format of userid (alphanumeric, etc.)
238         
239         my $realname = $member->{"name"};
240
241         # Determine what UID number to assign
242         my $maxid = 0;
243         my $uidnum;
244         while (1) { #Keep people from creating uid 0 accounts by mistake
245             my $pw = getpwent() || last;
246             my $idnum = $pw->uid;
247             if ($idnum > $maxid && $idnum < 50000) {
248                 $maxid = $idnum;
249             }
250         }
251         if( $maxid < 100 ) {
252             # This is bad.  getpwent() failed miserably.
253             # TODO: Really fix this sometime
254             die 1;
255         }
256         $uidnum = $maxid + 1;
257
258         open LDAPSECRET, "</etc/ldap.ceo";
259
260         my $secret = <LDAPSECRET>;
261         chomp $secret;
262         close LDAPSECRET;
263         
264         my $ldap = Net::LDAP->new('localhost');
265         if (!$ldap) {
266             # TODO: Print error in $@
267             return 0;
268         }
269
270         my $result = $ldap->bind('cn=ceo,dc=csclub,dc=uwaterloo,dc=ca',
271                       password => $secret);
272
273         if ($result->code) {
274             # TODO: Print $result->error
275             return 0;
276         }
277
278         my $userpass;
279         my $repeat;
280         # TODO: Use some UI Password prompting function
281         do {
282             $userpass = &UI::Prompt("User password: ", 'custom', \&prompt_password, 1);
283             $repeat = &UI::Prompt("Enter the password again: ", 'custom', \&prompt_password, 1);
284         } while ($userpass ne $repeat);
285         
286         $result =
287             $ldap->add("uid=$userid,ou=People,dc=csclub,dc=uwaterloo,dc=ca",
288                        attr => ['uid' => "$userid",
289                                 'cn'  => "$realname",
290                                 'objectClass' => ['account',
291                                                   'posixAccount',
292                                                   'top'],
293                                 'userPassword' => "$userpass",
294                                 'loginShell'   => "$shell",
295                                 'uidNumber'    => "$uidnum",
296                                 'gidNumber'    => "100",
297                                 'homeDirectory'=> "/users/$userid",
298                                 'gecos'        => "$realname"]
299                );
300
301         if ($result->code) {
302             # TODO: Print $result->error
303             return 0;
304         }
305
306         $ldap->unbind();
307
308         # Make the home directory
309         #system("/usr/local/bin/addhomedir_ceo", "$userid");
310         
311         # Add userid to database
312         my $statement = $Common::CEODB->prepare("UPDATE members " .
313                                                 "SET userid = ? WHERE memberid = ?");
314
315         $result = $statement->execute($userid, $memberid);
316
317         if (!$result) {
318                 &Common::Log(
319                         10,
320                         "Accounts::Add",
321                         "members table",
322                         sprintf("Error: set account for member %s uid %s failed",
323                                 $memberid,
324                                 $userid));
325                 $statement->finish();
326                 &UI::MsgWait("The user was created but adding the user ID to the database failed. Please contact the sysadmin.");
327                 return 0;
328         }
329
330         &UI::MsgWait("Please run \'addhomedir $userid\'.");
331
332 #       MailSysadmin("Account $userid created", "For member $memberid.");
333         return 1;
334 }
335
336
337 sub prompt_password($) {
338     (my $pw) = @_;
339
340     # TODO: Password constraints
341     
342     return (1, $pw);
343 }
344
345 1;