]> WPIA git - gigi.git/commitdiff
Merge changes I18f5f27f,I27ec303f,I78009fe3
authorFelix Dörre <felix@dogcraft.de>
Tue, 20 Feb 2018 20:21:39 +0000 (21:21 +0100)
committerGerrit Code Review <gigi-system@dogcraft.de>
Tue, 20 Feb 2018 20:21:39 +0000 (21:21 +0100)
* changes:
  fix: avoid resource leak when generating OCSP requests
  fix: prevent possible NPE on failure to list the CA directory
  chg: ensure actor, target and support ticket are non-null

26 files changed:
links.txt
src/club/wpia/gigi/Gigi.java
src/club/wpia/gigi/dbObjects/EmailAddress.java
src/club/wpia/gigi/dbObjects/Organisation.java
src/club/wpia/gigi/pages/RootCertPage.java
src/club/wpia/gigi/pages/RootCertPage.templ
src/club/wpia/gigi/pages/account/MyDetails.java
src/club/wpia/gigi/pages/account/MyOrganisationsForm.templ [deleted file]
src/club/wpia/gigi/pages/account/certs/CertificateRequest.java
src/club/wpia/gigi/pages/admin/support/FindUserByDomainForm.java
src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.java [new file with mode: 0644]
src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.templ [new file with mode: 0644]
src/club/wpia/gigi/pages/admin/support/SupportUserDetailsForm.java
src/club/wpia/gigi/pages/orga/AffiliationForm.java
src/club/wpia/gigi/pages/orga/CreateOrgForm.templ
src/club/wpia/gigi/pages/orga/CreateOrgPage.java
src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java [moved from src/club/wpia/gigi/pages/account/MyOrganisationsForm.java with 94% similarity]
src/club/wpia/gigi/pages/orga/MyOrganisationsForm.templ [new file with mode: 0644]
src/club/wpia/gigi/pages/orga/SwitchOrganisation.java [new file with mode: 0644]
src/club/wpia/gigi/pages/orga/ViewOrgPage.java
src/club/wpia/gigi/util/CertExporter.java
tests/club/wpia/gigi/pages/account/TestCertificateRequest.java
tests/club/wpia/gigi/pages/admin/TestSEAdminNotificationMail.java
tests/club/wpia/gigi/pages/admin/TestSEAdminPageUserDomainSearch.java
tests/club/wpia/gigi/pages/orga/TestOrgSwitch.java [new file with mode: 0644]
util-testing/club/wpia/gigi/pages/Manager.templ

index e68f4e90053cb94d2d367c7b8d0760ba638cf6ba..d9dd6808812621b2c7c099e464769d5f73d66be0 100644 (file)
--- a/links.txt
+++ b/links.txt
@@ -11,7 +11,9 @@
 /kb/lostPassword
 /kb/goodPassword
 /kb/verificationHandbook
+/kb/truststores
 /ttp/user
 /ttp/country
 /blog
 /imprint
+/isocode http://www.iso.org/iso/home/standards/country_codes/iso-3166-1_decoding_table.htm
index 6d8996c97cac5633a1d14b9ef5e7eb953bdc47e5..15a52143e745394df63b579e7dedb1ac2c14e003 100644 (file)
@@ -64,6 +64,7 @@ import club.wpia.gigi.pages.admin.support.FindCertPage;
 import club.wpia.gigi.pages.admin.support.FindUserByDomainPage;
 import club.wpia.gigi.pages.admin.support.FindUserByEmailPage;
 import club.wpia.gigi.pages.admin.support.SupportEnterTicketPage;
+import club.wpia.gigi.pages.admin.support.SupportOrgDomainPage;
 import club.wpia.gigi.pages.admin.support.SupportUserDetailsPage;
 import club.wpia.gigi.pages.error.AccessDenied;
 import club.wpia.gigi.pages.error.PageNotFound;
@@ -71,6 +72,7 @@ import club.wpia.gigi.pages.main.CertStatusRequestPage;
 import club.wpia.gigi.pages.main.KeyCompromisePage;
 import club.wpia.gigi.pages.main.RegisterPage;
 import club.wpia.gigi.pages.orga.CreateOrgPage;
+import club.wpia.gigi.pages.orga.SwitchOrganisation;
 import club.wpia.gigi.pages.orga.ViewOrgPage;
 import club.wpia.gigi.pages.statistics.StatisticsRoles;
 import club.wpia.gigi.pages.wot.Points;
