package org.cacert.gigi.dbObjects;
-import java.sql.Date;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Calendar;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
import org.cacert.gigi.GigiApiException;
-import org.cacert.gigi.database.DatabaseConnection;
+import org.cacert.gigi.database.GigiPreparedStatement;
+import org.cacert.gigi.database.GigiResultSet;
+import org.cacert.gigi.dbObjects.Assurance.AssuranceType;
+import org.cacert.gigi.dbObjects.CATS.CATSType;
+import org.cacert.gigi.dbObjects.Country.CountryCodeType;
import org.cacert.gigi.localisation.Language;
+import org.cacert.gigi.output.DateSelector;
+import org.cacert.gigi.pages.PasswordResetPage;
+import org.cacert.gigi.util.CalendarUtil;
+import org.cacert.gigi.util.DayDate;
import org.cacert.gigi.util.Notary;
import org.cacert.gigi.util.PasswordHash;
import org.cacert.gigi.util.PasswordStrengthChecker;
+import org.cacert.gigi.util.TimeConditions;
-public class User implements IdCachable {
+/**
+ * Represents an acting, assurable, user. Synchronizing on user means: no
+ * name-change and no assurance.
+ */
+public class User extends CertificateOwner {
- private int id;
+ private static final long serialVersionUID = -7915843843752264176L;
- private Name name = new Name(null, null, null, null);
-
- private Date dob;
+ private DayDate dob;
private String email;
- private Assurance[] receivedAssurances, madeAssurances;
+ private Assurance[] receivedAssurances;
+
+ private Assurance[] madeAssurances;
private Locale locale;
- private User(int id) {
- this.id = id;
- updateName(id);
- }
+ private Set<Group> groups = new HashSet<>();
- private void updateName(int id) {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `fname`, `lname`,`mname`, `suffix`, `dob`, `email`, `language` FROM `users` WHERE id=?");
- ps.setInt(1, id);
- ResultSet rs = ps.executeQuery();
- if (rs.next()) {
- name = new Name(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4));
- dob = rs.getDate(5);
- email = rs.getString(6);
- String localeStr = rs.getString(7);
- if (localeStr == null || localeStr.equals("")) {
- locale = Locale.getDefault();
- } else {
- locale = Language.getLocaleFromString(localeStr);
- }
- }
- rs.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
+ public static final int MINIMUM_AGE = 16;
- public User() {}
+ public static final int MAXIMUM_PLAUSIBLE_AGE = 120;
- public int getId() {
- return id;
- }
+ public static final int POJAM_AGE = 14;
- public String getFname() {
- return name.fname;
- }
+ public static final int ADULT_AGE = 18;
- public String getLname() {
- return name.lname;
- }
+ public static final boolean POJAM_ENABLED = false;
- public String getMname() {
- return name.mname;
- }
+ public static final int EXPERIENCE_POINTS = 4;
- public Name getName() {
- return name;
- }
+ /**
+ * Time in months a verification is considered "recent".
+ */
+ public static final int VERIFICATION_MONTHS = TimeConditions.getInstance().getVerificationMonths();
- public void setMname(String mname) {
- this.name.mname = mname;
- }
+ private Name preferredName;
- public String getSuffix() {
- return name.suffix;
- }
+ private Country residenceCountry;
+
+ protected User(GigiResultSet rs) throws GigiApiException {
+ super(rs.getInt("id"));
+
+ dob = new DayDate(rs.getDate("dob"));
+ email = rs.getString("email");
+ preferredName = Name.getById(rs.getInt("preferredName"));
+
+ if (rs.getString("country") != null) {
+ residenceCountry = Country.getCountryByCode(rs.getString("Country"), Country.CountryCodeType.CODE_2_CHARS);
+ }
+
+ String localeStr = rs.getString("language");
+ if (localeStr == null || localeStr.equals("")) {
+ locale = Locale.getDefault();
+ } else {
+ locale = Language.getLocaleFromString(localeStr);
+ }
- public void setSuffix(String suffix) {
- this.name.suffix = suffix;
+ refreshGroups();
}
- public Date getDob() {
- return dob;
+ public synchronized void refreshGroups() {
+ HashSet<Group> hs = new HashSet<>();
+ try (GigiPreparedStatement psg = new GigiPreparedStatement("SELECT `permission` FROM `user_groups` WHERE `user`=? AND `deleted` is NULL")) {
+ psg.setInt(1, getId());
+
+ try (GigiResultSet rs2 = psg.executeQuery()) {
+ while (rs2.next()) {
+ hs.add(Group.getByString(rs2.getString(1)));
+ }
+ }
+ }
+ groups = hs;
}
- public void setDob(Date dob) {
+ public User(String email, String password, DayDate dob, Locale locale, Country residenceCountry, NamePart... preferred) throws GigiApiException {
+ this.email = email;
this.dob = dob;
+ this.locale = locale;
+ this.preferredName = new Name(this, preferred);
+ try (GigiPreparedStatement query = new GigiPreparedStatement("INSERT INTO `users` SET `email`=?, `password`=?, `dob`=?, `language`=?, id=?, `preferredName`=?, `country` = ?")) {
+ query.setString(1, email);
+ query.setString(2, PasswordHash.hash(password));
+ query.setDate(3, dob.toSQLDate());
+ query.setString(4, locale.toString());
+ query.setInt(5, getId());
+ query.setInt(6, preferredName.getId());
+ query.setString(7, residenceCountry == null ? null : residenceCountry.getCode());
+ query.execute();
+ }
+ new EmailAddress(this, email, locale);
}
- public String getEmail() {
- return email;
+ public Name[] getNames() {
+ try (GigiPreparedStatement gps = new GigiPreparedStatement("SELECT `id` FROM `names` WHERE `uid`=? AND `deleted` IS NULL", true)) {
+ gps.setInt(1, getId());
+ return fetchNamesToArray(gps);
+ }
}
- public void setEmail(String email) {
- this.email = email;
+ public Name[] getNonDeprecatedNames() {
+ try (GigiPreparedStatement gps = new GigiPreparedStatement("SELECT `id` FROM `names` WHERE `uid`=? AND `deleted` IS NULL AND `deprecated` IS NULL", true)) {
+ gps.setInt(1, getId());
+ return fetchNamesToArray(gps);
+ }
}
- public void setId(int id) {
- this.id = id;
+ private Name[] fetchNamesToArray(GigiPreparedStatement gps) {
+ GigiResultSet rs = gps.executeQuery();
+ rs.last();
+ Name[] dt = new Name[rs.getRow()];
+ rs.beforeFirst();
+ for (int i = 0; rs.next(); i++) {
+ dt[i] = Name.getById(rs.getInt(1));
+ }
+ return dt;
}
- public void setFname(String fname) {
- this.name.fname = fname;
+ public DayDate getDoB() {
+ return dob;
}
- public void setLname(String lname) {
- this.name.lname = lname;
- }
+ public void setDoB(DayDate dob) throws GigiApiException {
+ synchronized (Notary.class) {
+ if (getReceivedAssurances().length != 0) {
+ throw new GigiApiException("No change after verification allowed.");
+ }
+
+ if ( !CalendarUtil.isOfAge(dob, User.MINIMUM_AGE)) {
+ throw new GigiApiException("Entered date of birth is below the restricted age requirements.");
+ }
- public void insert(String password) throws SQLException {
- if (id != 0) {
- throw new Error("refusing to insert");
+ if (CalendarUtil.isOfAge(dob, User.MAXIMUM_PLAUSIBLE_AGE)) {
+ throw new GigiApiException("Entered date of birth exceeds the maximum age set in our policies. Please check your DoB is correct and contact support if the issue persists.");
+ }
+ this.dob = dob;
+ rawUpdateUserData();
}
- PreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `created`=NOW(), locked=0, `language`=?");
- query.setString(1, email);
- query.setString(2, PasswordHash.hash(password));
- query.setString(3, name.fname);
- query.setString(4, name.mname);
- query.setString(5, name.lname);
- query.setString(6, name.suffix);
- query.setDate(7, new java.sql.Date(dob.getTime()));
- query.setString(8, locale.toString());
- synchronized (User.class) {
- query.execute();
- id = DatabaseConnection.lastInsertId(query);
- myCache.put(this);
+
+ }
+
+ protected void setDoBAsSupport(DayDate dob) throws GigiApiException {
+ synchronized (Notary.class) {
+ this.dob = dob;
+ rawUpdateUserData();
}
+
+ }
+
+ public String getEmail() {
+ return email;
}
public void changePassword(String oldPass, String newPass) throws GigiApiException {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `password` FROM users WHERE id=?");
- ps.setInt(1, id);
- ResultSet rs = ps.executeQuery();
- if ( !rs.next()) {
- throw new GigiApiException("User not found... very bad.");
- }
- if ( !PasswordHash.verifyHash(oldPass, rs.getString(1))) {
- throw new GigiApiException("Old password does not match.");
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `password` FROM `users` WHERE `id`=?")) {
+ ps.setInt(1, getId());
+ try (GigiResultSet rs = ps.executeQuery()) {
+ if ( !rs.next()) {
+ throw new GigiApiException("User not found... very bad.");
+ }
+ if (PasswordHash.verifyHash(oldPass, rs.getString(1)) == null) {
+ throw new GigiApiException("Old password does not match.");
+ }
}
- rs.close();
- PasswordStrengthChecker.assertStrongPassword(newPass, this);
- ps = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=? WHERE id=?");
+ }
+ setPassword(newPass);
+ }
+
+ private void setPassword(String newPass) throws GigiApiException {
+ PasswordStrengthChecker.assertStrongPassword(newPass, getNames(), getEmail());
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE users SET `password`=? WHERE id=?")) {
ps.setString(1, PasswordHash.hash(newPass));
- ps.setInt(2, id);
- if (ps.executeUpdate() != 1) {
- throw new GigiApiException("Password update failed.");
- }
- } catch (SQLException e) {
- throw new GigiApiException(e);
+ ps.setInt(2, getId());
+ ps.executeUpdate();
}
}
- public boolean canAssure() throws SQLException {
- if ( !isOfAge(14)) { // PoJAM
- return false;
+ public boolean canAssure() {
+ if (POJAM_ENABLED) {
+ if ( !CalendarUtil.isOfAge(dob, POJAM_AGE)) { // PoJAM
+ return false;
+ }
+ } else {
+ if ( !CalendarUtil.isOfAge(dob, ADULT_AGE)) {
+ return false;
+ }
}
if (getAssurancePoints() < 100) {
return false;
}
- public boolean hasPassedCATS() throws SQLException {
- PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `cats_passed` where `user_id`=?");
- query.setInt(1, id);
- ResultSet rs = query.executeQuery();
- if (rs.next()) {
- return true;
- } else {
- return false;
+ public boolean hasPassedCATS() {
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT 1 FROM `cats_passed` where `user_id`=? AND `variant_id`=?")) {
+ query.setInt(1, getId());
+ query.setInt(2, CATSType.ASSURER_CHALLENGE.getId());
+ try (GigiResultSet rs = query.executeQuery()) {
+ if (rs.next()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
}
}
- public int getAssurancePoints() throws SQLException {
- PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT sum(points) FROM `notary` where `to`=? AND `deleted`=0");
- query.setInt(1, id);
- ResultSet rs = query.executeQuery();
- int points = 0;
- if (rs.next()) {
- points = rs.getInt(1);
- }
- rs.close();
- return points;
- }
+ public int getAssurancePoints() {
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT SUM(lastpoints) FROM ( SELECT DISTINCT ON (`from`, `method`) `from`, `points` as lastpoints FROM `notary` INNER JOIN `names` ON `names`.`id`=`to` WHERE `notary`.`deleted` is NULL AND (`expire` IS NULL OR `expire` > CURRENT_TIMESTAMP) AND `names`.`uid` = ? ORDER BY `from`, `method`, `when` DESC) as p")) {
+ query.setInt(1, getId());
+
+ GigiResultSet rs = query.executeQuery();
+ int points = 0;
+
+ if (rs.next()) {
+ points = rs.getInt(1);
+ }
- public int getExperiencePoints() throws SQLException {
- PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT count(*) FROM `notary` where `from`=? AND `deleted`=0");
- query.setInt(1, id);
- ResultSet rs = query.executeQuery();
- int points = 0;
- if (rs.next()) {
- points = rs.getInt(1) * 2;
+ return points;
}
- rs.close();
- return points;
}
- @Override
- public boolean equals(Object obj) {
- if ( !(obj instanceof User)) {
- return false;
+ public int getExperiencePoints() {
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT count(*) FROM ( SELECT `names`.`uid` FROM `notary` INNER JOIN `names` ON `names`.`id` = `to` WHERE `from`=? AND `notary`.`deleted` IS NULL AND `method` = ? ::`notaryType` GROUP BY `names`.`uid`) as p")) {
+ query.setInt(1, getId());
+ query.setEnum(2, AssuranceType.FACE_TO_FACE);
+
+ GigiResultSet rs = query.executeQuery();
+ int points = 0;
+
+ if (rs.next()) {
+ points = rs.getInt(1) * EXPERIENCE_POINTS;
+ }
+
+ return points;
}
- User s = (User) obj;
- return name.equals(s.name) && email.equals(s.email) && dob.toString().equals(s.dob.toString()); // This
- // is
- // due
- // to
- // day
- // cutoff
}
/**
* Gets the maximum allowed points NOW. Note that an assurance needs to
* re-check PoJam as it has taken place in the past.
*
- * @return the maximal points
- * @throws SQLException
+ * @return the maximal points @
*/
- public int getMaxAssurePoints() throws SQLException {
- if ( !isOfAge(18)) {
+ @SuppressWarnings("unused")
+ public int getMaxAssurePoints() {
+ if ( !CalendarUtil.isOfAge(dob, ADULT_AGE) && POJAM_ENABLED) {
return 10; // PoJAM
}
int exp = getExperiencePoints();
int points = 10;
- if (exp >= 10) {
+ if (exp >= 5 * EXPERIENCE_POINTS) {
points += 5;
}
- if (exp >= 20) {
+ if (exp >= 10 * EXPERIENCE_POINTS) {
points += 5;
}
- if (exp >= 30) {
+ if (exp >= 15 * EXPERIENCE_POINTS) {
points += 5;
}
- if (exp >= 40) {
+ if (exp >= 20 * EXPERIENCE_POINTS) {
points += 5;
}
- if (exp >= 50) {
+ if (exp >= 25 * EXPERIENCE_POINTS) {
points += 5;
}
- return points;
- }
-
- public boolean isOfAge(int desiredAge) {
- Calendar c = Calendar.getInstance();
- c.setTime(dob);
- int year = c.get(Calendar.YEAR);
- int month = c.get(Calendar.MONTH);
- int day = c.get(Calendar.DAY_OF_MONTH);
- c.set(year, month, day);
- c.add(Calendar.YEAR, desiredAge);
- return System.currentTimeMillis() >= c.getTime().getTime();
- }
-
- public EmailAddress[] getEmails() {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM emails WHERE memid=? AND deleted=0");
- ps.setInt(1, id);
- ResultSet rs = ps.executeQuery();
- rs.last();
- int count = rs.getRow();
- EmailAddress[] data = new EmailAddress[count];
- rs.beforeFirst();
- for (int i = 0; i < data.length; i++) {
- if ( !rs.next()) {
- throw new Error("Internal sql api violation.");
- }
- data[i] = EmailAddress.getById(rs.getInt(1));
- }
- rs.close();
- return data;
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- public Domain[] getDomains() {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT id FROM domains WHERE memid=? AND deleted IS NULL");
- ps.setInt(1, id);
- ResultSet rs = ps.executeQuery();
- rs.last();
- int count = rs.getRow();
- Domain[] data = new Domain[count];
- rs.beforeFirst();
- for (int i = 0; i < data.length; i++) {
- if ( !rs.next()) {
- throw new Error("Internal sql api violation.");
- }
- data[i] = Domain.getById(rs.getInt(1));
- }
- rs.close();
- return data;
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- return null;
- }
- public Certificate[] getCertificates() {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT serial FROM certs WHERE memid=? AND revoked=0");
- ps.setInt(1, id);
- ResultSet rs = ps.executeQuery();
- rs.last();
- int count = rs.getRow();
- Certificate[] data = new Certificate[count];
- rs.beforeFirst();
- for (int i = 0; i < data.length; i++) {
- if ( !rs.next()) {
- throw new Error("Internal sql api violation.");
- }
- data[i] = Certificate.getBySerial(rs.getString(1));
- }
- rs.close();
- return data;
- } catch (SQLException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- public boolean isValidDomain(String domainname) {
- for (Domain d : getDomains()) {
- String sfx = d.getSuffix();
- if (domainname.equals(sfx) || domainname.endsWith("." + sfx)) {
- return true;
- }
- }
- return false;
+ return points;
}
- public boolean isValidEmail(String email) {
- for (EmailAddress em : getEmails()) {
- if (em.getAddress().equals(email)) {
+ public boolean isValidName(String name) {
+ for (Name n : getNames()) {
+ if (n.matches(name) && n.getAssurancePoints() >= 50) {
return true;
}
}
return false;
}
- public boolean isValidName(String name) {
- return getName().matches(name);
- }
-
public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
- try {
- EmailAddress[] adrs = getEmails();
- for (int i = 0; i < adrs.length; i++) {
- if (adrs[i].getAddress().equals(newMail.getAddress())) {
- if ( !adrs[i].isVerified()) {
- throw new GigiApiException("Email not verified.");
- }
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE users SET email=? WHERE id=?");
+ for (EmailAddress email : getEmails()) {
+ if (email.getAddress().equals(newMail.getAddress())) {
+ if ( !email.isVerified()) {
+ throw new GigiApiException("Email not verified.");
+ }
+
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE users SET email=? WHERE id=?")) {
ps.setString(1, newMail.getAddress());
ps.setInt(2, getId());
ps.execute();
- email = newMail.getAddress();
- return;
}
+
+ this.email = newMail.getAddress();
+ return;
}
- throw new GigiApiException("Given address not an address of the user.");
- } catch (SQLException e) {
- throw new GigiApiException(e);
}
+
+ throw new GigiApiException("Given address not an address of the user.");
}
- public void deleteEmail(EmailAddress mail) throws GigiApiException {
- if (getEmail().equals(mail.getAddress())) {
+ public void deleteEmail(EmailAddress delMail) throws GigiApiException {
+ if (getEmail().equals(delMail.getAddress())) {
throw new GigiApiException("Can't delete user's default e-mail.");
}
- EmailAddress[] emails = getEmails();
- for (int i = 0; i < emails.length; i++) {
- if (emails[i].getId() == mail.getId()) {
- try {
- PreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE emails SET deleted=? WHERE id=?");
- ps.setDate(1, new Date(System.currentTimeMillis()));
- ps.setInt(2, mail.getId());
+
+ for (EmailAddress email : getEmails()) {
+ if (email.getId() == delMail.getId()) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `emails` SET `deleted`=CURRENT_TIMESTAMP WHERE `id`=?")) {
+ ps.setInt(1, delMail.getId());
ps.execute();
- } catch (SQLException e) {
- e.printStackTrace();
- throw new GigiApiException(e);
}
return;
}
throw new GigiApiException("Email not one of user's email addresses.");
}
- public Assurance[] getReceivedAssurances() throws SQLException {
+ public synchronized Assurance[] getReceivedAssurances() {
if (receivedAssurances == null) {
- PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `to`=? AND deleted=0");
- query.setInt(1, getId());
- ResultSet res = query.executeQuery();
- res.last();
- Assurance[] assurances = new Assurance[res.getRow()];
- res.beforeFirst();
- for (int i = 0; i < assurances.length; i++) {
- res.next();
- assurances[i] = new Assurance(res);
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT * FROM `notary` INNER JOIN `names` ON `names`.`id` = `notary`.`to` WHERE `names`.`uid`=? AND `notary`.`deleted` IS NULL ORDER BY `when` DESC")) {
+ query.setInt(1, getId());
+
+ GigiResultSet res = query.executeQuery();
+ List<Assurance> assurances = new LinkedList<Assurance>();
+
+ while (res.next()) {
+ assurances.add(assuranceByRes(res));
+ }
+
+ this.receivedAssurances = assurances.toArray(new Assurance[0]);
}
- this.receivedAssurances = assurances;
- return assurances;
}
+
return receivedAssurances;
}
- public Assurance[] getMadeAssurances() throws SQLException {
+ public synchronized Assurance[] getMadeAssurances() {
if (madeAssurances == null) {
- PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted=0");
- query.setInt(1, getId());
- ResultSet res = query.executeQuery();
- res.last();
- Assurance[] assurances = new Assurance[res.getRow()];
- res.beforeFirst();
- for (int i = 0; i < assurances.length; i++) {
- res.next();
- assurances[i] = new Assurance(res);
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT * FROM notary WHERE `from`=? AND deleted is NULL ORDER BY `when` DESC")) {
+ query.setInt(1, getId());
+
+ try (GigiResultSet res = query.executeQuery()) {
+ List<Assurance> assurances = new LinkedList<Assurance>();
+
+ while (res.next()) {
+ assurances.add(assuranceByRes(res));
+ }
+
+ this.madeAssurances = assurances.toArray(new Assurance[0]);
+ }
}
- this.madeAssurances = assurances;
- return assurances;
}
+
return madeAssurances;
}
- public void invalidateMadeAssurances() {
+ public synchronized void invalidateMadeAssurances() {
madeAssurances = null;
}
- public void invalidateReceivedAssurances() {
+ public synchronized void invalidateReceivedAssurances() {
receivedAssurances = null;
}
- public void updateUserData() throws SQLException, GigiApiException {
- synchronized (Notary.class) {
- if (getAssurancePoints() != 0) {
- throw new GigiApiException("No change after assurance allowed.");
- }
- PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET fname=?, lname=?, mname=?, suffix=?, dob=? WHERE id=?");
- update.setString(1, getFname());
- update.setString(2, getLname());
- update.setString(3, getMname());
- update.setString(4, getSuffix());
- update.setDate(5, getDob());
- update.setInt(6, getId());
+ private void rawUpdateUserData() {
+ try (GigiPreparedStatement update = new GigiPreparedStatement("UPDATE users SET dob=? WHERE id=?")) {
+ update.setDate(1, getDoB().toSQLDate());
+ update.setInt(2, getId());
update.executeUpdate();
}
}
}
- public boolean wantsDirectoryListing() throws SQLException {
- PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
- get.setInt(1, getId());
- ResultSet exec = get.executeQuery();
- exec.next();
- return exec.getBoolean("listme");
+ public synchronized Name getPreferredName() {
+ return preferredName;
+ }
+
+ public synchronized void setPreferredName(Name preferred) throws GigiApiException {
+ if (preferred.getOwner() != this) {
+ throw new GigiApiException("Cannot set a name as preferred one that does not belong to this account.");
+ }
+ this.preferredName = preferred;
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `users` SET `preferredName`=? WHERE `id`=?")) {
+ ps.setInt(1, preferred.getId());
+ ps.setInt(2, getId());
+ ps.executeUpdate();
+ }
+
+ }
+
+ public boolean isInGroup(Group g) {
+ return groups.contains(g);
+ }
+
+ public Set<Group> getGroups() {
+ return Collections.unmodifiableSet(groups);
+ }
+
+ public void grantGroup(User granter, Group toGrant) throws GigiApiException {
+ if (toGrant.isManagedBySupport() && !granter.isInGroup(Group.SUPPORTER)) {
+ throw new GigiApiException("Group may only be managed by supporter");
+ }
+ if (toGrant.isManagedBySupport() && granter == this) {
+ throw new GigiApiException("Group may only be managed by supporter that is not oneself");
+ }
+ groups.add(toGrant);
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `user_groups` SET `user`=?, `permission`=?::`userGroup`, `grantedby`=?")) {
+ ps.setInt(1, getId());
+ ps.setEnum(2, toGrant);
+ ps.setInt(3, granter.getId());
+ ps.execute();
+ }
}
- public String getContactInformation() throws SQLException {
- PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
- get.setInt(1, getId());
- ResultSet exec = get.executeQuery();
- exec.next();
- return exec.getString("contactinfo");
+ public void revokeGroup(User revoker, Group toRevoke) throws GigiApiException {
+ if (toRevoke.isManagedBySupport() && !revoker.isInGroup(Group.SUPPORTER)) {
+ throw new GigiApiException("Group may only be managed by supporter");
+ }
+ groups.remove(toRevoke);
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `user_groups` SET `deleted`=CURRENT_TIMESTAMP, `revokedby`=? WHERE `deleted` IS NULL AND `permission`=?::`userGroup` AND `user`=?")) {
+ ps.setInt(1, revoker.getId());
+ ps.setEnum(2, toRevoke);
+ ps.setInt(3, getId());
+ ps.execute();
+ }
}
- public void setDirectoryListing(boolean on) throws SQLException {
- PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
- update.setBoolean(1, on);
- update.setInt(2, getId());
- update.executeUpdate();
+ public List<Organisation> getOrganisations() {
+ return getOrganisations(false);
}
- public void setContactInformation(String contactInfo) throws SQLException {
- PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
- update.setString(1, contactInfo);
- update.setInt(2, getId());
- update.executeUpdate();
+ public List<Organisation> getOrganisations(boolean isAdmin) {
+ List<Organisation> orgas = new ArrayList<>();
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT `orgid` FROM `org_admin` WHERE `memid`=? AND `deleted` IS NULL" + (isAdmin ? " AND master='y'" : ""))) {
+ query.setInt(1, getId());
+ try (GigiResultSet res = query.executeQuery()) {
+ while (res.next()) {
+ orgas.add(Organisation.getById(res.getInt(1)));
+ }
+
+ return orgas;
+ }
+ }
}
- private static ObjectCache<User> myCache = new ObjectCache<>();
+ public static synchronized User getById(int id) {
+ CertificateOwner co = CertificateOwner.getById(id);
+ if (co instanceof User) {
+ return (User) co;
+ }
+
+ return null;
+ }
- public static User getById(int id) {
- User u = myCache.get(id);
- if (u == null) {
- synchronized (User.class) {
- myCache.put(u = new User(id));
+ public static User getByEmail(String mail) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `users`.`id` FROM `users` INNER JOIN `certOwners` ON `certOwners`.`id` = `users`.`id` WHERE `email`=? AND `deleted` IS NULL")) {
+ ps.setString(1, mail);
+ GigiResultSet rs = ps.executeQuery();
+ if ( !rs.next()) {
+ return null;
}
+
+ return User.getById(rs.getInt(1));
+ }
+ }
+
+ public static User[] findByEmail(String mail) {
+ LinkedList<User> results = new LinkedList<User>();
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `users`.`id` FROM `users` INNER JOIN `certOwners` ON `certOwners`.`id` = `users`.`id` WHERE `users`.`email` LIKE ? AND `deleted` IS NULL GROUP BY `users`.`id` LIMIT 100")) {
+ ps.setString(1, mail);
+ GigiResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ results.add(User.getById(rs.getInt(1)));
+ }
+ return results.toArray(new User[results.size()]);
+ }
+ }
+
+ public EmailAddress[] getEmails() {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `id` FROM `emails` WHERE `memid`=? AND `deleted` IS NULL")) {
+ ps.setInt(1, getId());
+
+ GigiResultSet rs = ps.executeQuery();
+ LinkedList<EmailAddress> data = new LinkedList<EmailAddress>();
+
+ while (rs.next()) {
+ data.add(EmailAddress.getById(rs.getInt(1)));
+ }
+
+ return data.toArray(new EmailAddress[0]);
+ }
+ }
+
+ @Override
+ public boolean isValidEmail(String email) {
+ for (EmailAddress em : getEmails()) {
+ if (em.getAddress().equals(email)) {
+ return em.isVerified();
+ }
+ }
+
+ return false;
+ }
+
+ public String[] getTrainings() {
+ try (GigiPreparedStatement prep = new GigiPreparedStatement("SELECT `pass_date`, `type_text`, `language`, `version` FROM `cats_passed` LEFT JOIN `cats_type` ON `cats_type`.`id`=`cats_passed`.`variant_id` WHERE `user_id`=? ORDER BY `pass_date` ASC")) {
+ prep.setInt(1, getId());
+ GigiResultSet res = prep.executeQuery();
+ List<String> entries = new LinkedList<String>();
+
+ while (res.next()) {
+ StringBuilder training = new StringBuilder();
+ training.append(DateSelector.getDateFormat().format(res.getTimestamp(1)));
+ training.append(" (");
+ training.append(res.getString(2));
+ if (res.getString(3).length() > 0) {
+ training.append(" ");
+ training.append(res.getString(3));
+ }
+ if (res.getString(4).length() > 0) {
+ training.append(", ");
+ training.append(res.getString(4));
+ }
+ training.append(")");
+ entries.add(training.toString());
+ }
+
+ return entries.toArray(new String[0]);
+ }
+
+ }
+
+ public int generatePasswordResetTicket(User actor, String token, String privateToken) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `passwordResetTickets` SET `memid`=?, `creator`=?, `token`=?, `private_token`=?")) {
+ ps.setInt(1, getId());
+ ps.setInt(2, getId());
+ ps.setString(3, token);
+ ps.setString(4, PasswordHash.hash(privateToken));
+ ps.execute();
+ return ps.lastInsertId();
+ }
+ }
+
+ public static User getResetWithToken(int id, String token) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `memid` FROM `passwordResetTickets` WHERE `id`=? AND `token`=? AND `used` IS NULL AND `created` > CURRENT_TIMESTAMP - interval '1 hours' * ?")) {
+ ps.setInt(1, id);
+ ps.setString(2, token);
+ ps.setInt(3, PasswordResetPage.HOUR_MAX);
+ GigiResultSet res = ps.executeQuery();
+ if ( !res.next()) {
+ return null;
+ }
+ return User.getById(res.getInt(1));
+ }
+ }
+
+ public synchronized void consumePasswordResetTicket(int id, String private_token, String newPassword) throws GigiApiException {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `private_token` FROM `passwordResetTickets` WHERE `id`=? AND `memid`=? AND `used` IS NULL")) {
+ ps.setInt(1, id);
+ ps.setInt(2, getId());
+ GigiResultSet rs = ps.executeQuery();
+ if ( !rs.next()) {
+ throw new GigiApiException("Token could not be found, has already been used, or is expired.");
+ }
+ if (PasswordHash.verifyHash(private_token, rs.getString(1)) == null) {
+ throw new GigiApiException("Private token does not match.");
+ }
+ setPassword(newPassword);
+ }
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `passwordResetTickets` SET `used` = CURRENT_TIMESTAMP WHERE `id`=?")) {
+ ps.setInt(1, id);
+ ps.executeUpdate();
+ }
+ }
+
+ private Assurance assuranceByRes(GigiResultSet res) {
+ try {
+ return new Assurance(res.getInt("id"), User.getById(res.getInt("from")), Name.getById(res.getInt("to")), res.getString("location"), res.getString("method"), res.getInt("points"), res.getString("date"), res.getString("country") == null ? null : Country.getCountryByCode(res.getString("country"), CountryCodeType.CODE_2_CHARS));
+ } catch (GigiApiException e) {
+ throw new Error(e);
+ }
+ }
+
+ public boolean isInVerificationLimit() {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT 1 FROM `notary` INNER JOIN `names` ON `names`.`id`=`to` WHERE `names`.`uid` = ? AND `when` > (now() - (interval '1 month' * ?)) AND (`expire` IS NULL OR `expire` > now()) AND `notary`.`deleted` IS NULL;")) {
+ ps.setInt(1, getId());
+ ps.setInt(2, VERIFICATION_MONTHS);
+
+ GigiResultSet rs = ps.executeQuery();
+ return rs.next();
+ }
+ }
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {}
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {}
+
+ public Country getResidenceCountry() {
+ return residenceCountry;
+ }
+
+ public void setResidenceCountry(Country residenceCountry) {
+ this.residenceCountry = residenceCountry;
+ rawUpdateCountryData();
+ }
+
+ private void rawUpdateCountryData() {
+ try (GigiPreparedStatement update = new GigiPreparedStatement("UPDATE users SET country=? WHERE id=?")) {
+ update.setString(1, residenceCountry == null ? null : residenceCountry.getCode());
+ update.setInt(2, getId());
+ update.executeUpdate();
}
- return u;
}
}