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

Last change on this file since 10809 was 10618, checked in by Don-vip, 8 years ago

see #11390 - Java 8: use java.util.Base64

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