1 package org.cacert.gigi.dbObjects;
4 import java.util.ArrayList;
5 import java.util.Calendar;
6 import java.util.Collections;
7 import java.util.HashSet;
9 import java.util.Locale;
12 import org.cacert.gigi.GigiApiException;
13 import org.cacert.gigi.database.DatabaseConnection;
14 import org.cacert.gigi.database.GigiPreparedStatement;
15 import org.cacert.gigi.database.GigiResultSet;
16 import org.cacert.gigi.localisation.Language;
17 import org.cacert.gigi.util.Notary;
18 import org.cacert.gigi.util.PasswordHash;
19 import org.cacert.gigi.util.PasswordStrengthChecker;
21 public class User extends CertificateOwner {
23 private Name name = new Name(null, null, null, null);
29 private Assurance[] receivedAssurances, madeAssurances;
31 private Locale locale;
33 private Set<Group> groups = new HashSet<>();
35 protected User(GigiResultSet rs) {
36 super(rs.getInt("id"));
40 private void updateName(GigiResultSet rs) {
41 name = new Name(rs.getString("fname"), rs.getString("lname"), rs.getString("mname"), rs.getString("suffix"));
42 dob = rs.getDate("dob");
43 email = rs.getString("email");
44 String localeStr = rs.getString("language");
45 if (localeStr == null || localeStr.equals("")) {
46 locale = Locale.getDefault();
48 locale = Language.getLocaleFromString(localeStr);
50 GigiPreparedStatement psg = DatabaseConnection.getInstance().prepare("SELECT permission FROM user_groups WHERE user=? AND deleted is NULL");
51 psg.setInt(1, rs.getInt("id"));
52 GigiResultSet rs2 = psg.executeQuery();
54 groups.add(Group.getByString(rs2.getString(1)));
61 public String getFname() {
65 public String getLname() {
69 public String getMname() {
73 public Name getName() {
77 public void setMname(String mname) {
78 this.name.mname = mname;
81 public String getSuffix() {
85 public void setSuffix(String suffix) {
86 this.name.suffix = suffix;
89 public Date getDob() {
93 public void setDob(Date dob) {
97 public String getEmail() {
101 public void setEmail(String email) {
105 public void setFname(String fname) {
106 this.name.fname = fname;
109 public void setLname(String lname) {
110 this.name.lname = lname;
113 public void insert(String password) {
114 int id = super.insert();
115 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `language`=?, id=?");
116 query.setString(1, email);
117 query.setString(2, PasswordHash.hash(password));
118 query.setString(3, name.fname);
119 query.setString(4, name.mname);
120 query.setString(5, name.lname);
121 query.setString(6, name.suffix);
122 query.setDate(7, new java.sql.Date(dob.getTime()));
123 query.setString(8, locale.toString());
128 public void changePassword(String oldPass, String newPass) throws GigiApiException {
129 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `password` FROM users WHERE id=?");
130 ps.setInt(1, getId());
131 GigiResultSet rs = ps.executeQuery();
133 throw new GigiApiException("User not found... very bad.");
135 if ( !PasswordHash.verifyHash(oldPass, rs.getString(1))) {
136 throw new GigiApiException("Old password does not match.");
139 PasswordStrengthChecker.assertStrongPassword(newPass, this);
140 ps = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=? WHERE id=?");
141 ps.setString(1, PasswordHash.hash(newPass));
142 ps.setInt(2, getId());
143 if (ps.executeUpdate() != 1) {
144 throw new GigiApiException("Password update failed.");
148 public boolean canAssure() {
149 if ( !isOfAge(14)) { // PoJAM
152 if (getAssurancePoints() < 100) {
156 return hasPassedCATS();
160 public boolean hasPassedCATS() {
161 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `cats_passed` where `user_id`=?");
162 query.setInt(1, getId());
163 GigiResultSet rs = query.executeQuery();
171 public int getAssurancePoints() {
172 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT sum(points) FROM `notary` where `to`=? AND `deleted` is NULL");
173 query.setInt(1, getId());
174 GigiResultSet rs = query.executeQuery();
177 points = rs.getInt(1);
183 public int getExperiencePoints() {
184 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT count(*) FROM `notary` where `from`=? AND `deleted` is NULL");
185 query.setInt(1, getId());
186 GigiResultSet rs = query.executeQuery();
189 points = rs.getInt(1) * 2;
196 public boolean equals(Object obj) {
197 if ( !(obj instanceof User)) {
201 return name.equals(s.name) && email.equals(s.email) && dob.toString().equals(s.dob.toString()); // This
210 * Gets the maximum allowed points NOW. Note that an assurance needs to
211 * re-check PoJam as it has taken place in the past.
213 * @return the maximal points @
215 public int getMaxAssurePoints() {
220 int exp = getExperiencePoints();
241 public boolean isOfAge(int desiredAge) {
242 Calendar c = Calendar.getInstance();
244 int year = c.get(Calendar.YEAR);
245 int month = c.get(Calendar.MONTH);
246 int day = c.get(Calendar.DAY_OF_MONTH);
247 c.set(year, month, day);
248 c.add(Calendar.YEAR, desiredAge);
249 return System.currentTimeMillis() >= c.getTime().getTime();
252 public boolean isValidName(String name) {
253 return getName().matches(name);
256 public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
257 EmailAddress[] adrs = getEmails();
258 for (int i = 0; i < adrs.length; i++) {
259 if (adrs[i].getAddress().equals(newMail.getAddress())) {
260 if ( !adrs[i].isVerified()) {
261 throw new GigiApiException("Email not verified.");
263 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE users SET email=? WHERE id=?");
264 ps.setString(1, newMail.getAddress());
265 ps.setInt(2, getId());
267 email = newMail.getAddress();
271 throw new GigiApiException("Given address not an address of the user.");
274 public void deleteEmail(EmailAddress mail) throws GigiApiException {
275 if (getEmail().equals(mail.getAddress())) {
276 throw new GigiApiException("Can't delete user's default e-mail.");
278 EmailAddress[] emails = getEmails();
279 for (int i = 0; i < emails.length; i++) {
280 if (emails[i].getId() == mail.getId()) {
281 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE emails SET deleted=? WHERE id=?");
282 ps.setDate(1, new Date(System.currentTimeMillis()));
283 ps.setInt(2, mail.getId());
288 throw new GigiApiException("Email not one of user's email addresses.");
291 public Assurance[] getReceivedAssurances() {
292 if (receivedAssurances == null) {
293 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `to`=? AND deleted IS NULL");
294 query.setInt(1, getId());
295 GigiResultSet res = query.executeQuery();
297 Assurance[] assurances = new Assurance[res.getRow()];
299 for (int i = 0; i < assurances.length; i++) {
301 assurances[i] = new Assurance(res);
303 this.receivedAssurances = assurances;
306 return receivedAssurances;
309 public Assurance[] getMadeAssurances() {
310 if (madeAssurances == null) {
311 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted is NULL");
312 query.setInt(1, getId());
313 GigiResultSet res = query.executeQuery();
315 Assurance[] assurances = new Assurance[res.getRow()];
317 for (int i = 0; i < assurances.length; i++) {
319 assurances[i] = new Assurance(res);
321 this.madeAssurances = assurances;
324 return madeAssurances;
327 public void invalidateMadeAssurances() {
328 madeAssurances = null;
331 public void invalidateReceivedAssurances() {
332 receivedAssurances = null;
335 public void updateUserData() throws GigiApiException {
336 synchronized (Notary.class) {
337 if (getAssurancePoints() != 0) {
338 throw new GigiApiException("No change after assurance allowed.");
340 GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET fname=?, lname=?, mname=?, suffix=?, dob=? WHERE id=?");
341 update.setString(1, getFname());
342 update.setString(2, getLname());
343 update.setString(3, getMname());
344 update.setString(4, getSuffix());
345 update.setDate(5, getDob());
346 update.setInt(6, getId());
347 update.executeUpdate();
351 public Locale getPreferredLocale() {
355 public void setPreferredLocale(Locale locale) {
356 this.locale = locale;
360 public boolean wantsDirectoryListing() {
361 GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
362 get.setInt(1, getId());
363 GigiResultSet exec = get.executeQuery();
365 return exec.getBoolean("listme");
368 public String getContactInformation() {
369 GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
370 get.setInt(1, getId());
371 GigiResultSet exec = get.executeQuery();
373 return exec.getString("contactinfo");
376 public void setDirectoryListing(boolean on) {
377 GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
378 update.setBoolean(1, on);
379 update.setInt(2, getId());
380 update.executeUpdate();
383 public void setContactInformation(String contactInfo) {
384 GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
385 update.setString(1, contactInfo);
386 update.setInt(2, getId());
387 update.executeUpdate();
390 public boolean isInGroup(Group g) {
391 return groups.contains(g);
394 public Set<Group> getGroups() {
395 return Collections.unmodifiableSet(groups);
398 public void grantGroup(User granter, Group toGrant) {
400 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO user_groups SET user=?, permission=?, grantedby=?");
401 ps.setInt(1, getId());
402 ps.setString(2, toGrant.getDatabaseName());
403 ps.setInt(3, granter.getId());
407 public void revokeGroup(User revoker, Group toRevoke) {
408 groups.remove(toRevoke);
409 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE user_groups SET deleted=CURRENT_TIMESTAMP, revokedby=? WHERE deleted is NULL AND permission=? AND user=?");
410 ps.setInt(1, revoker.getId());
411 ps.setString(2, toRevoke.getDatabaseName());
412 ps.setInt(3, getId());
416 public List<Organisation> getOrganisations() {
417 List<Organisation> orgas = new ArrayList<>();
418 GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT orgid FROM org_admin WHERE `memid`=? AND deleted is NULL");
419 query.setInt(1, getId());
420 GigiResultSet res = query.executeQuery();
423 orgas.add(Organisation.getById(res.getInt(1)));
428 public static synchronized User getById(int id) {
429 CertificateOwner co = CertificateOwner.getById(id);
430 if (co instanceof User) {
436 public static User getByEmail(String mail) {
437 GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT users.id FROM users inner join certOwners on certOwners.id=users.id WHERE email=? AND deleted is null");
438 ps.setString(1, mail);
439 GigiResultSet rs = ps.executeQuery();
443 return User.getById(rs.getInt(1));
446 public boolean canIssue(CertificateProfile p) {
447 switch (p.getCAId()) {
451 return getAssurancePoints() > 50;
453 return getAssurancePoints() > 50 && isInGroup(Group.getByString("codesigning"));
456 return false; // has an orga