Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/dist-win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ jobs:
with:
ref: ${{ inputs.ref || github.ref }}

- name: Set up JDK 21 (x64)
- name: Set up JDK 25 (x64)
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: 21
java-version: 25
distribution: 'temurin'
architecture: x64
cache: maven
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
jdk: [21]
jdk: [25]

runs-on: ${{ matrix.os }}

Expand Down
5 changes: 5 additions & 0 deletions inception/inception-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-http-client-api</artifactId>
<version>${rdf4j.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-rio-api</artifactId>
Expand Down
10 changes: 8 additions & 2 deletions inception/inception-kb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-repository-manager</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-http-client-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.rdf4j</groupId>
<artifactId>rdf4j-sail-lucene-api</artifactId>
Expand Down Expand Up @@ -274,16 +278,18 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>

<!-- DEPENDENCIES FOR TESTING -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<scope>test</scope>
</dependency>

<!-- DEPENDENCIES FOR TESTING -->
<dependency>
<groupId>de.tudarmstadt.ukp.inception.app</groupId>
<artifactId>inception-layer-span-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package de.tudarmstadt.ukp.inception.kb;

import static de.tudarmstadt.ukp.inception.kb.RepositoryType.LOCAL;
import static de.tudarmstadt.ukp.inception.kb.RepositoryType.REMOTE;
import static de.tudarmstadt.ukp.inception.kb.http.PerThreadSslCheckingHttpClientUtils.restoreSslVerification;
import static de.tudarmstadt.ukp.inception.kb.http.PerThreadSslCheckingHttpClientUtils.skipCertificateChecks;
import static de.tudarmstadt.ukp.inception.kb.querybuilder.SPARQLQueryBuilder.DEFAULT_LIMIT;
Expand Down Expand Up @@ -227,8 +228,8 @@ public KnowledgeBaseServiceImpl(RepositoryProperties aRepoProperties,
}

repoManager = RepositoryProvider.getRepositoryManager(kbRepositoriesRoot);
repoManager.setHttpClient(PerThreadSslCheckingHttpClientUtils
.newPerThreadSslCheckingHttpClientBuilder().build());
repoManager.setHttpClient(
PerThreadSslCheckingHttpClientUtils.newPerThreadSslCheckingHttpClient());

BOOT_LOG.info("Knowledge base repository path: {}", kbRepositoriesRoot);
}
Expand Down Expand Up @@ -273,6 +274,9 @@ void onContextRefreshed()
if (LOCAL == kb.getType()) {
reconfigureLocalKnowledgeBase(kb);
}
else if (REMOTE == kb.getType()) {
migrateUrlEmbeddedCredentials(kb);
}
}

