Skip to content

Commit

Permalink
Merge pull request #15393 from iterate-ch/bugfix/GH-15113-port
Browse files Browse the repository at this point in the history
Save OAuth tokens with port number, scheme and hostname from token URL.
  • Loading branch information
dkocher authored Dec 4, 2023
2 parents d84a2c8 + 3aba693 commit 9891963
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,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))
var targets = ToUri(bookmark);
foreach (Uri descriptor in targets)
{
base.delete(bookmark);
WinCredentialManager.RemoveCredentials(descriptor.AbsoluteUri);
}
base.delete(bookmark);
}

public override void deletePassword(string serviceName, string user)
Expand All @@ -86,7 +87,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 +102,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 @@ -118,21 +119,28 @@ 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))
var targets = ToUri(bookmark);
foreach(Uri descriptor in targets)
{
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 with deprecated descriptors
}
return new(accessToken, refreshToken, new(expiry), idToken);
}

return base.findOAuthTokens(bookmark);
}

Expand All @@ -142,7 +150,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 +180,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 +217,42 @@ public override void save(Host bookmark)
}
}

private static Uri ToUri(Host bookmark)
private static List<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()))
List<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.getPort());
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()))
{
targetBuilder.Query = "user=" + credentials.getUsername();
}
descriptors.Add(targetBuilder.Uri);
}
targetBuilder.Path = pathBuilder.ToString();
if (!string.IsNullOrWhiteSpace(credentials.getUsername()))
return descriptors;
}

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() };
}
}
}
164 changes: 103 additions & 61 deletions core/src/main/java/ch/cyberduck/core/DefaultHostPasswordStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,37 +148,73 @@ 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(), bookmark.getPort(), hostname,
String.format("%s OAuth2 Access Token", prefix)),
this.getPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), hostname,
String.format("%s OAuth2 Refresh Token", prefix)),
expiry != null ? Long.parseLong(expiry) : -1L,
this.getPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(), hostname,
String.format("%s OIDC Id Token", prefix))));
final String[] descriptors = getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
if(log.isDebugEnabled()) {
log.debug(String.format("Search with prefix %s", prefix));
}
final String hostname = getOAuthHostname(bookmark);
if(log.isDebugEnabled()) {
log.debug(String.format("Search with hostname %s", hostname));
}
try {
final String expiry = this.getPassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
final OAuthTokens tokens = new OAuthTokens(
this.getPassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Access Token", prefix)),
this.getPassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname,
String.format("%s OAuth2 Refresh Token", prefix)),
expiry != null ? Long.parseLong(expiry) : -1L,
this.getPassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), hostname,
String.format("%s OIDC Id Token", prefix)));
if(tokens.validate()) {
return tokens;
}
// Continue with deprecated descriptors
}
catch(LocalAccessDeniedException e) {
log.warn(String.format("Failure %s searching in keychain", e));
return OAuthTokens.EMPTY;
}
}
catch(LocalAccessDeniedException e) {
log.warn(String.format("Failure %s searching in keychain", e));
return OAuthTokens.EMPTY;
return OAuthTokens.EMPTY;
}

protected static Scheme getOAuthScheme(final Host bookmark) {
final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl());
if(null == uri.getScheme()) {
return bookmark.getProtocol().getScheme();
}
return Scheme.valueOf(uri.getScheme());
}

