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

Last change on this file since 9797 was 9353, checked in by simon04, 8 years ago

Refactoring: introduce OsmApi#getServerUrl

  • Property svn:eol-style set to native
File size: 7.2 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.ByteBuffer;
11import java.nio.CharBuffer;
12import java.nio.charset.CharacterCodingException;
13import java.nio.charset.CharsetEncoder;
14import java.nio.charset.StandardCharsets;
15import java.util.Objects;
16import java.util.concurrent.Callable;
17import java.util.concurrent.FutureTask;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.oauth.OAuthParameters;
21import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
22import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
23import org.openstreetmap.josm.io.auth.CredentialsAgentException;
24import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
25import org.openstreetmap.josm.io.auth.CredentialsManager;
26import org.openstreetmap.josm.tools.Base64;
27import org.openstreetmap.josm.tools.HttpClient;
28
29import oauth.signpost.OAuthConsumer;
30import oauth.signpost.exception.OAuthException;
31import org.openstreetmap.josm.tools.Utils;
32
33import javax.swing.SwingUtilities;
34
35/**
36 * Base class that handles common things like authentication for the reader and writer
37 * to the osm server.
38 *
39 * @author imi
40 */
41public class OsmConnection {
42 protected boolean cancel;
43 protected HttpClient activeConnection;
44 protected OAuthParameters oauthParameters;
45
46 /**
47 * Cancels the connection.
48 */
49 public void cancel() {
50 cancel = true;
51 synchronized (this) {
52 if (activeConnection != null) {
53 activeConnection.disconnect();
54 }
55 }
56 }
57
58 /**
59 * Adds an authentication header for basic authentication
60 *
61 * @param con the connection
62 * @throws OsmTransferException if something went wrong. Check for nested exceptions
63 */
64 protected void addBasicAuthorizationHeader(HttpClient con) throws OsmTransferException {
65 CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder();
66 CredentialsAgentResponse response;
67 String token;
68 try {
69 synchronized (CredentialsManager.getInstance()) {
70 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER,
71 con.getURL().getHost(), false /* don't know yet whether the credentials will succeed */);
72 }
73 } catch (CredentialsAgentException e) {
74 throw new OsmTransferException(e);
75 }
76 if (response == null) {
77 token = ":";
78 } else if (response.isCanceled()) {
79 cancel = true;
80 return;
81 } else {
82 String username = response.getUsername() == null ? "" : response.getUsername();
83 String password = response.getPassword() == null ? "" : String.valueOf(response.getPassword());
84 token = username + ':' + password;
85 try {
86 ByteBuffer bytes = encoder.encode(CharBuffer.wrap(token));
87 con.setHeader("Authorization", "Basic "+Base64.encode(bytes));
88 } catch (CharacterCodingException e) {
89 throw new OsmTransferException(e);
90 }
91 }
92 }
93
94 /**
95 * Signs the connection with an OAuth authentication header
96 *
97 * @param connection the connection
98 *
99 * @throws OsmTransferException if there is currently no OAuth Access Token configured
100 * @throws OsmTransferException if signing fails
101 */
102 protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException {
103 if (oauthParameters == null) {
104 oauthParameters = OAuthParameters.createFromPreferences(Main.pref);
105 }
106 OAuthConsumer consumer = oauthParameters.buildConsumer();
107 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();
108 if (!holder.containsAccessToken()) {
109 obtainAccessToken(connection);
110 }
111 if (!holder.containsAccessToken()) { // check if wizard completed
112 throw new MissingOAuthAccessTokenException();
113 }
114 consumer.setTokenWithSecret(holder.getAccessTokenKey(), holder.getAccessTokenSecret());
115 try {
116 consumer.sign(connection);
117 } catch (OAuthException e) {
118 throw new OsmTransferException(tr("Failed to sign a HTTP connection with an OAuth Authentication header"), e);
119 }
120 }
121
122 /**
123 * Obtains an OAuth access token for the connection. Afterwards, the token is accessible via {@link OAuthAccessTokenHolder}.
124 * @param connection connection for which the access token should be obtained
125 * @throws MissingOAuthAccessTokenException if the process cannot be completec successfully
126 */
127 protected void obtainAccessToken(final HttpClient connection) throws MissingOAuthAccessTokenException {
128 try {
129 final URL apiUrl = new URL(OsmApi.getOsmApi().getServerUrl());
130 if (!Objects.equals(apiUrl.getHost(), connection.getURL().getHost())) {
131 throw new MissingOAuthAccessTokenException();
132 }
133 final Runnable authTask = new FutureTask<>(new Callable<OAuthAuthorizationWizard>() {
134 @Override
135 public OAuthAuthorizationWizard call() throws Exception {
136 // Concerning Utils.newDirectExecutor: Main.worker cannot be used since this connection is already
137 // executed via Main.worker. The OAuth connections would block otherwise.
138 final OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard(
139 Main.parent, apiUrl.toExternalForm(), Utils.newDirectExecutor());
140 wizard.showDialog();
141 OAuthAccessTokenHolder.getInstance().setSaveToPreferences(true);
142 OAuthAccessTokenHolder.getInstance().save(Main.pref, CredentialsManager.getInstance());
143 return wizard;
144 }
145 });
146 // exception handling differs from implementation at GuiHelper.runInEDTAndWait()
147 if (SwingUtilities.isEventDispatchThread()) {
148 authTask.run();
149 } else {
150 SwingUtilities.invokeAndWait(authTask);
151 }
152 } catch (MalformedURLException | InterruptedException | InvocationTargetException e) {
153 throw new MissingOAuthAccessTokenException();
154 }
155 }
156
157 protected void addAuth(HttpClient connection) throws OsmTransferException {
158 final String authMethod = OsmApi.getAuthMethod();
159 if ("basic".equals(authMethod)) {
160 addBasicAuthorizationHeader(connection);
161 } else if ("oauth".equals(authMethod)) {
162 addOAuthAuthorizationHeader(connection);
163 } else {
164 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod);
165 Main.warn(msg);
166 throw new OsmTransferException(msg);
167 }
168 }
169
170 /**
171 * Replies true if this connection is canceled
172 *
173 * @return true if this connection is canceled
174 */
175 public boolean isCanceled() {
176 return cancel;
177 }
178}
Note: See TracBrowser for help on using the repository browser.