]> WPIA git - gigi.git/blob - src/org/cacert/gigi/dbObjects/User.java
6de799aeacb4e7a5461e1482b112ebf3bff59643
[gigi.git] / src / org / cacert / gigi / dbObjects / User.java
1 package org.cacert.gigi.dbObjects;
2
3 import java.sql.Date;
4 import java.util.Calendar;
5 import java.util.Collections;
6 import java.util.HashSet;
7 import java.util.Locale;
8 import java.util.Set;
9
10 import org.cacert.gigi.GigiApiException;
11 import org.cacert.gigi.database.DatabaseConnection;
12 import org.cacert.gigi.database.GigiPreparedStatement;
13 import org.cacert.gigi.database.GigiResultSet;
14 import org.cacert.gigi.localisation.Language;
15 import org.cacert.gigi.util.Notary;
16 import org.cacert.gigi.util.PasswordHash;
17 import org.cacert.gigi.util.PasswordStrengthChecker;
18
19 public class User implements IdCachable {
20
21     private int id;
22
23     private Name name = new Name(null, null, null, null);
24
25     private Date dob;
26
27     private String email;
28
29     private Assurance[] receivedAssurances, madeAssurances;
30
31     private Locale locale;
32
33     private Set<Group> groups = new HashSet<>();
34
35     private User(int id) {
36         this.id = id;
37         updateName(id);
38     }
39
40     private void updateName(int id) {
41         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `fname`, `lname`,`mname`, `suffix`, `dob`, `email`, `language` FROM `users` WHERE id=?");
42         ps.setInt(1, id);
43         GigiResultSet rs = ps.executeQuery();
44         if (rs.next()) {
45             name = new Name(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4));
46             dob = rs.getDate(5);
47             email = rs.getString(6);
48             String localeStr = rs.getString(7);
49             if (localeStr == null || localeStr.equals("")) {
50                 locale = Locale.getDefault();
51             } else {
52                 locale = Language.getLocaleFromString(localeStr);
53             }
54         }
55         rs.close();
56         GigiPreparedStatement psg = DatabaseConnection.getInstance().prepare("SELECT permission FROM user_groups WHERE user=? AND deleted is NULL");
57         psg.setInt(1, id);
58         GigiResultSet rs2 = psg.executeQuery();
59         while (rs2.next()) {
60             groups.add(Group.getByString(rs2.getString(1)));
61         }
62         rs2.close();
63     }
64
65     public User() {}
66
67     public int getId() {
68         return id;
69     }
70
71     public String getFname() {
72         return name.fname;
73     }
74
75     public String getLname() {
76         return name.lname;
77     }
78
79     public String getMname() {
80         return name.mname;
81     }
82
83     public Name getName() {
84         return name;
85     }
86
87     public void setMname(String mname) {
88         this.name.mname = mname;
89     }
90
91     public String getSuffix() {
92         return name.suffix;
93     }
94
95     public void setSuffix(String suffix) {
96         this.name.suffix = suffix;
97     }
98
99     public Date getDob() {
100         return dob;
101     }
102
103     public void setDob(Date dob) {
104         this.dob = dob;
105     }
106
107     public String getEmail() {
108         return email;
109     }
110
111     public void setEmail(String email) {
112         this.email = email;
113     }
114
115     public void setId(int id) {
116         this.id = id;
117     }
118
119     public void setFname(String fname) {
120         this.name.fname = fname;
121     }
122
123     public void setLname(String lname) {
124         this.name.lname = lname;
125     }
126
127     public void insert(String password) {
128         if (id != 0) {
129             throw new Error("refusing to insert");
130         }
131         GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `created`=NOW(), locked=0, `language`=?");
132         query.setString(1, email);
133         query.setString(2, PasswordHash.hash(password));
134         query.setString(3, name.fname);
135         query.setString(4, name.mname);
136         query.setString(5, name.lname);
137         query.setString(6, name.suffix);
138         query.setDate(7, new java.sql.Date(dob.getTime()));
139         query.setString(8, locale.toString());
140         synchronized (User.class) {
141             query.execute();
142             id = query.lastInsertId();
143             myCache.put(this);
144         }
145     }
146
147     public void changePassword(String oldPass, String newPass) throws GigiApiException {
148         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `password` FROM users WHERE id=?");
149         ps.setInt(1, id);
150         GigiResultSet rs = ps.executeQuery();
151         if ( !rs.next()) {
152             throw new GigiApiException("User not found... very bad.");
153         }
154         if ( !PasswordHash.verifyHash(oldPass, rs.getString(1))) {
155             throw new GigiApiException("Old password does not match.");
156         }
157         rs.close();
158         PasswordStrengthChecker.assertStrongPassword(newPass, this);
159         ps = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=? WHERE id=?");
160         ps.setString(1, PasswordHash.hash(newPass));
161         ps.setInt(2, id);
162         if (ps.executeUpdate() != 1) {
163             throw new GigiApiException("Password update failed.");
164         }
165     }
166
167     public boolean canAssure() {
168         if ( !isOfAge(14)) { // PoJAM
169             return false;
170         }
171         if (getAssurancePoints() < 100) {
172             return false;
173         }
174
175         return hasPassedCATS();
176
177     }
178
179     public boolean hasPassedCATS() {
180         GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `cats_passed` where `user_id`=?");
181         query.setInt(1, id);
182         GigiResultSet rs = query.executeQuery();
183         if (rs.next()) {
184             return true;
185         } else {
186             return false;
187         }
188     }
189
190     public int getAssurancePoints() {
191         GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT sum(points) FROM `notary` where `to`=? AND `deleted`=0");
192         query.setInt(1, id);
193         GigiResultSet rs = query.executeQuery();
194         int points = 0;
195         if (rs.next()) {
196             points = rs.getInt(1);
197         }
198         rs.close();
199         return points;
200     }
201
202     public int getExperiencePoints() {
203         GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT count(*) FROM `notary` where `from`=? AND `deleted`=0");
204         query.setInt(1, id);
205         GigiResultSet rs = query.executeQuery();
206         int points = 0;
207         if (rs.next()) {
208             points = rs.getInt(1) * 2;
209         }
210         rs.close();
211         return points;
212     }
213
214     @Override
215     public boolean equals(Object obj) {
216         if ( !(obj instanceof User)) {
217             return false;
218         }
219         User s = (User) obj;
220         return name.equals(s.name) && email.equals(s.email) && dob.toString().equals(s.dob.toString()); // This
221                                                                                                         // is
222                                                                                                         // due
223                                                                                                         // to
224                                                                                                         // day
225                                                                                                         // cutoff
226     }
227
228     /**
229      * Gets the maximum allowed points NOW. Note that an assurance needs to
230      * re-check PoJam as it has taken place in the past.
231      * 
232      * @return the maximal points @
233      */
234     public int getMaxAssurePoints() {
235         if ( !isOfAge(18)) {
236             return 10; // PoJAM
237         }
238
239         int exp = getExperiencePoints();
240         int points = 10;
241
242         if (exp >= 10) {
243             points += 5;
244         }
245         if (exp >= 20) {
246             points += 5;
247         }
248         if (exp >= 30) {
249             points += 5;
250         }
251         if (exp >= 40) {
252             points += 5;
253         }
254         if (exp >= 50) {
255             points += 5;
256         }
257         return points;
258     }
259
260     public boolean isOfAge(int desiredAge) {
261         Calendar c = Calendar.getInstance();
262         c.setTime(dob);
263         int year = c.get(Calendar.YEAR);
264         int month = c.get(Calendar.MONTH);
265         int day = c.get(Calendar.DAY_OF_MONTH);
266         c.set(year, month, day);
267         c.add(Calendar.YEAR, desiredAge);
268         return System.currentTimeMillis() >= c.getTime().getTime();
269     }
270
271     public EmailAddress[] getEmails() {
272         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM emails WHERE memid=? AND deleted=0");
273         ps.setInt(1, id);
274         GigiResultSet rs = ps.executeQuery();
275         rs.last();
276         int count = rs.getRow();
277         EmailAddress[] data = new EmailAddress[count];
278         rs.beforeFirst();
279         for (int i = 0; i < data.length; i++) {
280             if ( !rs.next()) {
281                 throw new Error("Internal sql api violation.");
282             }
283             data[i] = EmailAddress.getById(rs.getInt(1));
284         }
285         rs.close();
286         return data;
287
288     }
289
290     public Domain[] getDomains() {
291         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM domains WHERE memid=? AND deleted IS NULL");
292         ps.setInt(1, id);
293         GigiResultSet rs = ps.executeQuery();
294         rs.last();
295         int count = rs.getRow();
296         Domain[] data = new Domain[count];
297         rs.beforeFirst();
298         for (int i = 0; i < data.length; i++) {
299             if ( !rs.next()) {
300                 throw new Error("Internal sql api violation.");
301             }
302             data[i] = Domain.getById(rs.getInt(1));
303         }
304         rs.close();
305         return data;
306
307     }
308
309     public Certificate[] getCertificates() {
310         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT serial FROM certs WHERE memid=? AND revoked=0");
311         ps.setInt(1, id);
312         GigiResultSet rs = ps.executeQuery();
313         rs.last();
314         int count = rs.getRow();
315         Certificate[] data = new Certificate[count];
316         rs.beforeFirst();
317         for (int i = 0; i < data.length; i++) {
318             if ( !rs.next()) {
319                 throw new Error("Internal sql api violation.");
320             }
321             data[i] = Certificate.getBySerial(rs.getString(1));
322         }
323         rs.close();
324         return data;
325
326     }
327
328     public boolean isValidDomain(String domainname) {
329         for (Domain d : getDomains()) {
330             String sfx = d.getSuffix();
331             if (domainname.equals(sfx) || domainname.endsWith("." + sfx)) {
332                 return true;
333             }
334         }
335         return false;
336     }
337
338     public boolean isValidEmail(String email) {
339         for (EmailAddress em : getEmails()) {
340             if (em.getAddress().equals(email)) {
341                 return true;
342             }
343         }
344         return false;
345     }
346
347     public boolean isValidName(String name) {
348         return getName().matches(name);
349     }
350
351     public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
352         EmailAddress[] adrs = getEmails();
353         for (int i = 0; i < adrs.length; i++) {
354             if (adrs[i].getAddress().equals(newMail.getAddress())) {
355                 if ( !adrs[i].isVerified()) {
356                     throw new GigiApiException("Email not verified.");
357                 }
358                 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE users SET email=? WHERE id=?");
359                 ps.setString(1, newMail.getAddress());
360                 ps.setInt(2, getId());
361                 ps.execute();
362                 email = newMail.getAddress();
363                 return;
364             }
365         }
366         throw new GigiApiException("Given address not an address of the user.");
367     }
368
369     public void deleteEmail(EmailAddress mail) throws GigiApiException {
370         if (getEmail().equals(mail.getAddress())) {
371             throw new GigiApiException("Can't delete user's default e-mail.");
372         }
373         EmailAddress[] emails = getEmails();
374         for (int i = 0; i < emails.length; i++) {
375             if (emails[i].getId() == mail.getId()) {
376                 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE emails SET deleted=? WHERE id=?");
377                 ps.setDate(1, new Date(System.currentTimeMillis()));
378                 ps.setInt(2, mail.getId());
379                 ps.execute();
380                 return;
381             }
382         }
383         throw new GigiApiException("Email not one of user's email addresses.");
384     }
385
386     public Assurance[] getReceivedAssurances() {
387         if (receivedAssurances == null) {
388             GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `to`=? AND deleted=0");
389             query.setInt(1, getId());
390             GigiResultSet res = query.executeQuery();
391             res.last();
392             Assurance[] assurances = new Assurance[res.getRow()];
393             res.beforeFirst();
394             for (int i = 0; i < assurances.length; i++) {
395                 res.next();
396                 assurances[i] = new Assurance(res);
397             }
398             this.receivedAssurances = assurances;
399             return assurances;
400         }
401         return receivedAssurances;
402     }
403
404     public Assurance[] getMadeAssurances() {
405         if (madeAssurances == null) {
406             GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted=0");
407             query.setInt(1, getId());
408             GigiResultSet res = query.executeQuery();
409             res.last();
410             Assurance[] assurances = new Assurance[res.getRow()];
411             res.beforeFirst();
412             for (int i = 0; i < assurances.length; i++) {
413                 res.next();
414                 assurances[i] = new Assurance(res);
415             }
416             this.madeAssurances = assurances;
417             return assurances;
418         }
419         return madeAssurances;
420     }
421
422     public void invalidateMadeAssurances() {
423         madeAssurances = null;
424     }
425
426     public void invalidateReceivedAssurances() {
427         receivedAssurances = null;
428     }
429
430     public void updateUserData() throws GigiApiException {
431         synchronized (Notary.class) {
432             if (getAssurancePoints() != 0) {
433                 throw new GigiApiException("No change after assurance allowed.");
434             }
435             GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET fname=?, lname=?, mname=?, suffix=?, dob=? WHERE id=?");
436             update.setString(1, getFname());
437             update.setString(2, getLname());
438             update.setString(3, getMname());
439             update.setString(4, getSuffix());
440             update.setDate(5, getDob());
441             update.setInt(6, getId());
442             update.executeUpdate();
443         }
444     }
445
446     public Locale getPreferredLocale() {
447         return locale;
448     }
449
450     public void setPreferredLocale(Locale locale) {
451         this.locale = locale;
452
453     }
454
455     public boolean wantsDirectoryListing() {
456         GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
457         get.setInt(1, getId());
458         GigiResultSet exec = get.executeQuery();
459         exec.next();
460         return exec.getBoolean("listme");
461     }
462
463     public String getContactInformation() {
464         GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
465         get.setInt(1, getId());
466         GigiResultSet exec = get.executeQuery();
467         exec.next();
468         return exec.getString("contactinfo");
469     }
470
471     public void setDirectoryListing(boolean on) {
472         GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
473         update.setBoolean(1, on);
474         update.setInt(2, getId());
475         update.executeUpdate();
476     }
477
478     public void setContactInformation(String contactInfo) {
479         GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
480         update.setString(1, contactInfo);
481         update.setInt(2, getId());
482         update.executeUpdate();
483     }
484
485     public boolean isInGroup(Group g) {
486         return groups.contains(g);
487     }
488
489     public Set<Group> getGroups() {
490         return Collections.unmodifiableSet(groups);
491     }
492
493     public void grantGroup(User granter, Group toGrant) throws GigiApiException {
494         groups.add(toGrant);
495         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO user_groups SET user=?, permission=?, grantedby=?");
496         ps.setInt(1, getId());
497         ps.setString(2, toGrant.getDatabaseName());
498         ps.setInt(3, granter.getId());
499         ps.execute();
500     }
501
502     public void revokeGroup(User revoker, Group toRevoke) throws GigiApiException {
503         groups.remove(toRevoke);
504         GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE user_groups SET deleted=CURRENT_TIMESTAMP, revokedby=? WHERE deleted is NULL AND permission=? AND user=?");
505         ps.setInt(1, revoker.getId());
506         ps.setString(2, toRevoke.getDatabaseName());
507         ps.setInt(3, getId());
508         ps.execute();
509     }
510
511     private static ObjectCache<User> myCache = new ObjectCache<>();
512
513     public static synchronized User getById(int id) {
514         User u = myCache.get(id);
515         if (u == null) {
516             myCache.put(u = new User(id));
517         }
518         return u;
519     }
520 }