Skip to content

Commit

Permalink
Save new tokens using OAuth Client ID as prefix. Fix #15284.
Browse files Browse the repository at this point in the history
  • Loading branch information
dkocher committed Nov 23, 2023
1 parent 36ae079 commit 8ef758d
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@
using ch.cyberduck.core.preferences;
using Ch.Cyberduck.Core.CredentialManager;
using org.apache.logging.log4j;
using org.apache.logging.log4j.core.net;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Text;
using static Windows.Win32.Security.Credentials.CRED_PERSIST;
Expand Down Expand Up @@ -61,9 +64,12 @@ public override void delete(Host bookmark)
logger.info(string.Format("Delete password for bookmark {0}", bookmark));
}
var target = ToUri(bookmark);
if (!WinCredentialManager.RemoveCredentials(target.AbsoluteUri))
foreach (Uri descriptor in target)
{
base.delete(bookmark);
if (!WinCredentialManager.RemoveCredentials(descriptor.AbsoluteUri))
{
base.delete(bookmark);
}
}
}

Expand All @@ -86,7 +92,7 @@ public override void deletePassword(Scheme scheme, int port, string hostName, st

public override string findLoginPassword(Host bookmark)
{
var target = ToUri(bookmark);
var target = ToUri(bookmark)[0];
var cred = WinCredentialManager.GetCredentials(target.AbsoluteUri);
if (!string.IsNullOrWhiteSpace(cred.Password))
{
Expand All @@ -101,7 +107,7 @@ public override string findLoginToken(Host bookmark)
{
logger.info(string.Format("Fetching login token from keychain for {0}", bookmark));
}
var target = ToUri(bookmark);
var target = ToUri(bookmark)[0];
var cred = WinCredentialManager.GetCredentials(target.AbsoluteUri);
if (cred.Attributes is Dictionary<string, string> attrs
&& attrs.TryGetValue("Token", out var token)
Expand All @@ -119,21 +125,30 @@ public override OAuthTokens findOAuthTokens(Host bookmark)
logger.info(string.Format("Fetching OAuth tokens from keychain for {0}", bookmark));
}
var target = ToUri(bookmark);
var cred = WinCredentialManager.GetCredentials(target.AbsoluteUri);
if (cred.Attributes is Dictionary<string, string> attrs
&& attrs.TryGetValue("OAuth Access Token", out var accessToken))
foreach(Uri descriptor in target)
{
attrs.TryGetValue("OAuth Refresh Token", out var refreshToken);
attrs.TryGetValue("OIDC Id Token", out var idToken);
long expiry = default;
if (attrs.TryGetValue("OAuth Expiry", out var expiryValue))
var cred = WinCredentialManager.GetCredentials(descriptor.AbsoluteUri);
if (cred.Attributes is Dictionary<string, string> attrs)
{
long.TryParse(expiryValue, out expiry);
attrs.TryGetValue("OAuth Access Token", out var accessToken);
attrs.TryGetValue("OAuth Refresh Token", out var refreshToken);
attrs.TryGetValue("OIDC Id Token", out var idToken);
long expiry = default;
if (attrs.TryGetValue("OAuth Expiry", out var expiryValue))
{
long.TryParse(expiryValue, out expiry);
}
OAuthTokens tokens = new(accessToken, refreshToken, new(expiry), idToken);
if(tokens.validate())
{
return tokens;
}
// Continue
}
return new(accessToken, refreshToken, new(expiry), idToken);
}

return base.findOAuthTokens(bookmark);
return base.findOAuthTokens(bookmark);
}
return OAuthTokens.EMPTY;
}

public override string findPrivateKeyPassphrase(Host bookmark)
Expand All @@ -142,7 +157,7 @@ public override string findPrivateKeyPassphrase(Host bookmark)
{
logger.info(string.Format("Fetching private key passphrase from keychain for {0}", bookmark));
}
var target = ToUri(bookmark);
var target = ToUri(bookmark)[0];
var cred = WinCredentialManager.GetCredentials(target.AbsoluteUri);
if (cred.Attributes is Dictionary<string, string> attrs
&& attrs.TryGetValue("Private Key Passphrase", out var passphrase)
Expand Down Expand Up @@ -172,7 +187,7 @@ public override void save(Host bookmark)
{
logger.info(string.Format("Add password for bookmark {0}", bookmark));
}
var target = ToUri(bookmark);
var target = ToUri(bookmark)[0];
var credential = bookmark.getCredentials();

var winCred = new WindowsCredentialManagerCredential(
Expand Down Expand Up @@ -209,29 +224,42 @@ public override void save(Host bookmark)
}
}

private static Uri ToUri(Host bookmark)
private static Uri[] ToUri(Host bookmark)
{
var protocol = bookmark.getProtocol();
var credentials = bookmark.getCredentials();

var targetBuilder = new UriBuilder(PreferencesFactory.get().getProperty("application.container.name"), string.Empty);
var pathBuilder = new StringBuilder();
pathBuilder.Append(protocol.getIdentifier());
if (protocol.isHostnameConfigurable() || !(protocol.isTokenConfigurable() || protocol.isOAuthConfigurable()))
Collection<Uri> descriptors = new();
foreach(string descriptor in ToDescriptor(bookmark))
{
pathBuilder.Append(":" + bookmark.getHostname());
if (protocol.isPortConfigurable() && !Equals(protocol.getDefaultPort(), bookmark.getPort()))
var protocol = bookmark.getProtocol();
var credentials = bookmark.getCredentials();

var targetBuilder = new UriBuilder(PreferencesFactory.get().getProperty("application.container.name"), string.Empty);
var pathBuilder = new StringBuilder();
pathBuilder.Append(descriptor);
if (protocol.isHostnameConfigurable() || !(protocol.isTokenConfigurable() || protocol.isOAuthConfigurable()))
{
pathBuilder.Append(":" + bookmark.getHostname());
if (protocol.isPortConfigurable() && !Equals(protocol.getDefaultPort(), bookmark.getPort()))
{
pathBuilder.Append(":" + bookmark.getPort());
}
}
targetBuilder.Path = pathBuilder.ToString();
if (!string.IsNullOrWhiteSpace(credentials.getUsername()))
{
pathBuilder.Append(":" + bookmark.getPort());
targetBuilder.Query = "user=" + credentials.getUsername();
}
descriptors.Add(targetBuilder.Uri);
}
targetBuilder.Path = pathBuilder.ToString();
if (!string.IsNullOrWhiteSpace(credentials.getUsername()))
return descriptors.ToArray();
}

private static string[] ToDescriptor(Host bookmark)
{
if(bookmark.getProtocol().isOAuthConfigurable())
{
targetBuilder.Query = "user=" + credentials.getUsername();
return new string[] { bookmark.getProtocol().getOAuthClientId(), bookmark.getProtocol().getIdentifier() };
}

return targetBuilder.Uri;
return new string[] { bookmark.getProtocol().getIdentifier() };
}
}
}
130 changes: 74 additions & 56 deletions core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,23 +148,30 @@ public OAuthTokens findOAuthTokens(final Host bookmark) {
if(log.isInfoEnabled()) {
log.info(String.format("Fetching OAuth tokens from keychain for %s", bookmark));
}
final String prefix = this.getOAuthPrefix(bookmark);
final String hostname = this.getOAuthHostname(bookmark);
try {
final String expiry = this.getPassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
return (new OAuthTokens(
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Access Token", prefix)),
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Refresh Token", prefix)),
expiry != null ? Long.parseLong(expiry) : -1L,
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OIDC Id Token", prefix))));
}
catch(LocalAccessDeniedException e) {
log.warn(String.format("Failure %s searching in keychain", e));
return OAuthTokens.EMPTY;
final String[] descriptors = this.getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
final String hostname = this.getOAuthHostname(bookmark);
try {
final String expiry = this.getPassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
final OAuthTokens tokens = new OAuthTokens(
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Access Token", prefix)),
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Refresh Token", prefix)),
expiry != null ? Long.parseLong(expiry) : -1L,
this.getPassword(bookmark.getProtocol().getScheme(), this.getOAuthPort(bookmark), hostname,
String.format("%s OIDC Id Token", prefix)));
if(tokens.validate()) {
return tokens;
}
// Continue
}
catch(LocalAccessDeniedException e) {
log.warn(String.format("Failure %s searching in keychain", e));
return OAuthTokens.EMPTY;
}
}
return OAuthTokens.EMPTY;
}

protected String getOAuthHostname(final Host bookmark) {
Expand All @@ -181,11 +188,17 @@ protected int getOAuthPort(final Host bookmark) {
return Scheme.valueOf(URI.create(bookmark.getProtocol().getOAuthTokenUrl()).getScheme()).getPort();
}

private String getOAuthPrefix(final Host bookmark) {
private String[] getOAuthPrefix(final Host bookmark) {
if(StringUtils.isNotBlank(bookmark.getCredentials().getUsername())) {
return String.format("%s (%s)", bookmark.getProtocol().getDescription(), bookmark.getCredentials().getUsername());
}
return bookmark.getProtocol().getDescription();
return new String[]{
String.format("%s (%s)", bookmark.getProtocol().getOAuthClientId(), bookmark.getCredentials().getUsername()),
String.format("%s (%s)", bookmark.getProtocol().getDescription(), bookmark.getCredentials().getUsername())
};
}
return new String[]{
bookmark.getProtocol().getOAuthClientId(),
bookmark.getProtocol().getDescription()
};
}

@Override
Expand Down Expand Up @@ -221,26 +234,29 @@ public void save(final Host bookmark) throws LocalAccessDeniedException {
credentials.getToken());
}
if(credentials.isOAuthAuthentication()) {
final String prefix = this.getOAuthPrefix(bookmark);
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix), credentials.getOauth().getAccessToken());
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix), credentials.getOauth().getRefreshToken());
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.addPassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix),
String.valueOf(credentials.getOauth().getExpiryInMilliseconds()));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix), credentials.getOauth().getIdToken());
final String[] descriptors = this.getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix), credentials.getOauth().getAccessToken());
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix), credentials.getOauth().getRefreshToken());
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.addPassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix),
String.valueOf(credentials.getOauth().getExpiryInMilliseconds()));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix), credentials.getOauth().getIdToken());
}
break;
}
}
}
Expand Down Expand Up @@ -269,22 +285,24 @@ public void delete(final Host bookmark) throws LocalAccessDeniedException {
protocol.getTokenPlaceholder() : String.format("%s (%s)", protocol.getTokenPlaceholder(), credentials.getUsername()));
}
if(protocol.isOAuthConfigurable()) {
final String prefix = this.getOAuthPrefix(bookmark);
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix));
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.deletePassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix));
final String[] descriptors = this.getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix));
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.deletePassword(this.getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.deletePassword(protocol.getScheme(), this.getOAuthPort(bookmark), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix));
}
}
}
}
Expand Down

0 comments on commit 8ef758d

Please sign in to comment.