X-Git-Url: https://code.wpia.club/?a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2FdbObjects%2FDomain.java;h=6093f6d85b74b82ce64083cbdadc2563f4ec3371;hb=8db0039e1d890573b6f5c8586d7ad3575bfdebf4;hp=13c8436cdd8fd63919d89a947b6b39cda65fb896;hpb=3e123160ad59a2e1162518923965562ff947b6d1;p=gigi.git diff --git a/src/org/cacert/gigi/dbObjects/Domain.java b/src/org/cacert/gigi/dbObjects/Domain.java index 13c8436c..6093f6d8 100644 --- a/src/org/cacert/gigi/dbObjects/Domain.java +++ b/src/org/cacert/gigi/dbObjects/Domain.java @@ -1,67 +1,44 @@ package org.cacert.gigi.dbObjects; +import java.io.IOException; +import java.io.InputStream; +import java.net.IDN; +import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Properties; +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.DomainPingConfiguration.PingType; +import org.cacert.gigi.util.PublicSuffixes; -public class Domain implements IdCachable { +public class Domain implements IdCachable, Verifyable { - public class DomainPingExecution { + private CertificateOwner owner; - private String state; - - private String type; - - private String info; - - private String result; - - private DomainPingConfiguration config; - - public DomainPingExecution(GigiResultSet rs) { - state = rs.getString(1); - type = rs.getString(2); - info = rs.getString(3); - result = rs.getString(4); - config = DomainPingConfiguration.getById(rs.getInt(5)); - } - - public String getState() { - return state; - } - - public String getType() { - return type; - } + private String suffix; - public String getInfo() { - return info; - } + private int id; - public String getResult() { - return result; - } + private static final Set IDNEnabledTLDs; - public DomainPingConfiguration getConfig() { - return config; + static { + Properties CPS = new Properties(); + try (InputStream resourceAsStream = Domain.class.getResourceAsStream("CPS.properties")) { + CPS.load(resourceAsStream); + IDNEnabledTLDs = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CPS.getProperty("IDN-enabled").split(",")))); + } catch (IOException e) { + throw new Error(e); } - } - private User owner; - - private String suffix; - - private int id; - private Domain(int id) { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT memid, domain FROM `domains` WHERE id=? AND deleted IS NULL"); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `memid`, `domain` FROM `domains` WHERE `id`=? AND `deleted` IS NULL"); ps.setInt(1, id); GigiResultSet rs = ps.executeQuery(); @@ -69,22 +46,80 @@ public class Domain implements IdCachable { throw new IllegalArgumentException("Invalid domain id " + id); } this.id = id; - owner = User.getById(rs.getInt(1)); + owner = CertificateOwner.getById(rs.getInt(1)); suffix = rs.getString(2); rs.close(); } - public Domain(User owner, String suffix) throws GigiApiException { - this.owner = owner; - this.suffix = suffix; + public Domain(User actor, CertificateOwner owner, String suffix) throws GigiApiException { + synchronized (Domain.class) { + checkCertifyableDomain(suffix, actor.isInGroup(Group.CODESIGNING)); + this.owner = owner; + this.suffix = suffix; + insert(); + } + } + public static void checkCertifyableDomain(String s, boolean hasPunycodeRight) throws GigiApiException { + String[] parts = s.split("\\.", -1); + if (parts.length < 2) { + throw new GigiApiException("Domain does not contain '.'."); + } + for (int i = parts.length - 1; i >= 0; i--) { + if ( !isVaildDomainPart(parts[i], hasPunycodeRight)) { + throw new GigiApiException("Syntax error in Domain"); + } + } + String publicSuffix = PublicSuffixes.getInstance().getRegistrablePart(s); + if ( !s.equals(publicSuffix)) { + throw new GigiApiException("You may only register a domain with exactly one lable before the public suffix."); + } + checkPunycode(parts[0], s.substring(parts[0].length() + 1)); + } + + private static void checkPunycode(String label, String domainContext) throws GigiApiException { + if (label.charAt(2) != '-' || label.charAt(3) != '-') { + return; // is no punycode + } + if ( !IDNEnabledTLDs.contains(domainContext)) { + throw new GigiApiException("Punycode label could not be positively verified."); + } + if ( !label.startsWith("xn--")) { + throw new GigiApiException("Unknown ACE prefix."); + } + try { + String unicode = IDN.toUnicode(label); + if (unicode.startsWith("xn--")) { + throw new GigiApiException("Punycode label could not be positively verified."); + } + } catch (IllegalArgumentException e) { + throw new GigiApiException("Punycode label could not be positively verified."); + } + } + + public static boolean isVaildDomainPart(String s, boolean allowPunycode) { + if ( !s.matches("[a-z0-9-]+")) { + return false; + } + if (s.charAt(0) == '-' || s.charAt(s.length() - 1) == '-') { + return false; + } + if (s.length() > 63) { + return false; + } + boolean canBePunycode = s.length() >= 4 && s.charAt(2) == '-' && s.charAt(3) == '-'; + if (canBePunycode && !allowPunycode) { + return false; + } + return true; } private static void checkInsert(String suffix) throws GigiApiException { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `domains` WHERE (domain=RIGHT(?,LENGTH(domain)) OR RIGHT(domain,LENGTH(?))=?) AND deleted IS NULL"); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `domains` WHERE (`domain`=? OR (CONCAT('.', `domain`)=RIGHT(?,LENGTH(`domain`)+1) OR RIGHT(`domain`,LENGTH(?)+1)=CONCAT('.',?))) AND `deleted` IS NULL"); ps.setString(1, suffix); ps.setString(2, suffix); ps.setString(3, suffix); + ps.setString(4, suffix); GigiResultSet rs = ps.executeQuery(); boolean existed = rs.next(); rs.close(); @@ -93,31 +128,29 @@ public class Domain implements IdCachable { } } - public void insert() throws GigiApiException { + private void insert() throws GigiApiException { if (id != 0) { throw new GigiApiException("already inserted."); } - synchronized (Domain.class) { - checkInsert(suffix); - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO `domains` SET memid=?, domain=?"); - ps.setInt(1, owner.getId()); - ps.setString(2, suffix); - ps.execute(); - id = ps.lastInsertId(); - myCache.put(this); - } + checkInsert(suffix); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO `domains` SET memid=?, domain=?"); + ps.setInt(1, owner.getId()); + ps.setString(2, suffix); + ps.execute(); + id = ps.lastInsertId(); + myCache.put(this); } public void delete() throws GigiApiException { if (id == 0) { throw new GigiApiException("not inserted."); } - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE `domains` SET deleted=CURRENT_TIMESTAMP WHERE id=?"); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE `domains` SET `deleted`=CURRENT_TIMESTAMP WHERE `id`=?"); ps.setInt(1, id); ps.execute(); } - public User getOwner() { + public CertificateOwner getOwner() { return owner; } @@ -149,8 +182,8 @@ public class Domain implements IdCachable { return Collections.unmodifiableList(configs); } - public void addPing(PingType type, String config) throws GigiApiException { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO pingconfig SET domainid=?, type=?, info=?"); + public void addPing(DomainPingType type, String config) throws GigiApiException { + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("INSERT INTO `pingconfig` SET `domainid`=?, `type`=?::`pingType`, `info`=?"); ps.setInt(1, id); ps.setString(2, type.toString().toLowerCase()); ps.setString(3, config); @@ -158,22 +191,22 @@ public class Domain implements IdCachable { configs = null; } - public void verify(String hash) throws GigiApiException { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE domainPinglog SET state='success' WHERE challenge=? AND configId IN (SELECT id FROM pingconfig WHERE domainId=?)"); + public synchronized void verify(String hash) throws GigiApiException { + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("UPDATE `domainPinglog` SET `state`='success' WHERE `challenge`=? AND `state`='open' AND `configId` IN (SELECT `id` FROM `pingconfig` WHERE `domainid`=? AND `type`='email')"); ps.setString(1, hash); ps.setInt(2, id); ps.executeUpdate(); } public boolean isVerified() { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT 1 FROM domainPinglog INNER JOIN pingconfig ON pingconfig.id=domainPinglog.configId WHERE domainid=? AND state='success'"); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT 1 FROM `domainPinglog` INNER JOIN `pingconfig` ON `pingconfig`.`id`=`domainPinglog`.`configId` WHERE `domainid`=? AND `state`='success'"); ps.setInt(1, id); GigiResultSet rs = ps.executeQuery(); return rs.next(); } public DomainPingExecution[] getPings() throws GigiApiException { - GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT state, type, info, result, configId FROM domainPinglog INNER JOIN pingconfig ON pingconfig.id=domainPinglog.configid WHERE pingconfig.domainid=? ORDER BY `when` DESC;"); + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepareScrollable("SELECT `state`, `type`, `info`, `result`, `configId`, `when` FROM `domainPinglog` INNER JOIN `pingconfig` ON `pingconfig`.`id`=`domainPinglog`.`configId` WHERE `pingconfig`.`domainid`=? ORDER BY `when` DESC;"); ps.setInt(1, id); GigiResultSet rs = ps.executeQuery(); rs.last(); @@ -186,7 +219,7 @@ public class Domain implements IdCachable { } - private static ObjectCache myCache = new ObjectCache<>(); + private static final ObjectCache myCache = new ObjectCache<>(); public static synchronized Domain getById(int id) throws IllegalArgumentException { Domain em = myCache.get(id); @@ -196,4 +229,15 @@ public class Domain implements IdCachable { return em; } + public static int searchUserIdByDomain(String domain) { + GigiPreparedStatement ps = DatabaseConnection.getInstance().prepare("SELECT `memid` FROM `domains` WHERE `domain` = ?"); + ps.setString(1, domain); + GigiResultSet res = ps.executeQuery(); + if (res.next()) { + return res.getInt(1); + } else { + return -1; + } + } + }