Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.webpieces.plugin.secure.sslcert.InstallSslCertController Maven / Gradle / Ivy
package org.webpieces.plugin.secure.sslcert;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.security.KeyPair;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jose4j.base64url.Base64;
import org.shredzone.acme4j.util.KeyPairUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.webpieces.ctx.api.Current;
import org.webpieces.ctx.api.RouterRequest;
import org.webpieces.plugin.backend.menu.MenuCreator;
import org.webpieces.plugin.secure.sslcert.acme.AcmeClientProxy;
import org.webpieces.plugin.secure.sslcert.acme.AcmeInfo;
import org.webpieces.plugin.secure.sslcert.acme.ProxyAuthorization;
import org.webpieces.plugin.secure.sslcert.acme.ProxyOrder;
import org.webpieces.router.api.controller.actions.Action;
import org.webpieces.router.api.controller.actions.Actions;
import org.webpieces.router.api.controller.actions.Redirect;
import org.webpieces.router.api.controller.actions.Render;
import org.webpieces.http.exception.NotFoundException;
import org.webpieces.router.api.extensions.SimpleStorage;
import org.webpieces.util.exceptions.SneakyThrow;
import com.webpieces.http2.api.dto.highlevel.Http2Response;
import com.webpieces.http2.api.dto.lowlevel.lib.Http2Header;
import com.webpieces.http2.api.dto.lowlevel.lib.Http2HeaderName;
@Singleton
public class InstallSslCertController {
private static final Logger log = LoggerFactory.getLogger(InstallSslCertController.class);
private static final String EMAIL = "email";
private static final String URL = "urlLocation";
private SimpleStorage storage;
private MenuCreator menuCreator;
private AcmeClientProxy acmeClient;
@Inject
public InstallSslCertController(MenuCreator menuCreator, SimpleStorage storage, AcmeClientProxy acmeClient) {
this.menuCreator = menuCreator;
this.storage = storage;
this.acmeClient = acmeClient;
}
public CompletableFuture sslSetup() {
CompletableFuture> read = this.storage.read(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY);
return read.thenCompose( (properties) -> decide(properties));
}
private CompletableFuture decide(Map properties) {
String base64 = properties.get(InstallSslCertPlugin.ACCOUNT_KEYPAIR_KEY);
if(base64 == null) {
log.info("accountKeyPair not found in database");
//It really sucks that this is synchronous(could throw into future pool next time)
CompletableFuture future = acmeClient.fetchRemoteInfo();
return future.thenApply((info) -> {
return Actions.renderThis(
"menu", menuCreator.getMenu(),
"agreement", info.getTermsOfServiceUri()+"",
"website", info.getWebsite(),
"email", null
);
});
}
log.info("accountKeyPair found in database. redirecting to step 2");
return CompletableFuture.completedFuture(Actions.redirect(InstallSslCertRouteId.STEP2));
}
public CompletableFuture postStartSslInstall(String email) {
log.info("create key pair");
KeyPair accountKeyPair = KeyPairUtils.createKeyPair(2048);
log.info("done creating key pair");
try (StringWriter writer = new StringWriter()) {
KeyPairUtils.writeKeyPair(accountKeyPair, writer);
log.info("done marshalling keypair to string");
Map properties = new HashMap<>();
properties.put(InstallSslCertPlugin.ACCOUNT_KEYPAIR_KEY, writer.toString());
properties.put(EMAIL, email);
return this.storage.save(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY, properties)
.thenApply( (v) -> {
log.info("done saving, redirecting to step2");
return Actions.redirect(InstallSslCertRouteId.STEP2);
});
} catch(IOException e) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(e);
return future;
}
}
public CompletableFuture step2() {
CompletableFuture> read = this.storage.read(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY);
return read.thenApply((props) -> decideStep2(props));
}
private Action decideStep2(Map properties) {
log.info("read in properties");
String keyPair = properties.get(InstallSslCertPlugin.ACCOUNT_KEYPAIR_KEY);
if(keyPair == null) {
log.info("keyPair not foudn, redirecting to first step");
return Actions.redirect(InstallSslCertRouteId.INSTALL_SSL_SETUP);
}
log.info("rendering step");
return Actions.renderThis(
"menu", menuCreator.getMenu(),
"keyPair", keyPair,
"organization", null);
}
public CompletableFuture postStep2(String organization) {
RouterRequest request = Current.request();
return this.storage.read(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY)
.thenCompose((props) -> process(props, request, organization));
}
private CompletableFuture process(Map props, RouterRequest request, String organization) {
log.info("read in properties from database");
String domain = request.domain;
String accountKeyPairString = props.get(InstallSslCertPlugin.ACCOUNT_KEYPAIR_KEY);
String email = props.get(EMAIL);
try {
KeyPair accountKeyPair = KeyPairUtils.readKeyPair(new StringReader(accountKeyPairString));
log.info("deserialized keypair");
return acmeClient.openAccount(email, accountKeyPair)
.thenCompose((url) -> saveUrlAndProcessOrder(url, accountKeyPair, email, domain, organization));
} catch (IOException e) {
throw SneakyThrow.sneak(e);
}
}
private CompletableFuture saveUrlAndProcessOrder(URL url, KeyPair accountKeyPair, String email, String domain, String organization) {
return storage.save(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY, URL, url+"")
.thenCompose((nothing) -> acmeClient.placeOrder(url, accountKeyPair))
.thenCompose((order) -> createWebPages(order))
.thenCompose((order) -> acmeClient.finalizeOrder(order, accountKeyPair, email, domain, organization))
.thenCompose( (cert) -> installCertAllServers(cert))
.thenApply((nothing) -> Actions.redirect(InstallSslCertRouteId.MAINTAIN_SSL));
}
/**
* WE ONLY use ONE webpage that renders ALL requests for these tokens and that web page just looks up the token in the database
* to see if the page exists and only renders an html page if that page exists
*/
private CompletableFuture createWebPages(ProxyOrder order) {
List authorizations = order.getAuthorizations();
Map properties = new HashMap<>();
for(ProxyAuthorization auth : authorizations) {
log.info("process domain="+auth.getDomain()+" expires="+auth.getExpires()+" status="+auth.getStatus()+" else="+auth.getLocation());
Instant expires = auth.getExpires();
String dateTime = expires.toString();
String domain = auth.getDomain();
String authContent = auth.getAuthContent();
String value = authContent+"---"+domain+"---"+dateTime;
String token = auth.getToken();
log.info("putting token in map="+token+" value="+value);
properties.put(token, value);
}
return storage.save(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY, properties)
.thenApply((nothing) -> order);
}
private CompletableFuture installCertAllServers(CertAndSigningRequest certInfo) {
List certChain = certInfo.getCertChain();
Map props = new HashMap<>();
try {
props.put(InstallSslCertPlugin.CSR, certInfo.getCsr());
for(int i = 0; i < certChain.size(); i++) {
X509Certificate cert = certChain.get(i);
String certString = Base64.encode(cert.getEncoded());
props.put(InstallSslCertPlugin.CERT_CHAIN_PREFIX, certString);
}
return storage.save(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY, props);
} catch (CertificateEncodingException e) {
throw SneakyThrow.sneak(e);
}
}
public CompletableFuture renderToken(String token) {
Current.getContext().addModifyResponse((http2Response) -> modifyResponse(http2Response));
CompletableFuture> future = storage.read(InstallSslCertPlugin.PLUGIN_PROPERTIES_KEY);
return future.thenApply((props) -> {
String result = props.get(token);
log.info("token="+token+" value="+result);
if(result == null)
throw new NotFoundException();
int index = result.indexOf("---");
String authContent = result.substring(0, index);
//check token exists in database
return Actions.renderThis("authContent", authContent);
});
}
private Object modifyResponse(Object http2Response) {
Http2Response resp = (Http2Response) http2Response;
Http2Header header = resp.getHeaderLookupStruct().getHeader(Http2HeaderName.CONTENT_TYPE);
header.setValue("text/plain");
return resp;
}
public Render maintainSsl() {
return Actions.renderThis();
}
}