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