@@ -164,6 +166,7 @@ public final class Gigi extends HttpServlet {
             putPage(TTPAdminPage.PATH + "/*", new TTPAdminPage(), admMenu);
             putPage(CreateOrgPage.DEFAULT_PATH, new CreateOrgPage(), orgAdm);
             putPage(ViewOrgPage.DEFAULT_PATH + "/*", new ViewOrgPage(), orgAdm);
+            putPage(SwitchOrganisation.PATH, new SwitchOrganisation(), orgAdm);
 
             Menu support = createMenu("Support Console");
             putPage(SupportEnterTicketPage.PATH, new SupportEnterTicketPage(), support);
@@ -173,6 +176,7 @@ public final class Gigi extends HttpServlet {
 
             Menu account = createMenu("My Account");
             putPage(SupportUserDetailsPage.PATH + "*", new SupportUserDetailsPage(), null);
+            putPage(SupportOrgDomainPage.PATH + "*", new SupportOrgDomainPage(), null);
             putPage(ChangePasswordPage.PATH, new ChangePasswordPage(), account);
             putPage(History.PATH, new History(false), account);
             putPage(FindAgentAccess.PATH, new OneFormPage("Access to Find Agent", FindAgentAccess.class), account);
index 319e415b93e86a97d89da13ddcf5b18e8df12ec8..7d4c984e8e8233892e537799ce8b3ec4b174e4d9 100644 (file)
@@ -13,6 +13,7 @@ import club.wpia.gigi.email.MailProbe;
 import club.wpia.gigi.localisation.Language;
 import club.wpia.gigi.output.template.SprintfCommand;
 import club.wpia.gigi.util.RandomToken;
+import club.wpia.gigi.util.TimeConditions;
 
 public class EmailAddress implements IdCachable, Verifyable {
 
@@ -122,9 +123,10 @@ public class EmailAddress implements IdCachable, Verifyable {
     }
 
     public boolean isVerified() {
-        try (GigiPreparedStatement statmt = new GigiPreparedStatement("SELECT 1 FROM `emailPinglog` WHERE `email`=? AND `uid`=? AND `type`='active' AND `status`='success'")) {
+        try (GigiPreparedStatement statmt = new GigiPreparedStatement("SELECT 1 FROM `emailPinglog` WHERE `email`=? AND `uid`=? AND `type`='active' AND `status`='success' AND `when` > (now() - interval '1 months' * ?::INTEGER)")) {
             statmt.setString(1, address);
             statmt.setInt(2, owner.getId());
+            statmt.setInt(3, TimeConditions.getInstance().getEmailPingMonths());
             GigiResultSet e = statmt.executeQuery();
             return e.next();
         }
index c9754565744b0d107baee2515f802e2e2cae9fbe..0e1c8661fab8b5033072f9d9cb3603935e196dae 100644 (file)
@@ -152,7 +152,7 @@ public class Organisation extends CertificateOwner {
 
     public synchronized void addAdmin(User admin, User actor, boolean master) throws GigiApiException {
         if (actor == admin) {
-            throw new GigiApiException("You may not add yourself as Organisation Admin. Ask another Organisation Agent to do so.");
+            throw new GigiApiException("You may not add yourself as Organisation Admin. Ask another Organisation Agent or Organisation Admin to do so.");
         }
         if ( !admin.canVerify()) {
             throw new GigiApiException("Cannot add person who is not RA Agent.");
index 9d1d9c90eff4ec01f5f7ba1fc22adac68048a25e..b065463d971f075a1e1cde81de4e868a0b183b51 100644 (file)
@@ -2,13 +2,13 @@ package club.wpia.gigi.pages;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 
@@ -16,11 +16,14 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.dbObjects.CACertificate;
 import club.wpia.gigi.localisation.Language;
 import club.wpia.gigi.output.template.Outputable;
+import club.wpia.gigi.util.CertExporter;
 import club.wpia.gigi.util.HTMLEncoder;
 import club.wpia.gigi.util.PEM;
+import club.wpia.gigi.util.ServerConstants;
 
 public class RootCertPage extends Page {
 
@@ -30,6 +33,8 @@ public class RootCertPage extends Page {
 
     private final OutputableCertificate rootP;
 
+    private final String appName = ServerConstants.getAppName().toLowerCase();
+
     private class OutputableCertificate implements Outputable {
 
         private final CACertificate target;
@@ -97,6 +102,7 @@ public class RootCertPage extends Page {
     public boolean beforeTemplate(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         if (req.getParameter("pem") != null && root != null) {
             resp.setContentType("application/x-x509-ca-cert");
+            resp.setHeader("Content-Disposition", "attachment; filename=\"" + appName + "_roots.crt\"");
             ServletOutputStream out = resp.getOutputStream();
             try {
                 out.println(PEM.encode("CERTIFICATE", root.getEncoded()));
@@ -104,8 +110,23 @@ public class RootCertPage extends Page {
                 e.printStackTrace();
             }
             return true;
+        } else if (req.getParameter("bundle") != null && root != null) {
+            resp.setContentType("application/x-x509-ca-cert");
+            resp.setHeader("Content-Disposition", "attachment; filename=\"" + appName + "_intermediate_bundle.p7b\"");
+            ServletOutputStream out = resp.getOutputStream();
+            try {
+                CertExporter.writeCertBundle(out);
+            } catch (CertificateEncodingException e) {
+                e.printStackTrace();
+            } catch (GeneralSecurityException e) {
+                e.printStackTrace();
+            } catch (GigiApiException e) {
+                e.printStackTrace();
+            }
+            return true;
         } else if (req.getParameter("cer") != null && root != null) {
             resp.setContentType("application/x-x509-ca-cert");
+            resp.setHeader("Content-Disposition", "attachment; filename=\"" + appName + "_roots.cer\"");
             ServletOutputStream out = resp.getOutputStream();
             try {
                 out.write(root.getEncoded());
@@ -119,8 +140,9 @@ public class RootCertPage extends Page {
 
     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        HashMap<String, Object> map = new HashMap<String, Object>();
+        Map<String, Object> map = Page.getDefaultVars(req);
         map.put("root", rootP);
+        map.put("bundle", appName + "_intermediate_bundle.p7b");
         getDefaultTemplate().output(resp.getWriter(), getLanguage(req), map);
 
     }
index 0f6a8f05ed93670581b92db891f056dc037fe6c8..899470ec688551f4415ab0420c14c6b5046bd062 100644 (file)
@@ -1,5 +1,8 @@
-<?=_The Root certificate is available for download here. Choose your preferred format:?><br/>
-<a href="?pem">PEM</a> <a href="?cer">DER</a>
+<p><?=_The Root certificate is available for download here. Choose your preferred format:?><br/>
+<a href="?pem">PEM</a> <a href="?cer">DER</a></p>
+<p><?=_A p7b file with all intermediate certificates is available for download here:?><br/>
+<a href="?bundle"><?=$bundle?></a></p>
+<p><?=_Find information how to add the root and intermediate certificates to the truststore of your browser or operating system in our !(/kb/truststores)knowledge base!'</a>'.?></p>
 <p>
 <?=_A full list of all DER-encoded intermediate certificates is provided below:?>
 </p>
index 12d127faf38f00bca879dbf6eda7446f7f4845b8..b4a52d4eb1609121127b2e4cb6731451f99f4214 100644 (file)
@@ -10,6 +10,7 @@ import javax.servlet.http.HttpServletResponse;
 import club.wpia.gigi.output.template.Form;
 import club.wpia.gigi.pages.LoginPage;
 import club.wpia.gigi.pages.Page;
+import club.wpia.gigi.pages.orga.MyOrganisationsForm;
 
 public class MyDetails extends Page {
 
diff --git a/src/club/wpia/gigi/pages/account/MyOrganisationsForm.templ b/src/club/wpia/gigi/pages/account/MyOrganisationsForm.templ
deleted file mode 100644 (file)
index 96d7bbe..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<input type='hidden' name='orgaForm' value='orga'/>
-<h2><?=_My Organisations?></h2>
-<table class="table">
-<? foreach($orgas) { ?>
-<tr><td><?=$orgName?></td><td><?=$orgID?></td><td><input class="btn btn-info" type='submit' value='<?=_switch to this organisation?>' name='org:<?=$orgID?>'/></td></tr>
-<? } ?>
-</table>
-<? if($personal) { ?>
-<input class="btn btn-primary" type='submit' value='<?=_switch back to personal use?>' name='org-leave'/>
-<? } ?>
index 8a1bc5943ea8dad8c66ed7a5e360edd83278f13c..cdf4dd41eed765b5959cb979d571a9462d0bd89f 100644 (file)
@@ -35,6 +35,7 @@ import club.wpia.gigi.util.DomainAssessment;
 import club.wpia.gigi.util.PEM;
 import club.wpia.gigi.util.RateLimit;
 import club.wpia.gigi.util.ServerConstants;
+import club.wpia.gigi.util.TimeConditions;
 import sun.security.pkcs.PKCS9Attribute;
 import sun.security.pkcs10.PKCS10;
 import sun.security.pkcs10.PKCS10Attribute;
@@ -356,8 +357,8 @@ public class CertificateRequest {
                         valid = false;
                     }
                 }
-            } else if (san.getType() == SANType.EMAIL) {
-                if (emailTemp != null && owner.isValidEmail(san.getName())) {
+            } else if (san.getType() == SANType.EMAIL && emailTemp != null) {
+                if (owner.isValidEmail(san.getName())) {
                     if (pMail != null && !emailTemp.isMultiple()) {
                         // remove
                     } else {
@@ -367,6 +368,11 @@ public class CertificateRequest {
                         filteredSANs.add(san);
                         continue;
                     }
+                } 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())));
+                    break;
                 }
             }
             error.mergeInto(new GigiApiException(SprintfCommand.createSimple(//
index 75c8c590166e1afe36485d2421d34b29c8f76225..500a3b0ae5e886cce3ba87859b338a6dfc9feab0 100644 (file)
@@ -59,7 +59,7 @@ public class FindUserByDomainForm extends Form {
         if (res instanceof User) {
             return new RedirectResult(SupportUserDetailsPage.PATH + res.getId() + "/");
         } else if (res instanceof Organisation) {
-            return new RedirectResult("/support/domain/" + res.getId());
+            return new RedirectResult(SupportOrgDomainPage.PATH + d.getId());
         } else {
             throw new PermamentFormException(new GigiApiException("Unknown owner type."));
         }
diff --git a/src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.java b/src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.java
new file mode 100644 (file)
index 0000000..019f249
--- /dev/null
@@ -0,0 +1,59 @@
+package club.wpia.gigi.pages.admin.support;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import club.wpia.gigi.dbObjects.Domain;
+import club.wpia.gigi.dbObjects.Organisation;
+import club.wpia.gigi.pages.Page;
+import club.wpia.gigi.util.AuthorizationContext;
+
+public class SupportOrgDomainPage extends Page {
+
+    public static final String PATH = "/support/domain/";
+
+    public SupportOrgDomainPage() {
+        super("Support: Organisation Domain");
+    }
+
+    @Override
+    public boolean isPermitted(AuthorizationContext ac) {
+        return ac != null && ac.canSupport();
+    }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        Domain orgDomain = getDomain(req, resp);
+        if (orgDomain == null) {
+            return;
+        }
+
+        Organisation org = Organisation.getById(orgDomain.getOwner().getId());
+        Map<String, Object> vars = getDefaultVars(req);
+        vars.put("domain", orgDomain.getSuffix());
+        vars.put("organisation", org.getName());
+
+        getDefaultTemplate().output(resp.getWriter(), getLanguage(req), vars);
+    }
+
+    private Domain getDomain(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        int id = -1;
+        String[] idP = req.getPathInfo().split("/");
+        try {
+            id = Integer.parseInt(idP[idP.length - 1]);
+        } catch (NumberFormatException e) {
+            resp.sendError(400);
+            return null;
+        }
+        final Domain domain = Domain.getById(id);
+        if (domain == null) {
+            resp.sendError(400);
+            return null;
+        }
+        return domain;
+    }
+
+}
diff --git a/src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.templ b/src/club/wpia/gigi/pages/admin/support/SupportOrgDomainPage.templ
new file mode 100644 (file)
index 0000000..79f5e34
--- /dev/null
@@ -0,0 +1 @@
+<?=_The domain '${domain}' is linked to the organisation '${organisation}'.?>
index 969cbe62fee6d1df01b09f37ec3eff885f48274a..7445c52c54946cc87c00c02780aebdefeef88f20 100644 (file)
@@ -6,6 +6,7 @@ import java.util.Set;
 
 import javax.servlet.http.HttpServletRequest;
 
+import club.wpia.gigi.Gigi;
 import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.dbObjects.Group;
 import club.wpia.gigi.dbObjects.Name;
@@ -20,6 +21,8 @@ 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.pages.account.MyDetails;
+import club.wpia.gigi.util.AuthorizationContext;
 
 public class SupportUserDetailsForm extends Form {
 
@@ -42,12 +45,31 @@ public class SupportUserDetailsForm extends Form {
         if (user.getTicket() == null) {
             throw new GigiApiException("No ticket number set.");
         }
+
+        int numActions = 0;
+        numActions += req.getParameter("detailupdate") != null ? 1 : 0;
+        numActions += req.getParameter("addGroup") != null ? 1 : 0;
+        numActions += req.getParameter("removeGroup") != null ? 1 : 0;
+        numActions += req.getParameter("resetPass") != null ? 1 : 0;
+
+        if (numActions != 1) {
+            throw new GigiApiException("More than one action requested!");
+        }
+
         if (user.getTargetUser() == LoginPage.getUser(req)) {
+            if (req.getParameter("removeGroup") != null) {
+                value.update(req);
+                Group toMod = value.getGroup();
+                if (toMod == Group.SUPPORTER) {
+                    user.revoke(toMod);
+                    AuthorizationContext ac = LoginPage.getAuthorizationContext(req);
+                    req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(ac.getActor(), ac.getActor()));
+                    return new RedirectResult(MyDetails.PATH);
+                }
+            }
             throw new GigiApiException("Supporter may not modify himself.");
         }
-        if ((req.getParameter("detailupdate") != null ? 1 : 0) + (req.getParameter("addGroup") != null ? 1 : 0) + (req.getParameter("removeGroup") != null ? 1 : 0) + (req.getParameter("resetPass") != null ? 1 : 0) != 1) {
-            throw new GigiApiException("More than one action requested!");
-        }
+
         if (req.getParameter("addGroup") != null || req.getParameter("removeGroup") != null) {
             value.update(req);
             Group toMod = value.getGroup();
index 278e38b7d56b2364d51e30b97191cfffcb46f23a..1fd1c010ff09eb102f23263aba1a610500e06638 100644 (file)
@@ -39,7 +39,7 @@ public class AffiliationForm extends Form {
         } else if (req.getParameter("do_affiliate") != null) {
             User byEmail = User.getByEmail(req.getParameter("email"));
             if (byEmail == null) {
-                throw new GigiApiException("To add an admin, the email address is required.");
+                throw new GigiApiException("To add an admin, the email address needs to be known to the system.");
             }
             if (byEmail.canVerify()) {
                 o.addAdmin(byEmail, LoginPage.getUser(req), req.getParameter("master") != null);
index 7fdd98deeb63be2e4a8d20c016d06dc11adc771f..865f8ddead483b0e570194c21a44871476354c2a 100644 (file)
@@ -28,7 +28,7 @@
     <td><?=_Country?>:</td>
     <td>
       <?=$C?>
-      <?=_(2 letter !'<a href="http://www.iso.org/iso/home/standards/country_codes/iso-3166-1_decoding_table.htm">'ISO code!'</a>')?>
+      <?=_(2 letter !(/isocode)ISO code!'</a>')?>
     </td>
   </tr>
   <? if($edit) { ?>
index 4eab3d6e274dcb79ba319e14fd2f9cea07a86eda..98076fe94dd347c5fbf13ada4d984c9cfd895acb 100644 (file)
@@ -1,7 +1,6 @@
 package club.wpia.gigi.pages.orga;
 
 import java.io.IOException;
-import java.util.HashMap;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -27,6 +26,6 @@ public class CreateOrgPage extends ManagedFormPage {
 
     @Override
     public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
-        new CreateOrgForm(req).output(resp.getWriter(), getLanguage(req), new HashMap<String, Object>());
+        new CreateOrgForm(req).output(resp.getWriter(), getLanguage(req), getDefaultVars(req));
     }
 }
similarity index 94%
rename from src/club/wpia/gigi/pages/account/MyOrganisationsForm.java
rename to src/club/wpia/gigi/pages/orga/MyOrganisationsForm.java
index 835973a12b385b300808daa05d8c6ee31a41cb8f..8858c5c0af631946f59850aabd348a8ed2e7be3d 100644 (file)
@@ -1,4 +1,4 @@
-package club.wpia.gigi.pages.account;
+package club.wpia.gigi.pages.orga;
 
 import java.io.PrintWriter;
 import java.util.Enumeration;
@@ -33,7 +33,7 @@ public class MyOrganisationsForm extends Form {
     public SubmissionResult submit(HttpServletRequest req) throws GigiApiException {
         if (req.getParameter("org-leave") != null) {
             req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(target.getActor(), target.getActor()));
-            return new RedirectResult(MyDetails.PATH);
+            return new RedirectResult(SwitchOrganisation.PATH);
         }
         Enumeration<String> i = req.getParameterNames();
         int orgId = -1;
@@ -52,7 +52,7 @@ public class MyOrganisationsForm extends Form {
             if (org.getId() == orgId) {
 
                 req.getSession().setAttribute(Gigi.AUTH_CONTEXT, new AuthorizationContext(org, target.getActor()));
-                return new RedirectResult(MyDetails.PATH);
+                return new RedirectResult(SwitchOrganisation.PATH);
             }
         }
         throw new PermamentFormException(new GigiApiException("Context switch failed."));
diff --git a/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.templ b/src/club/wpia/gigi/pages/orga/MyOrganisationsForm.templ
new file mode 100644 (file)
index 0000000..5c63f04
--- /dev/null
@@ -0,0 +1,9 @@
+<h2><?=_My Organisations?></h2>
+<? if($personal) { ?>
+<button class="btn btn-primary" type='submit' value='personal' name='org-leave'/><?=_Switch back to personal context?></button>
+<? } ?>
+<table class="table">
+<? foreach($orgas) { ?>
+<tr><td><?=$orgName?></td><td><?=$orgID?></td><td><button class="btn btn-info" type='submit' value='y' name='org:<?=$orgID?>'/><?=_Switch to this organisation?></button></td></tr>
+<? } ?>
+</table>
diff --git a/src/club/wpia/gigi/pages/orga/SwitchOrganisation.java b/src/club/wpia/gigi/pages/orga/SwitchOrganisation.java
new file mode 100644 (file)
index 0000000..16dfe6e
--- /dev/null
@@ -0,0 +1,29 @@
+package club.wpia.gigi.pages.orga;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import club.wpia.gigi.pages.ManagedFormPage;
+import club.wpia.gigi.util.AuthorizationContext;
+
+public class SwitchOrganisation extends ManagedFormPage {
+
+    public static final String PATH = "/orga/switch-orga";
+
+    public SwitchOrganisation() {
+        super("Switch to Organisation", MyOrganisationsForm.class);
+    }
+
+    @Override
+    public boolean isPermitted(AuthorizationContext ac) {
+        return ac != null && ac.getActor().getOrganisations().size() != 0;
+    }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        new MyOrganisationsForm(req).output(resp.getWriter(), getLanguage(req), new HashMap<String, Object>());
+    }
+}
index d676c1de567977e9fa08a17b2a41258d5bb91a2c..909f9efe62168bd22fe422e0d67a70b4c4dcefa3 100644 (file)
@@ -13,11 +13,12 @@ import club.wpia.gigi.dbObjects.Organisation;
 import club.wpia.gigi.dbObjects.User;
 import club.wpia.gigi.localisation.Language;
 import club.wpia.gigi.output.template.Form;
+import club.wpia.gigi.output.template.Form.CSRFException;
 import club.wpia.gigi.output.template.IterableDataset;
 import club.wpia.gigi.output.template.Template;
-import club.wpia.gigi.output.template.Form.CSRFException;
 import club.wpia.gigi.pages.LoginPage;
 import club.wpia.gigi.pages.ManagedMultiFormPage;
+import club.wpia.gigi.pages.Page;
 import club.wpia.gigi.pages.account.domain.DomainManagementForm;
 import club.wpia.gigi.util.AuthorizationContext;
 
@@ -91,7 +92,7 @@ public class ViewOrgPage extends ManagedMultiFormPage {
             resp.sendError(404);
             return;
         }
-        HashMap<String, Object> vars = new HashMap<>();
+        Map<String, Object> vars = Page.getDefaultVars(req);
         if (orgAss) {
             vars.put("editForm", new CreateOrgForm(req, o));
             vars.put("affForm", new AffiliationForm(req, o));
index 06102fc006ba482fe31ae80074552c3829875e36..5d465919331ef435e9bec93448eb1c54df1a9519 100644 (file)
@@ -1,6 +1,7 @@
 package club.wpia.gigi.util;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigInteger;
 import java.security.GeneralSecurityException;
 import java.security.cert.CRLException;
@@ -58,7 +59,12 @@ public class CertExporter {
     }
 
     private static PKCS7 toP7Chain(Certificate c) throws IOException, GeneralSecurityException, GigiApiException {
-        LinkedList<X509Certificate> ll = getChain(c);
+
+        return generateP7Bundle(getChain(c));
+
+    }
+
+    private static PKCS7 generateP7Bundle(LinkedList<X509Certificate> ll) {
         PKCS7 p7 = new PKCS7(new AlgorithmId[0], new ContentInfo(ContentInfo.DATA_OID, null), ll.toArray(new X509Certificate[ll.size()]), new SignerInfo[0]) {
 
             @Override
@@ -164,4 +170,17 @@ public class CertExporter {
         return ll;
     }
 
+    public static void writeCertBundle(OutputStream out) throws IOException, GeneralSecurityException, GigiApiException {
+
+        CACertificate[] cs = CACertificate.getAll();
+        LinkedList<X509Certificate> ll = new LinkedList<>();
+        for (CACertificate cb : cs) {
+            if ( !cb.isSelfsigned()) {
+                ll.add(cb.getCertificate());
+            }
+        }
+
+        PKCS7 p7 = generateP7Bundle(ll);
+        p7.encodeSignedData(out);
+    }
 }
index 79edd9327ad207e4ea981d362a3e131248190e0a..da580ccf48cae58a5d4aac3db449dc5cb553266b 100644 (file)
@@ -6,14 +6,19 @@ import static org.junit.Assert.*;
 import java.io.IOException;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
+import java.util.Locale;
 
 import org.junit.Test;
 
 import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.database.GigiPreparedStatement;
+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.ClientTest;
+import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
 import club.wpia.gigi.util.AuthorizationContext;
+import club.wpia.gigi.util.TimeConditions;
 
 public class TestCertificateRequest extends ClientTest {
 
@@ -85,4 +90,50 @@ public class TestCertificateRequest extends ClientTest {
         }
 
     }
+
+    @Test
+    public void testPingPeriodOneAddress() throws IOException, GeneralSecurityException, GigiApiException {
+        // get new email address with last ping in past
+        String furtherEmail = createUniqueName() + "@example.org";
+        EmailAddress ea = new EmailAddress(u, furtherEmail, Locale.ENGLISH);
+        TestMail mail = getMailReceiver().receive(furtherEmail);
+        try (GigiPreparedStatement stmt = new GigiPreparedStatement("UPDATE `emailPinglog` SET `status`='success'::`pingState`, `when` = (now() - interval '1 months' * ?::INTEGER) WHERE `email`=? ")) {
+            stmt.setInt(1, TimeConditions.getInstance().getEmailPingMonths());
+            stmt.setString(2, furtherEmail);
+            stmt.executeUpdate();
+        }
+
+        try {
+            CertificateRequest cr = new CertificateRequest(ac, generatePEMCSR(kp, "CN=a ab"));
+            cr.update("name", "SHA512", "mail", null, null, "email:" + furtherEmail);
+            cr.draft();
+            fail();
+        } catch (GigiApiException e) {
+            assertThat(e.getMessage(), containsString("needs an email ping within the past"));
+        }
+
+    }
+
+    @Test
+    public void testPingPeriodTwoAddresses() throws IOException, GeneralSecurityException, GigiApiException {
+        // get new email address with last ping in past
+        String furtherEmail = createUniqueName() + "@example.org";
+        EmailAddress ea = new EmailAddress(u, furtherEmail, Locale.ENGLISH);
+        TestMail mail = getMailReceiver().receive(furtherEmail);
+        try (GigiPreparedStatement stmt = new GigiPreparedStatement("UPDATE `emailPinglog` SET `status`='success'::`pingState`, `when` = (now() - interval '1 months' * ?::INTEGER) WHERE `email`=? ")) {
+            stmt.setInt(1, TimeConditions.getInstance().getEmailPingMonths());
+            stmt.setString(2, furtherEmail);
+            stmt.executeUpdate();
+        }
+
+        try {
+            CertificateRequest cr = new CertificateRequest(ac, generatePEMCSR(kp, "CN=a ab"));
+            cr.update("name", "SHA512", "mail", null, null, "email:" + furtherEmail + ",email:" + email);
+            cr.draft();
+            fail();
+        } catch (GigiApiException e) {
+            assertThat(e.getMessage(), containsString("needs an email ping within the past"));
+        }
+
+    }
 }
index 30aeb64f94a3b077acf1bb039ab0689fd470d794..97ac6a0065a05d6c8800f7c0b3777b666bb3d0c0 100644 (file)
@@ -7,6 +7,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.net.MalformedURLException;
+import java.net.URLConnection;
 import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Locale;
@@ -17,10 +18,13 @@ import club.wpia.gigi.GigiApiException;
 import club.wpia.gigi.dbObjects.Group;
 import club.wpia.gigi.dbObjects.User;
 import club.wpia.gigi.localisation.Language;
+import club.wpia.gigi.pages.account.MyDetails;
 import club.wpia.gigi.pages.admin.support.SupportUserDetailsPage;
+import club.wpia.gigi.testUtils.IOUtils;
 import club.wpia.gigi.testUtils.SEClientTest;
 import club.wpia.gigi.testUtils.TestEmailReceiver.TestMail;
 import club.wpia.gigi.util.ServerConstants;
+import club.wpia.gigi.util.ServerConstants.Host;
 
 public class TestSEAdminNotificationMail extends SEClientTest {
 
@@ -141,4 +145,34 @@ public class TestSEAdminNotificationMail extends SEClientTest {
         message = getMailReceiver().receive(targetEmail).getMessage();
         assertThat(message, containsString("All certificates in your account have been revoked."));
     }
+
+    @Test
+    public void testSupportSupporterGroup() throws MalformedURLException, IOException {
+        // supporter adds to his own groups
+        String s = IOUtils.readURL(post(SupportUserDetailsPage.PATH + u.getId() + "/", "addGroup&groupToModify=" + URLEncoder.encode(Group.ORG_AGENT.getDBName(), "UTF-8")));
+        assertThat(s, containsString("Supporter may not modify himself."));
+
+        // supporter removes from his own groups
+        s = IOUtils.readURL(post(SupportUserDetailsPage.PATH + u.getId() + "/", "removeGroup&groupToModify=" + URLEncoder.encode(Group.ORG_AGENT.getDBName(), "UTF-8")));
+        assertThat(s, containsString("Supporter may not modify himself."));
+
+        // supporter removes supporter flag
+        URLConnection uc = post(SupportUserDetailsPage.PATH + u.getId() + "/", "removeGroup&groupToModify=" + URLEncoder.encode(Group.SUPPORTER.getDBName(), "UTF-8"));
+        assertEquals("https://" + ServerConstants.getHostNamePortSecure(Host.WWW) + MyDetails.PATH, uc.getHeaderField("Location"));
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        Group.SUPPORTER.getName().output(pw, Language.getInstance(Locale.ENGLISH), new HashMap<String, Object>());
+        // mail to support
+        String message = getMailReceiver().receive(ServerConstants.getSupportMailAddress()).getMessage();
+        assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked."));
+        // mail to user
+        message = getMailReceiver().receive(u.getEmail()).getMessage();
+        assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked from your account."));
+        // mail to board
+        message = getMailReceiver().receive(ServerConstants.getBoardMailAddress()).getMessage();
+        assertThat(message, containsString("The group permission '" + sw.toString() + "' was revoked for '" + u.getPreferredName().toString() + "'."));
+        s = IOUtils.readURL(get(cookie, MyDetails.PATH));
+        assertThat(s, not(containsString("supporter")));
+    }
+
 }
index 383d23265094a5af76d339a773a31a0d22493103..d820a1d18cc464f35d38ddad25bae17ab36c9a26 100644 (file)
@@ -1,9 +1,11 @@
 package club.wpia.gigi.pages.admin;
 
+import static org.hamcrest.CoreMatchers.*;
 import static org.junit.Assert.*;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URLConnection;
 import java.net.URLEncoder;
@@ -13,9 +15,14 @@ import org.junit.Assume;
 import org.junit.Test;
 
 import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Country;
+import club.wpia.gigi.dbObjects.Country.CountryCodeType;
 import club.wpia.gigi.dbObjects.Domain;
+import club.wpia.gigi.dbObjects.Group;
+import club.wpia.gigi.dbObjects.Organisation;
 import club.wpia.gigi.dbObjects.User;
 import club.wpia.gigi.pages.admin.support.FindUserByDomainPage;
+import club.wpia.gigi.pages.admin.support.SupportOrgDomainPage;
 import club.wpia.gigi.pages.admin.support.SupportUserDetailsPage;
 import club.wpia.gigi.testUtils.IOUtils;
 import club.wpia.gigi.testUtils.SEClientTest;
@@ -72,4 +79,38 @@ public class TestSEAdminPageUserDomainSearch extends SEClientTest {
         URLConnection uc = post(FindUserByDomainPage.PATH, "process&domain=#" + id);
         assertNotNull(fetchStartErrorMessage(IOUtils.readURL(uc)));
     }
+
+    @Test
+    public void testOrgDomainSearch() throws MalformedURLException, UnsupportedEncodingException, IOException, GigiApiException {
+        // generate organisation with domain
+        u.grantGroup(getSupporter(), Group.ORG_AGENT);
+        Organisation o1 = new Organisation(createUniqueName(), Country.getCountryByCode("DE", CountryCodeType.CODE_2_CHARS), "pr", "city", "test@example.com", "", "", u);
+        String dom = createUniqueName() + ".de";
+        Domain d = new Domain(u, o1, dom);
+
+        // test
+        URLConnection uc = post(FindUserByDomainPage.PATH, "process&domain=" + URLEncoder.encode(dom, "UTF-8"));
+
+        assertEquals("https://" + ServerConstants.getHostNamePortSecure(Host.WWW) + SupportOrgDomainPage.PATH + d.getId(), uc.getHeaderField("Location"));
+
+        String s = IOUtils.readURL(get(cookie, SupportOrgDomainPage.PATH + d.getId()));
+        assertThat(s, containsString(dom));
+        assertThat(s, containsString(o1.getName()));
+
+        // test malformated id
+        HttpURLConnection uc1 = get(SupportOrgDomainPage.PATH + d.getId() + "a");
+        assertEquals(400, uc1.getResponseCode());
+
+        // test non existing id
+        uc1 = get(SupportOrgDomainPage.PATH + "5000");
+        assertEquals(400, uc1.getResponseCode());
+
+    }
+
+    @Test
+    public void testDomainSearchByMalformatedId() throws MalformedURLException, UnsupportedEncodingException, IOException, GigiApiException {
+        URLConnection uc = post(FindUserByDomainPage.PATH, "process&domain=#" + d.getId() + "a");
+        assertNotNull(fetchStartErrorMessage(IOUtils.readURL(uc)));
+    }
+
 }
diff --git a/tests/club/wpia/gigi/pages/orga/TestOrgSwitch.java b/tests/club/wpia/gigi/pages/orga/TestOrgSwitch.java
new file mode 100644 (file)
index 0000000..94586e3
--- /dev/null
@@ -0,0 +1,121 @@
+package club.wpia.gigi.pages.orga;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.sql.SQLException;
+
+import org.junit.After;
+import org.junit.Test;
+
+import club.wpia.gigi.GigiApiException;
+import club.wpia.gigi.dbObjects.Organisation;
+import club.wpia.gigi.dbObjects.User;
+import club.wpia.gigi.testUtils.IOUtils;
+import club.wpia.gigi.testUtils.OrgTest;
+
+public class TestOrgSwitch extends OrgTest {
+
+    private User u2;
+
+    private Organisation org1 = createUniqueOrg();
+
+    private Organisation org2 = createUniqueOrg();
+
+    public TestOrgSwitch() throws IOException, GigiApiException {
+
+        assertEquals(403, get(SwitchOrganisation.PATH).getResponseCode());
+
+        String email = createUniqueName() + "@testdom.com";
+        u2 = User.getById(createVerificationUser("testworker", "testname", email, TEST_PASSWORD));
+        assertNull(executeBasicWebInteraction(cookie, ViewOrgPage.DEFAULT_PATH + "/" + org1.getId(), "email=" + URLEncoder.encode(u2.getEmail(), "UTF-8") + "&do_affiliate=y&master=y", 1));
+        assertNull(executeBasicWebInteraction(cookie, ViewOrgPage.DEFAULT_PATH + "/" + org2.getId(), "email=" + URLEncoder.encode(u2.getEmail(), "UTF-8") + "&do_affiliate=y&master=y", 1));
+
+        // login with new user u2
+        cookie = login(email, TEST_PASSWORD);
+    }
+
+    @After
+    public void purgeDbAfterTest() throws SQLException, IOException {
+        purgeDatabase();
+    }
+
+    @Test
+    public void testSwitchToOrg() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+
+        String res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + org1.getName() + " (on behalf of " + u2.getPreferredName()));
+
+    }
+
+    @Test
+    public void testSwitchToNonOrg() throws IOException, GigiApiException {
+
+        String res = IOUtils.readURL(post(SwitchOrganisation.PATH, "org:5000=y"));
+        assertThat(res, containsString("Context switch failed"));
+
+    }
+
+    @Test
+    public void testSwitchToPersonal() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org-leave=personal", 0));
+
+        String res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + u2.getPreferredName()));
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org-leave=personal", 0));
+
+        res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + u2.getPreferredName()));
+
+    }
+
+    @Test
+    public void testSwitchOrgToOrg() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org2.getId() + "=y", 0));
+
+        String res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + org2.getName() + " (on behalf of " + u2.getPreferredName()));
+
+    }
+
+    @Test
+    public void testSwitchOrgToSameOrg() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+
+        String res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + org1.getName() + " (on behalf of " + u2.getPreferredName()));
+
+    }
+
+    @Test
+    public void testSwitchOrgToNonOrg() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+        String res = IOUtils.readURL(post(SwitchOrganisation.PATH, "org:5000=y"));
+        assertThat(res, containsString("Context switch failed"));
+
+    }
+
+    @Test
+    public void testSwitchOrgToPersonal() throws IOException, GigiApiException {
+
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org:" + org1.getId() + "=y", 0));
+        assertNull(executeBasicWebInteraction(cookie, SwitchOrganisation.PATH, "org-leave=personal", 0));
+
+        String res = IOUtils.readURL(get(SwitchOrganisation.PATH));
+        assertThat(res, containsString("Logged in as " + u2.getPreferredName()));
+
+    }
+
+}
index ea375e87862279ec0e3bf63a0c7ba6a11c586bd7..36afa44355dd55ed621bc85f14e48aad8b7d1539 100644 (file)
@@ -1,10 +1,10 @@
 <form method='post'>
 <table class="table">
 <tr><td>
-Batch create users:
+Batch Create Users:
 </td><td></td><td>
 <div>
-  Email: 
+  Email:
   <input type="text" name="prefix"/> NNN@
   <input type="text" name="suffix"/>
 </div>
@@ -12,9 +12,9 @@ Amount:  <input type="slider" name="amount"/> <input type="submit" name="create"
 </td></tr>
 
 <tr><td>
-Add privilege:
+Add Privilege:
 </td><td>
-Email: <input type="text" name="email"/> 
+Email: <input type="text" name="email"/>
 </td><td>
 <select name="priv">
 <option>supporter</option>
@@ -27,8 +27,8 @@ Email: <input type="text" name="email"/>
 <option>codesigning</option>
 <option>org-agent</option>
 </select>
-<input type="submit" name="addpriv" value="Grant Privillege"/>
-<input type="submit" name="delpriv" value="Revoke Privillege"/>
+<input type="submit" name="addpriv" value="Grant Privilege"/>
+<input type="submit" name="delpriv" value="Revoke Privilege"/>
 </td><tr>
 
 <tr><td>
@@ -40,7 +40,7 @@ Email: <input type="text" name="femail"/>
 </td><tr>
 
 <tr><td>
-Add CATs entry:
+Add Qualifying Challenge Entry:
 </td><td>
 Email: <input type="text" name="catsEmail"/>
 </td><td>
@@ -49,7 +49,7 @@ Email: <input type="text" name="catsEmail"/>
 <option value="<?=$id?>"><?=$name?></option>
 <? } ?>
 </select>
-<input type="submit" value="Add CATs" name="cats"/>
+<input type="submit" value="Add Challenge" name="cats"/>
 </td></tr>
 <tr><td>
 Add 100 Verification Points:
@@ -62,7 +62,7 @@ Verification Points to issue to preferred name: </br>
 </td></tr>
 
 <tr><td>
-Verify 25 others (get 100 Experience Points)   :
+Verify 25 Others (get 100 Experience Points):
 </td><td>
 Email: <input type="text" name="letverifyEmail"/>
 </td><td>
@@ -70,12 +70,12 @@ Email: <input type="text" name="letverifyEmail"/>
 </td></tr>
 
 <tr><td>
-Add verified Email:
+Add Verified Email:
 </td><td>
 Email: <input type="text" name="addEmailEmail"/>
 </td><td>
-new Email: <input type="text" name="addEmailNew"/>
-<input type="submit" value="Add verified Email" name="addEmail"/>
+New Email: <input type="text" name="addEmailNew"/>
+<input type="submit" value="Add Verified Email" name="addEmail"/>
 </td></tr>
 
 <tr><td>
@@ -83,7 +83,7 @@ Add Client Cert:
 </td><td>
 Email: <input type="text" name="addCertEmail"/>
 </td><td>
-<input type="submit" value="Add an certificate" name="addCert"/>
+<input type="submit" value="Add a Certificate" name="addCert"/>
 </td></tr>
 
 <tr><td>
@@ -98,7 +98,7 @@ Domain: <input type="text" name="exemptDom"/>
 
 <table>
 <tr><th colspan="2">
-Test server settings
+Test Server Settings
 </th></tr>
 
 <tr><td>