source: josm/trunk/src/org/openstreetmap/josm/io/OsmConnection.java@ 12928

Last change on this file since 12928 was 12928, checked in by bastiK, 7 years ago

see #15229 - do not copy the entire preferences list, just to set a custom server API in OAuth wizard

  • Property svn:eol-style set to native
File size: 7.0 KB
RevLine 
[6380]1// License: GPL. For details, see LICENSE file.
[626]2package org.openstreetmap.josm.io;
3
[2748]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[9352]6import java.lang.reflect.InvocationTargetException;
[6248]7import java.net.Authenticator.RequestorType;
[9352]8import java.net.MalformedURLException;
9import java.net.URL;
[7082]10import java.nio.charset.StandardCharsets;
[10618]11import java.util.Base64;
[9352]12import java.util.Objects;
[626]13
[2748]14import org.openstreetmap.josm.Main;
[12686]15import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
[2748]16import org.openstreetmap.josm.data.oauth.OAuthParameters;
[4245]17import org.openstreetmap.josm.io.auth.CredentialsAgentException;
[6248]18import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
[4245]19import org.openstreetmap.josm.io.auth.CredentialsManager;
[9172]20import org.openstreetmap.josm.tools.HttpClient;
[12803]21import org.openstreetmap.josm.tools.JosmRuntimeException;
[12620]22import org.openstreetmap.josm.tools.Logging;
[626]23
[8840]24import oauth.signpost.OAuthConsumer;
25import oauth.signpost.exception.OAuthException;
26
[626]27/**
28 * Base class that handles common things like authentication for the reader and writer
29 * to the osm server.
30 *
31 * @author imi
32 */
33public class OsmConnection {
[8840]34 protected boolean cancel;
[9309]35 protected HttpClient activeConnection;
[2748]36 protected OAuthParameters oauthParameters;
[626]37
[1169]38 /**
[12803]39 * Retrieves OAuth access token.
40 * @since 12803
41 */
42 public interface OAuthAccessTokenFetcher {
43 /**
44 * Obtains an OAuth access token for the connection. Afterwards, the token is accessible via {@link OAuthAccessTokenHolder}.
45 * @param serverUrl the URL to OSM server
46 * @throws InterruptedException if we're interrupted while waiting for the event dispatching thread to finish OAuth authorization task
47 * @throws InvocationTargetException if an exception is thrown while running OAuth authorization task
48 */
49 void obtainAccessToken(URL serverUrl) throws InvocationTargetException, InterruptedException;
50 }
51
[12869]52 static volatile OAuthAccessTokenFetcher fetcher = u -> {
[12803]53 throw new JosmRuntimeException("OsmConnection.setOAuthAccessTokenFetcher() has not been called");
54 };
55
56 /**
57 * Sets the OAuth access token fetcher.
58 * @param tokenFetcher new OAuth access token fetcher. Cannot be null
59 * @since 12803
60 */
61 public static void setOAuthAccessTokenFetcher(OAuthAccessTokenFetcher tokenFetcher) {
62 fetcher = Objects.requireNonNull(tokenFetcher, "tokenFetcher");
63 }
64
65 /**
[6643]66 * Cancels the connection.
67 */
[1169]68 public void cancel() {
69 cancel = true;
[2322]70 synchronized (this) {
71 if (activeConnection != null) {
72 activeConnection.disconnect();
73 }
74 }
[1169]75 }
[626]76
[2748]77 /**
78 * Adds an authentication header for basic authentication
[2801]79 *
[2748]80 * @param con the connection
[8291]81 * @throws OsmTransferException if something went wrong. Check for nested exceptions
[2748]82 */
[9172]83 protected void addBasicAuthorizationHeader(HttpClient con) throws OsmTransferException {
[4245]84 CredentialsAgentResponse response;
[1955]85 try {
[4245]86 synchronized (CredentialsManager.getInstance()) {
[4690]87 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER,
88 con.getURL().getHost(), false /* don't know yet whether the credentials will succeed */);
[1955]89 }
[4245]90 } catch (CredentialsAgentException e) {
[2641]91 throw new OsmTransferException(e);
[1955]92 }
[11544]93 if (response != null) {
94 if (response.isCanceled()) {
95 cancel = true;
96 return;
97 } else {
98 String username = response.getUsername() == null ? "" : response.getUsername();
99 String password = response.getPassword() == null ? "" : String.valueOf(response.getPassword());
100 String token = username + ':' + password;
101 con.setHeader("Authorization", "Basic "+Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8)));
102 }
[2641]103 }
[1169]104 }
[1881]105
106 /**
[2748]107 * Signs the connection with an OAuth authentication header
[2801]108 *
[2748]109 * @param connection the connection
[2801]110 *
[12470]111 * @throws MissingOAuthAccessTokenException if there is currently no OAuth Access Token configured
[8291]112 * @throws OsmTransferException if signing fails
[2748]113 */
[9172]114 protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException {
[2748]115 if (oauthParameters == null) {
[12928]116 oauthParameters = OAuthParameters.createFromApiUrl(OsmApi.getOsmApi().getServerUrl());
[2748]117 }
118 OAuthConsumer consumer = oauthParameters.buildConsumer();
119 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();
[9352]120 if (!holder.containsAccessToken()) {
121 obtainAccessToken(connection);
122 }
123 if (!holder.containsAccessToken()) { // check if wizard completed
[2862]124 throw new MissingOAuthAccessTokenException();
[9352]125 }
[2748]126 consumer.setTokenWithSecret(holder.getAccessTokenKey(), holder.getAccessTokenSecret());
127 try {
128 consumer.sign(connection);
[8510]129 } catch (OAuthException e) {
[2748]130 throw new OsmTransferException(tr("Failed to sign a HTTP connection with an OAuth Authentication header"), e);
131 }
132 }
133
[9352]134 /**
[12803]135 * Obtains an OAuth access token for the connection.
136 * Afterwards, the token is accessible via {@link OAuthAccessTokenHolder} / {@link CredentialsManager}.
[9352]137 * @param connection connection for which the access token should be obtained
[12803]138 * @throws MissingOAuthAccessTokenException if the process cannot be completed successfully
[9352]139 */
140 protected void obtainAccessToken(final HttpClient connection) throws MissingOAuthAccessTokenException {
141 try {
[9353]142 final URL apiUrl = new URL(OsmApi.getOsmApi().getServerUrl());
[9352]143 if (!Objects.equals(apiUrl.getHost(), connection.getURL().getHost())) {
144 throw new MissingOAuthAccessTokenException();
145 }
[12803]146 fetcher.obtainAccessToken(apiUrl);
147 OAuthAccessTokenHolder.getInstance().setSaveToPreferences(true);
[12928]148 OAuthAccessTokenHolder.getInstance().save(CredentialsManager.getInstance());
[9352]149 } catch (MalformedURLException | InterruptedException | InvocationTargetException e) {
[10237]150 throw new MissingOAuthAccessTokenException(e);
[9352]151 }
152 }
153
[9172]154 protected void addAuth(HttpClient connection) throws OsmTransferException {
[9352]155 final String authMethod = OsmApi.getAuthMethod();
[7012]156 if ("basic".equals(authMethod)) {
[2748]157 addBasicAuthorizationHeader(connection);
[7012]158 } else if ("oauth".equals(authMethod)) {
[2748]159 addOAuthAuthorizationHeader(connection);
160 } else {
[6248]161 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod);
[12620]162 Logging.warn(msg);
[2748]163 throw new OsmTransferException(msg);
164 }
165 }
166
167 /**
[1881]168 * Replies true if this connection is canceled
[2512]169 *
[1881]170 * @return true if this connection is canceled
171 */
172 public boolean isCanceled() {
173 return cancel;
174 }
[626]175}
Note: See TracBrowser for help on using the repository browser.