diff options
author | Jakob Heher <jakob.heher@iaik.tugraz.at> | 2022-10-05 13:58:26 +0200 |
---|---|---|
committer | Jakob Heher <jakob.heher@iaik.tugraz.at> | 2022-10-05 13:58:26 +0200 |
commit | f180a7e254d204aaa645ff4fd4e5fff7b1d7c47b (patch) | |
tree | 5b9efa1428db8bd599c981a7a1f0b2d175ed5ef2 /pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku | |
parent | 8d7000e17c41b5a09590202e7ce644c9b86bfe11 (diff) | |
download | pdf-over-f180a7e254d204aaa645ff4fd4e5fff7b1d7c47b.tar.gz pdf-over-f180a7e254d204aaa645ff4fd4e5fff7b1d7c47b.tar.bz2 pdf-over-f180a7e254d204aaa645ff4fd4e5fff7b1d7c47b.zip |
add waiting-for-app support
(starting to rip some old stuff out)
Diffstat (limited to 'pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku')
4 files changed, 120 insertions, 83 deletions
diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java index dc2258a1..1748f631 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/MobileBKUConnector.java @@ -13,18 +13,14 @@ import java.util.regex.Pattern; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.swing.text.html.HTML.Tag; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; -import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; -import org.apache.hc.client5.http.impl.classic.RequestFailedException; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.Header; @@ -228,6 +224,67 @@ public class MobileBKUConnector implements BkuSlConnector { return post; } + private static class LongPollThread extends Thread implements AutoCloseable { + + private final CloseableHttpClient httpClient = HttpClients.createDefault(); + private final HttpGet request; + private final Runnable signal; + private boolean done = false; + + @Override + public void run() { + long timeout = System.nanoTime() + (300l * 1000l * 1000l * 1000l); /* a-trust timeout is 5 minutes */ + log.debug("longPollThread hello"); + while (!done) { + try (final CloseableHttpResponse response = httpClient.execute(request)) { + JSONObject jsonResponse = new JSONObject(EntityUtils.toString(response.getEntity())); + if (jsonResponse.getBoolean("Fin")) + signal.run(); + else if (jsonResponse.getBoolean("Wait")) + { + log.debug("longPollThread continue..."); + continue; + } + else if (jsonResponse.getBoolean("Error")) + signal.run(); /* will trigger reload and find error; this is the same thing a-trust does */ + else { + log.warn("Unknown long poll response:\n{}", jsonResponse.toString(2)); + break; + } + } catch (NoHttpResponseException e) { + if (timeout <= System.nanoTime()) + signal.run(); /* reload to find the timeout error */ + continue; /* httpclient timeout */ + } catch (IOException | ParseException | IllegalStateException e) { + if (done) break; + log.warn("QR code long polling exception", e); + /* sleep so we don't hammer a-trust too hard in case this goes wrong */ + try { Thread.sleep(5000); } catch (InterruptedException e2) {} + } + } + log.debug("longPollThread goodbye"); + } + + public LongPollThread(URI uri, Runnable signal) { + this.request = new HttpGet(uri); + this.signal = signal; + } + + @Override + public void close() { + done = true; + if (this.request != null) + this.request.abort(); + + if (this.isAlive()) + try { this.join(1000); } catch (InterruptedException e) {} + + if (this.httpClient != null) + try { this.httpClient.close(); } catch (IOException e) { log.warn("Auto-close of long-poll HTTP client threw exception", e); } + } + + } + /** * Main lifting function for MobileBKU UX * @return the next request to make, or null if the current response should be returned @@ -270,58 +327,29 @@ public class MobileBKUConnector implements BkuSlConnector { return new HttpGet(html.htmlDocument.baseUri()); } if (html.qrCodeBlock != null) { - try (final CloseableHttpClient httpClient = HttpClients.custom().disableRedirectHandling().build()) { - final HttpGet request = new HttpGet(html.qrCodeBlock.pollingURI); - boolean[] done = new boolean[1]; - done[0] = false; - Thread longPollThread = new Thread(() -> { - long timeout = System.nanoTime() + (300l * 1000l * 1000l * 1000l); /* a-trust timeout is 5 minutes */ - log.debug("longPollThread hello"); - while (!done[0]) { - try (final CloseableHttpResponse response = httpClient.execute(request)) { - JSONObject jsonResponse = new JSONObject(EntityUtils.toString(response.getEntity())); - if (jsonResponse.getBoolean("Fin")) - state.signalQRScanned(); - else if (jsonResponse.getBoolean("Wait")) - { - log.debug("longPollThread continue..."); - continue; - } - else if (jsonResponse.getBoolean("Error")) - state.signalQRScanned(); /* will trigger reload and find error; this is the same thing a-trust does */ - else { - log.warn("Unknown long poll response:\n{}", jsonResponse.toString(2)); - break; - } - } catch (NoHttpResponseException e) { - if (timeout <= System.nanoTime()) - state.signalQRScanned(); /* reload to find the timeout error */ - continue; /* httpclient timeout */ - } catch (IOException | ParseException | IllegalStateException e) { - if (done[0]) break; - log.warn("QR code long polling exception", e); - /* sleep so we don't hammer a-trust too hard in case this goes wrong */ - try { Thread.sleep(5000); } catch (InterruptedException e2) {} - } - } - log.debug("longPollThread goodbye"); - }); - try { - longPollThread.start(); - MobileBKUState.QRResult result = this.state.showQRCode(html.qrCodeBlock.referenceValue, html.qrCodeBlock.qrCodeURI, html.signatureDataLink, html.smsTanLink != null, html.fido2Link != null, html.qrCodeBlock.errorMessage); - switch (result) { - case UPDATE: break; - case TO_FIDO2: if (html.fido2Link != null) return new HttpGet(html.fido2Link); break; - case TO_SMS: if (html.smsTanLink != null) return new HttpGet(html.smsTanLink); break; - } - return new HttpGet(html.htmlDocument.baseUri()); - } finally { - done[0] = true; - request.abort(); - try { longPollThread.join(1000); } catch (InterruptedException e) {} + try (LongPollThread longPollThread = new LongPollThread(html.qrCodeBlock.pollingURI, () -> { this.state.signalQRScanned(); })) { + this.state.showQRCode(html.qrCodeBlock.referenceValue, html.qrCodeBlock.qrCodeURI, html.signatureDataLink, html.smsTanLink != null, html.fido2Link != null, html.qrCodeBlock.errorMessage); + longPollThread.start(); + var result = this.state.waitForQRCodeResult(); + switch (result) { + case UPDATE: break; + case TO_FIDO2: if (html.fido2Link != null) return new HttpGet(html.fido2Link); break; + case TO_SMS: if (html.smsTanLink != null) return new HttpGet(html.smsTanLink); break; + } + return new HttpGet(html.htmlDocument.baseUri()); + } + } + if (html.waitingForAppBlock != null) { + try (LongPollThread longPollThread = new LongPollThread(html.waitingForAppBlock.pollingURI, () -> { this.state.signalAppOpened(); })) { + this.state.showWaitingForApp(html.waitingForAppBlock.referenceValue, html.signatureDataLink, html.smsTanLink != null, html.fido2Link != null); + longPollThread.start(); + var result = this.state.waitForAppOpen(); + switch (result) { + case UPDATE: break; + case TO_FIDO2: if (html.fido2Link != null) return new HttpGet(html.fido2Link); break; + case TO_SMS: if (html.smsTanLink != null) return new HttpGet(html.smsTanLink); break; } - } catch (IOException e) { - log.warn("closing long-polling HttpClient threw exception", e); + return new HttpGet(html.htmlDocument.baseUri()); } } if (html.fido2Block != null) { diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java index 8c054e95..c9ab164b 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDMobileBKUConnector.java @@ -15,9 +15,6 @@ */ package at.asit.pdfover.gui.bku; -import java.io.IOException; -import java.net.URISyntaxException; - // Imports import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -90,7 +87,7 @@ public class OLDMobileBKUConnector implements BkuSlConnector { if (responseData.contains("undecided.aspx?sid=")) { // handle polling - this.state.showOpenAppMessageWithSMSandCancel(); + //this.state.showOpenAppMessageWithSMSandCancel(); if (this.state.status.isSMSTan) { String response = handler.postSMSRequest(); @@ -124,11 +121,11 @@ public class OLDMobileBKUConnector implements BkuSlConnector { boolean enterTAN = true; String responseData = null; if (status.qrCodeURL != null) { - try { + /*try { this.state.OLDshowQR(); } catch (IOException | URISyntaxException e) { throw new SignatureException(e); - } + }*/ if ("cancel".equals(this.state.status.errorMessage)) throw new SignatureException(new IllegalStateException()); if (status.qrCodeURL == null) { @@ -178,7 +175,7 @@ public class OLDMobileBKUConnector implements BkuSlConnector { if (enterTAN) { try { // Get TAN - this.state.OLDcheckTAN(); + //this.state.OLDcheckTAN(); if ("cancel".equals(this.state.status.errorMessage)) throw new SignatureException(new IllegalStateException()); diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDmobile/ATrustHandler.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDmobile/ATrustHandler.java index ab85645b..e22db900 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDmobile/ATrustHandler.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/OLDmobile/ATrustHandler.java @@ -16,12 +16,10 @@ package at.asit.pdfover.gui.bku.OLDmobile; // Imports -import java.awt.Desktop; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URI; import java.net.URL; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -38,12 +36,8 @@ import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.commons.io.IOUtils; import org.eclipse.swt.SWT; -import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java index 90afc834..1fb3b8d6 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/bku/mobile/ATrustParser.java @@ -56,6 +56,24 @@ public class ATrustParser { throw new ComponentParseFailed(); } } + protected @Nonnull URI getLongPollURI() throws ComponentParseFailed { + var pollingScriptElm = getElementEnsureNotNull("#jsLongPoll script"); + String pollingScript = pollingScriptElm.data(); + int startIdx = pollingScript.indexOf("qrpoll(\""); + if (startIdx < 0) { log.warn("Failed to find 'qrpoll(\"' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); } + startIdx += 8; + + int endIdx = pollingScript.indexOf("\");", startIdx); + if (endIdx < 0) { log.warn("Failed to find qrpoll terminator '\");' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); } + + String pollingUriString = pollingScript.substring(startIdx, endIdx); + try { + return ISNOTNULL(new URI(pollingScriptElm.baseUri()).resolve(pollingUriString)); + } catch (URISyntaxException e) { + log.warn("Long-poll URI '{}' could not be parsed", pollingUriString); + throw new ComponentParseFailed(); + } + } } public static class ErrorBlock extends TopLevelFormBlock { @@ -126,25 +144,22 @@ public class ATrustParser { this.referenceValue = ISNOTNULL(getElementEnsureNotNull("#vergleichswert").ownText()); this.qrCodeURI = getURIAttributeEnsureNotNull("#qrimage", "abs:src"); + this.pollingURI = getLongPollURI(); - var pollingScriptElm = getElementEnsureNotNull("#jsLongPoll script"); - String pollingScript = pollingScriptElm.data(); - int startIdx = pollingScript.indexOf("qrpoll(\""); - if (startIdx < 0) { log.warn("Failed to find 'qrpoll(\"' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); } - startIdx += 8; + this.errorMessage = null; + } + } - int endIdx = pollingScript.indexOf("\");", startIdx); - if (endIdx < 0) { log.warn("Failed to find qrpoll terminator '\");' in jsLongPoll script:\n{}", pollingScript); throw new ComponentParseFailed(); } + public static class WaitingForAppBlock extends TopLevelFormBlock { + public final @Nonnull String referenceValue; + public final @Nonnull URI pollingURI; - String pollingUriString = pollingScript.substring(startIdx, endIdx); - try { - this.pollingURI = ISNOTNULL(new URI(pollingScriptElm.baseUri()).resolve(pollingUriString)); - } catch (URISyntaxException e) { - log.warn("URI '{}' could not be parsed", pollingUriString); - throw new ComponentParseFailed(); - } + private WaitingForAppBlock(@Nonnull org.jsoup.nodes.Document htmlDocument, @Nonnull Map<String, String> formOptions) throws ComponentParseFailed { + super(htmlDocument, formOptions); + abortIfElementMissing("#smartphoneAnimation"); - this.errorMessage = null; + this.referenceValue = ISNOTNULL(getElementEnsureNotNull("#vergleichswert").ownText()); + this.pollingURI = getLongPollURI(); } } @@ -179,6 +194,7 @@ public class ATrustParser { public final @CheckForNull UsernamePasswordBlock usernamePasswordBlock; public final @CheckForNull SMSTanBlock smsTanBlock; public final @CheckForNull QRCodeBlock qrCodeBlock; + public final @CheckForNull WaitingForAppBlock waitingForAppBlock; public final @CheckForNull Fido2Block fido2Block; private void validate() { @@ -188,6 +204,7 @@ public class ATrustParser { if (usernamePasswordBlock != null) populated.add("usernamePasswordBlock"); if (smsTanBlock != null) populated.add("smsTanBlock"); if (qrCodeBlock != null) populated.add("qrCodeBlock"); + if (waitingForAppBlock != null) populated.add("waitingForAppBlock"); if (fido2Block != null) populated.add("fido2Block"); switch (populated.size()) { @@ -271,6 +288,7 @@ public class ATrustParser { this.usernamePasswordBlock = TryParseMainBlock(UsernamePasswordBlock.class); this.smsTanBlock = TryParseMainBlock(SMSTanBlock.class); this.qrCodeBlock = TryParseMainBlock(QRCodeBlock.class); + this.waitingForAppBlock = TryParseMainBlock(WaitingForAppBlock.class); this.fido2Block = TryParseMainBlock(Fido2Block.class); validate(); |