]> WPIA git - gigi.git/blobdiff - src/org/cacert/gigi/dbObjects/User.java
UPD: small coverity changes
[gigi.git] / src / org / cacert / gigi / dbObjects / User.java
index 7a11cdb40b9a294a568c8c934f6d277f2d02f93b..e1425fecf05fb9e1b8d0c0094b165dc117c153a5 100644 (file)
@@ -1,22 +1,25 @@
 package org.cacert.gigi.dbObjects;
 
 import java.sql.Date;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.Calendar;
+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.localisation.Language;
 import org.cacert.gigi.util.Notary;
 import org.cacert.gigi.util.PasswordHash;
 import org.cacert.gigi.util.PasswordStrengthChecker;
 
-public class User implements IdCachable {
-
-    private int id;
+public class User extends CertificateOwner {
 
     private Name name = new Name(null, null, null, null);
 
@@ -24,52 +27,52 @@ public class User implements IdCachable {
 
     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<>();
+
+    protected User(GigiResultSet rs) {
+        super(rs.getInt("id"));
+        updateName(rs);
     }
 
-    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);
-                }
+    private void updateName(GigiResultSet rs) {
+        name = new Name(rs.getString("fname"), rs.getString("lname"), rs.getString("mname"), rs.getString("suffix"));
+        dob = rs.getDate("dob");
+        email = rs.getString("email");
+
+        String localeStr = rs.getString("language");
+        if (localeStr == null || localeStr.equals("")) {
+            locale = Locale.getDefault();
+        } else {
+            locale = Language.getLocaleFromString(localeStr);
+        }
+
+        GigiPreparedStatement psg = DatabaseConnection.getInstance().prepare("SELECT permission FROM user_groups WHERE user=? AND deleted is NULL");
+        psg.setInt(1, rs.getInt("id"));
+
+        try (GigiResultSet rs2 = psg.executeQuery()) {
+            while (rs2.next()) {
+                groups.add(Group.getByString(rs2.getString(1)));
             }
-            rs.close();
-        } catch (SQLException e) {
-            e.printStackTrace();
         }
     }
 
     public User() {}
 
-    public int getId() {
-        return id;
-    }
-
-    public String getFname() {
+    public String getFName() {
         return name.fname;
     }
 
-    public String getLname() {
+    public String getLName() {
         return name.lname;
     }
 
-    public String getMname() {
+    public String getMName() {
         return name.mname;
     }
 
@@ -77,7 +80,7 @@ public class User implements IdCachable {
         return name;
     }
 
-    public void setMname(String mname) {
+    public void setMName(String mname) {
         this.name.mname = mname;
     }
 
@@ -89,11 +92,11 @@ public class User implements IdCachable {
         this.name.suffix = suffix;
     }
 
-    public Date getDob() {
+    public Date getDoB() {
         return dob;
     }
 
-    public void setDob(Date dob) {
+    public void setDoB(Date dob) {
         this.dob = dob;
     }
 
@@ -105,23 +108,17 @@ public class User implements IdCachable {
         this.email = email;
     }
 
-    public void setId(int id) {
-        this.id = id;
-    }
-
-    public void setFname(String fname) {
+    public void setFName(String fname) {
         this.name.fname = fname;
     }
 
-    public void setLname(String lname) {
+    public void setLName(String lname) {
         this.name.lname = lname;
     }
 
-    public void insert(String password) throws SQLException {
-        if (id != 0) {
-            throw new Error("refusing to insert");
-        }
-        PreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `created`=NOW(), locked=0, `language`=?");
+    public void insert(String password) {
+        int id = super.insert();
+        GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("insert into `users` set `email`=?, `password`=?, " + "`fname`=?, `mname`=?, `lname`=?, " + "`suffix`=?, `dob`=?, `language`=?, id=?");
         query.setString(1, email);
         query.setString(2, PasswordHash.hash(password));
         query.setString(3, name.fname);
@@ -130,38 +127,34 @@ public class User implements IdCachable {
         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);
-        }
+        query.setInt(9, id);
+        query.execute();
     }
 
     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();
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("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))) {
+            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=?");
-            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);
         }
+
+        PasswordStrengthChecker.assertStrongPassword(newPass, this);
+        ps = DatabaseConnection.getInstance().prepare("UPDATE users SET `password`=? WHERE id=?");
+        ps.setString(1, PasswordHash.hash(newPass));
+        ps.setInt(2, getId());
+        ps.executeUpdate();
+    }
+
+    public void setName(Name name) {
+        this.name = name;
     }
 
-    public boolean canAssure() throws SQLException {
+    public boolean canAssure() {
         if ( !isOfAge(14)) { // PoJAM
             return false;
         }
@@ -173,63 +166,55 @@ public class User implements IdCachable {
 
     }
 
-    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() {
+        GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `cats_passed` where `user_id`=?");
+        query.setInt(1, 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() {
+        GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT sum(points) FROM `notary` where `to`=? AND `deleted` is NULL");
+        query.setInt(1, getId());
+
+        try (GigiResultSet rs = query.executeQuery()) {
+            int points = 0;
 
-    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;
+            if (rs.next()) {
+                points = rs.getInt(1);
+            }
+
+            return points;
         }
-        rs.close();
-        return points;
     }
 
-    @Override
-    public boolean equals(Object obj) {
-        if ( !(obj instanceof User)) {
-            return false;
+    public int getExperiencePoints() {
+        GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT count(*) FROM `notary` where `from`=? AND `deleted` is NULL");
+        query.setInt(1, getId());
+
+        try (GigiResultSet rs = query.executeQuery()) {
+            int points = 0;
+
+            if (rs.next()) {
+                points = rs.getInt(1) * 2;
+            }
+
+            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 {
+    public int getMaxAssurePoints() {
         if ( !isOfAge(18)) {
             return 10; // PoJAM
         }
@@ -252,6 +237,7 @@ public class User implements IdCachable {
         if (exp >= 50) {
             points += 5;
         }
+
         return points;
     }
 
@@ -266,178 +252,82 @@ public class User implements IdCachable {
         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;
-    }
-
-    public boolean isValidEmail(String email) {
-        for (EmailAddress em : getEmails()) {
-            if (em.getAddress().equals(email)) {
-                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=?");
-                    ps.setString(1, newMail.getAddress());
-                    ps.setInt(2, getId());
-                    ps.execute();
-                    email = newMail.getAddress();
-                    return;
+        for (EmailAddress email : getEmails()) {
+            if (email.getAddress().equals(newMail.getAddress())) {
+                if ( !email.isVerified()) {
+                    throw new GigiApiException("Email not verified.");
                 }
+
+                GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE users SET email=? WHERE id=?");
+                ps.setString(1, newMail.getAddress());
+                ps.setInt(2, getId());
+                ps.execute();
+
+                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());
-                    ps.execute();
-                } catch (SQLException e) {
-                    e.printStackTrace();
-                    throw new GigiApiException(e);
-                }
+
+        for (EmailAddress email : getEmails()) {
+            if (email.getId() == delMail.getId()) {
+                GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE emails SET deleted=? WHERE id=?");
+                ps.setDate(1, new Date(System.currentTimeMillis()));
+                ps.setInt(2, delMail.getId());
+                ps.execute();
                 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");
+            GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `to`=? AND deleted IS NULL");
             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 (GigiResultSet res = query.executeQuery()) {
+                List<Assurance> assurances = new LinkedList<Assurance>();
+
+                while (res.next()) {
+                    assurances.add(new Assurance(res));
+                }
+
+                this.receivedAssurances = assurances.toArray(new Assurance[0]);
             }
-            this.receivedAssurances = assurances;
-            return assurances;
         }
+
         return receivedAssurances;
     }
 
-    public Assurance[] getMadeAssurances() throws SQLException {
+    public Assurance[] getMadeAssurances() {
         if (madeAssurances == null) {
-            PreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted=0");
+            GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT * FROM notary WHERE `from`=? AND deleted is NULL");
             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 (GigiResultSet res = query.executeQuery()) {
+                List<Assurance> assurances = new LinkedList<Assurance>();
+
+                while (res.next()) {
+                    assurances.add(new Assurance(res));
+                }
+
+                this.madeAssurances = assurances.toArray(new Assurance[0]);
             }
-            this.madeAssurances = assurances;
-            return assurances;
         }
+
         return madeAssurances;
     }
 
@@ -449,17 +339,19 @@ public class User implements IdCachable {
         receivedAssurances = null;
     }
 
-    public void updateUserData() throws SQLException, GigiApiException {
+    public void updateUserData() throws GigiApiException {
         synchronized (Notary.class) {
+            // FIXME: No assurance, not no points.
             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());
+
+            GigiPreparedStatement 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.setDate(5, getDoB());
             update.setInt(6, getId());
             update.executeUpdate();
         }
@@ -474,45 +366,125 @@ public class User implements IdCachable {
 
     }
 
-    public boolean wantsDirectoryListing() throws SQLException {
-        PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
+    public boolean wantsDirectoryListing() {
+        GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT listme FROM users WHERE id=?");
         get.setInt(1, getId());
-        ResultSet exec = get.executeQuery();
-        exec.next();
-        return exec.getBoolean("listme");
+        try (GigiResultSet exec = get.executeQuery()) {
+            return exec.next() && exec.getBoolean("listme");
+        }
     }
 
-    public String getContactInformation() throws SQLException {
-        PreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
+    public String getContactInformation() {
+        GigiPreparedStatement get = DatabaseConnection.getInstance().prepare("SELECT contactinfo FROM users WHERE id=?");
         get.setInt(1, getId());
-        ResultSet exec = get.executeQuery();
-        exec.next();
-        return exec.getString("contactinfo");
+
+        try (GigiResultSet exec = get.executeQuery()) {
+            exec.next();
+            return exec.getString("contactinfo");
+        }
     }
 
-    public void setDirectoryListing(boolean on) throws SQLException {
-        PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
+    public void setDirectoryListing(boolean on) {
+        GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET listme = ? WHERE id = ?");
         update.setBoolean(1, on);
         update.setInt(2, getId());
         update.executeUpdate();
     }
 
-    public void setContactInformation(String contactInfo) throws SQLException {
-        PreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
+    public void setContactInformation(String contactInfo) {
+        GigiPreparedStatement update = DatabaseConnection.getInstance().prepare("UPDATE users SET contactinfo = ? WHERE id = ?");
         update.setString(1, contactInfo);
         update.setInt(2, getId());
         update.executeUpdate();
     }
 
-    private static ObjectCache<User> myCache = new ObjectCache<>();
+    public boolean isInGroup(Group g) {
+        return groups.contains(g);
+    }
+
+    public Set<Group> getGroups() {
+        return Collections.unmodifiableSet(groups);
+    }
+
+    public void grantGroup(User granter, Group toGrant) {
+        groups.add(toGrant);
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO user_groups SET user=?, permission=?, grantedby=?");
+        ps.setInt(1, getId());
+        ps.setString(2, toGrant.getDatabaseName());
+        ps.setInt(3, granter.getId());
+        ps.execute();
+    }
+
+    public void revokeGroup(User revoker, Group toRevoke) {
+        groups.remove(toRevoke);
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE user_groups SET deleted=CURRENT_TIMESTAMP, revokedby=? WHERE deleted is NULL AND permission=? AND user=?");
+        ps.setInt(1, revoker.getId());
+        ps.setString(2, toRevoke.getDatabaseName());
+        ps.setInt(3, getId());
+        ps.execute();
+    }
+
+    public List<Organisation> getOrganisations() {
+        List<Organisation> orgas = new ArrayList<>();
+        GigiPreparedStatement query = DatabaseConnection.getInstance().prepare("SELECT orgid FROM org_admin WHERE `memid`=? AND deleted is NULL");
+        query.setInt(1, getId());
+        try (GigiResultSet res = query.executeQuery()) {
+            while (res.next()) {
+                orgas.add(Organisation.getById(res.getInt(1)));
+            }
+
+            return orgas;
+        }
+    }
+
+    public static synchronized User getById(int id) {
+        CertificateOwner co = CertificateOwner.getById(id);
+        if (co instanceof User) {
+            return (User) co;
+        }
+
+        return null;
+    }
+
+    public static User getByEmail(String mail) {
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT users.id FROM users INNER JOIN certOwners ON certOwners.id = users.id WHERE email=? AND deleted IS NULL");
+        ps.setString(1, mail);
+        try (GigiResultSet rs = ps.executeQuery()) {
+            if ( !rs.next()) {
+                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));
+            return User.getById(rs.getInt(1));
+        }
+    }
+
+    public static User[] findByEmail(String mail) {
+        LinkedList<User> results = new LinkedList<User>();
+        GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("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 ASC LIMIT 100");
+        ps.setString(1, mail);
+        try (GigiResultSet rs = ps.executeQuery()) {
+            while (rs.next()) {
+                results.add(User.getById(rs.getInt(1)));
             }
+            return results.toArray(new User[results.size()]);
         }
-        return u;
     }
+
+    public boolean canIssue(CertificateProfile p) {
+        // FIXME: Use descriptive constants
+        switch (p.getCAId()) {
+        case 0:
+            return true;
+        case 1:
+            return getAssurancePoints() > 50;
+        case 2:
+            return getAssurancePoints() > 50 && isInGroup(Group.getByString("codesigning"));
+        case 3:
+        case 4:
+            return getOrganisations().size() > 0;
+        default:
+            return false;
+        }
+    }
+
 }