Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,18 @@ public AWSSessionCredentialsRetriever(final X509TrustManager trust, final X509Ke

@Override
public Credentials get() throws BackgroundException {
log.debug("Configure credentials from {}", url);
log.debug("Fetching AWS session credentials from URL: {}", url);
// Parse URL to get hostname for SSL configuration, but use original URL for the request
// to preserve encoded characters and path structure (e.g., %2F and double slashes)
final Host address = new HostParser(ProtocolFactory.get()).get(url);
final HttpConnectionPoolBuilder builder = new HttpConnectionPoolBuilder(address,
new ThreadLocalHostnameDelegatingTrustManager(trust, address.getHostname()), key, ProxyFactory.get());
final HttpClientBuilder configuration = builder.build(ProxyFactory.get(),
new DisabledTranscriptListener(), new DisabledLoginCallback());
try (CloseableHttpClient client = configuration.build()) {
final HttpRequestBase resource = new HttpGet(new HostUrlProvider().withUsername(false).withPath(true).get(address));
// Use the original URL directly to preserve encoding and structure
log.info("Making HTTP GET request to: {}", url);
final HttpRequestBase resource = new HttpGet(url);
return client.execute(resource, response -> {
switch(response.getStatusLine().getStatusCode()) {
case HttpStatus.SC_OK:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ public S3CredentialsConfigurator(final Local directory) {
@Override
public Credentials configure(final Host host) {
final Credentials credentials = new Credentials(host.getCredentials());
// Check for HTTP credentials URL in bookmark
final String httpCredentialsUrl = host.getProperty("aws.credentials.http.url");
log.debug("Retrieved aws.credentials.http.url property value: '{}'", httpCredentialsUrl);
if(StringUtils.isNotBlank(httpCredentialsUrl)) {
log.debug("HTTP credentials URL configured: {}. Returning placeholder credentials to skip credential validation.", httpCredentialsUrl);
// Return credentials with placeholder tokens AND username/password to pass validation
// The actual credentials will be fetched via HTTP during session connect
return credentials
.setUsername("http-credentials-placeholder")
.setPassword("http-credentials-placeholder")
.setTokens(new TemporaryAccessTokens("http-credentials-placeholder", "http-credentials-placeholder", "http-credentials-placeholder", -1L));
}
log.info("No HTTP credentials URL found, continuing with AWS CLI profile lookup");
final BasicProfile profile = profiles.entrySet().stream().filter(entry -> {
// Matching access key or profile name
if(StringUtils.equals(entry.getKey(), credentials.getUsername())) {
Expand Down
12 changes: 12 additions & 0 deletions s3/src/main/java/ch/cyberduck/core/s3/S3Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,18 @@ public void process(final HttpRequest request, final HttpContext context) {

protected S3CredentialsStrategy configureCredentialsStrategy(final HttpClientBuilder configuration,
final LoginCallback prompt) throws LoginCanceledException {
// Check for custom HTTP credentials URL in bookmark (stored in Custom map)
log.info("Checking for aws.credentials.http.url property in bookmark");
log.info("Host custom properties: {}", host.getCustom());
final String httpCredentialsUrl = host.getProperty("aws.credentials.http.url");
log.info("Retrieved aws.credentials.http.url value: {}", httpCredentialsUrl);
if(StringUtils.isNotBlank(httpCredentialsUrl)) {
log.info("Configure credentials from bookmark HTTP endpoint {}", httpCredentialsUrl);
final AWSSessionCredentialsRetriever retriever = new AWSSessionCredentialsRetriever(trust, key, httpCredentialsUrl);
log.info("Created AWSSessionCredentialsRetriever: {}", retriever);
return retriever;
}
log.info("No HTTP credentials URL found, continuing with other authentication methods");
if(host.getProtocol().isOAuthConfigurable()) {
final OAuth2RequestInterceptor oauth = new OAuth2RequestInterceptor(configuration.build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ch.cyberduck.core.s3;

import ch.cyberduck.core.DisabledCancelCallback;
import ch.cyberduck.core.DisabledHostKeyCallback;
import ch.cyberduck.core.DisabledLoginCallback;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.ProtocolFactory;
import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader;
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
import ch.cyberduck.core.ssl.DisabledX509TrustManager;

import org.junit.Test;

import java.util.Collections;
import java.util.HashSet;

import static org.junit.Assert.*;

public class S3HttpCredentialsTest {

@Test
public void testHttpCredentialsUrlProperty() throws Exception {
final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new S3Protocol())));
final Profile profile = new ProfilePlistReader(factory).read(
this.getClass().getResourceAsStream("/S3 (Credentials from Instance Metadata).cyberduckprofile"));
final Host host = new Host(profile, profile.getDefaultHostname());

// Test that the profile's Context field is readable
assertNotNull(host.getProtocol().getContext());
assertEquals("http://169.254.169.254/latest/meta-data/iam/security-credentials/s3access",
host.getProtocol().getContext());

// Test that custom property can be set and retrieved
host.setProperty("aws.credentials.http.url", "https://example.com/credentials");
assertEquals("https://example.com/credentials", host.getProperty("aws.credentials.http.url"));
}

@Test
public void testBookmarkWithHttpCredentialsUrl() throws Exception {
final S3Protocol protocol = new S3Protocol();
final Host host = new Host(protocol, "content-repo-prod-contentsupplier-touch.s3.amazonaws.com");

// Simulate bookmark with HTTP credentials URL in Custom dict
host.setProperty("aws.credentials.http.url",
"https://up.example.com/VMHGTtd0BDYpcZgWU2arm6SwElXsiOCK");

// Verify the property is accessible
assertEquals("https://up.example.com/VMHGTtd0BDYpcZgWU2arm6SwElXsiOCK",
host.getProperty("aws.credentials.http.url"));

// Verify session can be created (without actually connecting)
final S3Session session = new S3Session(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
assertNotNull(session);
}
}
Loading