]> WPIA git - gigi.git/blob - src/club/wpia/gigi/util/Notary.java
upd: terminology in code
[gigi.git] / src / club / wpia / gigi / util / Notary.java
1 package club.wpia.gigi.util;
2
3 import java.io.IOException;
4 import java.text.ParseException;
5 import java.util.Calendar;
6 import java.util.Date;
7 import java.util.GregorianCalendar;
8 import java.util.HashMap;
9 import java.util.Map;
10
11 import club.wpia.gigi.GigiApiException;
12 import club.wpia.gigi.database.GigiPreparedStatement;
13 import club.wpia.gigi.database.GigiResultSet;
14 import club.wpia.gigi.dbObjects.Country;
15 import club.wpia.gigi.dbObjects.Group;
16 import club.wpia.gigi.dbObjects.Name;
17 import club.wpia.gigi.dbObjects.User;
18 import club.wpia.gigi.dbObjects.Verification.VerificationType;
19 import club.wpia.gigi.localisation.Language;
20 import club.wpia.gigi.output.ArrayIterable;
21 import club.wpia.gigi.output.DateSelector;
22 import club.wpia.gigi.output.template.MailTemplate;
23 import club.wpia.gigi.output.template.SprintfCommand;
24
25 public class Notary {
26
27     // minimum date range between 2 verifications of the RA-Agent to the same
28     // Applicant
29     public final static int LIMIT_DAYS_VERIFICATION = TimeConditions.getInstance().getVerificationLimitDays();
30
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();
34
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);
43             q.execute();
44         }
45     }
46
47     public static boolean checkVerificationIsPossible(User agent, 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, agent.getId());
51             ps.setEnum(3, VerificationType.FACE_TO_FACE);
52             ps.setInt(4, LIMIT_DAYS_VERIFICATION);
53             GigiResultSet rs = ps.executeQuery();
54             return !rs.next();
55         }
56     }
57
58     public static final Group AGENT_BLOCKED = Group.BLOCKEDASSURER;
59
60     public static final Group APPLICANT_BLOCKED = Group.BLOCKEDASSUREE;
61
62     public static final Group VERIFY_NOTIFICATION = Group.VERIFY_NOTIFICATION;
63
64     /**
65      * This method verifies another user.
66      * 
67      * @see User#canVerify() (for agent)
68      * @see #checkVerificationIsPossible(User, User) (for agent or applicant)
69      * @param agent
70      *            the person that wants to verify
71      * @param applicant
72      *            the person that should be verified
73      * @param applicantName
74      *            the Name that was personally verified
75      * @param dob
76      *            the Date of birth that the agent verified
77      * @param awarded
78      *            the points that should be awarded in total
79      * @param location
80      *            the location where the verification took place
81      * @param date
82      *            the date when the verification took place
83      * @throws GigiApiException
84      *             if the verification fails (for various reasons)
85      */
86     public synchronized static void verify(User agent, User applicant, Name applicantName, DayDate dob, int awarded, String location, String date, VerificationType type, Country country) throws GigiApiException {
87         may(agent, applicant, VerificationType.FACE_TO_FACE);
88         GigiApiException gae = new GigiApiException();
89         if ( !gae.isEmpty()) {
90             throw gae;
91         }
92         if (date == null || date.equals("")) {
93             gae.mergeInto(new GigiApiException("You must enter the date when you met the applicant."));
94         } else {
95             try {
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."));
102                 }
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)));
107                 }
108             } catch (ParseException e) {
109                 gae.mergeInto(new GigiApiException("You must enter the date in this format: YYYY-MM-DD."));
110             }
111         }
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."));
117         }
118
119         if (country == null) {
120             gae.mergeInto(new GigiApiException("You failed to enter the country of your meeting."));
121         }
122
123         synchronized (applicant) {
124             if (agent.getId() == applicant.getId()) {
125                 throw new GigiApiException("You cannot verify yourself.");
126             }
127             if (applicantName.getOwner() != applicant) {
128                 throw new GigiApiException("Internal error, name does not belong to applicant.");
129             }
130             if ( !agent.canVerify()) {
131                 throw new GigiApiException("You are not an RA-Agent.");
132             }
133
134             if ( !checkVerificationIsPossible(agent, applicantName)) {
135                 gae.mergeInto(new GigiApiException(SprintfCommand.createSimple("You have already verified this applicant within the last {0} days.", LIMIT_DAYS_VERIFICATION)));
136             }
137
138             if ( !applicant.getDoB().equals(dob)) {
139                 gae.mergeInto(new GigiApiException("The person you are verifying changed his personal details."));
140             }
141
142             if (awarded < 0) {
143                 gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
144             } else {
145                 if (type == VerificationType.NUCLEUS) {
146                     if (awarded > 50) {
147                         gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
148                     }
149                 } else {
150                     if (awarded > agent.getMaxVerifyPoints()) {
151                         gae.mergeInto(new GigiApiException("The points you are trying to award are out of range."));
152                     }
153                 }
154             }
155
156             if ( !gae.isEmpty()) {
157                 throw gae;
158             }
159
160             if (type == VerificationType.FACE_TO_FACE) {
161                 verifyF2F(agent, applicant, applicantName, awarded, location, date, country);
162             } else if (type == VerificationType.NUCLEUS) {
163                 verifyNucleus(agent, applicant, applicantName, awarded, location, date, country);
164             } else if (type == VerificationType.TTP_ASSISTED) {
165                 verifyTTP(agent, applicant, applicantName, awarded, location, date, country);
166             } else {
167                 throw new GigiApiException(SprintfCommand.createSimple("Unknown Verification type: {0}", type.toString()));
168             }
169             agent.invalidateMadeVerifications();
170             applicant.invalidateReceivedVerifications();
171         }
172     }
173
174     private static void verifyF2F(User agent, User applicant, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
175         may(agent, applicant, VerificationType.FACE_TO_FACE);
176         try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, `points`=?, `location`=?, `date`=?, `country`=?")) {
177             ps.setInt(1, agent.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());
183             ps.execute();
184         }
185     }
186
187     private static void verifyTTP(User agent, User applicant, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
188         may(agent, applicant, VerificationType.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, agent.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());
196             ps.execute();
197             applicant.revokeGroup(agent, Group.TTP_APPLICANT);
198         }
199     }
200
201     public static void may(User agent, User applicant, VerificationType t) throws GigiApiException {
202         if (applicant.isInGroup(APPLICANT_BLOCKED)) {
203             throw new GigiApiException("The applicant is blocked.");
204         }
205         if (agent.isInGroup(AGENT_BLOCKED)) {
206             throw new GigiApiException("The RA Agent is blocked.");
207         }
208
209         if (t == VerificationType.NUCLEUS) {
210             if ( !agent.isInGroup(Group.NUCLEUS_ASSURER)) {
211                 throw new GigiApiException("RA Agent needs to be Nucleus RA Agent.");
212             }
213             return;
214         } else if (t == VerificationType.TTP_ASSISTED) {
215             if ( !agent.isInGroup(Group.TTP_ASSURER)) {
216                 throw new GigiApiException("RA Agent needs to be TTP RA Agent.");
217             }
218             if ( !applicant.isInGroup(Group.TTP_APPLICANT)) {
219                 throw new GigiApiException("Applicant needs to be TTP Applicant.");
220             }
221             return;
222         } else if (t == VerificationType.FACE_TO_FACE) {
223             return;
224         }
225         throw new GigiApiException("Verification type not possible.");
226     }
227
228     private static void verifyNucleus(User agent, User applicant, Name name, int awarded, String location, String date, Country country) throws GigiApiException {
229         may(agent, applicant, VerificationType.NUCLEUS);
230         // Do up to 35 points as f2f
231         int f2fPoints = Math.min(agent.getMaxVerifyPoints(), awarded);
232         verifyF2F(agent, applicant, name, f2fPoints, location, date, country);
233
234         awarded -= f2fPoints;
235         if (awarded <= 0) {
236             return;
237         }
238
239         // Verify 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, agent.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());
248             ps.execute();
249         }
250     }
251
252     public synchronized static void verifyAll(User agent, User applicant, DayDate dob, int awarded, String location, String date, VerificationType type, Name[] toVerify, Country country) throws GigiApiException {
253         if (toVerify.length == 0) {
254             throw new GigiApiException("You must confirm at least one name to verify an account.");
255         }
256         boolean[] hadLessThan50Points = new boolean[toVerify.length];
257         boolean hadTotalLessThan100 = applicant.getVerificationPoints() < 100;
258         for (int i = 0; i < toVerify.length; i++) {
259             hadLessThan50Points[i] = toVerify[i].getVerificationPoints() < 50;
260
261             verify(agent, applicant, toVerify[i], dob, awarded, location, date, type, country);
262         }
263         sendVerificationNotificationApplicant(agent, applicant, toVerify, awarded, hadLessThan50Points, hadTotalLessThan100);
264         if (agent.isInGroup(VERIFY_NOTIFICATION)) {
265             sendVerificationNotificationAgent(agent, applicant, toVerify, awarded, location, date, country);
266         }
267     }
268
269     private static final MailTemplate verificationEntered = new MailTemplate(Notary.class.getResource("VerificationEntered.templ"));
270
271     private static final MailTemplate verificationAgentEntered = new MailTemplate(Notary.class.getResource("VerificationAgentEntered.templ"));
272
273     private static void sendVerificationNotificationApplicant(User agent, User applicant, Name[] toVerify, final int awarded, final boolean[] hadLessThan50Points, boolean hadTotalLessThan100) {
274         HashMap<String, Object> mailVars = new HashMap<>();
275         mailVars.put("agent", agent.getPreferredName().toString());
276         mailVars.put("names", new ArrayIterable<Name>(toVerify) {
277
278             @Override
279             public void apply(Name t, Language l, Map<String, Object> vars) {
280                 int totalVP = t.getVerificationPoints();
281                 vars.put("name", t.toString());
282                 vars.put("points", Integer.toString(awarded));
283                 vars.put("total", totalVP);
284                 if (totalVP < 50) {
285                     vars.put("rem", (50 - totalVP));
286                     vars.remove("gotGreater");
287                 } else if (hadLessThan50Points[i]) {
288                     vars.put("gotGreater", true);
289                     vars.remove("rem");
290                 }
291             }
292
293         });
294
295         int grandTotalVP = applicant.getVerificationPoints();
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");
302         }
303         try {
304             verificationEntered.sendMail(Language.getInstance(applicant.getPreferredLocale()), mailVars, applicant.getEmail());
305         } catch (IOException e) {
306             e.printStackTrace();
307         }
308     }
309
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) {
318
319             @Override
320             public void apply(Name t, Language l, Map<String, Object> vars) {
321                 vars.put("name", t.toString());
322             }
323
324         });
325
326         try {
327             verificationAgentEntered.sendMail(Language.getInstance(applicant.getPreferredLocale()), mailVars, agent.getEmail());
328         } catch (IOException e) {
329             e.printStackTrace();
330         }
331     }
332 }