001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io.auth; 003 004import java.awt.Component; 005import java.net.Authenticator.RequestorType; 006import java.net.PasswordAuthentication; 007import java.util.Objects; 008 009import org.openstreetmap.josm.data.UserIdentityManager; 010import org.openstreetmap.josm.data.oauth.OAuthToken; 011import org.openstreetmap.josm.io.OsmApi; 012import org.openstreetmap.josm.tools.CheckParameterUtil; 013import org.openstreetmap.josm.tools.Logging; 014import org.openstreetmap.josm.tools.Utils; 015 016/** 017 * CredentialManager is a factory for the single credential agent used. 018 * 019 * Currently, it defaults to replying an instance of {@link JosmPreferencesCredentialAgent}. 020 * @since 2641 021 */ 022public class CredentialsManager implements CredentialsAgent { 023 024 private static volatile CredentialsManager instance; 025 026 /** 027 * Replies the single credential agent used in JOSM 028 * 029 * @return the single credential agent used in JOSM 030 */ 031 public static CredentialsManager getInstance() { 032 if (instance == null) { 033 CredentialsAgent delegate; 034 if (agentFactory == null) { 035 delegate = new JosmPreferencesCredentialAgent(); 036 } else { 037 delegate = agentFactory.getCredentialsAgent(); 038 } 039 instance = new CredentialsManager(delegate); 040 } 041 return instance; 042 } 043 044 private static CredentialsAgentFactory agentFactory; 045 046 /** 047 * Credentials agent factory. 048 */ 049 @FunctionalInterface 050 public interface CredentialsAgentFactory { 051 /** 052 * Returns the credentials agent instance. 053 * @return the credentials agent instance 054 */ 055 CredentialsAgent getCredentialsAgent(); 056 } 057 058 /** 059 * Plugins can register a CredentialsAgentFactory, thereby overriding 060 * JOSM's default credentials agent. 061 * @param agentFactory The Factory that provides the custom CredentialsAgent. 062 * Can be null to clear the factory and switch back to default behavior. 063 */ 064 public static void registerCredentialsAgentFactory(CredentialsAgentFactory agentFactory) { 065 CredentialsManager.agentFactory = agentFactory; 066 CredentialsManager.instance = null; 067 } 068 069 /* non-static fields and methods */ 070 071 /** 072 * The credentials agent doing the real stuff 073 */ 074 private final CredentialsAgent delegate; 075 076 /** 077 * Constructs a new {@code CredentialsManager}. 078 * @param delegate The credentials agent backing this credential manager. Must not be {@code null} 079 */ 080 public CredentialsManager(CredentialsAgent delegate) { 081 CheckParameterUtil.ensureParameterNotNull(delegate, "delegate"); 082 this.delegate = delegate; 083 } 084 085 /** 086 * Returns type of credentials agent backing this credentials manager. 087 * @return The type of credentials agent 088 */ 089 public final Class<? extends CredentialsAgent> getCredentialsAgentClass() { 090 return delegate.getClass(); 091 } 092 093 /** 094 * Returns the username for OSM API 095 * @return the username for OSM API 096 */ 097 public String getUsername() { 098 return getUsername(OsmApi.getOsmApi().getHost()); 099 } 100 101 /** 102 * Returns the username for a given host 103 * @param host The host for which username is wanted 104 * @return The username for {@code host} 105 */ 106 public String getUsername(String host) { 107 String username = null; 108 try { 109 PasswordAuthentication auth = lookup(RequestorType.SERVER, host); 110 if (auth != null) { 111 username = auth.getUserName(); 112 } 113 } catch (CredentialsAgentException ex) { 114 Logging.debug(ex); 115 return null; 116 } 117 if (username == null) return null; 118 username = username.trim(); 119 return username.isEmpty() ? null : username; 120 } 121 122 @Override 123 public PasswordAuthentication lookup(RequestorType requestorType, String host) throws CredentialsAgentException { 124 return delegate.lookup(requestorType, host); 125 } 126 127 @Override 128 public void store(RequestorType requestorType, String host, PasswordAuthentication credentials) throws CredentialsAgentException { 129 if (requestorType == RequestorType.SERVER && Objects.equals(OsmApi.getOsmApi().getHost(), host)) { 130 String username = credentials.getUserName(); 131 if (!Utils.isBlank(username)) { 132 UserIdentityManager.getInstance().setPartiallyIdentified(username); 133 } 134 } 135 // see #11914: clear cache before we store new value 136 purgeCredentialsCache(requestorType); 137 delegate.store(requestorType, host, credentials); 138 } 139 140 @Override 141 public CredentialsAgentResponse getCredentials(RequestorType requestorType, String host, boolean noSuccessWithLastResponse) 142 throws CredentialsAgentException { 143 CredentialsAgentResponse credentials = delegate.getCredentials(requestorType, host, noSuccessWithLastResponse); 144 if (requestorType == RequestorType.SERVER) { 145 // see #11914 : Keep UserIdentityManager up to date 146 String userName = credentials.getUsername(); 147 userName = userName == null ? "" : userName.trim(); 148 if (!Objects.equals(UserIdentityManager.getInstance().getUserName(), userName)) { 149 if (userName.isEmpty()) 150 UserIdentityManager.getInstance().setAnonymous(); 151 else 152 UserIdentityManager.getInstance().setPartiallyIdentified(userName); 153 } 154 } 155 return credentials; 156 } 157 158 @Override 159 public OAuthToken lookupOAuthAccessToken() throws CredentialsAgentException { 160 return delegate.lookupOAuthAccessToken(); 161 } 162 163 @Override 164 public void storeOAuthAccessToken(OAuthToken accessToken) throws CredentialsAgentException { 165 delegate.storeOAuthAccessToken(accessToken); 166 } 167 168 @Override 169 public Component getPreferencesDecorationPanel() { 170 return delegate.getPreferencesDecorationPanel(); 171 } 172 173 @Override 174 public void purgeCredentialsCache(RequestorType requestorType) { 175 delegate.purgeCredentialsCache(requestorType); 176 } 177}