001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io.auth;
003
004import java.awt.GraphicsEnvironment;
005import java.net.Authenticator.RequestorType;
006import java.net.PasswordAuthentication;
007import java.util.EnumMap;
008import java.util.Map;
009
010import org.openstreetmap.josm.gui.io.CredentialDialog;
011import org.openstreetmap.josm.gui.util.GuiHelper;
012
013public abstract class AbstractCredentialsAgent implements CredentialsAgent {
014
015    protected Map<RequestorType, PasswordAuthentication> memoryCredentialsCache = new EnumMap<>(RequestorType.class);
016
017    @Override
018    public CredentialsAgentResponse getCredentials(final RequestorType requestorType, final String host, boolean noSuccessWithLastResponse)
019            throws CredentialsAgentException {
020        if (requestorType == null)
021            return null;
022        PasswordAuthentication credentials =  lookup(requestorType, host);
023        final String username = (credentials == null || credentials.getUserName() == null) ? "" : credentials.getUserName();
024        final String password = (credentials == null || credentials.getPassword() == null) ? "" : String.valueOf(credentials.getPassword());
025
026        final CredentialsAgentResponse response = new CredentialsAgentResponse();
027
028        /*
029         * Last request was successful and there was no credentials stored
030         * in file (or only the username is stored).
031         * -> Try to recall credentials that have been entered
032         * manually in this session.
033         */
034        if (!noSuccessWithLastResponse && memoryCredentialsCache.containsKey(requestorType) &&
035                (credentials == null || credentials.getPassword() == null || credentials.getPassword().length == 0)) {
036            PasswordAuthentication pa = memoryCredentialsCache.get(requestorType);
037            response.setUsername(pa.getUserName());
038            response.setPassword(pa.getPassword());
039            response.setCanceled(false);
040        /*
041         * Prompt the user for credentials. This happens the first time each
042         * josm start if the user does not save the credentials to preference
043         * file (username=="") and each time after authentication failed
044         * (noSuccessWithLastResponse == true).
045         */
046        } else if (noSuccessWithLastResponse || username.isEmpty() || password.isEmpty()) {
047            if (!GraphicsEnvironment.isHeadless()) {
048                GuiHelper.runInEDTAndWait(new Runnable() {
049                    @Override
050                    public void run() {
051                        CredentialDialog dialog;
052                        if (requestorType.equals(RequestorType.PROXY))
053                            dialog = CredentialDialog.getHttpProxyCredentialDialog(
054                                    username, password, host, getSaveUsernameAndPasswordCheckboxText());
055                        else
056                            dialog = CredentialDialog.getOsmApiCredentialDialog(
057                                    username, password, host, getSaveUsernameAndPasswordCheckboxText());
058                        dialog.setVisible(true);
059                        response.setCanceled(dialog.isCanceled());
060                        if (dialog.isCanceled())
061                            return;
062                        response.setUsername(dialog.getUsername());
063                        response.setPassword(dialog.getPassword());
064                        response.setSaveCredentials(dialog.isSaveCredentials());
065                    }
066                });
067            }
068            if (response.isCanceled() || response.getUsername() == null || response.getPassword() == null) {
069                return response;
070            }
071            if (response.isSaveCredentials()) {
072                store(requestorType, host, new PasswordAuthentication(
073                        response.getUsername(),
074                        response.getPassword()
075                ));
076            /*
077             * User decides not to save credentials to file. Keep it
078             * in memory so we don't have to ask over and over again.
079             */
080            } else {
081                PasswordAuthentication pa = new PasswordAuthentication(response.getUsername(), response.getPassword());
082                memoryCredentialsCache.put(requestorType, pa);
083            }
084        /*
085         * We got it from file.
086         */
087        } else {
088            response.setUsername(username);
089            response.setPassword(password.toCharArray());
090            response.setCanceled(false);
091        }
092        return response;
093    }
094
095    /**
096     * Provide the text for a checkbox that offers to save the
097     * username and password that has been entered by the user.
098     * @return checkbox text
099     */
100    public abstract String getSaveUsernameAndPasswordCheckboxText();
101}