Ignore:
Timestamp:
2024-02-21T21:26:18+01:00 (3 months ago)
Author:
taylor.smock
Message:

Fix #22810: OSM OAuth 1.0a/Basic auth deprecation and removal

As of 2024-02-15, something changed in the OSM server configuration. This broke
our OAuth 1.0a implementation (see #23475). As such, we are removing OAuth 1.0a
from JOSM now instead of when the OSM server removes support in June 2024.

For third-party OpenStreetMap servers, the Basic Authentication method has been
kept. However, they should be made aware that it may be removed if a non-trivial
bug occurs with it. We highly recommend that the third-party servers update to
the current OpenStreetMap website implementation (if only for their own security).

Failing that, the third-party server can implement RFC8414. As of this commit,
we currently use the authorization_endpoint and token_endpoint fields.
To check and see if their third-party server implements RFC8414, they can go
to <server host>/.well-known/oauth-authorization-server.

Prominent third-party OpenStreetMap servers may give us a client id for their
specific server. That client id may be added to the hard-coded client id list
at maintainer discretion. At a minimum, the server must be publicly
available and have a significant user base.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/oauth/FullyAutomaticAuthorizationUI.java

    r18650 r18991  
    1313import java.awt.event.ActionEvent;
    1414import java.io.IOException;
    15 import java.net.Authenticator.RequestorType;
    16 import java.net.PasswordAuthentication;
    1715import java.util.concurrent.Executor;
    1816
     
    2422import javax.swing.JPanel;
    2523import javax.swing.JTabbedPane;
    26 import javax.swing.event.DocumentEvent;
    27 import javax.swing.event.DocumentListener;
    28 import javax.swing.text.JTextComponent;
    2924import javax.swing.text.html.HTMLEditorKit;
    3025
    31 import org.openstreetmap.josm.data.oauth.OAuthParameters;
    32 import org.openstreetmap.josm.data.oauth.OAuthToken;
     26import org.openstreetmap.josm.data.oauth.IOAuthToken;
     27import org.openstreetmap.josm.data.oauth.OAuthVersion;
    3328import org.openstreetmap.josm.gui.HelpAwareOptionPane;
    3429import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    3530import org.openstreetmap.josm.gui.help.HelpUtil;
    36 import org.openstreetmap.josm.gui.preferences.server.UserNameValidator;
    3731import org.openstreetmap.josm.gui.util.GuiHelper;
    38 import org.openstreetmap.josm.gui.widgets.DefaultTextComponentValidator;
    3932import org.openstreetmap.josm.gui.widgets.HtmlPanel;
    4033import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
    41 import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
    42 import org.openstreetmap.josm.gui.widgets.JosmTextField;
    43 import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
    4434import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
    45 import org.openstreetmap.josm.io.OsmApi;
    4635import org.openstreetmap.josm.io.OsmTransferException;
    47 import org.openstreetmap.josm.io.auth.CredentialsAgent;
    48 import org.openstreetmap.josm.io.auth.CredentialsAgentException;
    49 import org.openstreetmap.josm.io.auth.CredentialsManager;
    5036import org.openstreetmap.josm.tools.ImageProvider;
    5137import org.openstreetmap.josm.tools.Logging;
    52 import org.openstreetmap.josm.tools.Utils;
    5338import org.xml.sax.SAXException;
    5439
    5540/**
    56  * This is an UI which supports a JOSM user to get an OAuth Access Token in a fully
     41 * This is a UI which supports a JOSM user to get an OAuth Access Token in a fully
    5742 * automatic process.
    5843 *
     
    6045 */
    6146public class FullyAutomaticAuthorizationUI extends AbstractAuthorizationUI {
    62 
    63     private final JosmTextField tfUserName = new JosmTextField();
    64     private final JosmPasswordField tfPassword = new JosmPasswordField();
    65     private transient UserNameValidator valUserName;
    66     private transient PasswordValidator valPassword;
    6747    private final AccessTokenInfoPanel pnlAccessTokenInfo = new AccessTokenInfoPanel();
    6848    private OsmPrivilegesPanel pnlOsmPrivileges;
     
    10585                + tr("Please enter your OSM user name and password. The password will <strong>not</strong> be saved "
    10686                        + "in clear text in the JOSM preferences and it will be submitted to the OSM server <strong>only once</strong>. "
    107                         + "Subsequent data upload requests don''t use your password any more.").replaceAll("\\. ", ".<br>")
     87                        + "Subsequent data upload requests don''t use your password any more.").replace(". ", ".<br>")
    10888                        + "</p>"
    10989                        + "</body></html>");
     
    11999        pnl.add(new JLabel(tr("Username: ")), gc);
    120100
    121         gc.gridx = 1;
    122         gc.weightx = 1.0;
    123         pnl.add(tfUserName, gc);
    124         SelectAllOnFocusGainedDecorator.decorate(tfUserName);
    125         valUserName = new UserNameValidator(tfUserName);
    126         valUserName.validate();
    127 
    128101        // the password input field
    129102        gc.anchor = GridBagConstraints.NORTHWEST;
     
    134107        pnl.add(new JLabel(tr("Password:")), gc);
    135108
     109        // filler - grab remaining space
    136110        gc.gridx = 1;
    137         gc.weightx = 1.0;
    138         pnl.add(tfPassword, gc);
    139         SelectAllOnFocusGainedDecorator.decorate(tfPassword);
    140         valPassword = new PasswordValidator(tfPassword);
    141         valPassword.validate();
    142 
    143         // filler - grab remaining space
    144111        gc.gridy = 4;
    145112        gc.gridwidth = 2;
     
    168135
    169136    /**
    170      * Initializes the panel with values from the preferences
    171      * @param paramApiUrl the API URL
    172      */
    173     @Override
    174     public void initialize(String paramApiUrl) {
    175         super.initialize(paramApiUrl);
    176         CredentialsAgent cm = CredentialsManager.getInstance();
    177         try {
    178             PasswordAuthentication pa = cm.lookup(RequestorType.SERVER, OsmApi.getOsmApi().getHost());
    179             if (pa == null) {
    180                 tfUserName.setText("");
    181                 tfPassword.setText("");
    182             } else {
    183                 tfUserName.setText(pa.getUserName() == null ? "" : pa.getUserName());
    184                 tfPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
    185             }
    186         } catch (CredentialsAgentException e) {
    187             Logging.error(e);
    188             tfUserName.setText("");
    189             tfPassword.setText("");
    190         }
    191     }
    192 
    193     /**
    194137     * Builds the panel with the action button  for starting the authorisation
    195138     *
     
    200143
    201144        RunAuthorisationAction runAuthorisationAction = new RunAuthorisationAction();
    202         tfPassword.getDocument().addDocumentListener(runAuthorisationAction);
    203         tfUserName.getDocument().addDocumentListener(runAuthorisationAction);
    204145        pnl.add(new JButton(runAuthorisationAction));
    205146        return pnl;
     
    289230    }
    290231
    291     protected String getOsmUserName() {
    292         return tfUserName.getText();
    293     }
    294 
    295     protected String getOsmPassword() {
    296         return String.valueOf(tfPassword.getPassword());
    297     }
    298 
    299232    /**
    300233     * Constructs a new {@code FullyAutomaticAuthorizationUI} for the given API URL.
     
    302235     * @param executor the executor used for running the HTTP requests for the authorization
    303236     * @since 5422
    304      */
     237     * @deprecated since 18991
     238     */
     239    @Deprecated
    305240    public FullyAutomaticAuthorizationUI(String apiUrl, Executor executor) {
    306         super(apiUrl);
     241        this(apiUrl, executor, OAuthVersion.OAuth10a);
     242    }
     243
     244    /**
     245     * Constructs a new {@code FullyAutomaticAuthorizationUI} for the given API URL.
     246     * @param apiUrl The OSM API URL
     247     * @param executor the executor used for running the HTTP requests for the authorization
     248     * @param oAuthVersion The OAuth version to use for this UI
     249     * @since 18991
     250     */
     251    public FullyAutomaticAuthorizationUI(String apiUrl, Executor executor, OAuthVersion oAuthVersion) {
     252        super(apiUrl, oAuthVersion);
    307253        this.executor = executor;
    308254        build();
     
    315261
    316262    @Override
    317     protected void setAccessToken(OAuthToken accessToken) {
     263    protected void setAccessToken(IOAuthToken accessToken) {
    318264        super.setAccessToken(accessToken);
    319265        pnlAccessTokenInfo.setAccessToken(accessToken);
     
    323269     * Starts the authorisation process
    324270     */
    325     class RunAuthorisationAction extends AbstractAction implements DocumentListener {
     271    class RunAuthorisationAction extends AbstractAction {
    326272        RunAuthorisationAction() {
    327273            putValue(NAME, tr("Authorize now"));
    328274            new ImageProvider("oauth", "oauth-small").getResource().attachImageIcon(this);
    329275            putValue(SHORT_DESCRIPTION, tr("Click to redirect you to the authorization form on the JOSM web site"));
    330             updateEnabledState();
    331276        }
    332277
     
    334279        public void actionPerformed(ActionEvent evt) {
    335280            executor.execute(new FullyAutomaticAuthorisationTask(FullyAutomaticAuthorizationUI.this));
    336         }
    337 
    338         protected final void updateEnabledState() {
    339             setEnabled(valPassword.isValid() && valUserName.isValid());
    340         }
    341 
    342         @Override
    343         public void changedUpdate(DocumentEvent e) {
    344             updateEnabledState();
    345         }
    346 
    347         @Override
    348         public void insertUpdate(DocumentEvent e) {
    349             updateEnabledState();
    350         }
    351 
    352         @Override
    353         public void removeUpdate(DocumentEvent e) {
    354             updateEnabledState();
    355281        }
    356282    }
     
    386312                    FullyAutomaticAuthorizationUI.this,
    387313                    getApiUrl(),
    388                     (OAuthParameters) getAdvancedPropertiesPanel().getAdvancedParameters(),
    389314                    getAccessToken()
    390315            ));
    391         }
    392     }
    393 
    394     static class PasswordValidator extends DefaultTextComponentValidator {
    395         PasswordValidator(JTextComponent tc) {
    396             super(tc, tr("Please enter your OSM password"), tr("The password cannot be empty. Please enter your OSM password"));
    397316        }
    398317    }
     
    439358                            + "Please check your advanced setting and try again."
    440359                            + "</html>",
    441                             ((OAuthParameters) getAdvancedPropertiesPanel().getAdvancedParameters()).getAuthoriseUrl()),
     360                            getAdvancedPropertiesPanel().getAdvancedParameters().getAuthorizationUrl()),
    442361                    tr("OAuth authorization failed"),
    443362                    JOptionPane.ERROR_MESSAGE,
     
    446365        }
    447366
    448         protected void alertLoginFailed() {
    449             final String loginUrl = ((OAuthParameters) getAdvancedPropertiesPanel().getAdvancedParameters()).getOsmLoginUrl();
    450             HelpAwareOptionPane.showOptionDialog(
    451                     FullyAutomaticAuthorizationUI.this,
    452                     tr("<html>"
    453                             + "The automatic process for retrieving an OAuth Access Token<br>"
    454                             + "from the OSM server failed. JOSM failed to log into {0}<br>"
    455                             + "for user {1}.<br><br>"
    456                             + "Please check username and password and try again."
    457                             +"</html>",
    458                             loginUrl,
    459                             Utils.escapeReservedCharactersHTML(getOsmUserName())),
    460                     tr("OAuth authorization failed"),
    461                     JOptionPane.ERROR_MESSAGE,
    462                     HelpUtil.ht("/Dialog/OAuthAuthorisationWizard#FullyAutomaticProcessFailed")
    463             );
    464         }
    465 
    466367        protected void handleException(final OsmOAuthAuthorizationException e) {
    467             Runnable r = () -> {
    468                 if (e instanceof OsmLoginFailedException) {
    469                     alertLoginFailed();
    470                 } else {
    471                     alertAuthorisationFailed();
     368            Logging.error(e);
     369            GuiHelper.runInEDT(this::alertAuthorisationFailed);
     370        }
     371
     372        @Override
     373        protected void realRun() throws SAXException, IOException, OsmTransferException {
     374            getProgressMonitor().setTicksCount(2);
     375            OAuthAuthorizationWizard.authorize(true, token -> {
     376                if (!canceled) {
     377                    getProgressMonitor().worked(1);
     378                    GuiHelper.runInEDT(() -> {
     379                        prepareUIForResultDisplay();
     380                        setAccessToken(token.orElse(null));
     381                    });
    472382                }
    473             };
    474             Logging.error(e);
    475             GuiHelper.runInEDT(r);
    476         }
    477 
    478         @Override
    479         protected void realRun() throws SAXException, IOException, OsmTransferException {
    480             try {
    481                 getProgressMonitor().setTicksCount(3);
    482                 OsmOAuthAuthorizationClient authClient = new OsmOAuthAuthorizationClient(
    483                         (OAuthParameters) getAdvancedPropertiesPanel().getAdvancedParameters()
    484                 );
    485                 OAuthToken requestToken = authClient.getRequestToken(
    486                         getProgressMonitor().createSubTaskMonitor(1, false)
    487                 );
    488                 getProgressMonitor().worked(1);
    489                 if (canceled) return;
    490                 authClient.authorise(
    491                         requestToken,
    492                         getOsmUserName(),
    493                         getOsmPassword(),
    494                         pnlOsmPrivileges.getPrivileges(),
    495                         getProgressMonitor().createSubTaskMonitor(1, false)
    496                 );
    497                 getProgressMonitor().worked(1);
    498                 if (canceled) return;
    499                 final OAuthToken accessToken = authClient.getAccessToken(
    500                         getProgressMonitor().createSubTaskMonitor(1, false)
    501                 );
    502                 getProgressMonitor().worked(1);
    503                 if (canceled) return;
    504                 GuiHelper.runInEDT(() -> {
    505                     prepareUIForResultDisplay();
    506                     setAccessToken(accessToken);
    507                 });
    508             } catch (final OsmOAuthAuthorizationException e) {
    509                 handleException(e);
    510             }
     383            }, getApiUrl(), getOAuthVersion());
     384            getProgressMonitor().worked(1);
    511385        }
    512386    }
Note: See TracChangeset for help on using the changeset viewer.