Skip to content
Merged
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
6 changes: 4 additions & 2 deletions box/src/main/java/ch/cyberduck/core/box/BoxSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import ch.cyberduck.core.box.io.swagger.client.ApiException;
import ch.cyberduck.core.box.io.swagger.client.api.UsersApi;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.AttributesFinder;
import ch.cyberduck.core.features.Copy;
import ch.cyberduck.core.features.Delete;
Expand Down Expand Up @@ -64,9 +66,9 @@ public BoxSession(final Host host, final X509TrustManager trust, final X509KeyMa
}

@Override
public CloseableHttpClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) {
public CloseableHttpClient connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host)
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
configuration.addInterceptorLast(authorizationService);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.PasswordStoreFactory;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.preferences.PreferencesFactory;
Expand All @@ -43,7 +44,7 @@ public TerminalLoginService(final CommandLine input) {
}

@Override
public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException, LoginFailureException {
public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException {
final Credentials credentials = bookmark.getCredentials();
if(input.hasOption(TerminalOptionsBuilder.Params.anonymous.name())) {
credentials.setUsername(PreferencesFactory.get().getProperty("connection.login.anon.name"));
Expand Down
13 changes: 7 additions & 6 deletions core/src/main/java/ch/cyberduck/core/KeychainLoginService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LocalAccessDeniedException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
Expand All @@ -44,7 +45,7 @@ public KeychainLoginService(final HostPasswordStore keychain) {
}

@Override
public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException, LoginFailureException {
public void validate(final Host bookmark, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException, LoginFailureException {
if(log.isDebugEnabled()) {
log.debug(String.format("Validate login credentials for %s", bookmark));
}
Expand Down Expand Up @@ -137,7 +138,7 @@ public void validate(final Host bookmark, final LoginCallback prompt, final Logi
* @return True if credentials have been updated
* @throws LoginCanceledException Prompt canceled by user
*/
public boolean prompt(final Host bookmark, final String message, final LoginCallback prompt, final LoginOptions options) throws LoginCanceledException {
public boolean prompt(final Host bookmark, final String message, final LoginCallback prompt, final LoginOptions options) throws ConnectionCanceledException {
final Credentials credentials = bookmark.getCredentials();
if(options.password) {
final Credentials input = prompt.prompt(bookmark, credentials.getUsername(),
Expand All @@ -158,6 +159,9 @@ public boolean prompt(final Host bookmark, final String message, final LoginCall
credentials.setToken(input.getPassword());
}
if(options.oauth) {
prompt.warn(bookmark, LocaleFactory.localizedString("Login failed", "Credentials"), message,
LocaleFactory.localizedString("Continue", "Credentials"),
LocaleFactory.localizedString("Cancel", "Localizable"), null);
log.warn(String.format("Reset OAuth tokens for %s", bookmark));
credentials.setOauth(OAuthTokens.EMPTY);
}
Expand Down Expand Up @@ -204,10 +208,7 @@ else if(credentials.isCertificateAuthentication()) {
listener.message(LocaleFactory.localizedString("Login failed", "Credentials"));
credentials.setPassed(false);
final LoginOptions options = new LoginOptions(bookmark.getProtocol());
final StringAppender details = new StringAppender();
details.append(LocaleFactory.localizedString("Login failed", "Credentials"));
details.append(e.getDetail());
if(this.prompt(bookmark, details.toString(), prompt, options)) {
if(this.prompt(bookmark, e.getDetail(), prompt, options)) {
// Retry
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/ch/cyberduck/core/LoginService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.proxy.Proxy;
Expand All @@ -31,7 +32,7 @@ public interface LoginService {
* @param pompt Login prompt
* @param options Login mechanism features
*/
void validate(Host bookmark, LoginCallback pompt, LoginOptions options) throws LoginCanceledException, LoginFailureException;
void validate(Host bookmark, LoginCallback pompt, LoginOptions options) throws ConnectionCanceledException, LoginFailureException;

/**
* Login and prompt on failure
Expand Down
7 changes: 5 additions & 2 deletions core/src/main/java/ch/cyberduck/core/Profile.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public class Profile implements Protocol {
*/
private final Protocol parent;

public static final String OAUTH_CLIENT_ID_KEY = "OAuth Client ID";
public static final String OAUTH_CLIENT_SECRET_KEY = "OAuth Client Secret";

private Local disk;
private Local icon;

Expand Down Expand Up @@ -512,7 +515,7 @@ public String getOAuthRedirectUrl() {

@Override
public String getOAuthClientId() {
final String v = this.value("OAuth Client ID");
final String v = this.value(OAUTH_CLIENT_ID_KEY);
if(StringUtils.isBlank(v)) {
return parent.getOAuthClientId();
}
Expand All @@ -521,7 +524,7 @@ public String getOAuthClientId() {

@Override
public String getOAuthClientSecret() {
final String v = this.value("OAuth Client Secret");
final String v = this.value(OAUTH_CLIENT_SECRET_KEY);
if(StringUtils.isBlank(v)) {
return parent.getOAuthClientSecret();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public void process(final HttpRequest request, final HttpContext context) {
}
}
}
}).build(), host) {
}).build(), host, prompt) {
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if(request instanceof HttpRequestWrapper) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.UseragentProvider;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.DefaultHttpRateLimiter;
import ch.cyberduck.core.http.HttpSession;
Expand Down Expand Up @@ -72,9 +74,9 @@ public DropboxSession(final Host host, final X509TrustManager trust, final X509K
}

@Override
protected CustomDbxRawClientV2 connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) {
protected CustomDbxRawClientV2 connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host)
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
configuration.addInterceptorLast(authorizationService);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
2 changes: 1 addition & 1 deletion eue/src/main/java/ch/cyberduck/core/eue/EueSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void process(final HttpRequest request, final HttpContext context) {
request.addHeader(HttpHeaders.AUTHORIZATION,
String.format("Basic %s", Base64.encodeToString(String.format("%s:%s", host.getProtocol().getOAuthClientId(), host.getProtocol().getOAuthClientSecret()).getBytes(StandardCharsets.UTF_8), false)));
}
}).build(), host)
}).build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl()
);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.UseragentProvider;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.HostParserException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.DefaultHttpRateLimiter;
import ch.cyberduck.core.http.HttpSession;
Expand Down Expand Up @@ -68,9 +70,9 @@ public DriveSession(final Host host, final X509TrustManager trust, final X509Key
}

@Override
protected Drive connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException {
protected Drive connect(final Proxy proxy, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException, ConnectionCanceledException {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host)
authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
configuration.addInterceptorLast(authorizationService);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import ch.cyberduck.core.UseragentProvider;
import ch.cyberduck.core.cdn.DistributionConfiguration;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.HttpSession;
import ch.cyberduck.core.http.UserAgentHttpRequestInitializer;
Expand Down Expand Up @@ -63,9 +65,9 @@ public GoogleStorageSession(final Host host, final X509TrustManager trust, final
}

@Override
protected Storage connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) {
protected Storage connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host)
authorizationService = new OAuth2RequestInterceptor(builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
configuration.addInterceptorLast(authorizationService);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import ch.cyberduck.core.OAuthTokens;
import ch.cyberduck.core.cdn.DistributionConfiguration;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor;
import ch.cyberduck.core.oauth.OAuth2RequestInterceptor;
import ch.cyberduck.core.openstack.SwiftExceptionMappingService;
Expand Down Expand Up @@ -50,9 +52,9 @@ public HubicSession(final Host host, final X509TrustManager trust, final X509Key
}

@Override
protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) {
protected Client connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws ConnectionCanceledException {
final HttpClientBuilder configuration = builder.build(proxy, this, prompt);
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host)
authorizationService = new OAuth2RequestInterceptor(configuration.build(), host, prompt)
.withRedirectUri(host.getProtocol().getOAuthRedirectUrl());
configuration.addInterceptorLast(authorizationService);
configuration.setServiceUnavailableRetryStrategy(new OAuth2ErrorResponseInterceptor(host, authorizationService, prompt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.OAuthTokens;
import ch.cyberduck.core.PreferencesUseragentProvider;
import ch.cyberduck.core.Profile;
import ch.cyberduck.core.StringAppender;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InteroperabilityException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService;
import ch.cyberduck.core.http.UserAgentHttpRequestInitializer;
import ch.cyberduck.core.preferences.HostPreferences;
import ch.cyberduck.core.preferences.PreferencesFactory;
import ch.cyberduck.core.threading.CancelCallback;

Expand Down Expand Up @@ -90,21 +92,23 @@ public class OAuth2AuthorizationService {

private final HttpTransport transport;

public OAuth2AuthorizationService(final HttpClient client,
public OAuth2AuthorizationService(final HttpClient client, final Host host,
final String tokenServerUrl, final String authorizationServerUrl,
final String clientid, final String clientsecret, final List<String> scopes, final boolean pkce) {
this(new ApacheHttpTransport(client),
tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce);
final String clientid, final String clientsecret, final List<String> scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException {
this(new ApacheHttpTransport(client), host,
tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce, prompt);
}

public OAuth2AuthorizationService(final HttpTransport transport,
public OAuth2AuthorizationService(final HttpTransport transport, final Host host,
final String tokenServerUrl, final String authorizationServerUrl,
final String clientid, final String clientsecret, final List<String> scopes, final boolean pkce) {
final String clientid, final String clientsecret, final List<String> scopes, final boolean pkce, final LoginCallback prompt) throws LoginCanceledException {
this.transport = transport;
this.tokenServerUrl = tokenServerUrl;
this.authorizationServerUrl = authorizationServerUrl;
this.clientid = clientid;
this.clientsecret = clientsecret;
this.clientid = prompt(host, prompt, Profile.OAUTH_CLIENT_ID_KEY, LocaleFactory.localizedString(
Profile.OAUTH_CLIENT_ID_KEY, "Credentials"), clientid);
this.clientsecret = prompt(host, prompt, Profile.OAUTH_CLIENT_SECRET_KEY, LocaleFactory.localizedString(
Profile.OAUTH_CLIENT_SECRET_KEY, "Credentials"), clientsecret);
this.scopes = scopes;
this.pkce = pkce;
}
Expand Down Expand Up @@ -165,11 +169,32 @@ private TokenResponse authorizeWithCode(final Host bookmark, final LoginCallback
);
}
// Start OAuth2 flow within browser
final ClientParametersAuthentication clientParameters;
if(StringUtils.isBlank(clientid)) {
if(null != new HostPreferences(bookmark).getProperty("oauth.clientid")) {
clientParameters = new ClientParametersAuthentication(new HostPreferences(bookmark).getProperty("oauth.clientid"), StringUtils.EMPTY);
}
else {
final Credentials clientid = prompt.prompt(bookmark,
LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials"),
LocaleFactory.localizedString("Provide additional login credentials", "Credentials"),
new LoginOptions(bookmark.getProtocol())
.user(true).password(true)
.passwordPlaceholder(LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials")));
if(clientid.isSaved()) {
bookmark.setProperty("oauth.clientid", clientid.getPassword());
}
clientParameters = new ClientParametersAuthentication(clientid.getUsername(), StringUtils.EMPTY);
}
}
else {
clientParameters = new ClientParametersAuthentication(clientid, clientsecret);
}
final AuthorizationCodeFlow.Builder flowBuilder = new AuthorizationCodeFlow.Builder(
method,
transport, json,
new GenericUrl(tokenServerUrl),
new ClientParametersAuthentication(clientid, clientsecret),
clientParameters,
clientid,
authorizationServerUrl)
.setScopes(scopes)
Expand Down Expand Up @@ -355,4 +380,20 @@ public TokenResponse toTokenResponse() {
}
}

/**
* Prompt for value if missing
*/
private static String prompt(final Host bookmark, final LoginCallback prompt,
final String property, final String message, final String value) throws LoginCanceledException {
if(null == value) {
final Credentials input = prompt.prompt(bookmark, message,
LocaleFactory.localizedString("Provide additional login credentials", "Credentials"),
new LoginOptions().icon(bookmark.getProtocol().disk()));
if(input.isSaved()) {
bookmark.setProperty(property, input.getPassword());
}
return input.getPassword();
}
return value;
}
}
Loading