protected String getOAuthHostname(final Host bookmark) {
if(StringUtils.isNotBlank(URI.create(bookmark.getProtocol().getOAuthTokenUrl()).getHost())) {
return URI.create(bookmark.getProtocol().getOAuthTokenUrl()).getHost();
protected static String getOAuthHostname(final Host bookmark) {
final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl());
if(StringUtils.isNotBlank(uri.getHost())) {
return uri.getHost();
}
return bookmark.getHostname();
}

private String getOAuthPrefix(final Host bookmark) {
if(StringUtils.isNotBlank(bookmark.getCredentials().getUsername())) {
return String.format("%s (%s)", bookmark.getProtocol().getDescription(), bookmark.getCredentials().getUsername());
protected static int getOAuthPort(final Host bookmark) {
final URI uri = URI.create(bookmark.getProtocol().getOAuthTokenUrl());
if(-1 != uri.getPort()) {
return uri.getPort();
}
return bookmark.getProtocol().getDescription();
return getOAuthScheme(bookmark).getPort();
}

protected static String[] getOAuthPrefix(final Host bookmark) {
if(StringUtils.isNotBlank(bookmark.getCredentials().getUsername())) {
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 All @@ -188,6 +224,7 @@ public void save(final Host bookmark) throws LocalAccessDeniedException {
return;
}
final Credentials credentials = bookmark.getCredentials();
final Protocol protocol = bookmark.getProtocol();
if(log.isInfoEnabled()) {
log.info(String.format("Save credentials %s for bookmark %s", credentials, bookmark));
}
Expand All @@ -204,36 +241,39 @@ public void save(final Host bookmark) throws LocalAccessDeniedException {
log.warn(String.format("No password in credentials for bookmark %s", bookmark.getHostname()));
return;
}
this.addPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(),
this.addPassword(protocol.getScheme(), bookmark.getPort(),
bookmark.getHostname(), credentials.getUsername(), credentials.getPassword());
}
if(credentials.isTokenAuthentication()) {
this.addPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(),
this.addPassword(protocol.getScheme(), bookmark.getPort(),
bookmark.getHostname(), StringUtils.isEmpty(credentials.getUsername()) ?
bookmark.getProtocol().getTokenPlaceholder() : String.format("%s (%s)", bookmark.getProtocol().getTokenPlaceholder(), credentials.getUsername()),
protocol.getTokenPlaceholder() : String.format("%s (%s)", protocol.getTokenPlaceholder(), credentials.getUsername()),
credentials.getToken());
}
if(credentials.isOAuthAuthentication()) {
final String prefix = this.getOAuthPrefix(bookmark);
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
bookmark.getPort(), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix), credentials.getOauth().getAccessToken());
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.addPassword(bookmark.getProtocol().getScheme(),
bookmark.getPort(), 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(),
bookmark.getPort(), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix), credentials.getOauth().getIdToken());
final String[] descriptors = getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.addPassword(getOAuthScheme(bookmark),
getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix), credentials.getOauth().getAccessToken());
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.addPassword(getOAuthScheme(bookmark),
getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix), credentials.getOauth().getRefreshToken());
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.addPassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix),
String.valueOf(credentials.getOauth().getExpiryInMilliseconds()));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.addPassword(getOAuthScheme(bookmark),
getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix), credentials.getOauth().getIdToken());
}
break;
}
}
}
Expand Down Expand Up @@ -262,22 +302,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(), bookmark.getPort(), this.getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.deletePassword(protocol.getScheme(), bookmark.getPort(), 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(), bookmark.getPort(), this.getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix));
final String[] descriptors = getOAuthPrefix(bookmark);
for(String prefix : descriptors) {
if(StringUtils.isNotBlank(credentials.getOauth().getAccessToken())) {
this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OAuth2 Access Token", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getRefreshToken())) {
this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OAuth2 Refresh Token", prefix));
}
// Save expiry
if(credentials.getOauth().getExpiryInMilliseconds() != null) {
this.deletePassword(getOAuthHostname(bookmark), String.format("%s OAuth2 Token Expiry", prefix));
}
if(StringUtils.isNotBlank(credentials.getOauth().getIdToken())) {
this.deletePassword(getOAuthScheme(bookmark), getOAuthPort(bookmark), getOAuthHostname(bookmark),
String.format("%s OIDC Id Token", prefix));
}
}
}
}
Expand Down
Loading

0 comments on commit 9891963

Please sign in to comment.