1 package org.cacert.gigi.util;
3 import java.io.IOException;
4 import java.text.ParseException;
5 import java.util.Calendar;
7 import java.util.GregorianCalendar;
8 import java.util.HashMap;
11 import org.cacert.gigi.GigiApiException;
12 import org.cacert.gigi.database.GigiPreparedStatement;
13 import org.cacert.gigi.database.GigiResultSet;
14 import org.cacert.gigi.dbObjects.Assurance.AssuranceType;
15 import org.cacert.gigi.dbObjects.Country;
16 import org.cacert.gigi.dbObjects.Group;
17 import org.cacert.gigi.dbObjects.Name;
18 import org.cacert.gigi.dbObjects.User;
19 import org.cacert.gigi.localisation.Language;
20 import org.cacert.gigi.output.ArrayIterable;
21 import org.cacert.gigi.output.DateSelector;
22 import org.cacert.gigi.output.template.MailTemplate;
23 import org.cacert.gigi.output.template.SprintfCommand;
27 // minimum date range between 2 verifications of the RA-Agent to the same
29 public final static int LIMIT_DAYS_VERIFICATION = TimeConditions.getInstance().getVerificationLimitDays();
31 // maximum date range from date when the verification took place and the
32 // entering to the system
33 public final static int LIMIT_MAX_MONTHS_VERIFICATION = TimeConditions.getInstance().getVerificationMaxAgeMonths();
35 public static void writeUserAgreement(User member, String document, String method, String comment, boolean active, int secmemid) {
36 try (GigiPreparedStatement q = new GigiPreparedStatement("INSERT INTO `user_agreements` SET `memid`=?, `secmemid`=?," + " `document`=?,`date`=NOW(), `active`=?,`method`=?,`comment`=?")) {
37 q.setInt(1, member.getId());
38 q.setInt(2, secmemid);
39 q.setString(3, document);
40 q.setBoolean(4, active);
41 q.setString(5, method);
42 q.setString(6, comment);
47 public static boolean checkAssuranceIsPossible(User assurer, Name target) {
48 try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT 1 FROM `notary` where `to`=? and `from`=? and `method` = ? ::`notaryType` AND `deleted` IS NULL AND `when` > (now() - interval '1 days' * ?)")) {
49 ps.setInt(1, target.getId());
50 ps.setInt(2, assurer.getId());
51 ps.setEnum(3, AssuranceType.FACE_TO_FACE);
52 ps.setInt(4, LIMIT_DAYS_VERIFICATION);
53 GigiResultSet rs = ps.executeQuery();
58 public static final Group ASSURER_BLOCKED = Group.BLOCKEDASSURER;
60 public static final Group ASSUREE_BLOCKED = Group.BLOCKEDASSUREE;
62 public static final Group VERIFY_NOTIFICATION = Group.VERIFY_NOTIFICATION;
65 * This method assures another user.
67 * @see User#canAssure() (for assurer)
68 * @see #checkAssuranceIsPossible(User, User) (for assurer or assuree)
70 * the person that wants to assure
72 * the person that should be assured
74 * the Name that was personally verified
76 * the Date of birth that the assurer verified
78 * the points that should be awarded in total
80 * the location where the assurance took place
82 * the date when the assurance took place
83 * @throws GigiApiException
84 * if the assurance fails (for various reasons)
86 public synchronized static void assure(User assurer, User assuree, Name assureeName, DayDate dob, int awarded, String location, String date, AssuranceType type, Country country) throws GigiApiException {
87 may(assurer, assuree, AssuranceType.FACE_TO_FACE);
88 GigiApiException gae = new GigiApiException();
89 if ( !gae.isEmpty()) {
92 if (date == null || date.equals("")) {
93 gae.mergeInto(new GigiApiException("You must enter the date when you met the applicant."));
96 Date d = DateSelector.getDateFormat().parse(date);
97 Calendar gc = GregorianCalendar.getInstance();
98 gc.setTimeInMillis(System.currentTimeMillis());
99 gc.add(Calendar.HOUR_OF_DAY, 12);
100 if (d.getTime() > gc.getTimeInMillis()) {
101 gae.mergeInto(new GigiApiException("You must not enter a date in the future."));
103 gc.setTimeInMillis(System.currentTimeMillis());
104 gc.add(Calendar.MONTH, -LIMIT_MAX_MONTHS_VERIFICATION);
105 if (d.getTime() < gc.getTimeInMillis()) {
106 gae.mergeInto(new GigiApiException(SprintfCommand.createSimple("Verifications older than {0} months are not accepted.", LIMIT_MAX_MONTHS_VERIFICATION)));
108 } catch (ParseException e) {
109 gae.mergeInto(new GigiApiException("You must enter the date in this format: YYYY-MM-DD."));
112 // check location, min 3 characters
113 if (location == null || location.equals("")) {
114 gae.mergeInto(new GigiApiException("You failed to enter a location of your meeting."));
115 } else if (location.length() <= 2) {
116 gae.mergeInto(new GigiApiException("You must enter a location with at least 3 characters eg town and country."));
119 if (country == null) {
120 gae.mergeInto(new GigiApiException("You failed to enter the country of your meeting."));
123 synchronized (assuree) {
124 if (assurer.getId() == assuree.getId()) {
125 throw new GigiApiException("You cannot verify yourself.");
127 if (assureeName.getOwner() != assuree) {
128 throw new GigiApiException("Internal error, name does not belong to applicant.");
130 if ( !assurer.canAssure()) {
131 throw new GigiApiException("You are not an RA-Agent.");
134 if ( !checkAssuranceIsPossible(assurer, assureeName)) {
135 gae.mergeInto(new GigiApiException(SprintfCommand.createSimple("You have already verified this applicant within the last {0} days.", LIMIT_DAYS_VERIFICATION)));
138 if ( !assuree.getDoB().equals(dob)) {
139 gae.mergeInto(new GigiApiException("The person you are verifying changed his personal details."));
143 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
145 if (type == AssuranceType.NUCLEUS) {
147 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
150 if (awarded > assurer.getMaxAssurePoints()) {
151 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
156 if ( !gae.isEmpty()) {
160 if (type == AssuranceType.FACE_TO_FACE) {
161 assureF2F(assurer, assuree, assureeName, awarded, location, date, country);
162 } else if (type == AssuranceType.NUCLEUS) {
163 assureNucleus(assurer, assuree, assureeName, awarded, location, date, country);
164 } else if (type == AssuranceType.TTP_ASSISTED) {
165 assureTTP(assurer, assuree, assureeName, awarded, location, date, country);
167 throw new GigiApiException(SprintfCommand.createSimple("Unknown Verification type: {0}", type.toString()));
169 assurer.invalidateMadeAssurances();
170 assuree.invalidateReceivedAssurances();
174 private static void assureF2F(User assurer, User assuree, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
175 may(assurer, assuree, AssuranceType.FACE_TO_FACE);
176 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?")) {
177 ps.setInt(1, assurer.getId());
178 ps.setInt(2, name.getId());
179 ps.setInt(3, awarded);
180 ps.setString(4, location);
181 ps.setString(5, date);
182 ps.setString(6, country.getCode());
187 private static void assureTTP(User assurer, User assuree, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
188 may(assurer, assuree, AssuranceType.TTP_ASSISTED);
189 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?, `method`='TTP-Assisted'")) {
190 ps.setInt(1, assurer.getId());
191 ps.setInt(2, name.getId());
192 ps.setInt(3, awarded);
193 ps.setString(4, location);
194 ps.setString(5, date);
195 ps.setString(6, country.getCode());
197 assuree.revokeGroup(assurer, Group.TTP_APPLICANT);
201 public static void may(User assurer, User assuree, AssuranceType t) throws GigiApiException {
202 if (assuree.isInGroup(ASSUREE_BLOCKED)) {
203 throw new GigiApiException("The applicant is blocked.");
205 if (assurer.isInGroup(ASSURER_BLOCKED)) {
206 throw new GigiApiException("The RA Agent is blocked.");
209 if (t == AssuranceType.NUCLEUS) {
210 if ( !assurer.isInGroup(Group.NUCLEUS_ASSURER)) {
211 throw new GigiApiException("RA Agent needs to be Nucleus RA Agent.");
214 } else if (t == AssuranceType.TTP_ASSISTED) {
215 if ( !assurer.isInGroup(Group.TTP_ASSURER)) {
216 throw new GigiApiException("RA Agent needs to be TTP RA Agent.");
218 if ( !assuree.isInGroup(Group.TTP_APPLICANT)) {
219 throw new GigiApiException("Applicant needs to be TTP Applicant.");
222 } else if (t == AssuranceType.FACE_TO_FACE) {
225 throw new GigiApiException("Verification type not possible.");
228 private static void assureNucleus(User assurer, User assuree, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
229 may(assurer, assuree, AssuranceType.NUCLEUS);
230 // Do up to 35 points as f2f
231 int f2fPoints = Math.min(assurer.getMaxAssurePoints(), awarded);
232 assureF2F(assurer, assuree, name, f2fPoints, location, date, country);
234 awarded -= f2fPoints;
239 // Assure remaining points as "Nucleus Bonus"
240 // Valid for 4 Weeks = 28 days
241 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?, `method`='Nucleus Bonus', `expire` = CURRENT_TIMESTAMP + interval '28 days'")) {
242 ps.setInt(1, assurer.getId());
243 ps.setInt(2, name.getId());
244 ps.setInt(3, awarded);
245 ps.setString(4, location);
246 ps.setString(5, date);
247 ps.setString(6, country.getCode());
252 public synchronized static void assureAll(User assurer, User assuree, DayDate dob, int awarded, String location, String date, AssuranceType type, Name[] toAssure, Country country) throws GigiApiException {
253 if (toAssure.length == 0) {
254 throw new GigiApiException("You must confirm at least one name to verify an account.");
256 boolean[] hadLessThan50Points = new boolean[toAssure.length];
257 boolean hadTotalLessThan100 = assuree.getAssurancePoints() < 100;
258 for (int i = 0; i < toAssure.length; i++) {
259 hadLessThan50Points[i] = toAssure[i].getAssurancePoints() < 50;
261 assure(assurer, assuree, toAssure[i], dob, awarded, location, date, type, country);
263 sendVerificationNotificationApplicant(assurer, assuree, toAssure, awarded, hadLessThan50Points, hadTotalLessThan100);
264 if (assurer.isInGroup(VERIFY_NOTIFICATION)) {
265 sendVerificationNotificationAgent(assurer, assuree, toAssure, awarded, location, date, country);
269 private static final MailTemplate verificationEntered = new MailTemplate(Notary.class.getResource("VerificationEntered.templ"));
271 private static final MailTemplate verificationAgentEntered = new MailTemplate(Notary.class.getResource("VerificationAgentEntered.templ"));
273 private static void sendVerificationNotificationApplicant(User assurer, User assuree, Name[] toAssure, final int awarded, final boolean[] hadLessThan50Points, boolean hadTotalLessThan100) {
274 HashMap<String, Object> mailVars = new HashMap<>();
275 mailVars.put("agent", assurer.getPreferredName().toString());
276 mailVars.put("names", new ArrayIterable<Name>(toAssure) {
279 public void apply(Name t, Language l, Map<String, Object> vars) {
280 int totalVP = t.getAssurancePoints();
281 vars.put("name", t.toString());
282 vars.put("points", Integer.toString(awarded));
283 vars.put("total", totalVP);
285 vars.put("rem", (50 - totalVP));
286 vars.remove("gotGreater");
287 } else if (hadLessThan50Points[i]) {
288 vars.put("gotGreater", true);
295 int grandTotalVP = assuree.getAssurancePoints();
296 if (grandTotalVP >= 50 && grandTotalVP < 100) {
297 mailVars.put("remAll", (100 - grandTotalVP));
298 mailVars.remove("gotGreaterAll");
299 } else if (hadTotalLessThan100) {
300 mailVars.put("gotGreaterAll", true);
301 mailVars.remove("remAll");
304 verificationEntered.sendMail(Language.getInstance(assuree.getPreferredLocale()), mailVars, assuree.getEmail());
305 } catch (IOException e) {
310 private static void sendVerificationNotificationAgent(User agent, User applicant, Name[] toVerify, final int awarded, String location, String date, Country country) {
311 HashMap<String, Object> mailVars = new HashMap<>();
312 mailVars.put("email", applicant.getEmail());
313 mailVars.put("location", location);
314 mailVars.put("date", date);
315 mailVars.put("country", country.getName());
316 mailVars.put("points", Integer.toString(awarded));
317 mailVars.put("names", new ArrayIterable<Name>(toVerify) {
320 public void apply(Name t, Language l, Map<String, Object> vars) {
321 vars.put("name", t.toString());
327 verificationAgentEntered.sendMail(Language.getInstance(applicant.getPreferredLocale()), mailVars, agent.getEmail());
328 } catch (IOException e) {