Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update user agent and log URL #11852

Merged
merged 13 commits into from
Sep 29, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv
- ⚠️ Renamed command line parameters `embeddBibfileInPdf` to `embedBibFileInPdf`, `writeMetadatatoPdf` to `writeMetadataToPdf`, and `writeXMPtoPdf` to `writeXmpToPdf`. [#11575](https://github.com/JabRef/jabref/pull/11575)
- The browse button for a Custom theme now opens in the directory of the current used CSS file. [#11597](https://github.com/JabRef/jabref/pull/11597)
- The browse button for a Custom exporter now opens in the directory of the current used exporter file. [#11717](https://github.com/JabRef/jabref/pull/11717)
- JabRef now uses TLS 1.2 for all HTTPS connections. [#11852](https://github.com/JabRef/jabref/pull/11852)
- We improved the display of long messages in the integrity check dialog. [#11619](https://github.com/JabRef/jabref/pull/11619)
- We improved the undo/redo buttons in the main toolbar and main menu to be disabled when there is nothing to undo/redo. [#8807](https://github.com/JabRef/jabref/issues/8807)
- We improved the DOI detection in PDF imports. [#11782](https://github.com/JabRef/jabref/pull/11782)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
import java.util.List;
import java.util.Optional;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocketFactory;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
Expand Down Expand Up @@ -243,8 +240,6 @@ private boolean checkSSLHandshake(URLDownload urlDownload) {
}

private BackgroundTask<Path> prepareDownloadTask(Path targetDirectory, URLDownload urlDownload) {
SSLSocketFactory defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
return BackgroundTask
.wrap(() -> {
String suggestedName;
Expand All @@ -263,7 +258,6 @@ private BackgroundTask<Path> prepareDownloadTask(Path targetDirectory, URLDownlo
.then(destination -> new FileDownloadTask(urlDownload.getSource(), destination))
.onFailure(ex -> LOGGER.error("Error in download", ex))
.onFinished(() -> {
URLDownload.setSSLVerification(defaultSSLSocketFactory, defaultHostnameVerifier);
downloadProgress.unbind();
downloadProgress.set(1);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
import java.util.Optional;
import java.util.stream.Collectors;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
Expand Down Expand Up @@ -217,15 +213,10 @@ public void checkCustomApiKey() {
if (!apiKey.isEmpty()) {
URLDownload urlDownload;
try {
SSLSocketFactory defaultSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
HostnameVerifier defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();

urlDownload = new URLDownload(testUrlWithoutApiKey + apiKey);
// The HEAD request cannot be used because its response is not 200 (maybe 404 or 596...).
int statusCode = ((HttpURLConnection) urlDownload.getSource().openConnection()).getResponseCode();
keyValid = (statusCode >= 200) && (statusCode < 300);

URLDownload.setSSLVerification(defaultSslSocketFactory, defaultHostnameVerifier);
} catch (IOException | UnirestException e) {
keyValid = false;
}
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/org/jabref/logic/importer/FetcherException.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@

public class FetcherException extends JabRefException {
private static final Logger LOGGER = LoggerFactory.getLogger(FetcherException.class);

Pattern API_KEY_PATTERN = Pattern.compile("(?i)(api|key|api[-_]?key)=[^&]*");

String REDACTED_STRING = "[REDACTED]";
private static final Pattern API_KEY_PATTERN = Pattern.compile("(?i)(api|key|api[-_]?key)=[^&]*");
private static String REDACTED_STRING = "[REDACTED]";

private final String url;
private final SimpleHttpResponse httpResponse;
Expand Down Expand Up @@ -91,6 +89,10 @@ private String getRedactedUrl() {
return API_KEY_PATTERN.matcher(url).replaceAll(REDACTED_STRING);
}

public static Object getRedactedUrl(URL source) {
return API_KEY_PATTERN.matcher(source.toString()).replaceAll(REDACTED_STRING);
}

private String getPrefix() {
String superLocalizedMessage = super.getLocalizedMessage();
if (!StringUtil.isBlank(superLocalizedMessage)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public Optional<URL> findFullText(BibEntry entry) throws IOException, FetcherExc
try {
html = getHTML(entry);
} catch (FetcherException | NullPointerException e) {
LOGGER.debug("ResearchGate server is not available");
LOGGER.debug("ResearchGate server is not available", e);
return Optional.empty();
}
Elements eLink = html.getElementsByTag("section");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ private void putElectronicLocation(BibEntry bibEntry, Element datafield) {
try {
LinkedFile linkedFile = new LinkedFile(URI.create(resource).toURL(), "PDF");
bibEntry.setField(StandardField.FILE, linkedFile.toString());
} catch (MalformedURLException e) {
} catch (MalformedURLException | IllegalArgumentException e) {
LOGGER.info("Malformed URL: {}", resource);
}
} else {
Expand Down
36 changes: 22 additions & 14 deletions src/main/java/org/jabref/logic/net/URLDownload.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -32,9 +35,8 @@
import java.util.Map.Entry;
import java.util.Optional;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLContext;

import org.jabref.http.dto.SimpleHttpResponse;
import org.jabref.logic.importer.FetcherClientException;
Expand Down Expand Up @@ -65,7 +67,7 @@
*/
public class URLDownload {

public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36";
public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0";
private static final Logger LOGGER = LoggerFactory.getLogger(URLDownload.class);
private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(30);
private static final int MAX_RETRIES = 3;
Expand All @@ -74,6 +76,7 @@ public class URLDownload {
private final Map<String, String> parameters = new HashMap<>();
private String postData = "";
private Duration connectTimeout = DEFAULT_CONNECT_TIMEOUT;
private SSLContext sslContext;

static {
Unirest.config()
Expand All @@ -96,18 +99,14 @@ public URLDownload(String source) throws MalformedURLException {
public URLDownload(URL source) {
this.source = source;
this.addHeader("User-Agent", URLDownload.USER_AGENT);
}

/**
* @param socketFactory trust manager
* @param verifier host verifier
*/
public static void setSSLVerification(SSLSocketFactory socketFactory, HostnameVerifier verifier) {
try {
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will have an impact of using custom SSL certifactes now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked. --> TrustStore is created at org.jabref.logic.net.ssl.TrustStoreManager#configureTrustStore.

I created JavaDoc for it: 300b7b3

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, maybe, this only works if we do this setting?! - I am not sure. How can we test this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I see it should be enough if we set in the truststore manager

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to revert the change ^^. -- Since its an alpha, we can take the risk?

HttpsURLConnection.setDefaultHostnameVerifier(verifier);
} catch (Exception e) {
LOGGER.error("A problem occurred when reset SSL verification", e);
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, new SecureRandom());
// Note: SSL certificates are installed at {@link TrustStoreManager#configureTrustStore(Path)}
} catch (NoSuchAlgorithmException | KeyManagementException e) {
LOGGER.error("Could not initialize SSL context", e);
sslContext = null;
}
}

Expand Down Expand Up @@ -384,7 +383,7 @@ public URLConnection openConnection() throws FetcherException {
} else if (status >= 400) {
// in case of an error, propagate the error message
SimpleHttpResponse httpResponse = new SimpleHttpResponse(httpURLConnection);
LOGGER.info("{}", httpResponse);
LOGGER.info("{}: {}", FetcherException.getRedactedUrl(this.source), httpResponse);
if (status < 500) {
throw new FetcherClientException(this.source, httpResponse);
} else {
Expand All @@ -397,6 +396,15 @@ public URLConnection openConnection() throws FetcherException {

private URLConnection getUrlConnection() throws IOException {
URLConnection connection = this.source.openConnection();

if (connection instanceof HttpURLConnection httpConnection) {
httpConnection.setInstanceFollowRedirects(true);
}

if ((sslContext != null) && (connection instanceof HttpsURLConnection httpsConnection)) {
httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory());
}

connection.setConnectTimeout((int) connectTimeout.toMillis());
for (Entry<String, String> entry : this.parameters.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @implNote SSL certificates are installed at {@link TrustStoreManager#configureTrustStore(Path)}
*/
public class TrustStoreManager {

private static final Logger LOGGER = LoggerFactory.getLogger(TrustStoreManager.class);
Expand Down Expand Up @@ -163,7 +166,9 @@ public static void createTruststoreFileIfNotExist(Path storePath) {
}
}

// based on https://stackoverflow.com/a/62586564/3450689
/**
* @implNote based on https://stackoverflow.com/a/62586564/3450689
*/
private static void configureTrustStore(Path myStorePath) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException,
CertificateException, IOException {
X509TrustManager jreTrustManager = getJreTrustManager();
Expand Down
62 changes: 24 additions & 38 deletions src/test/java/org/jabref/logic/importer/fetcher/BvbFetcherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode;
import org.apache.lucene.queryparser.flexible.standard.parser.StandardSyntaxParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.jabref.logic.importer.fetcher.transformers.AbstractQueryTransformer.NO_EXPLICIT_FIELD;
Expand All @@ -23,8 +22,30 @@
class BvbFetcherTest {

BvbFetcher fetcher = new BvbFetcher();
BibEntry bibEntryISBN0134685997;
BibEntry bibEntryISBN9783960886402;
BibEntry bibEntryISBN9783960886402 = new BibEntry(StandardEntryType.Misc)
.withField(StandardField.TITLE, "Effective Java")
.withField(StandardField.YEAR, "2018")
.withField(StandardField.SUBTITLE, "best practices für die Java-Plattform")
.withField(StandardField.AUTHOR, "Bloch, Joshua")
.withField(StandardField.TITLEADDON, "Joshua Bloch")
.withField(StandardField.EDITION, "3. Auflage, Übersetzung der englischsprachigen 3. Originalausgabe 2018")
.withField(StandardField.FILE, "ParsedFileField{description='', link='http://search.ebscohost.com/login.aspx?direct=true&scope=site&db=nlebk&db=nlabk&AN=1906353', fileType='PDF'}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better use withFiles(List.of(new LInkedFile(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was a bug in the fetcher. Fixed at 654616c.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more: d1c33d9

.withField(StandardField.ISBN, "9783960886402")
.withField(StandardField.KEYWORDS, "Klassen, Interfaces, Generics, Enums, Annotationen, Lambdas, Streams, Module, parallel, Parallele Programmierung, Serialisierung, funktional, funktionale Programmierung, Java EE, Jakarta EE")
.withField(StandardField.ADDRESS, "Heidelberg")
.withField(StandardField.PAGETOTAL, "396")
.withField(StandardField.PUBLISHER, "{dpunkt.verlag} and {Dpunkt. Verlag (Heidelberg)}");

BibEntry bibEntryISBN0134685997 = new BibEntry(StandardEntryType.Misc)
.withField(StandardField.TITLE, "Effective Java")
.withField(StandardField.YEAR, "2018")
.withField(StandardField.AUTHOR, "Bloch, Joshua")
.withField(StandardField.TITLEADDON, "Joshua Bloch")
.withField(StandardField.EDITION, "Third edition")
.withField(StandardField.ISBN, "0134685997")
.withField(StandardField.PAGETOTAL, "392")
.withField(StandardField.ADDRESS, "Boston")
.withField(StandardField.PUBLISHER, "{Addison-Wesley}");

@Test
void performTest() throws Exception {
Expand All @@ -38,41 +59,6 @@ void performTest() throws Exception {
// result.forEach(entry -> System.out.println(entry.toString()));
}

@BeforeEach
void setUp() {
fetcher = new BvbFetcher();

bibEntryISBN9783960886402 = new BibEntry(StandardEntryType.Misc)
.withField(StandardField.TITLE, "Effective Java")
.withField(StandardField.YEAR, "2018")
.withField(StandardField.SUBTITLE, "best practices für die Java-Plattform")
.withField(StandardField.AUTHOR, "Bloch, Joshua")
.withField(StandardField.TITLEADDON, "Joshua Bloch")
.withField(StandardField.EDITION, "3. Auflage, Übersetzung der englischsprachigen 3. Originalausgabe 2018")
.withField(StandardField.FILE, "ParsedFileField{description='', link='http://search.ebscohost.com/login.aspx?direct=true&scope=site&db=nlebk&db=nlabk&AN=1906353', fileType='PDF'}")
.withField(StandardField.ISBN, "9783960886402")
.withField(StandardField.KEYWORDS, "Klassen, Interfaces, Generics, Enums, Annotationen, Lambdas, Streams, Module, parallel, Parallele Programmierung, Serialisierung, funktional, funktionale Programmierung, Java EE, Jakarta EE")
.withField(StandardField.ADDRESS, "Heidelberg")
.withField(StandardField.PAGETOTAL, "396")
.withField(StandardField.PUBLISHER, "{dpunkt.verlag} and {Dpunkt. Verlag (Heidelberg)}");

bibEntryISBN0134685997 = new BibEntry(StandardEntryType.Misc)
.withField(StandardField.TITLE, "Effective Java")
.withField(StandardField.YEAR, "2018")
.withField(StandardField.AUTHOR, "Bloch, Joshua")
.withField(StandardField.TITLEADDON, "Joshua Bloch")
.withField(StandardField.EDITION, "Third edition")
.withField(StandardField.ISBN, "0134685997")
.withField(StandardField.PAGETOTAL, "392")
.withField(StandardField.ADDRESS, "Boston")
.withField(StandardField.PUBLISHER, "{Addison-Wesley}");
}

@Test
void getName() {
assertEquals("Bibliotheksverbund Bayern (Experimental)", fetcher.getName());
}

@Test
void simpleSearchQueryURLCorrect() throws Exception {
String query = "java jdk";
Expand Down
Loading