From 38d3d566a28a94855d8a5bee1c72555c8d46c6f1 Mon Sep 17 00:00:00 2001 From: Yves Langisch Date: Tue, 14 May 2024 10:40:54 +0200 Subject: [PATCH] Make sure to store cookies from a successful login only. --- .../ctera/CteraAuthenticationHandler.java | 4 +- .../core/ctera/CteraCookieInterceptor.java | 49 +++++++++++++++++++ .../ch/cyberduck/core/ctera/CteraSession.java | 1 + .../core/ctera/CteraSessionTest.java | 37 ++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 ctera/src/main/java/ch/cyberduck/core/ctera/CteraCookieInterceptor.java diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java index 1ac66b66322..b1368c24878 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraAuthenticationHandler.java @@ -39,6 +39,8 @@ public class CteraAuthenticationHandler implements ServiceUnavailableRetryStrategy { private static final Logger log = LogManager.getLogger(CteraAuthenticationHandler.class); + public static final String AUTH_PATH = "/ServicesPortal/api/login?format=jsonext"; + private final CteraSession session; private CteraTokens tokens = CteraTokens.EMPTY; @@ -52,7 +54,7 @@ public void setTokens(final CteraTokens tokens) { } public void authenticate() throws BackgroundException { - final HttpPost login = new HttpPost("/ServicesPortal/api/login?format=jsonext"); + final HttpPost login = new HttpPost(AUTH_PATH); try { login.setEntity( new StringEntity(String.format("j_username=device%%5c%s&j_password=%s", tokens.getDeviceId(), tokens.getSharedSecret()), diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraCookieInterceptor.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraCookieInterceptor.java new file mode 100644 index 00000000000..53a5267826b --- /dev/null +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraCookieInterceptor.java @@ -0,0 +1,49 @@ +package ch.cyberduck.core.ctera; + +/* + * Copyright (c) 2002-2024 iterate GmbH. All rights reserved. + * https://cyberduck.io/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpException; +import org.apache.http.HttpResponse; +import org.apache.http.HttpResponseInterceptor; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.protocol.HttpContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; + +public class CteraCookieInterceptor implements HttpResponseInterceptor { + + private static final Logger log = LogManager.getLogger(CteraCookieInterceptor.class); + + @Override + public void process(final HttpResponse response, final HttpContext context) throws HttpException, IOException { + if(response.containsHeader("Set-Cookie")) { + final HttpClientContext clientContext = HttpClientContext.adapt(context); + if(StringUtils.equals(CteraAuthenticationHandler.AUTH_PATH, clientContext.getRequest().getRequestLine().getUri()) && + response.getStatusLine().getStatusCode() == HttpStatus.SC_OK && + HttpPost.METHOD_NAME.equals(clientContext.getRequest().getRequestLine().getMethod())) { + log.debug(String.format("Accept cookie %s from login response", response.getFirstHeader("Set-Cookie"))); + } + else { + response.removeHeaders("Set-Cookie"); + } + } + } +} diff --git a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java index 73a6dc2c76c..f8e0539b512 100644 --- a/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java +++ b/ctera/src/main/java/ch/cyberduck/core/ctera/CteraSession.java @@ -115,6 +115,7 @@ protected DAVClient connect(final Proxy proxy, final HostKeyCallback key, final configuration.setRedirectStrategy(new DAVRedirectStrategy(new PreferencesRedirectCallback())); configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host, new ExecutionCountServiceUnavailableRetryStrategy(authentication))); + configuration.addInterceptorFirst(new CteraCookieInterceptor()); return new DAVClient(new HostUrlProvider().withUsername(false).get(host), configuration); } diff --git a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraSessionTest.java b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraSessionTest.java index 2835d2440ca..b696add4fbb 100644 --- a/ctera/src/test/java/ch/cyberduck/core/ctera/CteraSessionTest.java +++ b/ctera/src/test/java/ch/cyberduck/core/ctera/CteraSessionTest.java @@ -15,15 +15,29 @@ * GNU General Public License for more details. */ +import ch.cyberduck.core.Credentials; +import ch.cyberduck.core.DisabledCancelCallback; +import ch.cyberduck.core.DisabledHostKeyCallback; import ch.cyberduck.core.DisabledListProgressListener; +import ch.cyberduck.core.DisabledLoginCallback; +import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; +import ch.cyberduck.core.dav.DAVListService; +import ch.cyberduck.core.exception.ConnectionCanceledException; +import ch.cyberduck.core.proxy.Proxy; +import ch.cyberduck.core.ssl.DefaultX509KeyManager; +import ch.cyberduck.core.ssl.DisabledX509TrustManager; +import ch.cyberduck.core.threading.CancelCallback; import ch.cyberduck.test.IntegrationTest; +import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.experimental.categories.Category; import java.util.EnumSet; +import static org.junit.Assert.*; + @Category(IntegrationTest.class) public class CteraSessionTest extends AbstractCteraTest { @@ -31,4 +45,27 @@ public class CteraSessionTest extends AbstractCteraTest { public void testLoginNonSAML() throws Exception { new CteraListService(session).list(new Path(session.getHost().getDefaultPath(), EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); } + + @Test + public void testLoginRefreshCookie() throws Exception { + final Host host = new Host(new CteraProtocol(), "mountainduck.ctera.me", new Credentials( + StringUtils.EMPTY, StringUtils.EMPTY, + PROPERTIES.get("ctera.token") + )); + host.setDefaultPath("/ServicesPortal/webdav"); + final CteraSession session = new CteraSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); + assertNotNull(session.open(Proxy.DIRECT, new DisabledHostKeyCallback(), new DisabledLoginCallback(), new DisabledCancelCallback())); + assertTrue(session.isConnected()); + assertNotNull(session.getClient()); + session.login(Proxy.DIRECT, new DisabledLoginCallback(), new CancelCallback() { + @Override + public void verify() throws ConnectionCanceledException { + fail("OAuth tokens need to be refreshed"); + } + }); + assertEquals("mountainduck@cterasendbox1.onmicrosoft.com", host.getCredentials().getUsername()); + assertTrue(host.getCredentials().isSaved()); + new DAVListService(session).list(new Path(host.getDefaultPath(), EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); + session.close(); + } }