if (!orphanedIDs.isEmpty()) {
Expand Down Expand Up @@ -700,7 +704,14 @@ public RepositoryImplConfig getNativeConfig()
@Override
public RepositoryImplConfig getRemoteConfig(String url)
{
return new SPARQLRepositoryConfig(url);
var split = splitUrlUserInfo(url);
if (split.userInfo() != null) {
LOG.warn(
"URL [{}] contains embedded credentials. Stripping them - configure "
+ "authentication via the KB auth-traits UI instead.",
split.cleanUrl());
}
return new SPARQLRepositoryConfig(split.cleanUrl());
}

@Override
Expand Down Expand Up @@ -833,25 +844,100 @@ private void addAdditionalHeaders(SPARQLRepository aSparqlRepo, Map<String, Stri
private void applyBasicHttpAuthenticationConfigurationFromUrl(
SPARQLRepositoryConfig sparqlRepoConfig, SPARQLRepository sparqlRepo)
{
var uri = URI.create(sparqlRepoConfig.getQueryEndpointUrl());
var userInfo = uri.getUserInfo();
if (isNotBlank(userInfo)) {
userInfo = userInfo.trim();
String username;
String password;
if (userInfo.contains(":")) {
username = substringBefore(userInfo, ":");
password = substringAfter(userInfo, ":");
var split = splitUrlUserInfo(sparqlRepoConfig.getQueryEndpointUrl());
if (split.user() != null) {
sparqlRepo.setUsernameAndPassword(split.user(), split.password());
}
}

/**
* Migrates URL-embedded credentials ({@code http://user:pass@host/...}) on a REMOTE KB into
* {@link BasicAuthenticationTraits} and a cleaned URL. Apache HttpClient 5 (used by RDF4J 6)
* rejects URIs with a userinfo component outright, so legacy configs that worked under RDF4J 5
* must be normalized before the next connection attempt. Invoked once per KB at startup.
*/
private void migrateUrlEmbeddedCredentials(KnowledgeBase aKB)
{
try {
var cfg = getKnowledgeBaseConfig(aKB);
if (!(cfg instanceof SPARQLRepositoryConfig sparqlCfg)) {
return;
}

var queryUrl = sparqlCfg.getQueryEndpointUrl();
var updateUrl = sparqlCfg.getUpdateEndpointUrl();
var querySplit = splitUrlUserInfo(queryUrl);
var updateSplit = splitUrlUserInfo(updateUrl);

if (querySplit.userInfo() == null && updateSplit.userInfo() == null) {
return;
}

var newCfg = updateUrl == null //
? new SPARQLRepositoryConfig(querySplit.cleanUrl()) //
: new SPARQLRepositoryConfig(querySplit.cleanUrl(), updateSplit.cleanUrl());

// Prefer the query URL's credentials; fall back to the update URL's.
var credSource = querySplit.user() != null ? querySplit : updateSplit;

var traits = isNotBlank(aKB.getTraits()) ? readTraits(aKB) : null;
if (traits == null) {
traits = new RemoteRepositoryTraits();
}
var hadAuth = traits.getAuthentication() != null;
if (!hadAuth && credSource.user() != null) {
var basic = new BasicAuthenticationTraits();
basic.setUsername(credSource.user());
basic.setPassword(credSource.password());
traits.setAuthentication(basic);
aKB.setTraits(JSONUtil.toJsonString(traits));
}

updateKnowledgeBase(aKB, newCfg);

if (hadAuth) {
LOG.info(
"Migrated KB [{}]: stripped URL-embedded credentials "
+ "(KB already had explicit auth traits configured).",
aKB.getName());
}
else {
username = userInfo;
password = "";
LOG.info("Migrated KB [{}]: moved URL-embedded credentials into "
+ "basic-auth traits.", aKB.getName());
}
}
catch (Exception e) {
LOG.error(
"Unable to migrate URL-embedded credentials for KB [{}]. "
+ "Remote connections may fail until the URL is corrected manually.",
aKB.getName(), e);
}
}

sparqlRepo.setUsernameAndPassword(username, password);
/**
* Parses a URL, returning the userinfo (or {@code null}) and the URL with the userinfo
* stripped. Returns {@code (cleanUrl=null, user=null, password=null, userInfo=null)} for a
* {@code null} input URL.
*/
private static UrlUserInfo splitUrlUserInfo(String aUrl)
{
if (aUrl == null) {
return new UrlUserInfo(null, null, null, null);
}
var uri = URI.create(aUrl);
var userInfo = uri.getUserInfo();
if (!isNotBlank(userInfo)) {
return new UrlUserInfo(aUrl, null, null, null);
}
userInfo = userInfo.trim();
var user = userInfo.contains(":") ? substringBefore(userInfo, ":") : userInfo;
var password = userInfo.contains(":") ? substringAfter(userInfo, ":") : "";
var cleanUrl = aUrl.replace(uri.getRawUserInfo() + "@", "");
return new UrlUserInfo(cleanUrl, user, password, userInfo);
}

private record UrlUserInfo(String cleanUrl, String user, String password, String userInfo) {}

@SuppressWarnings("resource")
@Override
public void importData(KnowledgeBase kb, String aFilename, InputStream aIS)
Expand Down
Loading
Loading