001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.net.Authenticator.RequestorType; 007import java.nio.ByteBuffer; 008import java.nio.CharBuffer; 009import java.nio.charset.CharacterCodingException; 010import java.nio.charset.CharsetEncoder; 011import java.nio.charset.StandardCharsets; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.oauth.OAuthParameters; 015import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 016import org.openstreetmap.josm.io.auth.CredentialsAgentException; 017import org.openstreetmap.josm.io.auth.CredentialsAgentResponse; 018import org.openstreetmap.josm.io.auth.CredentialsManager; 019import org.openstreetmap.josm.tools.Base64; 020import org.openstreetmap.josm.tools.HttpClient; 021 022import oauth.signpost.OAuthConsumer; 023import oauth.signpost.exception.OAuthException; 024 025/** 026 * Base class that handles common things like authentication for the reader and writer 027 * to the osm server. 028 * 029 * @author imi 030 */ 031public class OsmConnection { 032 protected boolean cancel; 033 protected HttpClient activeConnection; 034 protected OAuthParameters oauthParameters; 035 036 /** 037 * Cancels the connection. 038 */ 039 public void cancel() { 040 cancel = true; 041 synchronized (this) { 042 if (activeConnection != null) { 043 activeConnection.disconnect(); 044 } 045 } 046 } 047 048 /** 049 * Adds an authentication header for basic authentication 050 * 051 * @param con the connection 052 * @throws OsmTransferException if something went wrong. Check for nested exceptions 053 */ 054 protected void addBasicAuthorizationHeader(HttpClient con) throws OsmTransferException { 055 CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder(); 056 CredentialsAgentResponse response; 057 String token; 058 try { 059 synchronized (CredentialsManager.getInstance()) { 060 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER, 061 con.getURL().getHost(), false /* don't know yet whether the credentials will succeed */); 062 } 063 } catch (CredentialsAgentException e) { 064 throw new OsmTransferException(e); 065 } 066 if (response == null) { 067 token = ":"; 068 } else if (response.isCanceled()) { 069 cancel = true; 070 return; 071 } else { 072 String username = response.getUsername() == null ? "" : response.getUsername(); 073 String password = response.getPassword() == null ? "" : String.valueOf(response.getPassword()); 074 token = username + ':' + password; 075 try { 076 ByteBuffer bytes = encoder.encode(CharBuffer.wrap(token)); 077 con.setHeader("Authorization", "Basic "+Base64.encode(bytes)); 078 } catch (CharacterCodingException e) { 079 throw new OsmTransferException(e); 080 } 081 } 082 } 083 084 /** 085 * Signs the connection with an OAuth authentication header 086 * 087 * @param connection the connection 088 * 089 * @throws OsmTransferException if there is currently no OAuth Access Token configured 090 * @throws OsmTransferException if signing fails 091 */ 092 protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException { 093 if (oauthParameters == null) { 094 oauthParameters = OAuthParameters.createFromPreferences(Main.pref); 095 } 096 OAuthConsumer consumer = oauthParameters.buildConsumer(); 097 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance(); 098 if (!holder.containsAccessToken()) 099 throw new MissingOAuthAccessTokenException(); 100 consumer.setTokenWithSecret(holder.getAccessTokenKey(), holder.getAccessTokenSecret()); 101 try { 102 consumer.sign(connection); 103 } catch (OAuthException e) { 104 throw new OsmTransferException(tr("Failed to sign a HTTP connection with an OAuth Authentication header"), e); 105 } 106 } 107 108 protected void addAuth(HttpClient connection) throws OsmTransferException { 109 String authMethod = Main.pref.get("osm-server.auth-method", "basic"); 110 if ("basic".equals(authMethod)) { 111 addBasicAuthorizationHeader(connection); 112 } else if ("oauth".equals(authMethod)) { 113 addOAuthAuthorizationHeader(connection); 114 } else { 115 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod); 116 Main.warn(msg); 117 throw new OsmTransferException(msg); 118 } 119 } 120 121 /** 122 * Replies true if this connection is canceled 123 * 124 * @return true if this connection is canceled 125 */ 126 public boolean isCanceled() { 127 return cancel; 128 } 129}