Skip to content

Commit

Permalink
Prompt for OAuth Client ID and OAuth Client Secret if missing in conn…
Browse files Browse the repository at this point in the history
…ection profile.
  • Loading branch information
dkocher committed Aug 11, 2023
1 parent 6a6d70a commit cbb5a22
Show file tree
Hide file tree
Showing 15 changed files with 77 additions and 36 deletions.
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
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
6 changes: 4 additions & 2 deletions hubic/src/main/java/ch/cyberduck/core/hubic/HubicSession.java
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,6 +24,7 @@
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;
Expand Down Expand Up @@ -91,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 @@ -173,11 +176,11 @@ private TokenResponse authorizeWithCode(final Host bookmark, final LoginCallback
}
else {
final Credentials clientid = prompt.prompt(bookmark,
LocaleFactory.localizedString("OAuth Client ID", "Credentials"),
LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials"),
LocaleFactory.localizedString("Provide additional login credentials", "Credentials"),
new LoginOptions(bookmark.getProtocol())
.user(true).password(true)
.passwordPlaceholder("OAuth Client ID"));
.passwordPlaceholder(LocaleFactory.localizedString(Profile.OAUTH_CLIENT_ID_KEY, "Credentials")));
if(clientid.isSaved()) {
bookmark.setProperty("oauth.clientid", clientid.getPassword());
}
Expand Down Expand Up @@ -377,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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.LocalAccessDeniedException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.threading.CancelCallback;

import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -54,7 +55,7 @@ public class OAuth2RequestInterceptor extends OAuth2AuthorizationService impleme
private final HostPasswordStore store = PasswordStoreFactory.get();
private final Host host;

public OAuth2RequestInterceptor(final HttpClient client, final Host host) {
public OAuth2RequestInterceptor(final HttpClient client, final Host host, final LoginCallback prompt) throws LoginCanceledException {
this(client, host,
Scheme.isURL(host.getProtocol().getOAuthTokenUrl()) ? host.getProtocol().getOAuthTokenUrl() : new HostUrlProvider().withUsername(false).withPath(true).get(
host.getProtocol().getScheme(), host.getPort(), null, host.getHostname(), host.getProtocol().getOAuthTokenUrl()),
Expand All @@ -63,12 +64,12 @@ public OAuth2RequestInterceptor(final HttpClient client, final Host host) {
host.getProtocol().getOAuthClientId(),
host.getProtocol().getOAuthClientSecret(),
host.getProtocol().getOAuthScopes(),
host.getProtocol().isOAuthPKCE());
host.getProtocol().isOAuthPKCE(), prompt);
}

public OAuth2RequestInterceptor(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) {
super(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 {
super(client, host, tokenServerUrl, authorizationServerUrl, clientid, clientsecret, scopes, pkce, prompt);
this.host = host;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import ch.cyberduck.core.SimplePathPredicate;
import ch.cyberduck.core.UrlProvider;
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.HttpSession;
import ch.cyberduck.core.oauth.OAuth2ErrorResponseInterceptor;
Expand Down Expand Up @@ -125,10 +127,10 @@ public User.Metadata getUser() {
}

@Override
protected OneDriveAPI connect(final Proxy proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws HostParserException {
protected OneDriveAPI connect(final Proxy proxy, final HostKeyCallback key, 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) {
builder.build(ProxyFactory.get().find(host.getProtocol().getOAuthAuthorizationUrl()), this, prompt).build(), host, prompt) {
@Override
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
if(request.containsHeader(HttpHeaders.AUTHORIZATION)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import ch.cyberduck.core.cdn.DistributionConfiguration;
import ch.cyberduck.core.exception.AccessDeniedException;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.ConnectionCanceledException;
import ch.cyberduck.core.exception.InteroperabilityException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.exception.LoginFailureException;
import ch.cyberduck.core.features.*;
import ch.cyberduck.core.http.HttpSession;
Expand Down Expand Up @@ -71,7 +73,7 @@ public SwiftSession(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 {
// Always inject new pool to builder on connect because the pool is shutdown on disconnect
final HttpClientBuilder pool = builder.build(proxy, this, prompt);
pool.disableContentCompression();
Expand Down
Loading

0 comments on commit cbb5a22

Please sign in to comment.