Ticket #2710: initial_multiuser_rework.patch
| File initial_multiuser_rework.patch, 23.3 KB (added by , 7 years ago) |
|---|
-
src/org/openstreetmap/josm/data/UserIdentityManager.java
4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 6 import java.text.MessageFormat; 7 import java.util.ArrayList; 8 import java.util.LinkedHashMap; 9 import java.util.List; 10 import java.util.Map; 7 11 8 12 import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder; 9 13 import org.openstreetmap.josm.data.osm.User; … … 78 82 instance.initFromPreferences(); 79 83 } 80 84 Config.getPref().addPreferenceChangeListener(instance); 85 instance.populateAllUsers(); 81 86 } 82 87 return instance; 83 88 } 84 89 85 private String userName;86 private UserInfo userInfo;87 90 private boolean accessTokenKeyChanged; 88 91 private boolean accessTokenSecretChanged; 89 92 93 private String userName; 94 private UserInfo userInfo; 95 private LinkedHashMap<String, UserInfo> users; 96 90 97 private UserIdentityManager() { 98 users = new LinkedHashMap<>(); 91 99 } 92 100 93 101 /** … … 112 120 if (trimmedUserName.isEmpty()) 113 121 throw new IllegalArgumentException( 114 122 MessageFormat.format("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName)); 115 this.userName = trimmedUserName;123 userName = trimmedUserName; 116 124 userInfo = null; 117 125 } 118 126 … … 132 140 if (trimmedUserName.isEmpty()) 133 141 throw new IllegalArgumentException(tr("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName)); 134 142 CheckParameterUtil.ensureParameterNotNull(userInfo, "userInfo"); 135 this.userName = trimmedUserName;143 userName = trimmedUserName; 136 144 this.userInfo = userInfo; 137 145 } 138 146 … … 238 246 } 239 247 240 248 /** 249 * Initializes the user identity manager from OAuth request of user details. 250 * @param oauth The {@code OAuthAccessTokenHolder} with the key and secret 251 * @see #initFromPreferences 252 * @since xxx 253 */ 254 public void initFromOauth(OAuthAccessTokenHolder oauth) { 255 try { 256 OsmServerUserInfoReader osmServerReader = new OsmServerUserInfoReader(); 257 UserInfo info = osmServerReader.fetchUserInfo(NullProgressMonitor.INSTANCE, oauth); 258 setFullyIdentified(info.getDisplayName(), info); 259 } catch (IllegalArgumentException | OsmTransferException e) { 260 Logging.error(e); 261 } 262 } 263 264 265 private List<List<String>> getDefaultOAuthList() { 266 List<String> variables = new ArrayList<>(); 267 variables.add(Config.getPref().get("oauth.settings.access-token-url")); 268 variables.add(Config.getPref().get("oauth.access-token.key")); 269 variables.add(Config.getPref().get("oauth.access-token.secret")); 270 List<List<String>> rList = new ArrayList<>(); 271 rList.add(variables); 272 return rList; 273 } 274 /** 275 * Populate the users 276 * @since xxx 277 */ 278 public void populateAllUsers() { 279 if (OsmApi.isUsingOAuth() && OAuthAccessTokenHolder.getInstance().containsAccessToken() && 280 !NetworkManager.isOffline(OnlineResource.OSM_API)) { 281 OAuthAccessTokenHolder oauth = new OAuthAccessTokenHolder(); 282 List<List<String>> authList = Config.getPref().getListOfLists("oauth.all-tokens", getDefaultOAuthList()); 283 for (List<String> list : authList) { // TODO fix 284 oauth.setAccessToken(list.get(1), list.get(2)); 285 instance.initFromOauth(oauth); 286 users.put(getUserName(), getUserInfo()); 287 } 288 try { 289 instance.initFromOAuth(); 290 } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) { 291 Logging.error(e); 292 // Fall back to preferences if OAuth identification fails for any reason 293 instance.initFromPreferences(); 294 } 295 } else { 296 instance.initFromPreferences(); 297 } 298 299 } 300 301 /** 241 302 * Replies true if the user with name <code>username</code> is the current user 242 303 * 243 304 * @param userName the user name … … 264 325 } 265 326 } 266 327 328 /** 329 * Get all information on all users that have logged in to JOSM 330 * @return A {@code HashMap} with username/UserInfo pairs. 331 */ 332 public Map<String, UserInfo> getAllUserInformation() { 333 return users; 334 } 335 267 336 /* ------------------------------------------------------------------- */ 268 337 /* interface PreferenceChangeListener */ 269 338 /* ------------------------------------------------------------------- */ -
src/org/openstreetmap/josm/data/oauth/OAuthAccessTokenHolder.java
3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import org.openstreetmap.josm.io.OsmApi; 6 7 import org.openstreetmap.josm.io.auth.CredentialsAgent; 7 8 import org.openstreetmap.josm.io.auth.CredentialsAgentException; 8 9 import org.openstreetmap.josm.spi.preferences.Config; … … 16 17 public class OAuthAccessTokenHolder { 17 18 private static OAuthAccessTokenHolder instance; 18 19 20 private OsmApi osmApi = OsmApi.getOsmApi(); 21 19 22 /** 20 23 * Replies the unique instance. 21 24 * @return The unique instance of {@code OAuthAccessTokenHolder} … … 186 189 } 187 190 188 191 /** 192 * Set the API to use with the user 193 * @param serverUrl The URL for the OSM server 194 * @since xxx 195 */ 196 public void setOsmApi(String serverUrl) { 197 osmApi = OsmApi.getOsmApi(serverUrl); 198 } 199 200 /** 201 * Get the osmApi for use with this oauth object 202 * @return The OsmApi to use 203 */ 204 public OsmApi getOsmApi() { 205 return osmApi; 206 } 207 208 /** 189 209 * Clears the content of this holder 190 210 */ 191 211 public void clear() { -
src/org/openstreetmap/josm/gui/io/UploadParameterSummaryPanel.java
10 10 import java.util.Optional; 11 11 12 12 import javax.swing.BorderFactory; 13 import javax.swing.DefaultComboBoxModel; 14 import javax.swing.JComboBox; 13 15 import javax.swing.JLabel; 14 16 import javax.swing.JPanel; 15 17 import javax.swing.event.HyperlinkEvent; 16 18 import javax.swing.event.HyperlinkListener; 17 19 20 import org.openstreetmap.josm.data.UserIdentityManager; 18 21 import org.openstreetmap.josm.data.osm.Changeset; 22 import org.openstreetmap.josm.data.osm.UserInfo; 19 23 import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 20 24 import org.openstreetmap.josm.io.Capabilities; 21 25 import org.openstreetmap.josm.io.OsmApi; … … 114 118 return msg; 115 119 } 116 120 121 protected JComboBox<String> buildPossibleUserBox() { 122 DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>(); 123 UserInfo user = UserIdentityManager.getInstance().getUserInfo(); 124 String userName = user.getDisplayName() != null ? user.getDisplayName() : tr("Please login"); 125 model.addElement(userName); 126 JComboBox<String> rBox = new JComboBox<>(model); 127 return rBox; 128 } 129 117 130 protected void build() { 118 131 jepMessage = new JMultilineLabel(""); 119 132 jepMessage.addHyperlinkListener(this); … … 128 141 JPanel pnl = new JPanel(new BorderLayout()); 129 142 pnl.add(lblWarning, BorderLayout.NORTH); 130 143 add(pnl, BorderLayout.WEST); 144 add(buildPossibleUserBox(), BorderLayout.SOUTH); 131 145 } 132 146 133 147 public void setConfigurationParameterRequestListener(ConfigurationParameterRequestHandler handler) { -
src/org/openstreetmap/josm/io/OsmConnection.java
104 104 * Adds an authentication header for basic authentication 105 105 * 106 106 * @param con the connection 107 * @ throws OsmTransferException if something went wrong. Check for nested exceptions107 * @param response the response with username/password information 108 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 } 109 protected void addBasicAuthorizationHeader(HttpClient con, CredentialsAgentResponse response) { 119 110 if (response != null) { 120 111 if (response.isCanceled()) { 121 112 cancel = true; … … 132 123 * Signs the connection with an OAuth authentication header 133 124 * 134 125 * @param connection the connection 126 * @param holder specific OAuth access token 135 127 * 136 128 * @throws MissingOAuthAccessTokenException if there is currently no OAuth Access Token configured 137 129 * @throws OsmTransferException if signing fails 138 130 */ 139 protected void addOAuthAuthorizationHeader(HttpClient connection ) throws OsmTransferException {131 protected void addOAuthAuthorizationHeader(HttpClient connection, OAuthAccessTokenHolder holder) throws OsmTransferException { 140 132 if (oauthParameters == null) { 141 133 oauthParameters = OAuthParameters.createFromApiUrl(OsmApi.getOsmApi().getServerUrl()); 142 134 } 143 135 OAuthConsumer consumer = oauthParameters.buildConsumer(); 144 OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();145 136 if (!holder.containsAccessToken()) { 146 137 obtainAccessToken(connection); 147 138 } … … 177 168 } 178 169 179 170 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); 171 addAuth(connection, null); 172 } 173 174 /** 175 * Add authorization information to a connection 176 * @param connection to add authorization information to 177 * @param holder A {@code CredentialsAgentResponse} for basic authorization, 178 * {@code OAuthAccessTokenHolder} for OAuth, or {@code null} for defaults. 179 * @throws OsmTransferException if the authorization is not valid 180 */ 181 protected void addAuth(HttpClient connection, Object holder) throws OsmTransferException{ 182 if (holder == null) { 183 final String authMethod = OsmApi.getAuthMethod(); 184 if ("basic".equals(authMethod)) { 185 CredentialsAgentResponse response; 186 try { 187 synchronized (CredentialsManager.getInstance()) { 188 response = CredentialsManager.getInstance().getCredentials(RequestorType.SERVER, 189 connection.getURL().getHost(), false /* don't know yet whether the credentials will succeed */); 190 } 191 } catch (CredentialsAgentException e) { 192 throw new OsmTransferException(e); 193 } 194 holder = response; 195 } else if ("oauth".equals(authMethod)) { 196 holder = OAuthAccessTokenHolder.getInstance(); 197 } else { 198 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod); 199 Logging.warn(msg); 200 throw new OsmTransferException(msg); 201 } 202 } 203 204 if (holder instanceof OAuthAccessTokenHolder) { 205 addOAuthAuthorizationHeader(connection, (OAuthAccessTokenHolder) holder); 206 } else if (holder instanceof CredentialsAgentResponse) { 207 addBasicAuthorizationHeader(connection, (CredentialsAgentResponse) holder); 185 208 } else { 186 String msg = tr("Unexpected value for preference ''{0}''. Got ''{1}''.", "osm-server.auth-method", authMethod);209 String msg = tr("Unexpected object for authorizations. Got ''{0}''.", holder.getClass().getName()); 187 210 Logging.warn(msg); 188 211 throw new OsmTransferException(msg); 189 212 } -
src/org/openstreetmap/josm/io/OsmServerReader.java
79 79 * @throws OsmTransferException if data transfer errors occur 80 80 */ 81 81 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException { 82 return getInputStream(urlStr, progressMonitor, reason, null); 83 } 84 85 /** 86 * Open a connection to the given url and return a reader on the input stream 87 * from that connection. In case of user cancel, return <code>null</code>. 88 * Relative URL's are directed to API base URL. 89 * @param urlStr The url to connect to. 90 * @param progressMonitor progress monitoring and abort handler 91 * @param reason The reason to show on console. Can be {@code null} if no reason is given 92 * @param authentication A {@code CredentialsAgentResponse} for basic authorization, 93 * {@code OAuthAccessTokenHolder} for OAuth, or {@code null} for defaults. 94 * @return A reader reading the input stream (servers answer) or <code>null</code>. 95 * @throws OsmTransferException if data transfer errors occur 96 */ 97 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason, Object authentication) throws OsmTransferException { 82 98 try { 83 99 api.initialize(progressMonitor); 84 100 String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr); 85 return getInputStreamRaw(url, progressMonitor, reason );101 return getInputStreamRaw(url, progressMonitor, reason, authentication); 86 102 } finally { 87 103 progressMonitor.invalidate(); 88 104 } … … 122 138 } 123 139 124 140 /** 141 * Open a connection to the given url and return a reader on the input stream 142 * from that connection. In case of user cancel, return <code>null</code>. 143 * @param urlStr The exact url to connect to. 144 * @param progressMonitor progress monitoring and abort handler 145 * @param reason The reason to show on console. Can be {@code null} if no reason is given 146 * @return An reader reading the input stream (servers answer) or <code>null</code>. 147 * @throws OsmTransferException if data transfer errors occur 148 * @since xxx 149 */ 150 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, Object auth) throws OsmTransferException { 151 return getInputStreamRaw(urlStr, progressMonitor, reason, false, "GET", null, auth); 152 } 153 154 /** 125 155 * Open a connection to the given url (if HTTP, trough a GET request) and return a reader on the input stream 126 156 * from that connection. In case of user cancel, return <code>null</code>. 127 157 * @param urlStr The exact url to connect to. … … 151 181 * @throws OsmTransferException if data transfer errors occur 152 182 * @since 12596 153 183 */ 184 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, 185 boolean uncompressAccordingToContentDisposition, String httpMethod, byte[] requestBody) throws OsmTransferException { 186 return getInputStreamRaw(urlStr, progressMonitor, reason, uncompressAccordingToContentDisposition, httpMethod, requestBody, null); 187 } 188 189 /** 190 * Open a connection to the given url (if HTTP, with the specified method) and return a reader on the input stream 191 * from that connection. In case of user cancel, return <code>null</code>. 192 * @param urlStr The exact url to connect to. 193 * @param progressMonitor progress monitoring and abort handler 194 * @param reason The reason to show on console. Can be {@code null} if no reason is given 195 * @param uncompressAccordingToContentDisposition Whether to inspect the HTTP header {@code Content-Disposition} 196 * for {@code filename} and uncompress a gzip/bzip2/xz/zip stream. 197 * @param httpMethod HTTP method ("GET", "POST" or "PUT") 198 * @param requestBody HTTP request body (for "POST" and "PUT" methods only). Must be null for "GET" method. 199 * @param auth A {@code CredentialsAgentResponse} for basic authorization, 200 * {@code OAuthAccessTokenHolder} for OAuth, or {@code null} for defaults. 201 * @return An reader reading the input stream (servers answer) or {@code null}. 202 * @throws OsmTransferException if data transfer errors occur 203 * @since xxx 204 */ 154 205 @SuppressWarnings("resource") 155 206 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, 156 boolean uncompressAccordingToContentDisposition, String httpMethod, byte[] requestBody) throws OsmTransferException { 207 boolean uncompressAccordingToContentDisposition, String httpMethod, byte[] requestBody, 208 Object auth) throws OsmTransferException { 157 209 try { 158 210 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Config.getUrls().getJOSMWebsite()); 159 211 OnlineResource.OSM_API.checkOfflineAccess(urlStr, OsmApi.getOsmApi().getServerUrl()); … … 182 234 activeConnection = client; 183 235 adaptRequest(client); 184 236 if (doAuthenticate) { 185 addAuth(client );237 addAuth(client, auth); 186 238 } 187 239 if (cancel) 188 240 throw new OsmTransferCanceledException("Operation canceled"); … … 415 467 */ 416 468 public <T> T fetchData(String api, String subtask, DomParser<T> parser, ProgressMonitor monitor, String reason) 417 469 throws OsmTransferException { 470 return fetchData(api, subtask, parser, monitor, reason, null); 471 } 472 473 /** 474 * Fetches generic data from the DOM document resulting an API call. 475 * @param api the OSM API call 476 * @param subtask the subtask translated message 477 * @param parser the parser converting the DOM document (OSM API result) 478 * @param <T> data type 479 * @param monitor The progress monitor 480 * @param reason The reason to show on console. Can be {@code null} if no reason is given 481 * @param authentication A {@code CredentialsAgentResponse} for basic authorization, 482 * {@code OAuthAccessTokenHolder} for OAuth, or {@code null} for defaults. 483 * @return The converted data 484 * @throws OsmTransferException if something goes wrong 485 * @since 12510 486 */ 487 public <T> T fetchData(String api, String subtask, DomParser<T> parser, ProgressMonitor monitor, String reason, Object authentication) 488 throws OsmTransferException { 418 489 try { 419 490 monitor.beginTask(""); 420 491 monitor.indeterminateSubTask(subtask); 421 try (InputStream in = getInputStream(api, monitor.createSubTaskMonitor(1, true), reason )) {492 try (InputStream in = getInputStream(api, monitor.createSubTaskMonitor(1, true), reason, authentication)) { 422 493 return parser.parse(XmlUtils.parseSafeDOM(in)); 423 494 } 424 495 } catch (OsmTransferException e) { -
src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java
13 13 import javax.xml.xpath.XPathFactory; 14 14 15 15 import org.openstreetmap.josm.data.coor.LatLon; 16 import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder; 16 17 import org.openstreetmap.josm.data.osm.DataSet; 17 18 import org.openstreetmap.josm.data.osm.UserInfo; 18 19 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 20 import org.openstreetmap.josm.io.auth.CredentialsAgentResponse; 21 import org.openstreetmap.josm.tools.Logging; 19 22 import org.openstreetmap.josm.tools.UncheckedParseException; 20 23 import org.openstreetmap.josm.tools.XmlParsingException; 21 24 import org.openstreetmap.josm.tools.date.DateUtils; … … 159 162 } 160 163 161 164 /** 165 * Fetches user info without explicit reason with a specific authentication 166 * @param monitor The progress monitor 167 * @param authentication The authentication object ({@code OAuthAccessTokenHolder} 168 * or {@code CredentialsAgentResponse}) 169 * @return The user info 170 * @throws OsmTransferException if something goes wrong 171 * @since xxx 172 */ 173 public UserInfo fetchUserInfo(ProgressMonitor monitor, Object authentication) throws OsmTransferException { 174 if (authentication instanceof String) { 175 return fetchUserInfo(monitor, null, (String) authentication); 176 } else { 177 return fetchUserInfo(monitor, authentication, null); 178 } 179 } 180 181 /** 162 182 * Fetches user info, with an explicit reason. 163 183 * @param monitor The progress monitor 164 184 * @param reason The reason to show on console. Can be {@code null} if no reason is given … … 167 187 * @since 6695 168 188 */ 169 189 public UserInfo fetchUserInfo(ProgressMonitor monitor, String reason) throws OsmTransferException { 170 return fetchData("user/details", tr("Reading user info ..."), 171 OsmServerUserInfoReader::buildFromXML, monitor, reason); 190 return fetchUserInfo(monitor, null, reason); 172 191 } 192 193 /** 194 * Fetches user info, with an explicit reason. 195 * @param monitor The progress monitor 196 * @param authentication A {@code CredentialsAgentResponse} for basic authorization, 197 * {@code OAuthAccessTokenHolder} for OAuth, or {@code null} for defaults. 198 * @param reason The reason to show on console. Can be {@code null} if no reason is given 199 * @return The user info 200 * @throws OsmTransferException if something goes wrong 201 * @since xxx 202 */ 203 public UserInfo fetchUserInfo(ProgressMonitor monitor, Object authentication , String reason) throws OsmTransferException { 204 if (authentication instanceof OAuthAccessTokenHolder || authentication instanceof CredentialsAgentResponse 205 || authentication == null) { 206 return fetchData("user/details", tr("Reading user info ..."), 207 OsmServerUserInfoReader::buildFromXML, monitor, reason, authentication); 208 } else { 209 String msg = tr("We did not get a valid authentication object ({0})", authentication.getClass().getName()); 210 Logging.warn(msg); 211 throw new OsmTransferException(msg); 212 } 213 } 173 214 }
