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
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.?>
+
return false;
}
+ if ( !Contract.hasSignedContract(this, Contract.ContractType.RA_AGENT_CONTRACT)) {
+ return false;
+ }
+
return hasPassedCATS();
}
--- /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.pages.LoginPage;
+import club.wpia.gigi.pages.Page;
+
+public class MyContracts extends Page {
+
+ public static final String PATH = "/account/contracts";
+
+ 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()));
+ }
+
+ 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>Name: <?=$raname?> - RA Agent hereafter -</p>
+
+ <p>and</p>
+
+ <p>SomeCA - CA hereafter -</p>
+ <p>SomeAddress</p>
+
+ <p>regarding the conduction of registration authority tasks.</p>
+
+ <p>signed <?=$csdate?></p>
+ </div>
+</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 {
} 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;
}
}
<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>
--- /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";
@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();
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"));
}
}
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);
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 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());
+ ps2.execute();
+ }
+ }
+
+ 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 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;
ps.setString(6, getRandomCountry().getCode());
ps.execute();
}
+ new Contract(u, ContractType.RA_AGENT_CONTRACT);
return u;
}
}
}
int vp = 0;
- int agentNumber = 0;
try {
- try {
- vp = Integer.parseInt(verificationPoints);
- } catch (NumberFormatException e) {
- throw new GigiApiException("No valid Verification Points entered.");
- }
-
- 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);
+ vp = Integer.parseInt(verificationPoints);
+ } catch (NumberFormatException e) {
+ resp.getWriter().println("No valid Verification Points entered.</br>");
+ vp = 0;
}
+ int agentNumber = addVerificationPoints(vp, byEmail);
+
resp.getWriter().println("User has been verified " + agentNumber + " times.");
} else if (req.getParameter("letverify") != null) {
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<>();
<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"/>
-
</td></tr>
<tr><td>