</target>
<target name="update-effective-tlds">
<mkdir dir="bin/club/wpia/gigi/util"/>
- <exec executable="wget" dir="bin/club/wpia/gigi/util">
- <arg value="-N"/>
- <arg value="-q"/>
- <arg value="https://publicsuffix.org/list/effective_tld_names.dat"/>
- </exec>
+ <copy file="/usr/share/publicsuffix/effective_tld_names.dat" tofile="bin/club/wpia/gigi/util/effective_tld_names.dat"/>
</target>
</project>
openssl req -newkey rsa:2048 -keyout mail.key -out mail.csr -nodes -subj "/CN=gigi system"
# Sign the two requests with the keys in the config of the simple signer. Use the serial_base with extensions 1 and 2. These serials are long enough to probably not collide with the "simple signer"
- openssl x509 -req -days 356 -in www.csr -out www.crt -CA $ca.crt -CAkey $ca.key -set_serial ${serial_base}1 -extfile <(printf '[ext]\nsubjectAltName=DNS:www.%s,DNS:secure.%s,DNS:static.%s,DNS:api.%s\nbasicConstraints=CA:FALSE\nextendedKeyUsage=serverAuth\nkeyUsage=digitalSignature,keyEncipherment\n' "$DOMAIN" "$DOMAIN" "$DOMAIN" "$DOMAIN") -extensions ext
- openssl x509 -req -days 356 -in mail.csr -out mail.crt -CA $ca.crt -CAkey $ca.key -set_serial ${serial_base}2 -extfile <(printf '[ext]\nsubjectAltName=email:support@%s\nbasicConstraints=CA:FALSE\nextendedKeyUsage=emailProtection\nkeyUsage=digitalSignature,keyEncipherment\n' "$DOMAIN") -extensions ext
+ openssl x509 -req -days 365 -in www.csr -out www.crt -CA $ca.crt -CAkey $ca.key -set_serial ${serial_base}1 -extfile <(printf '[ext]\nsubjectAltName=DNS:www.%s,DNS:secure.%s,DNS:static.%s,DNS:api.%s\nbasicConstraints=CA:FALSE\nextendedKeyUsage=serverAuth\nkeyUsage=digitalSignature,keyEncipherment\n' "$DOMAIN" "$DOMAIN" "$DOMAIN" "$DOMAIN") -extensions ext
+ openssl x509 -req -days 365 -in mail.csr -out mail.crt -CA $ca.crt -CAkey $ca.key -set_serial ${serial_base}2 -extfile <(printf '[ext]\nsubjectAltName=email:support@%s\nbasicConstraints=CA:FALSE\nextendedKeyUsage=emailProtection\nkeyUsage=digitalSignature,keyEncipherment\n' "$DOMAIN") -extensions ext
# Store the webserver cert in 4 different pkcs12-keystores to have different "key aliases" and import them all into the "keystore.pkcs12" using the "importP"-method
for t in www api secure static; do
Section: java
Priority: optional
Maintainer: unknown <software@wpia.club>
-Build-Depends: debhelper (>= 8.0.0), dh-systemd, java7-sdk-headless, ant, wget
+Build-Depends: debhelper (>= 8.0.0), dh-systemd, java7-sdk-headless, ant, wget, publicsuffix
Standards-Version: 3.9.4
Homepage: https://wpia.club
#Vcs-Git: git://git.debian.org/collab-maint/gigi.git
import club.wpia.gigi.pages.account.ChangePasswordPage;
import club.wpia.gigi.pages.account.FindAgentAccess;
import club.wpia.gigi.pages.account.History;
+import club.wpia.gigi.pages.account.MyContracts;
import club.wpia.gigi.pages.account.MyDetails;
import club.wpia.gigi.pages.account.UserTrainings;
import club.wpia.gigi.pages.account.certs.CertificateAdd;
putPage(UserTrainings.SUPPORT_PATH, new UserTrainings(true), null);
putPage(Points.SUPPORT_PATH, new Points(true), null);
putPage(Certificates.SUPPORT_PATH + "/*", new Certificates(true), null);
+ putPage(MyContracts.PATH, new MyContracts(), null);
putPage(PasswordResetPage.PATH, new PasswordResetPage(), null);
putPage(LogoutPage.PATH, new LogoutPage(), null);
}
- public static final int CURRENT_SCHEMA_VERSION = 37;
+ public static final int CURRENT_SCHEMA_VERSION = 38;
public static final int CONNECTION_TIMEOUT = 24 * 60 * 60;
"version" smallint NOT NULL,
PRIMARY KEY ("version")
);
-INSERT INTO "schemeVersion" (version) VALUES(37);
+INSERT INTO "schemeVersion" (version) VALUES(38);
DROP TABLE IF EXISTS `passwordResetTickets`;
CREATE TABLE `passwordResetTickets` (
PRIMARY KEY ("jobid", "attempt")
);
CREATE INDEX ON "jobLog" ("jobid");
+
+DROP TABLE IF EXISTS "user_contracts";
+DROP TYPE IF EXISTS "contractType";
+CREATE TYPE "contractType" AS ENUM ('RA Agent Contract', 'Org RA Agent Contract');
+
+CREATE TABLE "user_contracts" (
+ "id" serial NOT NULL,
+ "token" varchar(32) NOT NULL,
+ "memid" int NOT NULL,
+ "document" "contractType" NOT NULL,
+ "agentname" varchar(255) NOT NULL,
+ "datesigned" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "daterevoked" timestamp DEFAULT NULL,
+ PRIMARY KEY ("id")
+);
+CREATE INDEX ON "user_contracts" ("memid");
+CREATE INDEX ON "user_contracts" ("document");
+CREATE INDEX ON "user_contracts" ("datesigned");
+CREATE INDEX ON "user_contracts" ("daterevoked");
--- /dev/null
+DROP TABLE IF EXISTS "user_contracts";
+DROP TYPE IF EXISTS "contractType";
+CREATE TYPE "contractType" AS ENUM ('RA Agent Contract', 'Org RA Agent Contract');
+
+CREATE TABLE "user_contracts" (
+ "id" serial NOT NULL,
+ "token" varchar(32) NOT NULL,
+ "memid" int NOT NULL,
+ "document" "contractType" NOT NULL,
+ "agentname" varchar(255) NOT NULL,
+ "datesigned" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "daterevoked" timestamp DEFAULT NULL,
+ PRIMARY KEY ("id")
+);
+CREATE INDEX ON "user_contracts" ("memid");
+CREATE INDEX ON "user_contracts" ("document");
+CREATE INDEX ON "user_contracts" ("datesigned");
+CREATE INDEX ON "user_contracts" ("daterevoked");
--- /dev/null
+package club.wpia.gigi.dbObjects;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.database.DBEnum;
+import club.wpia.gigi.database.GigiPreparedStatement;
+import club.wpia.gigi.database.GigiResultSet;
+import club.wpia.gigi.localisation.Language;
+import club.wpia.gigi.output.template.MailTemplate;
+import club.wpia.gigi.util.RandomToken;
+
+public class Contract {
+
+ public enum ContractType implements DBEnum {
+ RA_AGENT_CONTRACT("RA Agent Contract"), ORG_RA_AGENT_CONTRACT("Org RA Agent Contract");
+
+ private final String description;
+
+ private ContractType(String description) {
+ this.description = description;
+ }
+
+ public String getDBName() {
+ return description;
+ }
+ }
+
+ private final ContractType contractType;
+
+ private final User user;
+
+ private String agentname = "";
+
+ private String token = "";
+
+ private Date dateSigned = null;
+
+ private Date dateRevoked = null;
+
+ private int contractID;
+
+ private static final MailTemplate contractNotice = new MailTemplate(Contract.class.getResource("ContractNotice.templ"));
+
+ public Contract(User u, ContractType contractType) throws GigiApiException {
+ this.contractType = contractType;
+ this.user = u;
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT * FROM `user_contracts` WHERE `memid`=? AND `document`=?::`contractType` and `daterevoked` IS NULL ORDER BY `datesigned` DESC LIMIT 1")) {
+ query.setInt(1, user.getId());
+ query.setEnum(2, contractType);
+ GigiResultSet rs = query.executeQuery();
+ if (rs.next()) {
+ throw new GigiApiException("Contract exists");
+ } else {
+ signContract();
+ }
+ }
+
+ }
+
+ private void signContract() throws GigiApiException {
+ agentname = user.getPreferredName().toString();
+ token = RandomToken.generateToken(32);
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `user_contracts` SET `memid`=?, `token`=?, `document`=?::`contractType`,`agentname`=?")) {
+ ps.setInt(1, user.getId());
+ ps.setString(2, token);
+ ps.setEnum(3, this.contractType);
+ ps.setString(4, agentname);
+ ps.execute();
+ contractID = ps.lastInsertId();
+ dateSigned = new Date();
+
+ HashMap<String, Object> vars = new HashMap<>();
+ Language l = Language.getInstance(user.getPreferredLocale());
+ vars.put("user", agentname);
+ vars.put("actionsubject", "Signing");
+ vars.put("actionbody", "signed");
+
+ try {
+ contractNotice.sendMail(l, vars, user.getEmail());
+ } catch (IOException e) {
+ throw new GigiApiException("Sending the notification mail failed.");
+ }
+ }
+ }
+
+ public void revokeContract() throws GigiApiException {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `user_contracts` SET `daterevoked`=NOW() WHERE `id`=?")) {
+ ps.setInt(1, contractID);
+ ps.execute();
+ }
+ dateRevoked = new Date();
+ HashMap<String, Object> vars = new HashMap<>();
+ Language l = Language.getInstance(user.getPreferredLocale());
+ vars.put("user", user.getPreferredName());
+ vars.put("actionsubject", "Revoking");
+ vars.put("actionbody", "revoked");
+
+ try {
+ contractNotice.sendMail(l, vars, user.getEmail());
+ } catch (IOException e) {
+ throw new GigiApiException("Sending the notification mail failed.");
+ }
+ }
+
+ private Contract(GigiResultSet rs) {
+ contractID = rs.getInt("id");
+ user = User.getById(rs.getInt("memid"));
+ token = rs.getString("token");
+ contractType = ContractType.valueOf(rs.getString("document").toUpperCase().replace(" ", "_"));
+ dateSigned = rs.getDate("datesigned");
+ dateRevoked = rs.getDate("daterevoked");
+ agentname = rs.getString("agentname");
+ }
+
+ public static Contract getById(int id) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT * FROM `user_contracts` WHERE `id` = ?")) {
+ ps.setInt(1, id);
+ GigiResultSet rs = ps.executeQuery();
+ if ( !rs.next()) {
+ return null;
+ }
+
+ Contract c = new Contract(rs);
+
+ return c;
+ }
+ }
+
+ public int getID() {
+ return contractID;
+ }
+
+ public Date getDateSigned() {
+ return dateSigned;
+ }
+
+ public Date getDateRevoked() {
+ return dateRevoked;
+ }
+
+ public String getRAAgentName() {
+ return agentname;
+ }
+
+ public ContractType getContractType() {
+ return contractType;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public static boolean hasSignedContract(User u, Contract.ContractType ct) {
+ return getContractByUser(u, ct) != null;
+ }
+
+ public static Contract getRAAgentContractByUser(User u) {
+ return getContractByUser(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ }
+
+ public static Contract getContractByUser(User u, ContractType ct) {
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT * FROM `user_contracts` WHERE `memid`=? AND `document`=?::`contractType` and `daterevoked` IS NULL ORDER BY `datesigned` DESC LIMIT 1")) {
+ query.setInt(1, u.getId());
+ query.setEnum(2, ct);
+ GigiResultSet rs = query.executeQuery();
+ if ( !rs.next()) {
+ return null;
+ }
+ Contract c = new Contract(rs);
+ return c;
+ }
+ }
+
+ public static Contract getRAAgentContractByToken(String token) throws GigiApiException {
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT * FROM `user_contracts` WHERE `token`=? LIMIT 1")) {
+ query.setString(1, token);
+ GigiResultSet rs = query.executeQuery();
+ if ( !rs.next()) {
+ return null;
+ }
+ Contract c = new Contract(rs);
+ return c;
+ }
+ }
+}
--- /dev/null
+Subject: <?=_${actionsubject} of RA Agent Contract?>
+
+<?=_Hello ${user}, ?>
+
+<?=_you just ${actionbody} the RA Agent Contract.?>
+
package club.wpia.gigi.dbObjects;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
import java.util.Map;
import club.wpia.gigi.GigiApiException;
import club.wpia.gigi.localisation.Language;
import club.wpia.gigi.output.template.Outputable;
import club.wpia.gigi.util.HTMLEncoder;
+import club.wpia.gigi.util.TimeConditions;
public class Name implements Outputable, IdCachable {
}
return initals.toString();
}
+
+ public boolean isValidVerification() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ try (GigiPreparedStatement query = new GigiPreparedStatement("SELECT COUNT(id) FROM `notary` WHERE `to` = ? AND `deleted` IS NULL AND (`expire` IS NULL OR `expire` > CURRENT_TIMESTAMP) AND `date` > ?")) {
+ query.setInt(1, getId());
+ query.setString(2, date);
+ GigiResultSet rs = query.executeQuery();
+
+ if (rs.next()) {
+ if (rs.getInt(1) > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
}
setPassword(newPass);
}
- private void setPassword(String newPass) throws GigiApiException {
+ public void setPassword(String newPass) throws GigiApiException {
Name[] names = getNames();
TreeSet<String> nameParts = new TreeSet<>();
for (int i = 0; i < names.length; i++) {
return false;
}
+ if ( !Contract.hasSignedContract(this, Contract.ContractType.RA_AGENT_CONTRACT)) {
+ return false;
+ }
+
return hasPassedCATS();
}
return false;
}
+ public boolean isValidNameVerification(String name) {
+ for (Name n : getNames()) {
+ if (n.matches(name) && n.isValidVerification()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public void updateDefaultEmail(EmailAddress newMail) throws GigiApiException {
for (EmailAddress email : getEmails()) {
if (email.getAddress().equals(newMail.getAddress())) {
<li><?=$orgName?></li>
<? } ?>
</ul>
- <? if($certlogin) { ?><p><?=_!'<a href="/account/details">'change to organisation administrator context.'</a>'?><? } else { ?><p class="alert alert-warning"><?=_You need to be logged in via certificate to get access to the organisations.?><? } ?></p>
+ <? if($certlogin) { ?><p><a href="/account/details"><?=_Change to organisation administrator context.?></a><? } else { ?><p class="alert alert-warning"><?=_You need to be logged in via certificate to get access to the organisations.?><? } ?></p>
</div>
<? } ?>
import club.wpia.gigi.output.template.Form;
import club.wpia.gigi.output.template.Template;
import club.wpia.gigi.output.template.TranslateCommand;
+import club.wpia.gigi.pages.LoginPage;
+import club.wpia.gigi.util.AuthorizationContext;
public class ChangeForm extends Form {
private User target;
+ private AuthorizationContext c;
+
public ChangeForm(HttpServletRequest hsr, User target) {
super(hsr);
this.target = target;
+ c = LoginPage.getAuthorizationContext(hsr);
}
private static final Template t = new Template(ChangePasswordPage.class.getResource("ChangePasswordForm.templ"));
@Override
public void outputContent(PrintWriter out, Language l, Map<String, Object> vars) {
+ if ( !c.isStronglyAuthenticated()) {
+ vars.put("currentLoginMethod", "pw");
+ }
t.output(out, l, vars);
}
String p1 = req.getParameter("pword1");
String p2 = req.getParameter("pword2");
GigiApiException error = new GigiApiException();
- if (oldpassword == null || p1 == null || p2 == null) {
+ if ((oldpassword == null && !c.isStronglyAuthenticated()) || p1 == null || p2 == null) {
throw new GigiApiException("All fields are required.");
}
if ( !p1.equals(p2)) {
throw new GigiApiException("New passwords do not match.");
}
try {
- target.changePassword(oldpassword, p1);
+ if (c.isStronglyAuthenticated()) {
+ target.setPassword(p1);
+ } else {
+ target.changePassword(oldpassword, p1);
+ }
target.writeUserLog(target, "User triggered password reset");
} catch (GigiApiException e) {
error.mergeInto(e);
</tr>
</thead>
<tbody>
+ <? if($currentLoginMethod){ ?>
<tr>
<td><?=_Old Password?>: </td>
<td><input class="form-control" type="password" name="oldpassword" required></td>
</tr>
+ <? } ?>
<tr>
<td><?=_New Password?><span class="formMandatory">*</span>: </td>
<td><input class="form-control" type="password" name="pword1" required></td>
<table class="table">
<tbody>
-<tr><th><? if($username) { ?><?=_Support actions for ${username}?><? } else { ?><?=_Support actions?><? } ?></th></tr>
+<tr><th><? if($username) { ?><?=_Actions in user account of ${username}?><? } else { ?><?=_Actions in user account?><? } ?></th></tr>
<? foreach($entries) { ?>
<tr><td><?=$entry?></td></tr>
<? } ?>
--- /dev/null
+package club.wpia.gigi.pages.account;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import club.wpia.gigi.dbObjects.Contract;
+import club.wpia.gigi.dbObjects.User;
+import club.wpia.gigi.localisation.Language;
+import club.wpia.gigi.output.template.Template;
+import club.wpia.gigi.pages.LoginPage;
+import club.wpia.gigi.pages.Page;
+
+public class MyContracts extends Page {
+
+ public static final String PATH = "/account/contracts";
+
+ private static final Template contractDE = new Template(MyDetailsForm.class.getResource("MyContractsDE.templ"));
+
+ public MyContracts() {
+ super("My Contracts");
+ }
+
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+ PrintWriter out = resp.getWriter();
+ Map<String, Object> vars = getDefaultVars(req);
+ Language l = LoginPage.getLanguage(req);
+ User u = getUser(req);
+ vars.put("raname", u.getPreferredName());
+ vars.put("csdate", l.getTranslation("not yet"));
+
+ Contract c = Contract.getRAAgentContractByUser(u);
+ if (c != null) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ vars.put("csdate", sdf.format(c.getDateSigned()));
+ }
+
+ switch (l.getLocale().toString()) {
+ case "de":
+ contractDE.output(out, getLanguage(req), vars);
+ break;
+ default:
+ getDefaultTemplate().output(out, getLanguage(req), vars);
+ }
+ }
+}
--- /dev/null
+<div class="panel panel-default">
+ <div class="panel-heading"><?=_RA Agents Contract?></div>
+ <div class="panel-body">
+
+<p>This contract concludes an agreement between</p>
+<p>RA Agent: <?=$raname?></p>
+<p>and</p>
+<p>World Privacy and Identity Association (WPIA) - Verein zur Foerderung von sicheren Technologien und Grundrechten im Internet (ZVR: 910115306)</p>
+<p>c/o realraum</p>
+<p>Brockmangasse 15</p>
+<p>8010 Graz</p>
+<p>Austria</p>
+<p>- CA hereafter -</p>
+<p>regarding the conduction of registration authority tasks.</p>
+<p>§ 1 Contract matter 1. The RA Agent is entitled to conduct registration authority tasks as defined on the applicant's request and based on documents presented: * validation of an applicant's identity, and * validation of an applicant's personal data</p>
+<ol start="2" style="list-style-type: decimal">
+<li>The RA Agent is entitled to conduct those tasks on behalf of the CA as long as the RA Agent fulfills the following requirements:
+<ul>
+<li>100 Verification Points,</li>
+<li>passed the RA Agent test,</li>
+<li>agrees the CA's Terms and Conditions,</li>
+<li>agrees the Dispute Resolution system, and</li>
+<li>signed this contract.</li>
+</ul></li>
+<li><p>The parties agree, that this contract does not establish an employment relationship.</p></li>
+<li><p>This contract starts on the day it is signed and is valid until it is terminated by either side with a notification period of fourteen (14) days.</p></li>
+</ol>
+<p>§ 2 Terms and Definitions</p>
+<table>
+<colgroup>
+<col width="26%" />
+<col width="73%" />
+</colgroup>
+<thead>
+<tr class="header">
+<th>Term</th>
+<th>Definition</th>
+</tr>
+</thead>
+<tbody>
+<tr class="odd">
+<td><p>Applicant</p></td>
+<td><p>Person who has an account on the systems of CA and requests to get the identity verified</p></td>
+</tr>
+<tr class="even">
+<td><p>Certificate Authority (CA)</p></td>
+<td><p>Certificate Authority who issues S/MIME certificates to individuals and organisations</p></td>
+</tr>
+<tr class="odd">
+<td><p>RA Agent</p></td>
+<td><p>Person who conducts a identity verification on behalf of the CA</p></td>
+</tr>
+<tr class="even">
+<td><p>Registration Authority (RA)</p></td>
+<td><p>Registration Authority who controls the correct entries of an applicant</p></td>
+</tr>
+<tr class="odd">
+<td><p>Verification</p></td>
+<td><p>Process to verify the identity of person</p></td>
+</tr>
+<tr class="even">
+<td><p>Verification Points</p></td>
+<td><p>Points issued during the verification</p></td>
+</tr>
+</tbody>
+</table>
+<p>§ 3 Rights and Obligations of the RA Agent</p>
+<ol style="list-style-type: decimal">
+<li><p>The RA Agent conducts the transferred duties independently according to the <a href="https://policy.wpia.club/policies/verification_policy.html">Verification Policy</a>, i.e. free of instructions other than essential for the proper execution of the transferred tasks.</p></li>
+<li><p>The RA Agent is entitled to use the brand and logo of the CA for her work as RA Agent based on this contract.</p></li>
+<li><p>The RA Agent MUST identify herself to an Applicant as RA Agent with the RA Agent Card provided by the CA.</p></li>
+<li><p>The RA Agent MUST NOT engage another person to do verifications on her behalf.</p></li>
+<li><p>The RA Agent MUST NOT verify applicants that are spouses or life partners and first-degree relatives (next of kin).</p></li>
+<li><p>All data collected and processed during the work as RA Auditor are object to strict confidentiality. They SHOULD NOT shared with others than those parties entitled by the CA and MUST be kept in accordance to their confidentiality.</p></li>
+<li><p>The RA Agent is bound to the latest version of the data protection policy of the CA, in all regards of processing personal data of applicants. Personal data collected, used, and processed are titles, full names, date of birth, and email address of an applicant. The RA Agent is not allowed to transfer personal data to other entities than the CA, unless required to do so by the European Union or Member State law to which the RA Agent is subject to. In such a case, the RA Agent MUST to inform the CA of this legal requirement and the type of data transferred, unless that a law prohibits such information on important ground of public interest.</p></li>
+</ol>
+<p>§ 4 Payment and Fees</p>
+<ol style="list-style-type: decimal">
+<li><p>The RA Agent operates free of charge for the CA. This implies that the action is voluntary, honorary and unpaid without any liability to take an action and for the CA no obligation to register social security and labour legislation related data.</p></li>
+<li><p>The RA agent is entitled to request reimbursement of travel expenses or other costs if this has been previously agreed with the applicant.</p></li>
+</ol>
+<p>§ 5 Dispute Resolution</p>
+<p>Every dispute between the RA Agent and an applicant or the RA Agent and the CA MUST be filed to the CA's dispute resolution forum (arbitration). Each party is bound to the ruling of this forum.</p>
+<p>§ 6 Termination</p>
+<p>On termination of this contract all data that is older than thirty nine (39) months MUST be deleted in a secure manner. Data younger than thirty nine (39) months MUST be collected and made available to the CA unless otherwise requested by the CA. The RA Agent is accountable for the deletion or the handover to the CA.</p>
+<p>§ 7 Subsidiary agreements</p>
+<p>Subsidiary or deviating agreements and amendments to the contract MUST be in written form.</p>
+
+ <p><?=_Signed?> <?=$csdate?></p>
+
+</div>
--- /dev/null
+<div class="panel panel-default">
+ <div class="panel-heading"><?=_RA Agents Contract?></div>
+ <div class="panel-body">
+
+ <p>Dieser Vertrag schließt eine Vereinbarung zwischen</p>
+ <p>RA Agent: <?=$raname?></p>
+ <p>und</p>
+ <p>World Privacy and Identity Association (WPIA) - Verein zur Förderung von sicheren Technologien und Grundrechten im Internet (ZVR: 910115306)</p>
+ <p>c/o realraum</p>
+ <p>Brockmangasse 15</p>
+ <p>8010 Graz</p>
+ <p>Austria</p>
+ <p>- folgend CA -</p>
+ <p>für die Durchführung von "Registration Authority"-Aufgaben.</p>
+ <p>§ 1 Vertragsgegenstand</p>
+ <ol style="list-style-type: decimal">
+ <li>Der RA Agent ist ermächtigt "Registration Authority"-Aufgaben, wie folgend aufgeführt, auf Antrag des Applicant und der vorgelegten Dokumente durchzuführen:
+ <ul>
+ <li>Validierung der Identität des Applicant</li>
+ <li>Validierung der persönlichen Daten des Applicant</li>
+ </ul></li>
+ <li>Der RA Agent ist ermächtig diese Aufgaben im Auftrag der CA auszuführen sofern die folgenden Voraussetzungen erfüllt sind:
+ <ul>
+ <li>100 Verification Points</li>
+ <li>bestandener RA Agent Test</li>
+ <li>Zustimmung zu den "Terms and Condition" der CA</li>
+ <li>Zustimmung zum Schiedsgerichtswesen</li>
+ <li>Unterschrift für diesen Vertrag</li>
+ </ul></li>
+ <li><p>Die Parteien vereinbaren, dass aus diesem Vertrag keine Anstellungsverhältnis entsteht.</p></li>
+ <li><p>Dieser Vertrag beginnt mit dem Tag der Unterzeichnung und endet mit der Terminierung von jeder Seite mit einer Frist von vierzehn (14) Tagen nach Ankündigung.</p></li>
+ </ol>
+ <p>§ 2 Begriffe und Definitionen</p>
+ <table>
+ <colgroup>
+ <col width="26%" />
+ <col width="73%" />
+ </colgroup>
+ <thead>
+ <tr class="header">
+ <th>Begriffe</th>
+ <th>Definition</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="odd">
+ <td><p>Applicant</p></td>
+ <td><p>Person die ein Konto bei der CA hat und ihre Identität verifiziert haben möchte.</p></td>
+ </tr>
+<tr class="even">
+<td><p>Certificate Authority (CA)</p></td>
+<td><p>Certificate Authority, die S/MIME Zertifikate für Personen und Organisationen austellt.</p></td>
+</tr>
+<tr class="odd">
+<td><p>RA Agent Registration Authority (RA)</p></td>
+<td><p>Person, die Verifications im Auftrag der CA durchführt. Registration Authority, die die Richtigkeit der Einträge eines Applicant sicherstellt.</p></td>
+</tr>
+<tr class="even">
+<td><p>Verification</p></td>
+<td><p>Prozess die Identität einer Person festzustellen.</p></td>
+</tr>
+<tr class="odd">
+<td><p>Verification Points</p></td>
+<td><p>Punkte die während der verification vergeben werden.</p></td>
+</tr>
+</tbody>
+</table>
+<p>§ 3 Rechte und Pflichten des RA Agent</p>
+<ol style="list-style-type: decimal">
+<li><p>Der RA Agent führt die auferlegten Pflichten gemäß der <a href="https://policy.wpia.club/policies/verification_policy.html">Verification Policy</a> unabhängig durch, z.B. frei von Vorgaben anderer unter Berücksichtigung der ordnungsgemäßen Durchführung der Aufgaben.</p></li>
+<li><p>Der RA Agent ist ermächtigt, die Marke und das Logo der CA für die Arbeit als RA Agent im Rahmen dieses Vertrages zu nutzen.</p></li>
+<li><p>Der RA Agent muss (MUST) sich gegenüber dem Applicant als RA Agent mit seiner von der CA zur Verfügung gestellten RA Agent Card ausweisen.</p></li>
+<li><p>Der RA Agent darf nicht (MUST NOT) andere Personen auffordern die Verification an seiner statt durchzuführen.</p></li>
+<li><p>Der RA Agent darf nicht (MUST NOT) Ehe- und Lebenspartner und Verwandte ersten Grades als Applicant verifizieren.</p></li>
+<li><p>Alle Informationen, die während der Arbeit als RA Agent gesammelt werden, sind als vertraulich zu behandeln. Diese sollten nicht (SHOULD NOT) an Dritte außer den von der CA bestimmten weitergeleitet werden und müssen (MUST) entsprechend gesichert aufbewahrt werden.</p></li>
+<li><p>Der RA Agent ist an die aktuell gültige Version der Datenschutzrichtlinie der CA in allen Belangen der Verarbeitung der Daten des Applicant gebunden. Die gesammelten und zu verarbeitenden personenbezogenen Daten sind Titel, voller Namen, Geburtsdatum und E-Mail-Adresse des Applicant. Der RA Agent ist nicht ermächtigt, personenbezogenen Daten an Dritte außer der CA weiterzuleiten es sei den das Recht der Europäischen Union oder des Landes des RA Agent verlangt dieses. In diesem Fall muss (MUST) der RA Agent die CA über den Vorfall mit Angabe der Begründung und der übergebenen Daten melden, es sei denn ein Gesetz verbietet die Offenlegung auf Grund von gesteigerten öffentlichen Interesses.</p></li>
+</ol>
+<p>§ 4 Bezahlung und Gebühren</p>
+<ol style="list-style-type: decimal">
+<li><p>Der RA Agent arbeitet ohne Bezahlung durch die CA. Dies bedeutet, dass es sich bei dieser Tätigkeit um eine freiwillige, ehrenamtliche und unentgeltliche Tätigkeit ohne jede Arbeitsleistungsverpflichtung handelt, und seitens der CA keinerlei sozialversicherungs- und arbeitsrechtliche Meldepflichten bestehen.</p></li>
+<li><p>Der RA Agent darf Reise- oder ähnliche Kosten dem Applicant in Rechnung stellen, sofern dies im Vorfeld vereinbart wird.</p></li>
+</ol>
+<p>§ 5 Schiedsgerichtsbarkeit</p>
+<p>Jede Unstimmigkeit zwischen dem RA Agent und dem Applicant oder dem RA Agent und der CA muss (MUST) bei dem Schiedsgericht der CA eingreicht werden. Jede Partei ist an den Schiedspruch gebunden.</p>
+<p>§ 6 Vertragsbeendigung</p>
+<p>Mit Vertragsbeendigung dieses Vertrages sind (MUST) alle Daten älter als neununddreißig (39) Monate zu vernichten. Daten jünger als neununddreißig (39) Monate müssen gesammelt und der CA zukommen gelassen werden, es sei denn anders von der CA bestimmt. Der RA Agent ist dafür verantwortlich, dass die Daten entsprechend vernichtet oder an die CA weitergereicht werden.</p>
+<p>§ 7 Zusätzliche Bestimmungen</p>
+<p>Zusätzliche oder abweichende Bestimmungen oder Vereinbarungen benötigen (MUST) die Schriftform.</p>
+
+ <p><?=_Signed?> <?=$csdate?></p>
+
+</div>
--- /dev/null
+<div class="panel panel-default">
+ <div class="panel-heading"><?=_RA Agents Contract?></div>
+ <div class="panel-body">
+ <button class="btn btn-info" name="action" value="viewContract"><?=_View RA Agent Contract?></button>
+ <button class="btn btn-primary" name="action" value="signContract" <?=$contractsign?> ><?=_Sign RA Agent Contract?></button>
+ <button class="btn btn-danger" name="action" value="revokeContract" <?=$contractrevoke?> ><?=_Revoke RA Agent Contract?></button>
+ </div>
+</div>
import javax.servlet.http.HttpServletRequest;
import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Contract;
import club.wpia.gigi.dbObjects.Group;
import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.User;
private static final Template roles = new Template(MyDetailsForm.class.getResource("MyDetailsRoles.templ"));
+ private static final Template contracts = new Template(MyDetailsForm.class.getResource("MyDetailsContracts.templ"));
+
private User target;
private DateSelector ds;
target.revokeGroup(target, toMod);
}
return new RedirectResult(MyDetails.PATH);
+ } else if ("viewContract".equals(action)) {
+ return new RedirectResult(MyContracts.PATH);
+ } else if ("signContract".equals(action)) {
+ new Contract(target, Contract.ContractType.RA_AGENT_CONTRACT);
+ return new RedirectResult(MyDetails.PATH);
+ } else if ("revokeContract".equals(action)) {
+ Contract c = Contract.getRAAgentContractByUser(target);
+ if (c != null) {
+ c.revokeContract();
+ }
+ return new RedirectResult(MyDetails.PATH);
} else {
throw new GigiApiException("Invalid action.");
}
vars.put("groups", new GroupList(gr, false));
vars.put("groupSelector", selectedGroup);
roles.output(out, l, vars);
+
+ boolean hasSignedContract = Contract.hasSignedContract(target, Contract.ContractType.RA_AGENT_CONTRACT);
+ vars.put("contractsign", hasSignedContract ? "disabled" : "");
+ vars.put("contractrevoke", hasSignedContract ? "" : "disabled");
+ contracts.output(out, l, vars);
}
}
import club.wpia.gigi.pages.LoginPage;
import club.wpia.gigi.util.AuthorizationContext;
import club.wpia.gigi.util.HTMLEncoder;
-import club.wpia.gigi.util.RandomToken;
import club.wpia.gigi.util.ServerConstants;
import club.wpia.gigi.util.ServerConstants.Host;
private AuthorizationContext c;
- private String spkacChallenge;
-
private boolean login;
public CertificateIssueForm(HttpServletRequest hsr) {
super(hsr);
c = LoginPage.getAuthorizationContext(hsr);
- spkacChallenge = RandomToken.generateToken(16);
}
private Certificate result;
@Override
public SubmissionResult submit(HttpServletRequest req) throws GigiApiException {
String csr = req.getParameter("CSR");
- String spkac = req.getParameter("SPKAC");
try {
if (csr != null) {
cr = new CertificateRequest(c, csr);
// TODO cr.checkKeyStrength(out);
return new FormContinue();
- } else if (spkac != null) {
- cr = new CertificateRequest(c, spkac, spkacChallenge);
- // TODO cr.checkKeyStrength(out);
- return new FormContinue();
} else if (cr != null) {
login = "1".equals(req.getParameter("login"));
issueDate.update(req);
HashMap<String, Object> vars2 = new HashMap<String, Object>(vars);
vars2.put("csrf", getCSRFToken());
vars2.put("csrf_name", getCsrfFieldName());
- vars2.put("spkacChallenge", spkacChallenge);
tIni.output(out, l, vars2);
return;
} else {
<h3><?=_${appName} Acceptable Use Policy?></h3>
<p><?=_I hereby represent that I am fully authorized by the owner of the information contained in the CSR sent to ${appName} to apply for an Digital Certificate for secure and authenticated electronic transactions. I understand that a digital certificate serves to identify the Subscriber for the purposes of electronic communication and that the management of the private keys associated with such certificates is the responsibility of the subscriber's technical staff and/or contractors.?></p>
-<p><?=_${appName}'s public certification services are governed by a CPS as amended from time to time which is incorporated into this Agreement by reference. The Subscriber will use the SSL Server Certificate in accordance with ${appName}'s CPS and supporting documentation published !(/policy/CPS)here!'</a>'.?></p>
+<p><?=_${appName}'s public certification services are governed by a CPS as amended from time to time which is incorporated into this Agreement by reference. The Subscriber will use client and server certificates in accordance with ${appName}'s CPS and supporting documentation published !(/policy/CPS)here!'</a>'.?></p>
<p><?=_If the Subscriber's name and/or domain name registration change the subscriber will immediately inform ${appName} Inc. who shall revoke the digital certificate. When the Digital Certificate expires or is revoked the company will permanently remove the certificate from the server on which it is installed and will not use it for any purpose thereafter. The person responsible for key management and security is fully authorized to install and utilize the certificate to represent this organization's electronic presence.?></p>
<span id="placeholderName" class="js-hint"><?=$placeholderName?></span>
</tr>
<? if($orga) { ?>
<tr>
- <td><?=_Department?></td>
+ <td><?=_Department (OU)?></td>
<td align="left"><input type='text' name='OU' value='<?=$department?>'/></td>
</tr>
<? } ?>
} else {
// remove
error.mergeInto(new GigiApiException(SprintfCommand.createSimple(//
- "The requested subject alternate name email address \"{0}\" needs an email ping within the past {1} months.", san.getType().toString().toLowerCase() + ":" + san.getName(), TimeConditions.getInstance().getEmailPingMonths())));
+ "The requested subject alternate name email address \"{0}\" needs a verification via email ping within the past {1} months.", san.getType().toString().toLowerCase() + ":" + san.getName(), TimeConditions.getInstance().getEmailPingMonths())));
break;
}
}
subject.put("OU", ou);
}
}
- System.out.println(subject);
+
if ( !error.isEmpty()) {
throw error;
}
User u = (User) ctx.getTarget();
if (name != null && u.isValidName(name)) {
if (realIsOK) {
- verifiedCN = name;
+ if (u.isValidNameVerification(name)) {
+ verifiedCN = name;
+ } else {
+ error.mergeInto(new GigiApiException(SprintfCommand.createSimple("The entered name needs a valid verification within the last {0} months.", TimeConditions.getInstance().getVerificationMonths())));
+ }
} else {
error.mergeInto(new GigiApiException("Your real name is not allowed in this certificate."));
if (defaultIsOK) {
<p><?=_${appName} offers two ways to create a certificate.?>
-<?=_One is to paste a certificate signing request (CSR) created from an existing or newly created private key.?> <?=_If you do not know what a CSR is or how to create one take a look at the !(/kb/CSR)FAQ!'</a>'.?>
-<?=_As an alternative you can generate the private key inside your browser and export it once the certificate has been issued.?></p>
+<?=_One is to paste a certificate signing request (CSR) created from an existing or newly created private key.?> <?=_If you do not know what a CSR is or how to create one take a look at the !(/kb/CSR)FAQ!'</a>'.?></p>
+<p><?=_For inexperienced users the usage of !(/kb/XCA)XCA!'</a>' is recommended and described in !(/kb/XCADocu)XCA usage documentation!'</a>'?></p>
<form method="post">
<table class="table">
<thead>
</tbody>
</table>
</form>
-<form method="post">
-<table class="table">
- <thead>
- <tr>
- <th colspan="2" class="title"><?=_Create a fresh key in the browser (SPKAC)?></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><?=_I do not have a CSR.?></td>
- <td align="left">
- <?=_key size (2048 recommended)?>: <keygen name="SPKAC" challenge="<?=$spkacChallenge?>"/>
- </td>
- </tr>
- <tr>
- <td colspan="2">
- <input class="btn btn-primary" type="submit" name="process" value="<?=_Next?>" />
- <input type='hidden' name='<?=$csrf_name?>' value='<?=$csrf?>'>
- </td>
- </tr>
- </tbody>
-</table>
-</form>
+
import club.wpia.gigi.Gigi;
import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Contract;
import club.wpia.gigi.dbObjects.Group;
import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.SupportedUser;
});
vars.put("agent", user.canVerify());
+ vars.put("agentcontract", Contract.hasSignedContract(user, Contract.ContractType.RA_AGENT_CONTRACT));
vars.put("dob", dobSelector);
vars.put("verificationPoints", user.getVerificationPoints());
vars.put("exppoints", user.getExperiencePoints());
<? if($agent) { ?>
<?=_Yes?>
<? } else { ?>
- <?=_No?>
+ <?=_No?> (<?=_RA Agent Contract signed:?> <? if($agentcontract) { ?><?=_Yes?><? } else { ?><?=_No?><?}?>)
<? } ?>
</td>
</tr>
import javax.servlet.http.HttpServletRequest;
import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.Organisation;
import club.wpia.gigi.dbObjects.Organisation.Affiliation;
import club.wpia.gigi.dbObjects.User;
return false;
}
Affiliation aff = iter.next();
- vars.put("name", aff.getTarget().getPreferredName());
+ Name n = aff.getTarget().getPreferredName();
+ vars.put("name", n);
+ vars.put("nameString", n.toString());
vars.put("master", aff.isMaster() ? l.getTranslation("Master") : "");
vars.put("e-mail", aff.getTarget().getEmail());
return true;
<td><?=$name?></td>
<td><?=$e-mail?></td>
<td><?=$master?></td>
- <td><button class="btn btn-warning btn-confirm" data-confirm="<?=_Do you really want to delete the affiliation of ${name} (${e-mail}) to this organisation??>" data-reply="<?=_Cancel?>,<?=_Delete?>" type="submit" name="del" value="<?=$e-mail?>">X</button> </td>
+ <td><button class="btn btn-warning btn-confirm" data-confirm="<?=_Do you really want to delete the affiliation of ${nameString} (${e-mail}) to this organisation??>" data-reply="<?=_Cancel?>,<?=_Delete?>" type="submit" name="del" value="<?=$e-mail?>">X</button>
+ </td>
</tr>
<? } ?>
<tr>
<h4><?=_Fees?></h4>
<p><?=_You may charge a fee for your expenses if the Applicant has been informed of such costs beforehand.?></p>
-<p><?=_Liability?></h4>
+<h4><?=_Liability?></h4>
<?=_A ${appName} RA Agent who knowingly, or reasonably ought to have known, verifies the identity of an Applicant contrary to the underlying policy may be held liable.?></p>
<h4><?=_Verification Points?></h4>
--- /dev/null
+package club.wpia.gigi.dbObjects;
+
+import static org.junit.Assert.*;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.testUtils.ClientBusinessTest;
+import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
+import club.wpia.gigi.util.RandomToken;
+
+public class TestContract extends ClientBusinessTest {
+
+ @Test
+ public void testContract() throws GigiApiException {
+
+ assertEquals(Contract.getContractByUser(u, Contract.ContractType.RA_AGENT_CONTRACT), null);
+
+ assertFalse(Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+
+ Contract c = Contract.getRAAgentContractByUser(u);
+ assertEquals(c, null);
+
+ c = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ TestMail rc = getMailReceiver().receive(u.getEmail());
+
+ assertEquals(u.getEmail(), rc.getTo());
+ assertThat(rc.getMessage(), CoreMatchers.containsString("signed the RA Agent Contract"));
+ assertEquals(u.getPreferredName().toString(), c.getRAAgentName());
+ assertTrue(Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+
+ Contract c1 = null;
+ try {
+ c1 = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ fail("double add contract must fail");
+ } catch (GigiApiException e) {
+ assertEquals("Contract exists", e.getMessage());
+ }
+
+ c1 = Contract.getContractByUser(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ assertEquals(c.getID(), c1.getID());
+
+ c1 = Contract.getRAAgentContractByUser(u);
+ assertEquals(c.getID(), c1.getID());
+
+ c1 = Contract.getRAAgentContractByToken(c.getToken());
+ assertEquals(c.getID(), c1.getID());
+
+ c1 = Contract.getRAAgentContractByToken(RandomToken.generateToken(16));
+ assertEquals(c1, null);
+
+ }
+
+ @Test
+ public void testRevokeContract() throws GigiApiException {
+ Contract c = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+
+ TestMail rc = getMailReceiver().receive(u.getEmail());
+ assertThat(rc.getMessage(), CoreMatchers.containsString("signed the RA Agent Contract"));
+
+ c.revokeContract();
+
+ rc = getMailReceiver().receive(u.getEmail());
+ assertEquals(u.getEmail(), rc.getTo());
+ assertThat(rc.getMessage(), CoreMatchers.containsString("revoked the RA Agent Contract"));
+ assertFalse(Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+
+ Contract c1 = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ rc = getMailReceiver().receive(u.getEmail());
+
+ assertNotEquals(c.getID(), c1.getID());
+ }
+
+ @Test
+ public void testContractInt() throws GigiApiException {
+ Contract c = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+
+ TestMail rc = getMailReceiver().receive(u.getEmail());
+ assertThat(rc.getMessage(), CoreMatchers.containsString("signed the RA Agent Contract"));
+
+ Contract c1 = Contract.getById(c.getID());
+
+ assertEquals(c.getID(), c1.getID());
+ assertEquals(c.getContractType(), c1.getContractType());
+
+ c1 = Contract.getById(0);
+ assertEquals(null, c1);
+ }
+
+}
assertTrue(u.hasValidTTPAgentChallenge());
}
+ @Test
+ public void testHasContract() throws GigiApiException {
+ assertEquals(false, Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+
+ Contract c = new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ getMailReceiver().receive(u.getEmail());
+ assertEquals(true, Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+
+ c.revokeContract();
+ getMailReceiver().receive(u.getEmail());
+ assertEquals(false, Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT));
+ }
+
@Test
public void testWriteUserLog() throws GigiApiException {
String type = "Log test";
assertThat(result, ArrayContains.contains(CoreMatchers.equalTo(type)));
}
+ @Test
+ public void testValidVerification() throws GigiApiException {
+ User u0 = User.getById(createVerifiedUser("f", "l", createUniqueName() + "@email.com", TEST_PASSWORD));
+ assertFalse(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ add100Points(u0.getId());
+ assertTrue(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ setVerificationDateToPast(u0.getPreferredName());
+ assertFalse(u0.isValidNameVerification(u0.getPreferredName().toString()));
+
+ add100Points(u0.getId());
+ assertTrue(u0.isValidNameVerification(u0.getPreferredName().toString()));
+ }
+
}
import org.junit.Test;
import club.wpia.gigi.GigiApiException;
-import club.wpia.gigi.dbObjects.Country;
-import club.wpia.gigi.dbObjects.Name;
-import club.wpia.gigi.dbObjects.NamePart;
-import club.wpia.gigi.dbObjects.User;
-import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.dbObjects.Country.CountryCodeType;
import club.wpia.gigi.dbObjects.NamePart.NamePartType;
+import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.testUtils.ClientBusinessTest;
import club.wpia.gigi.util.Notary;
assertEquals(10, n4.getVerificationPoints());
assertEquals(10, u.getMaxVerifyPoints());
}
+
+ @Test
+ public void testValidVerification() throws GigiApiException {
+ User u0 = User.getById(createVerifiedUser("f", "l", createUniqueName() + "@email.com", TEST_PASSWORD));
+ assertFalse(u0.getPreferredName().isValidVerification());
+
+ add100Points(u0.getId());
+ assertTrue(u0.getPreferredName().isValidVerification());
+
+ setVerificationDateToPast(u0.getPreferredName());
+ assertFalse(u0.getPreferredName().isValidVerification());
+
+ add100Points(u0.getId());
+ assertTrue(u0.getPreferredName().isValidVerification());
+ }
+
}
authenticate((HttpURLConnection) uc);
String content = IOUtils.readURL(uc);
- assertThat(content, containsString("change to organisation administrator context"));
+ assertThat(content, containsString("Change to organisation administrator context"));
assertThat(content, containsString("You are authenticated via certificate, so you will be able to perform all actions."));
}
@Test
public void testValidChallenges() throws GeneralSecurityException, IOException, GigiApiException, InterruptedException {
-
+ insertRAContract(u.getId());
// test RA Agent challenge
cookie = cookieWithCertificateLogin(u);
+
testChallengeText("you need to pass the RA Agent Challenge", false);
add100Points(u.getId());
import java.security.cert.X509Certificate;
import java.text.SimpleDateFormat;
import java.util.Arrays;
-import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.junit.Test;
-import club.wpia.gigi.crypto.SPKAC;
import club.wpia.gigi.dbObjects.CertificateOwner;
import club.wpia.gigi.dbObjects.Digest;
import club.wpia.gigi.pages.account.certs.CertificateAdd;
import sun.security.x509.GeneralNames;
import sun.security.x509.RFC822Name;
import sun.security.x509.SubjectAlternativeNameExtension;
-import sun.security.x509.X509Key;
public class TestCertificateAdd extends ClientTest {
}, res);
}
- @Test
- public void testSPKAC() throws GeneralSecurityException, IOException {
- testSPKAC(false);
- testSPKAC(true);
- }
-
@Test
public void testIssue() throws IOException, GeneralSecurityException {
HttpURLConnection huc = sendCertificateForm("description");
return uc;
}
- protected String testSPKAC(boolean correctChallenge) throws GeneralSecurityException, IOException {
- HttpURLConnection uc = (HttpURLConnection) ncert.openConnection();
- uc.setRequestProperty("Cookie", cookie);
- String s = IOUtils.readURL(uc);
-
- csrf = extractPattern(s, Pattern.compile("<input [^>]*name='csrf' [^>]*value='([^']*)'>"));
- String challenge = extractPattern(s, Pattern.compile("<keygen [^>]*name=\"SPKAC\" [^>]*challenge=\"([^\"]*)\"/>"));
-
- SPKAC spk = new SPKAC((X509Key) kp.getPublic(), challenge + (correctChallenge ? "" : "b"));
- Signature sign = Signature.getInstance("SHA512WithRSA");
- sign.initSign(kp.getPrivate());
- try {
- String[] res = fillOutFormDirect("SPKAC=" + URLEncoder.encode(Base64.getEncoder().encodeToString(spk.getEncoded(sign)), "UTF-8"));
- if ( !correctChallenge) {
- fail("Should not succeed with wrong challenge.");
- }
- assertArrayEquals(new String[] {
- "client", CertificateRequest.DEFAULT_CN, "", Digest.SHA512.toString()
- }, res);
- } catch (OnPageError e) {
- String error = fetchStartErrorMessage(e.getMessage());
- assertTrue(error, error.startsWith("<p>Challenge mismatch"));
- }
- return csrf;
- }
-
private PKCS10Attributes buildAtts(ObjectIdentifier[] ekuOIDs, GeneralNameInterface... SANs) throws IOException {
CertificateExtensions attributeValue = new CertificateExtensions();
GeneralNames names = new GeneralNames();
import club.wpia.gigi.dbObjects.EmailAddress;
import club.wpia.gigi.dbObjects.Group;
import club.wpia.gigi.pages.account.certs.CertificateRequest;
+import club.wpia.gigi.testUtils.ClientBusinessTest;
import club.wpia.gigi.testUtils.ClientTest;
import club.wpia.gigi.util.AuthorizationContext;
import club.wpia.gigi.util.TimeConditions;
cr.draft();
fail();
} catch (GigiApiException e) {
- assertThat(e.getMessage(), containsString("needs an email ping within the past"));
+ assertThat(e.getMessage(), containsString("needs a verification via email ping within the past"));
}
}
cr.draft();
fail();
} catch (GigiApiException e) {
- assertThat(e.getMessage(), containsString("needs an email ping within the past"));
+ assertThat(e.getMessage(), containsString("needs a verification via email ping within the past"));
+ }
+
+ }
+
+ @Test
+ public void testVerificationInPast() throws IOException, GeneralSecurityException, GigiApiException {
+
+ ClientBusinessTest.setVerificationDateToPast(u.getPreferredName());
+ try {
+ CertificateRequest cr = new CertificateRequest(ac, generatePEMCSR(kp, "CN=a ab"));
+ cr.update(u.getPreferredName().toString(), "SHA512", "client-a", null, null, "email:" + email);
+ cr.draft();
+ fail();
+ } catch (GigiApiException e) {
+ assertThat(e.getMessage(), containsString("The entered name needs a valid verification within the last"));
}
}
}
+ @Test
+ public void testChangePasswordCertLogin() throws IOException, GigiApiException {
+ // no cert login
+ String np = URLEncoder.encode(TEST_PASSWORD + "v1", "UTF-8");
+ String error = executeBasicWebInteraction(cookie, path, "pword1=" + np + "&pword2=" + np);
+ assertNotNull(error);
+
+ // cert login
+ cookie = cookieWithCertificateLogin(u);
+ error = executeBasicWebInteraction(cookie, path, "pword1=" + np + "&pword2=" + np);
+ assertNull(error);
+
+ cookie = login(u.getEmail(), TEST_PASSWORD);
+ loginCertificate = null;
+ }
}
--- /dev/null
+package club.wpia.gigi.pages.account;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.*;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Test;
+
+import club.wpia.gigi.testUtils.ClientTest;
+import club.wpia.gigi.testUtils.IOUtils;
+
+public class TestContract extends ClientTest {
+
+ @Test
+ public void TestContractSignRevoke() throws IOException {
+ // empty contract
+ String res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("This contract concludes an agreement between"));
+ assertThat(res, containsString("not yet"));
+
+ // sign contract
+ executeBasicWebInteraction(cookie, MyDetails.PATH, "action=signContract", 0);
+ getMailReceiver().receive(u.getEmail());
+ res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("This contract concludes an agreement between"));
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ assertThat(res, containsString(sdf.format(new Date())));
+
+ // sign contract
+ executeBasicWebInteraction(cookie, MyDetails.PATH, "action=revokeContract", 0);
+ getMailReceiver().receive(u.getEmail());
+ res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("This contract concludes an agreement between"));
+ assertThat(res, containsString("not yet"));
+ }
+
+ @Test
+ public void TestLanguageSwitch() throws IOException {
+
+ String res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("This contract concludes an agreement between"));
+ // switch to German
+ executeBasicWebInteraction(cookie, MyDetails.PATH, "lang=de", 0);
+ res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("Dieser Vertrag schließt eine Vereinbarung zwischen"));
+ // switch to Turkish, should return default language English
+ executeBasicWebInteraction(cookie, MyDetails.PATH, "lang=tr", 0);
+ res = IOUtils.readURL(get(MyContracts.PATH));
+ assertThat(res, containsString("This contract concludes an agreement between"));
+ }
+
+}
}
private int getLogEntryCount(String readURL) {
- String s = "<tr><th>Support actions";
+ String s = "<tr><th>Actions in user account";
int start = readURL.indexOf(s);
int end = readURL.indexOf("</table>", start);
String logs = readURL.substring(start + s.length(), end);
return c;
}
+ @Test
+ public void testUserDetailsRAAgent() throws IOException, GigiApiException {
+ User u0 = User.getById(createVerifiedUser("Kurti", "Hansel", createUniqueName() + "@email.com", TEST_PASSWORD));
+
+ HttpURLConnection uc = get(cookie, SupportUserDetailsPage.PATH + u0.getId() + "/");
+ String res = IOUtils.readURL(uc);
+ assertThat(res, containsString("No (RA Agent Contract signed: No)"));
+
+ signRAContract(u0);
+ uc = get(cookie, SupportUserDetailsPage.PATH + u0.getId() + "/");
+ res = IOUtils.readURL(uc);
+ assertThat(res, containsString("No (RA Agent Contract signed: Yes)"));
+
+ insertPassedTest(u0.getId());
+ uc = get(cookie, SupportUserDetailsPage.PATH + u0.getId() + "/");
+ res = IOUtils.readURL(uc);
+ assertThat(res, containsString("No (RA Agent Contract signed: Yes)"));
+
+ insertVerificationPoints(u0.getId());
+ uc = get(cookie, SupportUserDetailsPage.PATH + u0.getId() + "/");
+ res = IOUtils.readURL(uc);
+ assertThat(res, not(containsString("RA Agent Contract signed:")));
+
+ }
}
public void testVerifyWithoutValidChallenge() throws IOException, GigiApiException {
cookie = cookieWithCertificateLogin(User.getById(applicantId));
add100Points(applicantId);
+ insertRAContract(applicantId);
addChallengeInPast(applicantId, CATSType.AGENT_CHALLENGE);
assertEquals(403, get(cookie, VerifyPage.PATH).getResponseCode());
addChallenge(applicantId, CATSType.AGENT_CHALLENGE);
package club.wpia.gigi.testUtils;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.database.GigiPreparedStatement;
import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.User;
+import club.wpia.gigi.util.TimeConditions;
public class ClientBusinessTest extends BusinessTest {
throw new Error(e);
}
}
+
+ public static void setVerificationDateToPast(Name name) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `notary` SET `date`=? WHERE `to`=? AND `date`>?");
+ ps.setString(1, date);
+ ps.setInt(2, name.getId());
+ ps.setString(3, date);
+ ps.execute();
+ ps.close();
+ }
}
import club.wpia.gigi.dbObjects.CATS;
import club.wpia.gigi.dbObjects.CATS.CATSType;
import club.wpia.gigi.dbObjects.CertificateProfile;
+import club.wpia.gigi.dbObjects.Contract;
+import club.wpia.gigi.dbObjects.Contract.ContractType;
import club.wpia.gigi.dbObjects.Domain;
import club.wpia.gigi.dbObjects.DomainPingType;
import club.wpia.gigi.dbObjects.User;
import club.wpia.gigi.util.Notary;
import club.wpia.gigi.util.PEM;
import club.wpia.gigi.util.PasswordHash;
+import club.wpia.gigi.util.RandomToken;
import club.wpia.gigi.util.ServerConstants;
import club.wpia.gigi.util.TimeConditions;
import sun.security.pkcs10.PKCS10;
public static void makeAgent(int uid) {
addChallenge(uid, CATSType.AGENT_CHALLENGE);
add100Points(uid);
+ insertRAContract(uid);
}
public static void addChallenge(int uid, CATSType ct) {
}
public static void add100Points(int uid) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ try (GigiPreparedStatement ps2 = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, points='100', `date`=?")) {
+ ps2.setInt(1, uid);
+ ps2.setInt(2, User.getById(uid).getPreferredName().getId());
+ ps2.setString(3, sdf.format(new Date(System.currentTimeMillis())));
+ ps2.execute();
+ }
+ }
+
+ public static void insertRAContract(int uid) {
+ // insert signed RA Contract
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("INSERT INTO `user_contracts` SET `memid`=?, `token`=?, `document`=?::`contractType`,`agentname`=?")) {
+ ps.setInt(1, uid);
+ ps.setString(2, RandomToken.generateToken(32));
+ ps.setEnum(3, ContractType.RA_AGENT_CONTRACT);
+ ps.setString(4, User.getById(uid).getPreferredName().toString());
+ ps.execute();
+ }
+ }
+
+ public static void insertVerificationPoints(int uid) {
+ // insert Verification Points
try (GigiPreparedStatement ps2 = new GigiPreparedStatement("INSERT INTO `notary` SET `from`=?, `to`=?, points='100'")) {
ps2.setInt(1, uid);
ps2.setInt(2, User.getById(uid).getPreferredName().getId());
}
}
+ public static void insertPassedTest(int uid) {
+ // insert passed test
+ try (GigiPreparedStatement ps1 = new GigiPreparedStatement("INSERT INTO cats_passed SET user_id=?, variant_id=?, language='en_EN', version='1'")) {
+ ps1.setInt(1, uid);
+ ps1.setInt(2, CATSType.AGENT_CHALLENGE.getId());
+ ps1.execute();
+ }
+ }
+
public MailReceiver getMailReceiver() {
throw new Error("Feature requires Business or ManagedTest.");
}
c.add(Calendar.MONTH, -Notary.LIMIT_MAX_MONTHS_VERIFICATION + 1);
return sdf.format(new Date(c.getTimeInMillis()));
}
+
+ public void signRAContract(User u) throws GigiApiException {
+ new Contract(u, ContractType.RA_AGENT_CONTRACT);
+ getMailReceiver().receive(u.getEmail());
+ }
}
import club.wpia.gigi.database.GigiPreparedStatement;
import club.wpia.gigi.dbObjects.CATS;
import club.wpia.gigi.dbObjects.CATS.CATSType;
+import club.wpia.gigi.dbObjects.Contract;
import club.wpia.gigi.dbObjects.Country;
import club.wpia.gigi.dbObjects.Country.CountryCodeType;
import club.wpia.gigi.dbObjects.Group;
import club.wpia.gigi.dbObjects.Verification.VerificationType;
import club.wpia.gigi.output.DateSelector;
import club.wpia.gigi.testUtils.BusinessTest;
+import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
public class TestNotary extends BusinessTest {
assertEquals(100, applicant.getVerificationPoints());
assertFalse(applicant.canVerify());
CATS.enterResult(applicant, CATSType.AGENT_CHALLENGE, new Date(), "de", "1");
+ new Contract(applicant, Contract.ContractType.RA_AGENT_CONTRACT);
+ TestMail rc = getMailReceiver().receive(applicant.getEmail());
assertTrue(applicant.canVerify());
}
}
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
+import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Calendar;
import club.wpia.gigi.GigiApiException;
import club.wpia.gigi.crypto.SPKAC;
import club.wpia.gigi.database.GigiPreparedStatement;
+import club.wpia.gigi.database.GigiResultSet;
import club.wpia.gigi.dbObjects.CATS;
import club.wpia.gigi.dbObjects.CATS.CATSType;
import club.wpia.gigi.dbObjects.Certificate;
import club.wpia.gigi.dbObjects.Certificate.CertificateStatus;
import club.wpia.gigi.dbObjects.CertificateOwner;
+import club.wpia.gigi.dbObjects.Contract;
+import club.wpia.gigi.dbObjects.Contract.ContractType;
import club.wpia.gigi.dbObjects.Country;
import club.wpia.gigi.dbObjects.Digest;
import club.wpia.gigi.dbObjects.Domain;
import club.wpia.gigi.dbObjects.DomainPingType;
import club.wpia.gigi.dbObjects.EmailAddress;
import club.wpia.gigi.dbObjects.Group;
+import club.wpia.gigi.dbObjects.Name;
import club.wpia.gigi.dbObjects.NamePart;
import club.wpia.gigi.dbObjects.NamePart.NamePartType;
import club.wpia.gigi.dbObjects.User;
ps.setString(6, getRandomCountry().getCode());
ps.execute();
}
+ new Contract(u, ContractType.RA_AGENT_CONTRACT);
return u;
}
}
CATS.enterResult(u, t, new Date(System.currentTimeMillis()), "en_EN", "1");
}
+ private void expireCATS(User u, CATSType t) {
+ try (GigiPreparedStatement ps = new GigiPreparedStatement("SELECT `id` FROM `cats_passed` WHERE `user_id`=? AND `variant_id`=? AND `pass_date`>?")) {
+ ps.setInt(1, u.getId());
+ ps.setInt(2, t.getId());
+ ps.setTimestamp(3, new Timestamp(System.currentTimeMillis() - DayDate.MILLI_DAY * 366));
+ ps.execute();
+ GigiResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ GigiPreparedStatement ps1 = new GigiPreparedStatement("UPDATE `cats_passed` SET `pass_date`=? WHERE `id`=?");
+ ps1.setTimestamp(1, new Timestamp(System.currentTimeMillis() - DayDate.MILLI_DAY * 367));
+ ps1.setInt(2, rs.getInt(1));
+ ps1.execute();
+ ps1.close();
+ }
+ }
+
+ }
+
private static Manager instance;
private static final Template t = new Template(Manager.class.getResource("ManagerMails.templ"));
CATSType test = CATSType.values()[Integer.parseInt(testId)];
passCATS(byEmail, test);
resp.getWriter().println("Test '" + test.getDisplayName() + "' was added to user account.");
+ } else if (req.getParameter("catsexpire") != null) {
+ String mail = req.getParameter("catsEmail");
+ String testId = req.getParameter("catsType");
+ User byEmail = User.getByEmail(mail);
+ if (byEmail == null) {
+ resp.getWriter().println("User not found.");
+ return;
+ }
+ if (testId == null) {
+ resp.getWriter().println("No test given.");
+ return;
+ }
+ CATSType test = CATSType.values()[Integer.parseInt(testId)];
+ expireCATS(byEmail, test);
+ resp.getWriter().println("Test '" + test.getDisplayName() + "' is set expired for user account.");
} else if (req.getParameter("verify") != null) {
String mail = req.getParameter("verifyEmail");
String verificationPoints = req.getParameter("verificationPoints");
}
int vp = 0;
- int agentNumber = 0;
+ int verifications = 0;
+ String info = "";
try {
try {
vp = Integer.parseInt(verificationPoints);
} catch (NumberFormatException e) {
- throw new GigiApiException("No valid Verification Points entered.");
+ resp.getWriter().println("The value for Verification Points must be an integer.</br>");
+ vp = 0;
}
- if (vp > 100) { // only allow max 100 Verification points
- vp = 100;
- }
+ int agentNumber = addVerificationPoints(vp, byEmail);
while (vp > 0) {
int currentVP = 10;
if (vp < 10) {
currentVP = vp;
}
- Notary.verify(getAgent(agentNumber), byEmail, byEmail.getPreferredName(), byEmail.getDoB(), currentVP, "Testmanager Verify up code", validVerificationDateString(), VerificationType.FACE_TO_FACE, getRandomCountry());
+ if (Notary.checkVerificationIsPossible(getAgent(agentNumber), byEmail.getPreferredName())) {
+
+ Notary.verify(getAgent(agentNumber), byEmail, byEmail.getPreferredName(), byEmail.getDoB(), currentVP, "Testmanager Verify up code", validVerificationDateString(), VerificationType.FACE_TO_FACE, getRandomCountry());
+ vp -= currentVP;
+ verifications += 1;
+
+ }
agentNumber += 1;
- vp -= currentVP;
+ if (agentNumber >= agents.length) {
+ info = "<br/>The limit of agents is reached. You cannot add any more Verification Points to the preferred name of this user using this method.";
+ break;
+ }
}
} catch (GigiApiException e) {
throw new Error(e);
}
- resp.getWriter().println("User has been verified " + agentNumber + " times.");
+ resp.getWriter().println("User has been verified " + verifications + " times." + info);
+
+ } else if (req.getParameter("verifyexpire") != null) {
+ String mail = req.getParameter("verifyEmail");
+ User byEmail = User.getByEmail(mail);
+ if (byEmail == null) {
+ resp.getWriter().println("User not found.");
+ return;
+ } else {
+ setVerificationDateToPast(byEmail.getPreferredName());
+ }
+ resp.getWriter().println("Verification set to time past the limit.");
} else if (req.getParameter("letverify") != null) {
String mail = req.getParameter("letverifyEmail");
User byEmail = User.getByEmail(mail);
pingExempt.remove(dom);
resp.getWriter().println("Updated domains exempt from pings. Current set: <br/>");
resp.getWriter().println(HTMLEncoder.encodeHTML(pingExempt.toString()));
+ } else if (req.getParameter("makeAgent") != null) {
+ User u = User.getByEmail(req.getParameter("agentEmail"));
+ if (u == null) {
+ resp.getWriter().println("User not found, or found user is not allowed to verify.");
+ } else {
+ if (u.getVerificationPoints() < 100) {
+ addVerificationPoints(100, u);
+ }
+ if ( !u.hasPassedCATS()) {
+ passCATS(u, CATSType.AGENT_CHALLENGE);
+ }
+ if ( !Contract.hasSignedContract(u, Contract.ContractType.RA_AGENT_CONTRACT)) {
+ try {
+ new Contract(u, Contract.ContractType.RA_AGENT_CONTRACT);
+ } catch (GigiApiException e) {
+ throw new Error(e);
+ }
+ }
+ resp.getWriter().println("User has all requirements to be an RA Agent");
+ }
}
resp.getWriter().println("<br/><a href='" + PATH + "'>Go back</a>");
}
+ private int addVerificationPoints(int vp, User byEmail) throws Error {
+ int agentNumber = 0;
+
+ try {
+ if (vp > 100) { // only allow max 100 Verification points
+ vp = 100;
+ }
+
+ while (vp > 0) {
+ int currentVP = 10;
+ if (vp < 10) {
+ currentVP = vp;
+ }
+ Notary.verify(getAgent(agentNumber), byEmail, byEmail.getPreferredName(), byEmail.getDoB(), currentVP, "Testmanager Verify up code", validVerificationDateString(), VerificationType.FACE_TO_FACE, getRandomCountry());
+ agentNumber += 1;
+ vp -= currentVP;
+ }
+
+ } catch (GigiApiException e) {
+ throw new Error(e);
+ }
+ return agentNumber;
+ }
+
private void fetchMails(HttpServletRequest req, HttpServletResponse resp, String mail) throws IOException {
final LinkedList<String> mails = emails.get(mail);
HashMap<String, Object> vars = new HashMap<>();
form.output(resp.getWriter(), getLanguage(req), vars);
}
+
+ private static void setVerificationDateToPast(Name name) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ c.add(Calendar.MONTH, -TimeConditions.getInstance().getVerificationMonths());
+ String date = sdf.format(new Date(c.getTimeInMillis()));
+ GigiPreparedStatement ps = new GigiPreparedStatement("UPDATE `notary` SET `date`=? WHERE `to`=? AND `date`>?");
+ ps.setString(1, date);
+ ps.setInt(2, name.getId());
+ ps.setString(3, date);
+ ps.execute();
+ ps.close();
+ }
}
<? } ?>
</select>
<input type="submit" value="Add Challenge" name="cats"/>
+<input type="submit" value="Set Challenge expired" name="catsexpire"/>
</td></tr>
+
+</td></tr>
+<tr><td>
+Make RA Agent:
+</td><td>
+Email: <input type="text" name="agentEmail"/>
+</td><td>
+Current requirements: 100 VP, passed Assurer Challenge, signed RA Agent Contract</br>
+<input type="submit" value="Add requirements" name="makeAgent"/>
+</td></tr>
+
<tr><td>
Add 100 Verification Points:
</td><td>
</td><td>
Verification Points to issue to preferred name: </br>
<input type="text" name="verificationPoints" value="100"/> <input type="submit" value="Add Points" name="verify"/>
-
+<input type="submit" value="Set Verification date past limit" name="verifyexpire"/>
</td></tr>
<tr><td>