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

Last change on this file since 14628 was 13849, checked in by Don-vip, 6 years ago

SonarQube - fix minor code issues

  • Property svn:eol-style set to native
File size: 7.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
34 private static final String BASIC_AUTH = "Basic ";
35
36 protected boolean cancel;
37 protected HttpClient activeConnection;
38 protected OAuthParameters oauthParameters;
39
40 /**
41 * Retrieves OAuth access token.
42 * @since 12803
43 */
44 public interface OAuthAccessTokenFetcher {
45 /**
46 * Obtains an OAuth access token for the connection. Afterwards, the token is accessible via {@link OAuthAccessTokenHolder}.
47 * @param serverUrl the URL to OSM server
48 * @throws InterruptedException if we're interrupted while waiting for the event dispatching thread to finish OAuth authorization task
49 * @throws InvocationTargetException if an exception is thrown while running OAuth authorization task
50 */
51 void obtainAccessToken(URL serverUrl) throws InvocationTargetException, InterruptedException;
52 }
53
54 static volatile OAuthAccessTokenFetcher fetcher = u -> {
55 throw new JosmRuntimeException("OsmConnection.setOAuthAccessTokenFetcher() has not been called");
56 };
57
58 /**
59 * Sets the OAuth access token fetcher.
60 * @param tokenFetcher new OAuth access token fetcher. Cannot be null
61 * @since 12803
62 */
63 public static void setOAuthAccessTokenFetcher(OAuthAccessTokenFetcher tokenFetcher) {
64 fetcher = Objects.requireNonNull(tokenFetcher, "tokenFetcher");
65 }
66
67 /**
68 * Cancels the connection.
69 */
70 public void cancel() {
71 cancel = true;
72 synchronized (this) {
73 if (activeConnection != null) {
74 activeConnection.disconnect();
75 }
76 }
77 }
78
79 /**
80 * Retrieves login from basic authentication header, if set.
81 *
82 * @param con the connection
83 * @return login from basic authentication header, or {@code null}
84 * @throws OsmTransferException if something went wrong. Check for nested exceptions
85 * @since 12992
86 */
87 protected String retrieveBasicAuthorizationLogin(HttpClient con) throws OsmTransferException {
88 String auth = con.getRequestHeader("Authorization");
89 if (auth != null && auth.startsWith(BASIC_AUTH)) {
90 try {
91 String[] token = new String(Base64.getDecoder().decode(auth.substring(BASIC_AUTH.length())),
92 StandardCharsets.UTF_8).split(":");
93 if (token.length == 2) {
94 return token[0];
95 }
96 } catch (IllegalArgumentException e) {
97 Logging.error(e);
98 }
99 }
100 return null;
101 }
102
103 /**
104 * Adds an authentication header for basic authentication
105 *
106 * @param con the connection
107 * @throws OsmTransferException if something went wrong. Check for nested exceptions
108 */
109 protected void addBasicAuthorizationHeader(HttpClient con) throws OsmTransferException {
110 CredentialsAgentResponse response;
111 try {
112 synchronized (CredentialsManager.getInstance()) {
113 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER,
114 con.getURL().getHost(), false /* don't know yet whether the credentials will succeed */);
115 }
116 } catch (CredentialsAgentException e) {
117 throw new OsmTransferException(e);
118 }
119 if (response != null) {
120 if (response.isCanceled()) {
121 cancel = true;
122 } else {
123 String username = response.getUsername() == null ? "" : response.getUsername();
124 String password = response.getPassword() == null ? "" : String.valueOf(response.getPassword());
125 String token = username + ':' + password;
126 con.setHeader("Authorization", BASIC_AUTH + Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8)));
127 }
128 }
129 }
130
131 /**
132 * Signs the connection with an OAuth authentication header
133 *
134 * @param connection the connection
135 *
136 * @throws MissingOAuthAccessTokenException if there is currently no OAuth Access Token configured
137 * @throws OsmTransferException if signing fails
138 */
139 protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException {
140 if (oauthParameters == null) {
141 oauthParameters = OAuthParameters.createFromApiUrl(OsmApi.getOsmApi().getServerUrl());
142 }
143 OAuthConsumer consumer = oauthParameters.buildConsumer();
144 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();
145 if (!holder.containsAccessToken()) {
146 obtainAccessToken(connection);
147 }
148 if (!holder.containsAccessToken()) { // check if wizard completed
149 throw new MissingOAuthAccessTokenException();
150 }
151 consumer.setTokenWithSecret(holder.getAccessTokenKey(), holder.getAccessTokenSecret());
152 try {
153 consumer.sign(connection);
154 } catch (OAuthException e) {
155 throw new OsmTransferException(tr("Failed to sign a HTTP connection with an OAuth Authentication header"), e);
156 }
157 }
158
159 /**
160 * Obtains an OAuth access token for the connection.
161 * Afterwards, the token is accessible via {@link OAuthAccessTokenHolder} / {@link CredentialsManager}.
162 * @param connection connection for which the access token should be obtained
163 * @throws MissingOAuthAccessTokenException if the process cannot be completed successfully
164 */
165 protected void obtainAccessToken(final HttpClient connection) throws MissingOAuthAccessTokenException {
166 try {
167 final URL apiUrl = new URL(OsmApi.getOsmApi().getServerUrl());
168 if (!Objects.equals(apiUrl.getHost(), connection.getURL().getHost())) {
169 throw new MissingOAuthAccessTokenException();
170 }
171 fetcher.obtainAccessToken(apiUrl);
172 OAuthAccessTokenHolder.getInstance().setSaveToPreferences(true);
173 OAuthAccessTokenHolder.getInstance().save(CredentialsManager.getInstance());
174 } catch (MalformedURLException | InterruptedException | InvocationTargetException e) {
175 throw new MissingOAuthAccessTokenException(e);
176 }
177 }
178
179 protected void addAuth(HttpClient connection) throws OsmTransferException {
180 final String authMethod = OsmApi.getAuthMethod();
181 if ("basic".equals(authMethod)) {
182 addBasicAuthorizationHeader(connection);
183 } else if ("oauth".equals(authMethod)) {
184 addOAuthAuthorizationHeader(connection);
185 } else {
186 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod);
187 Logging.warn(msg);
188 throw new OsmTransferException(msg);
189 }
190 }
191
192 /**
193 * Replies true if this connection is canceled
194 *
195 * @return true if this connection is canceled
196 */
197 public boolean isCanceled() {
198 return cancel;
199 }
200}
Note: See TracBrowser for help on using the repository browser.