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

Last change on this file since 12931 was 12931, checked in by Don-vip, 7 years ago

see #14602 - Override digit group separator to be consistent across languages with ISO 80000-1 + checkstyle fixes

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