Ticket #2710: initial_multiuser_rework_v2.patch

File initial_multiuser_rework_v2.patch, 47.9 KB (added by taylor.smock, 6 years ago)

Starts refactoring the preferences panel in order to allow adding multiple users (preferences panel is currently broken).

  • src/org/openstreetmap/josm/data/UserIdentityManager.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    66import java.text.MessageFormat;
     7import java.util.ArrayList;
     8import java.util.LinkedHashMap;
     9import java.util.List;
     10import java.util.Map;
     11import java.util.TreeMap;
    712
    813import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
    914import org.openstreetmap.josm.data.osm.User;
     
    1419import org.openstreetmap.josm.io.OsmApi;
    1520import org.openstreetmap.josm.io.OsmServerUserInfoReader;
    1621import org.openstreetmap.josm.io.OsmTransferException;
     22import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
    1723import org.openstreetmap.josm.io.auth.CredentialsManager;
    1824import org.openstreetmap.josm.spi.preferences.Config;
    1925import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
     
    7884                instance.initFromPreferences();
    7985            }
    8086            Config.getPref().addPreferenceChangeListener(instance);
     87            instance.populateAllUsers();
    8188        }
    8289        return instance;
    8390    }
    8491
    85     private String userName;
    86     private UserInfo userInfo;
    8792    private boolean accessTokenKeyChanged;
    8893    private boolean accessTokenSecretChanged;
    8994
     95    private String userName;
     96    private UserInfo userInfo;
     97    private LinkedHashMap<String, UserInfo> users;
     98
    9099    private UserIdentityManager() {
     100        users = new LinkedHashMap<>();
    91101    }
    92102
    93103    /**
     
    112122        if (trimmedUserName.isEmpty())
    113123            throw new IllegalArgumentException(
    114124                    MessageFormat.format("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName));
    115         this.userName = trimmedUserName;
     125        userName = trimmedUserName;
    116126        userInfo = null;
    117127    }
    118128
     
    132142        if (trimmedUserName.isEmpty())
    133143            throw new IllegalArgumentException(tr("Expected non-empty value for parameter ''{0}'', got ''{1}''", "userName", userName));
    134144        CheckParameterUtil.ensureParameterNotNull(userInfo, "userInfo");
    135         this.userName = trimmedUserName;
     145        userName = trimmedUserName;
    136146        this.userInfo = userInfo;
    137147    }
    138148
     
    238248    }
    239249
    240250    /**
     251     * Initializes the user identity manager from OAuth request of user details.
     252     * @param oauth The {@code OAuthAccessTokenHolder} with the key and secret
     253     * @see #initFromPreferences
     254     * @since xxx
     255     */
     256    public void initFromOauth(OAuthAccessTokenHolder oauth) {
     257        try {
     258            OsmServerUserInfoReader osmServerReader = new OsmServerUserInfoReader();
     259            UserInfo info = osmServerReader.fetchUserInfo(NullProgressMonitor.INSTANCE, oauth);
     260            setFullyIdentified(info.getDisplayName(), info);
     261        } catch (IllegalArgumentException | OsmTransferException e) {
     262            Logging.error(e);
     263        }
     264    }
     265
     266    private List<Map<String, String>> getDefaultOAuthList() {
     267        Map<String, String> variables = new TreeMap<>();
     268        TreeMap<String, String> vars = new TreeMap<>();
     269        vars.put("url", "osm-server.url");
     270        vars.put("login", "oauth.access-token.key");
     271        vars.put("password", "oauth.access-token.secret");
     272        vars.put("login-token-url", "oauth.settings.authorise-token-url");
     273        vars.put("auth-url", "oauth.settings.authorise-url");
     274        vars.put("consumer-key", "oauth.settings.consumer-key");
     275        vars.put("consumer-secret", "oauth.settings.consumer-secret");
     276        vars.put("login-url", "oauth.settings.osm-login-url");
     277        vars.put("logout-url", "oauth.settings.osm-logout-url");
     278        variables.put("auth-type", "oauth");
     279
     280        vars.forEach((key, value) -> variables.put(key, Config.getPref().get(value)));
     281        List<Map<String, String>> rList = new ArrayList<>();
     282        rList.add(variables);
     283        return rList;
     284    }
     285
     286    private List<Map<String, String>> getDefaultBasicAuthList() {
     287        Map<String, String> variables = new TreeMap<>();
     288        TreeMap<String, String> vars = new TreeMap<>();
     289
     290        vars.put("url", "osm-server-url");
     291        vars.put("login", "osm-server.username");
     292        vars.put("password", "osm-server.password");
     293
     294        variables.put("auth-type", "basic");
     295        vars.forEach((key, value) -> variables.put(key, Config.getPref().get(value)));
     296        List<Map<String, String>> rList = new ArrayList<>();
     297        return rList;
     298    }
     299
     300    private List<Map<String, String>> getDefaultAuthList() {
     301        if ("oauth".equals(Config.getPref().get("osm-server.auth-method"))) {
     302            return getDefaultOAuthList();
     303        } else {
     304            return getDefaultBasicAuthList();
     305        }
     306    }
     307
     308    /**
     309     * Populate the users
     310     * @since xxx
     311     */
     312    public void populateAllUsers() {
     313        List<Map<String, String>> authList = Config.getPref().getListOfMaps("all-authorizations", getDefaultAuthList());
     314        for (Map<String, String> map : authList) { // TODO fix
     315            if ("oauth".equals(map.get("auth-type"))) {
     316                OAuthAccessTokenHolder oauth = new OAuthAccessTokenHolder();
     317                oauth.setAccessToken(map.get("login"), map.get("password"));
     318                oauth.setOsmApi(map.get("url"));
     319                instance.initFromOauth(oauth);
     320                users.put(getUserName(), getUserInfo());
     321            } else if ("basic".equals(map.get("auth-type"))) {
     322                CredentialsAgentResponse response = new CredentialsAgentResponse();
     323                response.setUsername(map.get("login"));
     324                response.setPassword(map.get("password").toCharArray());
     325            }
     326        }
     327
     328        if (OsmApi.isUsingOAuth() && OAuthAccessTokenHolder.getInstance().containsAccessToken() &&
     329                !NetworkManager.isOffline(OnlineResource.OSM_API)) {
     330            try {
     331                instance.initFromOAuth();
     332            } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
     333                Logging.error(e);
     334                // Fall back to preferences if OAuth identification fails for any reason
     335                instance.initFromPreferences();
     336            }
     337        } else {
     338            instance.initFromPreferences();
     339        }
     340    }
     341
     342    /**
    241343     * Replies true if the user with name <code>username</code> is the current user
    242344     *
    243345     * @param userName the user name
     
    264366        }
    265367    }
    266368
     369    /**
     370     * Get all information on all users that have logged in to JOSM
     371     * @return A {@code HashMap} with username/UserInfo pairs.
     372     * @since xxx
     373     */
     374    public Map<String, UserInfo> getAllUserInformation() {
     375        if (users == null) {
     376            populateAllUsers();
     377        }
     378        return users;
     379    }
     380
     381    /**
     382     * Get user authentication information
     383     * @return The user information (passwords are protected)
     384     * @since xxx
     385     */
     386    public List<Map<String, String>> getUserAuthInformation() {
     387        List<Map<String, String>> authList = Config.getPref().getListOfMaps("all-authorizations", getDefaultAuthList());
     388        for (Map<String, String> map : authList) {
     389            if ("basic".equals(map.get("auth-type"))) {
     390                // TODO protect passwords, even though they are stored in plain text
     391                // map.put("password", "PROTECTED");
     392            }
     393        }
     394        return authList;
     395    }
     396
    267397    /* ------------------------------------------------------------------- */
    268398    /* interface PreferenceChangeListener                                  */
    269399    /* ------------------------------------------------------------------- */
  • src/org/openstreetmap/josm/data/oauth/OAuthAccessTokenHolder.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.util.List;
     7import java.util.Map;
     8import java.util.TreeMap;
     9
     10import org.openstreetmap.josm.data.UserIdentityManager;
     11import org.openstreetmap.josm.io.OsmApi;
    612import org.openstreetmap.josm.io.auth.CredentialsAgent;
    713import org.openstreetmap.josm.io.auth.CredentialsAgentException;
    814import org.openstreetmap.josm.spi.preferences.Config;
     
    1622public class OAuthAccessTokenHolder {
    1723    private static OAuthAccessTokenHolder instance;
    1824
     25    private OsmApi osmApi = OsmApi.getOsmApi();
     26
    1927    /**
    2028     * Replies the unique instance.
    2129     * @return The unique instance of {@code OAuthAccessTokenHolder}
     
    183191            Logging.warn(tr("Failed to store OAuth Access Token to credentials manager"));
    184192            Logging.warn(tr("Current credential manager is of type ''{0}''", cm.getClass().getName()));
    185193        }
     194        List<Map<String, String>> users = UserIdentityManager.getInstance().getUserAuthInformation();
     195        boolean present = false;
     196        for (Map<String, String> user : users) {
     197            if ("oauth".equals(user.get("auth-type")) && accessTokenKey.equals(user.get("auth-type"))) {
     198                user.put("password", accessTokenSecret);
     199                user.put("url", osmApi.getHost());
     200                present = true;
     201                break;
     202            }
     203        }
     204        if (!present) {
     205            Map<String, String> map = new TreeMap<>();
     206            map.put("login", accessTokenKey);
     207            map.put("password", accessTokenSecret);
     208            map.put("url", osmApi.getHost());
     209        }
    186210    }
    187211
    188212    /**
     213     * Set the API to use with the user
     214     * @param serverUrl The URL for the OSM server
     215     * @since xxx
     216     */
     217    public void setOsmApi(String serverUrl) {
     218        osmApi = OsmApi.getOsmApi(serverUrl);
     219    }
     220
     221    /**
     222     * Get the osmApi for use with this oauth object
     223     * @return The OsmApi to use
     224     */
     225    public OsmApi getOsmApi() {
     226        return osmApi;
     227    }
     228
     229    /**
    189230     * Clears the content of this holder
    190231     */
    191232    public void clear() {
  • src/org/openstreetmap/josm/gui/io/UploadParameterSummaryPanel.java

     
    55import static org.openstreetmap.josm.tools.I18n.trn;
    66
    77import java.awt.BorderLayout;
     8import java.awt.event.ItemEvent;
     9import java.awt.event.ItemListener;
    810import java.beans.PropertyChangeEvent;
    911import java.beans.PropertyChangeListener;
     12import java.util.Map;
    1013import java.util.Optional;
    1114
    1215import javax.swing.BorderFactory;
     16import javax.swing.DefaultComboBoxModel;
     17import javax.swing.JComboBox;
    1318import javax.swing.JLabel;
    1419import javax.swing.JPanel;
    1520import javax.swing.event.HyperlinkEvent;
    1621import javax.swing.event.HyperlinkListener;
    1722
     23import org.openstreetmap.josm.data.UserIdentityManager;
    1824import org.openstreetmap.josm.data.osm.Changeset;
     25import org.openstreetmap.josm.data.osm.UserInfo;
    1926import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
    2027import org.openstreetmap.josm.io.Capabilities;
    2128import org.openstreetmap.josm.io.OsmApi;
     
    2229import org.openstreetmap.josm.io.UploadStrategySpecification;
    2330import org.openstreetmap.josm.spi.preferences.Config;
    2431import org.openstreetmap.josm.tools.ImageProvider;
     32import org.openstreetmap.josm.tools.Logging;
    2533
    2634/**
    2735 * A panel that displays a summary of data the user is about to upload
     
    114122        return msg;
    115123    }
    116124
     125    protected JComboBox<String> buildPossibleUserBox() {
     126        DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
     127        Map<String, UserInfo> info = UserIdentityManager.getInstance().getAllUserInformation();
     128        info.forEach((userName, userInfo) -> model.addElement(userName));
     129        JComboBox<String> rBox = new JComboBox<>(model);
     130        UserInfo user = UserIdentityManager.getInstance().getUserInfo();
     131        String userName = user.getDisplayName() != null ? user.getDisplayName() : tr("Please login");
     132        if (model.getIndexOf(userName) < 0) model.addElement(userName);
     133        model.setSelectedItem(userName);
     134        rBox.addItemListener(new ItemListener() {
     135            @Override
     136            public void itemStateChanged(ItemEvent e) {
     137                if (e.getStateChange() == ItemEvent.SELECTED) {
     138                    Logging.info("{0} was selected, switching to {1}", userName, e.getItem());
     139                    model.setSelectedItem(e.getItem());
     140                }
     141            }
     142        });
     143        return rBox;
     144    }
     145
    117146    protected void build() {
    118147        jepMessage = new JMultilineLabel("");
    119148        jepMessage.addHyperlinkListener(this);
     
    128157        JPanel pnl = new JPanel(new BorderLayout());
    129158        pnl.add(lblWarning, BorderLayout.NORTH);
    130159        add(pnl, BorderLayout.WEST);
     160        JComboBox<String> options = buildPossibleUserBox();
     161        if (options.getItemCount() > 1) add(buildPossibleUserBox(), BorderLayout.SOUTH);
    131162    }
    132163
    133164    public void setConfigurationParameterRequestListener(ConfigurationParameterRequestHandler handler) {
  • src/org/openstreetmap/josm/gui/preferences/server/AuthenticationPreferencesPanel.java

     
    77import java.awt.GridBagConstraints;
    88import java.awt.GridBagLayout;
    99import java.awt.Insets;
    10 import java.awt.event.ItemEvent;
    11 import java.awt.event.ItemListener;
     10import java.awt.event.ActionEvent;
    1211import java.beans.PropertyChangeEvent;
    1312import java.beans.PropertyChangeListener;
     13import java.util.ArrayList;
     14import java.util.List;
     15import java.util.Map;
    1416
     17import javax.swing.AbstractAction;
    1518import javax.swing.ButtonGroup;
     19import javax.swing.JButton;
    1620import javax.swing.JPanel;
    17 import javax.swing.JRadioButton;
    1821import javax.swing.JSeparator;
    1922
    20 import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
     23import org.openstreetmap.josm.data.UserIdentityManager;
    2124import org.openstreetmap.josm.gui.help.HelpUtil;
    2225import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel;
    2326import org.openstreetmap.josm.io.OsmApi;
    24 import org.openstreetmap.josm.io.auth.CredentialsManager;
    2527import org.openstreetmap.josm.spi.preferences.Config;
    2628import org.openstreetmap.josm.tools.Logging;
    2729
     
    3133 */
    3234public class AuthenticationPreferencesPanel extends VerticallyScrollablePanel implements PropertyChangeListener {
    3335
    34     /** indicates whether we use basic authentication */
    35     private final JRadioButton rbBasicAuthentication = new JRadioButton();
    36     /** indicates whether we use OAuth as authentication scheme */
    37     private final JRadioButton rbOAuth = new JRadioButton();
     36    /** add a basic authentication method */
     37    private final JButton rbBasicAuthentication = new JButton();
     38    /** add a OAuth method */
     39    private final JButton rbOAuth = new JButton();
    3840    /** the panel which contains the authentication parameters for the respective authentication scheme */
    3941    private final JPanel pnlAuthenticationParameteters = new JPanel(new BorderLayout());
    40     /** the panel for the basic authentication parameters */
    41     private BasicAuthenticationPreferencesPanel pnlBasicAuthPreferences;
    42     /** the panel for the OAuth authentication parameters */
    43     private OAuthAuthenticationPreferencesPanel pnlOAuthPreferences;
     42    /** the panels for the basic authentication parameters */
     43    private List<BasicAuthenticationPreferencesPanel> pnlBasicAuthPreferences;
     44    /** the panels for the OAuth authentication parameters */
     45    private List<OAuthAuthenticationPreferencesPanel> pnlOAuthPreferences;
    4446    /** the panel for messages notifier preferences */
    4547    private FeaturesPanel pnlFeaturesPreferences;
    4648
     
    6062        setLayout(new GridBagLayout());
    6163        GridBagConstraints gc = new GridBagConstraints();
    6264
    63         AuthenticationMethodChangeListener authChangeListener = new AuthenticationMethodChangeListener();
     65        //-- the panel for API feature preferences
     66        int tGridy = gc.gridy;
     67        gc.gridy = 3;
     68        gc.fill = GridBagConstraints.NONE;
     69        pnlFeaturesPreferences = new FeaturesPanel();
     70        add(pnlFeaturesPreferences, gc);
     71        gc.gridy = tGridy;
    6472
    6573        // -- radio button for basic authentication
    6674        gc.anchor = GridBagConstraints.NORTHWEST;
     
    6977        gc.weightx = 1.0;
    7078        gc.insets = new Insets(0, 0, 0, 3);
    7179        add(rbBasicAuthentication, gc);
    72         rbBasicAuthentication.setText(tr("Use Basic Authentication"));
    73         rbBasicAuthentication.setToolTipText(tr("Select to use HTTP basic authentication with your OSM username and password"));
    74         rbBasicAuthentication.addItemListener(authChangeListener);
     80        rbBasicAuthentication.setText(tr("Add Basic Authentication"));
     81        rbBasicAuthentication.setToolTipText(tr("Select to add HTTP basic authentication with your OSM username and password"));
     82        rbBasicAuthentication.setAction(new AbstractAction() {
     83            @Override
     84            public void actionPerformed(ActionEvent e) {
     85                BasicAuthenticationPreferencesPanel panel = new BasicAuthenticationPreferencesPanel();
     86                panel.initFromPreferences();
     87                pnlBasicAuthPreferences.add(panel);
     88                pnlAuthenticationParameteters.add(panel, BorderLayout.CENTER);
     89                repaint();
     90            }
     91        });
    7592
    7693        //-- radio button for OAuth
    7794        gc.gridx = 0;
    7895        gc.weightx = 0.0;
    7996        add(rbOAuth, gc);
    80         rbOAuth.setText(tr("Use OAuth"));
    81         rbOAuth.setToolTipText(tr("Select to use OAuth as authentication mechanism"));
    82         rbOAuth.addItemListener(authChangeListener);
     97        rbOAuth.setText(tr("Add OAuth"));
     98        rbOAuth.setToolTipText(tr("Select to add a OAuth authentication mechanism"));
     99        rbOAuth.setAction(new AbstractAction() {
     100            @Override
     101            public void actionPerformed(ActionEvent e) {
     102                OAuthAuthenticationPreferencesPanel panel = new OAuthAuthenticationPreferencesPanel();
     103                panel.initFromPreferences();
     104                pnlOAuthPreferences.add(panel);
     105                pnlAuthenticationParameteters.add(panel, BorderLayout.CENTER);
     106                repaint();
     107            }
     108        });
    83109
    84110        //-- radio button for OAuth
    85111        ButtonGroup bg = new ButtonGroup();
     
    96122        add(pnlAuthenticationParameteters, gc);
    97123
    98124        //-- the two panels for authentication parameters
    99         pnlBasicAuthPreferences = new BasicAuthenticationPreferencesPanel();
    100         pnlOAuthPreferences = new OAuthAuthenticationPreferencesPanel();
     125        pnlBasicAuthPreferences = new ArrayList<>();
     126        pnlOAuthPreferences = new ArrayList<>();
    101127
    102         rbBasicAuthentication.setSelected(true);
    103         pnlAuthenticationParameteters.add(pnlBasicAuthPreferences, BorderLayout.CENTER);
     128        populateAuthPanels();
    104129
    105130        gc.gridy = 2;
    106131        add(new JSeparator(), gc);
     132    }
    107133
    108         //-- the panel for API feature preferences
    109         gc.gridy = 3;
    110         gc.fill = GridBagConstraints.NONE;
    111         pnlFeaturesPreferences = new FeaturesPanel();
    112         add(pnlFeaturesPreferences, gc);
     134    private void populateAuthPanels() {
     135        List<Map<String, String>> users = UserIdentityManager.getInstance().getUserAuthInformation();
     136        for (Map<String, String> user : users) {
     137            if ("oauth".equals(user.get("auth-type"))) {
     138                OAuthAuthenticationPreferencesPanel panel = new OAuthAuthenticationPreferencesPanel();
     139                panel.initFromMap(user);
     140                pnlOAuthPreferences.add(panel);
     141                pnlAuthenticationParameteters.add(panel);
     142            } else if ("basic".equals(user.get("auth-type"))) {
     143                BasicAuthenticationPreferencesPanel panel = new BasicAuthenticationPreferencesPanel();
     144                panel.initFromMap(user);
     145                pnlBasicAuthPreferences.add(panel);
     146                pnlAuthenticationParameteters.add(panel);
     147            }
     148        }
     149        repaint();
    113150    }
    114151
    115152    /**
     
    126163                    "osm-server.auth-method", authMethod));
    127164            rbBasicAuthentication.setSelected(true);
    128165        }
    129         pnlBasicAuthPreferences.initFromPreferences();
    130         pnlOAuthPreferences.initFromPreferences();
     166        List<Map<String, String>> users = UserIdentityManager.getInstance().getUserAuthInformation();
     167        for (Map<String, String> user : users) {
     168            if ("oauth".equals(user.get("auth-type"))) {
     169                OAuthAuthenticationPreferencesPanel panel = new OAuthAuthenticationPreferencesPanel();
     170                panel.initFromMap(user);
     171                pnlOAuthPreferences.add(panel);
     172            } else if ("basic".equals(user.get("auth-type"))) {
     173                BasicAuthenticationPreferencesPanel panel = new BasicAuthenticationPreferencesPanel();
     174                panel.initFromMap(user);
     175                pnlBasicAuthPreferences.add(panel);
     176            }
     177        }
    131178        pnlFeaturesPreferences.initFromPreferences();
    132179    }
    133180
     
    143190            authMethod = "oauth";
    144191        }
    145192        Config.getPref().put("osm-server.auth-method", authMethod);
    146         if ("basic".equals(authMethod)) {
    147             // save username and password and clear the OAuth token
    148             pnlBasicAuthPreferences.saveToPreferences();
    149             OAuthAccessTokenHolder.getInstance().clear();
    150             OAuthAccessTokenHolder.getInstance().save(CredentialsManager.getInstance());
    151         } else if ("oauth".equals(authMethod)) {
    152             // clear the password in the preferences
    153             pnlBasicAuthPreferences.clearPassword();
    154             pnlBasicAuthPreferences.saveToPreferences();
    155             pnlOAuthPreferences.saveToPreferences();
     193        for (BasicAuthenticationPreferencesPanel panel : pnlBasicAuthPreferences) {
     194            panel.saveToPreferences();
    156195        }
     196        for (OAuthAuthenticationPreferencesPanel panel : pnlOAuthPreferences) {
     197            panel.saveToPreferences();
     198        }
    157199        // save message notifications preferences. To be done after authentication preferences.
    158200        pnlFeaturesPreferences.saveToPreferences();
    159201    }
    160202
    161     /**
    162      * Listens to changes in the authentication method
    163      */
    164     class AuthenticationMethodChangeListener implements ItemListener {
    165         @Override
    166         public void itemStateChanged(ItemEvent e) {
    167             if (rbBasicAuthentication.isSelected()) {
    168                 pnlAuthenticationParameteters.removeAll();
    169                 pnlAuthenticationParameteters.add(pnlBasicAuthPreferences, BorderLayout.CENTER);
    170                 pnlBasicAuthPreferences.revalidate();
    171             } else {
    172                 pnlAuthenticationParameteters.removeAll();
    173                 pnlAuthenticationParameteters.add(pnlOAuthPreferences, BorderLayout.CENTER);
    174                 pnlOAuthPreferences.revalidate();
    175             }
    176             repaint();
    177         }
    178     }
    179 
    180203    @Override
    181204    public void propertyChange(PropertyChangeEvent evt) {
    182         if (pnlOAuthPreferences != null) {
    183             pnlOAuthPreferences.propertyChange(evt);
     205        /** TODO is this still necessary? */
     206        if (pnlOAuthPreferences != null && !pnlOAuthPreferences.isEmpty()) {
     207            for (OAuthAuthenticationPreferencesPanel panel : pnlOAuthPreferences) {
     208                panel.propertyChange(evt);
     209            }
    184210        }
    185211    }
    186212}
  • src/org/openstreetmap/josm/gui/preferences/server/BasicAuthenticationPreferencesPanel.java

     
    99import java.awt.Insets;
    1010import java.net.Authenticator.RequestorType;
    1111import java.net.PasswordAuthentication;
     12import java.util.List;
     13import java.util.Map;
     14import java.util.TreeMap;
    1215
    1316import javax.swing.BorderFactory;
    1417import javax.swing.JLabel;
    1518import javax.swing.JPanel;
    1619
     20import org.openstreetmap.josm.data.UserIdentityManager;
    1721import org.openstreetmap.josm.gui.widgets.JosmPasswordField;
    1822import org.openstreetmap.josm.gui.widgets.JosmTextField;
    1923import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
     
    9397     * Inits contents from preferences.
    9498     */
    9599    public void initFromPreferences() {
    96         CredentialsAgent cm = CredentialsManager.getInstance();
    97         try {
    98             decorationPanel.removeAll();
    99             decorationPanel.add(cm.getPreferencesDecorationPanel(), BorderLayout.CENTER);
    100             PasswordAuthentication pa = cm.lookup(RequestorType.SERVER, OsmApi.getOsmApi().getHost());
    101             if (pa == null) {
    102                 tfOsmUserName.setText("");
    103                 tfOsmPassword.setText("");
    104             } else {
    105                 tfOsmUserName.setText(pa.getUserName() == null ? "" : pa.getUserName());
    106                 tfOsmPassword.setText(pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
     100        initFromMap(null);
     101    }
     102
     103    /**
     104     * Initializes contents from a map
     105     * @param user The map with the login/password
     106     */
     107    public void initFromMap(Map<String, String> user) {
     108        if (user == null) {
     109            user = new TreeMap<>();
     110            CredentialsAgent cm = CredentialsManager.getInstance();
     111            try {
     112                decorationPanel.removeAll();
     113                decorationPanel.add(cm.getPreferencesDecorationPanel(), BorderLayout.CENTER);
     114                PasswordAuthentication pa = cm.lookup(RequestorType.SERVER, OsmApi.getOsmApi().getHost());
     115                if (pa == null) {
     116                    user.put("login", "");
     117                    user.put("password", "");
     118                } else {
     119                    user.put("login", pa.getUserName() == null ? "" : pa.getUserName());
     120                    user.put("password", pa.getPassword() == null ? "" : String.valueOf(pa.getPassword()));
     121                }
     122            } catch (CredentialsAgentException e) {
     123                Logging.error(e);
     124                Logging.warn(tr("Failed to retrieve OSM credentials from credential manager."));
     125                Logging.warn(tr("Current credential manager is of type ''{0}''", cm.getClass().getName()));
     126                user.put("login",  "");
     127                user.put("password", "");
    107128            }
    108         } catch (CredentialsAgentException e) {
    109             Logging.error(e);
    110             Logging.warn(tr("Failed to retrieve OSM credentials from credential manager."));
    111             Logging.warn(tr("Current credential manager is of type ''{0}''", cm.getClass().getName()));
    112             tfOsmUserName.setText("");
    113             tfOsmPassword.setText("");
    114129        }
     130
     131        tfOsmUserName.setText(user.get("login") != null ? user.get("login") : "");
     132        tfOsmPassword.setText(user.get("password") != null ? user.get("password") : "");
    115133    }
    116134
    117135    /**
     
    130148            Logging.warn(tr("Failed to save OSM credentials to credential manager."));
    131149            Logging.warn(tr("Current credential manager is of type ''{0}''", cm.getClass().getName()));
    132150        }
     151        List<Map<String, String>> users = UserIdentityManager.getInstance().getUserAuthInformation();
     152        boolean present = false;
     153        for (Map<String, String> user : users) {
     154            if ("basic".equals(user.get("auth-type"))) {
     155                if (tfOsmUserName.getText().trim().equals(user.get("login"))) {
     156                    user.put("password", tfOsmPassword.getPassword().toString());
     157                    user.put("url", OsmApi.getOsmApi().getHost());
     158                    present = true;
     159                    break;
     160                }
     161            }
     162        }
     163        if (!present) {
     164            Map<String, String> map = new TreeMap<>();
     165            map.put("login", tfOsmUserName.getText().trim());
     166            map.put("password", tfOsmPassword.getParent().toString());
     167            map.put("url", OsmApi.getOsmApi().getHost());
     168        }
    133169    }
    134170
    135171    /**
  • src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java

     
    1414import java.awt.event.ItemEvent;
    1515import java.beans.PropertyChangeEvent;
    1616import java.beans.PropertyChangeListener;
     17import java.util.Map;
     18import java.util.TreeMap;
    1719
    1820import javax.swing.AbstractAction;
    1921import javax.swing.BorderFactory;
     
    5153    private final JPanel pnlAuthorisationMessage = new JPanel(new BorderLayout());
    5254    private final NotYetAuthorisedPanel pnlNotYetAuthorised = new NotYetAuthorisedPanel();
    5355    private final AdvancedOAuthPropertiesPanel pnlAdvancedProperties = new AdvancedOAuthPropertiesPanel();
    54     private final AlreadyAuthorisedPanel pnlAlreadyAuthorised = new AlreadyAuthorisedPanel();
     56    private final AlreadyAuthorisedPanel pnlAlreadyAuthorised = new AlreadyAuthorisedPanel(null);
    5557    private String apiUrl;
    5658
    5759    /**
     
    125127    }
    126128
    127129    protected void refreshView() {
     130        Map<String, String> map = new TreeMap<>();
     131        if (OAuthAccessTokenHolder.getInstance().containsAccessToken()) {
     132            map.put("login", OAuthAccessTokenHolder.getInstance().getAccessTokenKey());
     133            map.put("password", OAuthAccessTokenHolder.getInstance().getAccessTokenSecret());
     134        }
     135        refreshView(map);
     136    }
     137
     138    protected void refreshView(Map<String, String> user) {
    128139        pnlAuthorisationMessage.removeAll();
    129         if (OAuthAccessTokenHolder.getInstance().containsAccessToken()) {
     140        if (user.containsKey("login") && user.containsKey("password")) {
    130141            pnlAuthorisationMessage.add(pnlAlreadyAuthorised, BorderLayout.CENTER);
    131             pnlAlreadyAuthorised.refreshView();
     142            pnlAlreadyAuthorised.refreshView(user);
    132143            pnlAlreadyAuthorised.revalidate();
    133144        } else {
    134145            pnlAuthorisationMessage.add(pnlNotYetAuthorised, BorderLayout.CENTER);
     
    156167    }
    157168
    158169    /**
     170     * Initializes the panel from a map
     171     * @param user The map with a {@code login} and {@code password}.
     172     * Optional information includes {@code url}.
     173     */
     174    public void initFromMap(Map<String, String> user) {
     175        if (user.containsKey("url")) {
     176            setApiUrl(user.get("url").trim());
     177        } else {
     178            setApiUrl(OsmApi.getOsmApi().getServerUrl().trim());
     179        }
     180        refreshView(user);
     181    }
     182
     183    /**
    159184     * Saves the current values to preferences
    160185     */
    161186    public void saveToPreferences() {
     
    215240        private final JosmTextField tfAccessTokenSecret = new JosmTextField();
    216241
    217242        /**
    218          * Constructs a new {@code AlreadyAuthorisedPanel}.
     243         * Constructs a new {@code AlreadyAUthorisedPanel}.
     244         * @param map The map with the authentication information
    219245         */
    220         AlreadyAuthorisedPanel() {
     246        AlreadyAuthorisedPanel(Map<String, String> map) {
     247            if (map == null) {
     248                map = new TreeMap<>();
     249                map.put("login", OAuthAccessTokenHolder.getInstance().getAccessTokenKey());
     250                map.put("password", OAuthAccessTokenHolder.getInstance().getAccessTokenSecret());
     251            }
     252            tfAccessTokenKey.setText(map.get("login"));
     253            tfAccessTokenSecret.setText(map.get("password"));
    221254            build();
    222             refreshView();
     255            refreshView(map);
    223256        }
    224257
    225258        protected void build() {
     
    291324            add(new JPanel(), gc);
    292325        }
    293326
    294         protected final void refreshView() {
    295             String v = OAuthAccessTokenHolder.getInstance().getAccessTokenKey();
     327        protected final void refreshView(Map<String, String> user) {
     328            String v = user.get("login");
    296329            tfAccessTokenKey.setText(v == null ? "" : v);
    297             v = OAuthAccessTokenHolder.getInstance().getAccessTokenSecret();
     330            v = user.get("password");
    298331            tfAccessTokenSecret.setText(v == null ? "" : v);
    299332            cbSaveToPreferences.setSelected(OAuthAccessTokenHolder.getInstance().isSaveToPreferences());
    300333        }
  • src/org/openstreetmap/josm/io/OsmConnection.java

     
    104104     * Adds an authentication header for basic authentication
    105105     *
    106106     * @param con the connection
    107      * @throws OsmTransferException if something went wrong. Check for nested exceptions
     107     * @param response the response with username/password information
    108108     */
    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) {
    119110        if (response != null) {
    120111            if (response.isCanceled()) {
    121112                cancel = true;
     
    132123     * Signs the connection with an OAuth authentication header
    133124     *
    134125     * @param connection the connection
     126     * @param holder specific OAuth access token
    135127     *
    136128     * @throws MissingOAuthAccessTokenException if there is currently no OAuth Access Token configured
    137129     * @throws OsmTransferException if signing fails
    138130     */
    139     protected void addOAuthAuthorizationHeader(HttpClient connection) throws OsmTransferException {
     131    protected void addOAuthAuthorizationHeader(HttpClient connection, OAuthAccessTokenHolder holder) throws OsmTransferException {
    140132        if (oauthParameters == null) {
    141133            oauthParameters = OAuthParameters.createFromApiUrl(OsmApi.getOsmApi().getServerUrl());
    142134        }
    143135        OAuthConsumer consumer = oauthParameters.buildConsumer();
    144         OAuthAccessTokenHolder holder = OAuthAccessTokenHolder.getInstance();
    145136        if (!holder.containsAccessToken()) {
    146137            obtainAccessToken(connection);
    147138        }
     
    177168    }
    178169
    179170    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);
    185208        } 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());
    187210            Logging.warn(msg);
    188211            throw new OsmTransferException(msg);
    189212        }
  • src/org/openstreetmap/josm/io/OsmServerReader.java

     
    7979     * @throws OsmTransferException if data transfer errors occur
    8080     */
    8181    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 {
    8298        try {
    8399            api.initialize(progressMonitor);
    84100            String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr);
    85             return getInputStreamRaw(url, progressMonitor, reason);
     101            return getInputStreamRaw(url, progressMonitor, reason, authentication);
    86102        } finally {
    87103            progressMonitor.invalidate();
    88104        }
     
    122138    }
    123139
    124140    /**
     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    /**
    125155     * Open a connection to the given url (if HTTP, trough a GET request) and return a reader on the input stream
    126156     * from that connection. In case of user cancel, return <code>null</code>.
    127157     * @param urlStr The exact url to connect to.
     
    151181     * @throws OsmTransferException if data transfer errors occur
    152182     * @since 12596
    153183     */
     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     */
    154205    @SuppressWarnings("resource")
    155206    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 {
    157209        try {
    158210            OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Config.getUrls().getJOSMWebsite());
    159211            OnlineResource.OSM_API.checkOfflineAccess(urlStr, OsmApi.getOsmApi().getServerUrl());
     
    182234            activeConnection = client;
    183235            adaptRequest(client);
    184236            if (doAuthenticate) {
    185                 addAuth(client);
     237                addAuth(client, auth);
    186238            }
    187239            if (cancel)
    188240                throw new OsmTransferCanceledException("Operation canceled");
     
    415467     */
    416468    public <T> T fetchData(String api, String subtask, DomParser<T> parser, ProgressMonitor monitor, String reason)
    417469            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 {
    418489        try {
    419490            monitor.beginTask("");
    420491            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)) {
    422493                return parser.parse(XmlUtils.parseSafeDOM(in));
    423494            }
    424495        } catch (OsmTransferException e) {
  • src/org/openstreetmap/josm/io/OsmServerUserInfoReader.java

     
    1313import javax.xml.xpath.XPathFactory;
    1414
    1515import org.openstreetmap.josm.data.coor.LatLon;
     16import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
    1617import org.openstreetmap.josm.data.osm.DataSet;
    1718import org.openstreetmap.josm.data.osm.UserInfo;
    1819import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     20import org.openstreetmap.josm.io.auth.CredentialsAgentResponse;
     21import org.openstreetmap.josm.tools.Logging;
    1922import org.openstreetmap.josm.tools.UncheckedParseException;
    2023import org.openstreetmap.josm.tools.XmlParsingException;
    2124import org.openstreetmap.josm.tools.date.DateUtils;
     
    159162    }
    160163
    161164    /**
     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    /**
    162182     * Fetches user info, with an explicit reason.
    163183     * @param monitor The progress monitor
    164184     * @param reason The reason to show on console. Can be {@code null} if no reason is given
     
    167187     * @since 6695
    168188     */
    169189    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);
    172191    }
     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    }
    173214}