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