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.CountryCode;
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.setString(3, AssuranceType.FACE_TO_FACE.getDescription());
52 ps.setInt(4, LIMIT_DAYS_VERIFICATION);
53 GigiResultSet rs = ps.executeQuery();
58 public static final Group ASSURER_BLOCKED = Group.getByString("blockedassurer");
60 public static final Group ASSUREE_BLOCKED = Group.getByString("blockedassuree");
63 * This method assures another user.
65 * @see User#canAssure() (for assurer)
66 * @see #checkAssuranceIsPossible(User, User) (for assurer or assuree)
68 * the person that wants to assure
70 * the person that should be assured
72 * the Name that was personally verified
74 * the Date of birth that the assurer verified
76 * the points that should be awarded in total
78 * the location where the assurance took place
80 * the date when the assurance took place
81 * @throws GigiApiException
82 * if the assurance fails (for various reasons)
84 public synchronized static void assure(User assurer, User assuree, Name assureeName, DayDate dob, int awarded, String location, String date, AssuranceType type, CountryCode country) throws GigiApiException {
85 may(assurer, assuree, AssuranceType.FACE_TO_FACE);
86 GigiApiException gae = new GigiApiException();
87 if ( !gae.isEmpty()) {
90 if (date == null || date.equals("")) {
91 gae.mergeInto(new GigiApiException("You must enter the date when you met the applicant."));
94 Date d = DateSelector.getDateFormat().parse(date);
95 Calendar gc = GregorianCalendar.getInstance();
96 gc.setTimeInMillis(System.currentTimeMillis());
97 gc.add(Calendar.HOUR_OF_DAY, 12);
98 if (d.getTime() > gc.getTimeInMillis()) {
99 gae.mergeInto(new GigiApiException("You must not enter a date in the future."));
101 gc.setTimeInMillis(System.currentTimeMillis());
102 gc.add(Calendar.MONTH, -LIMIT_MAX_MONTHS_VERIFICATION);
103 if (d.getTime() < gc.getTimeInMillis()) {
104 gae.mergeInto(new GigiApiException(SprintfCommand.createSimple("Verifications older than {0} months are not accepted.", LIMIT_MAX_MONTHS_VERIFICATION)));
106 } catch (ParseException e) {
107 gae.mergeInto(new GigiApiException("You must enter the date in this format: YYYY-MM-DD."));
110 // check location, min 3 characters
111 if (location == null || location.equals("")) {
112 gae.mergeInto(new GigiApiException("You failed to enter a location of your meeting."));
113 } else if (location.length() <= 2) {
114 gae.mergeInto(new GigiApiException("You must enter a location with at least 3 characters eg town and country."));
117 if (country == null) {
118 gae.mergeInto(new GigiApiException("You failed to enter the country of your meeting."));
121 synchronized (assuree) {
122 if (assurer.getId() == assuree.getId()) {
123 throw new GigiApiException("You cannot verify yourself.");
125 if (assureeName.getOwner() != assuree) {
126 throw new GigiApiException("Internal error, name does not belong to applicant.");
128 if ( !assurer.canAssure()) {
129 throw new GigiApiException("You are not an RA-Agent.");
132 if ( !checkAssuranceIsPossible(assurer, assureeName)) {
133 gae.mergeInto(new GigiApiException(SprintfCommand.createSimple("You have already verified this applicant within the last {0} days.", LIMIT_DAYS_VERIFICATION)));
136 if ( !assuree.getDoB().equals(dob)) {
137 gae.mergeInto(new GigiApiException("The person you are verifying changed his personal details."));
141 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
143 if (type == AssuranceType.NUCLEUS) {
145 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
148 if (awarded > assurer.getMaxAssurePoints()) {
149 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
154 if ( !gae.isEmpty()) {
158 if (type == AssuranceType.FACE_TO_FACE) {
159 assureF2F(assurer, assuree, assureeName, awarded, location, date, country);
160 } else if (type == AssuranceType.NUCLEUS) {
161 assureNucleus(assurer, assuree, assureeName, awarded, location, date, country);
162 } else if (type == AssuranceType.TTP_ASSISTED) {
163 assureTTP(assurer, assuree, assureeName, awarded, location, date, country);
165 throw new GigiApiException(SprintfCommand.createSimple("Unknown Verification type: {0}", type.toString()));
167 assurer.invalidateMadeAssurances();
168 assuree.invalidateReceivedAssurances();
172 private static void assureF2F(User assurer, User assuree, Name name, int awarded, String location, String date, CountryCode country) throws GigiApiException {
173 may(assurer, assuree, AssuranceType.FACE_TO_FACE);
174 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?")) {
175 ps.setInt(1, assurer.getId());
176 ps.setInt(2, name.getId());
177 ps.setInt(3, awarded);
178 ps.setString(4, location);
179 ps.setString(5, date);
180 ps.setString(6, country.getCountryCode());
185 private static void assureTTP(User assurer, User assuree, Name name, int awarded, String location, String date, CountryCode country) throws GigiApiException {
186 may(assurer, assuree, AssuranceType.TTP_ASSISTED);
187 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?, `method`='TTP-Assisted'")) {
188 ps.setInt(1, assurer.getId());
189 ps.setInt(2, name.getId());
190 ps.setInt(3, awarded);
191 ps.setString(4, location);
192 ps.setString(5, date);
193 ps.setString(6, country.getCountryCode());
195 assuree.revokeGroup(assurer, Group.TTP_APPLICANT);
199 public static void may(User assurer, User assuree, AssuranceType t) throws GigiApiException {
200 if (assuree.isInGroup(ASSUREE_BLOCKED)) {
201 throw new GigiApiException("The applicant is blocked.");
203 if (assurer.isInGroup(ASSURER_BLOCKED)) {
204 throw new GigiApiException("The RA Agent is blocked.");
207 if (t == AssuranceType.NUCLEUS) {
208 if ( !assurer.isInGroup(Group.NUCLEUS_ASSURER)) {
209 throw new GigiApiException("RA Agent needs to be Nucleus RA Agent.");
212 } else if (t == AssuranceType.TTP_ASSISTED) {
213 if ( !assurer.isInGroup(Group.TTP_ASSURER)) {
214 throw new GigiApiException("RA Agent needs to be TTP RA Agent.");
216 if ( !assuree.isInGroup(Group.TTP_APPLICANT)) {
217 throw new GigiApiException("Applicant needs to be TTP Applicant.");
220 } else if (t == AssuranceType.FACE_TO_FACE) {
223 throw new GigiApiException("Verification type not possible.");
226 private static void assureNucleus(User assurer, User assuree, Name name, int awarded, String location, String date, CountryCode country) throws GigiApiException {
227 may(assurer, assuree, AssuranceType.NUCLEUS);
228 // Do up to 35 points as f2f
229 int f2fPoints = Math.min(assurer.getMaxAssurePoints(), awarded);
230 assureF2F(assurer, assuree, name, f2fPoints, location, date, country);
232 awarded -= f2fPoints;
237 // Assure remaining points as "Nucleus Bonus"
238 // Valid for 4 Weeks = 28 days
239 try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?, `method`='Nucleus Bonus', `expire` = CURRENT_TIMESTAMP + interval '28 days'")) {
240 ps.setInt(1, assurer.getId());
241 ps.setInt(2, name.getId());
242 ps.setInt(3, awarded);
243 ps.setString(4, location);
244 ps.setString(5, date);
245 ps.setString(6, country.getCountryCode());
250 public synchronized static void assureAll(User assurer, User assuree, DayDate dob, int awarded, String location, String date, AssuranceType type, Name[] toAssure, CountryCode country) throws GigiApiException {
251 if (toAssure.length == 0) {
252 throw new GigiApiException("You must confirm at least one name to verify an account.");
254 boolean[] hadLessThan50Points = new boolean[toAssure.length];
255 boolean hadTotalLessThan100 = assuree.getAssurancePoints() < 100;
256 for (int i = 0; i < toAssure.length; i++) {
257 hadLessThan50Points[i] = toAssure[i].getAssurancePoints() < 50;
259 assure(assurer, assuree, toAssure[i], dob, awarded, location, date, type, country);
261 sendVerificationNotificationApplicant(assurer, assuree, toAssure, awarded, hadLessThan50Points, hadTotalLessThan100);
264 private static final MailTemplate verificationEntered = new MailTemplate(Notary.class.getResource("VerificationEntered.templ"));
266 private static void sendVerificationNotificationApplicant(User assurer, User assuree, Name[] toAssure, final int awarded, final boolean[] hadLessThan50Points, boolean hadTotalLessThan100) {
267 HashMap<String, Object> mailVars = new HashMap<>();
268 mailVars.put("agent", assurer.getPreferredName().toString());
269 mailVars.put("names", new ArrayIterable<Name>(toAssure) {
272 public void apply(Name t, Language l, Map<String, Object> vars) {
273 int totalVP = t.getAssurancePoints();
274 vars.put("name", t.toString());
275 vars.put("points", Integer.toString(awarded));
276 vars.put("total", totalVP);
278 vars.put("rem", (50 - totalVP));
279 vars.remove("gotGreater");
280 } else if (hadLessThan50Points[i]) {
281 vars.put("gotGreater", true);
288 int grandTotalVP = assuree.getAssurancePoints();
289 if (grandTotalVP >= 50 && grandTotalVP < 100) {
290 mailVars.put("remAll", (100 - grandTotalVP));
291 mailVars.remove("gotGreaterAll");
292 } else if (hadTotalLessThan100) {
293 mailVars.put("gotGreaterAll", true);
294 mailVars.remove("remAll");
297 verificationEntered.sendMail(Language.getInstance(assuree.getPreferredLocale()), mailVars, assuree.getEmail());
298 } catch (IOException e) {