org.rssowl.ui.internal.CredentialsPreferencesPage.java Source code

Java tutorial

Introduction

Here is the source code for org.rssowl.ui.internal.CredentialsPreferencesPage.java

Source

/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2005-2008 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License v1.0 which accompanies this    **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl Development Team - initial API and implementation             **
 **                                                                          **
 **  **********************************************************************  */

package org.rssowl.ui.internal;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.rssowl.core.Owl;
import org.rssowl.core.connection.CredentialsException;
import org.rssowl.core.connection.IConnectionService;
import org.rssowl.core.connection.ICredentials;
import org.rssowl.core.connection.ICredentialsProvider;
import org.rssowl.core.connection.PlatformCredentialsProvider;
import org.rssowl.core.internal.persist.pref.DefaultPreferences;
import org.rssowl.core.persist.IBookMark;
import org.rssowl.core.persist.dao.DynamicDAO;
import org.rssowl.core.persist.pref.IPreferenceScope;
import org.rssowl.core.util.StringUtils;
import org.rssowl.core.util.URIUtils;
import org.rssowl.ui.internal.dialogs.ConfirmDeleteDialog;
import org.rssowl.ui.internal.util.LayoutUtils;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Preferences page to manage stored credentials for bookmarks.
 *
 * @author bpasero
 */
@SuppressWarnings("restriction")
public class CredentialsPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage {

    /* Dummy for creating and changing the master password */
    private static final String DUMMY_LINK = "http://www.rssowl.org";

    private IConnectionService fConService = Owl.getConnectionService();
    private TableViewer fViewer;
    private Button fRemoveAll;
    private Button fRemoveSelected;
    private Button fUseMasterPasswordCheck;
    private IPreferenceScope fGlobalScope = Owl.getPreferenceService().getGlobalScope();
    private Button fResetMasterPassword;

    /* Model used in the Viewer */
    private static class CredentialsModelData {
        private URI fNormalizedLink;
        private String fRealm;
        private String fUsername;
        private String fPassword;

        CredentialsModelData(String username, String password, URI normalizedLink, String realm) {
            fUsername = username;
            fPassword = password;
            fNormalizedLink = normalizedLink;
            fRealm = realm;
        }

        String getUsername() {
            return fUsername;
        }

        String getPassword() {
            return fPassword;
        }

        URI getNormalizedLink() {
            return fNormalizedLink;
        }

        String getRealm() {
            return fRealm;
        }

        ICredentials toCredentials() {
            return new ICredentials() {
                public String getDomain() {
                    return "";
                }

                public String getPassword() {
                    return fPassword;
                }

                public String getUsername() {
                    return fUsername;
                }
            };
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((fNormalizedLink == null) ? 0 : fNormalizedLink.hashCode());
            result = prime * result + ((fRealm == null) ? 0 : fRealm.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;

            if (obj == null)
                return false;

            if (getClass() != obj.getClass())
                return false;

            CredentialsModelData other = (CredentialsModelData) obj;
            if (fNormalizedLink == null) {
                if (other.fNormalizedLink != null)
                    return false;
            } else if (!fNormalizedLink.equals(other.fNormalizedLink))
                return false;

            if (fRealm == null) {
                if (other.fRealm != null)
                    return false;
            } else if (!fRealm.equals(other.fRealm))
                return false;

            return true;
        }
    }

    /*
     * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench)
     */
    public void init(IWorkbench workbench) {
    }

    /*
     * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
     */
    @Override
    protected Control createContents(Composite parent) {
        Composite container = createComposite(parent);

        /* Use a master password */
        Composite masterContainer = new Composite(container, SWT.NONE);
        masterContainer.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
        masterContainer.setLayout(LayoutUtils.createGridLayout(2, 0, 0));
        ((GridLayout) masterContainer.getLayout()).marginBottom = 15;
        ((GridLayout) masterContainer.getLayout()).verticalSpacing = 10;

        StyledText infoText = new StyledText(masterContainer, SWT.WRAP);
        infoText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        ((GridData) infoText.getLayoutData()).widthHint = 200;
        infoText.setBackground(masterContainer.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));

        if (Application.IS_WINDOWS)
            infoText.setText(
                    "Set a master password to protect your passwords from unauthorized access. Note that on Windows, the passwords are automatically protected using your system password.");
        else if (Application.IS_MAC)
            infoText.setText(
                    "Set a master password to protect your passwords from unauthorized access. Note that on MacOS, the passwords are automatically protected using your system password.");
        else
            infoText.setText(
                    "It is recommended to set a master password to protect your passwords from unauthorized access.");

        fUseMasterPasswordCheck = new Button(masterContainer, SWT.CHECK);
        fUseMasterPasswordCheck.setText("Use a master password");
        fUseMasterPasswordCheck.setLayoutData(new GridData(SWT.FILL, SWT.END, true, true));
        fUseMasterPasswordCheck.setSelection(fGlobalScope.getBoolean(DefaultPreferences.USE_MASTER_PASSWORD));
        fUseMasterPasswordCheck.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                fResetMasterPassword.setEnabled(fUseMasterPasswordCheck.getSelection());
            }
        });

        fResetMasterPassword = new Button(masterContainer, SWT.PUSH);
        fResetMasterPassword.setEnabled(fUseMasterPasswordCheck.getSelection());
        fResetMasterPassword.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, true));
        fResetMasterPassword.setText("Change Master Password...");
        fResetMasterPassword.addSelectionListener(new SelectionAdapter() {
            @SuppressWarnings("restriction")
            @Override
            public void widgetSelected(SelectionEvent e) {
                reSetAllCredentials();
            }
        });

        /* Label */
        Label infoLabel = new Label(container, SWT.NONE);
        infoLabel.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false, 2, 1));
        infoLabel.setText("RSSOwl has saved login information for the following sites and realms:");

        /* Viewer to display Passwords */
        int style = SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER;

        CTable customTable = new CTable(container, style);
        customTable.getControl().setHeaderVisible(true);

        fViewer = new TableViewer(customTable.getControl());
        fViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
        fViewer.getControl().setData(ApplicationWorkbenchWindowAdvisor.FOCUSLESS_SCROLL_HOOK, new Object());

        /* Create Columns */
        TableViewerColumn col = new TableViewerColumn(fViewer, SWT.LEFT);
        customTable.manageColumn(col.getColumn(), new CColumnLayoutData(CColumnLayoutData.Size.FILL, 45), "Site",
                null, false, true);
        col.getColumn().setMoveable(false);

        col = new TableViewerColumn(fViewer, SWT.LEFT);
        customTable.manageColumn(col.getColumn(), new CColumnLayoutData(CColumnLayoutData.Size.FILL, 30), "Realm",
                null, false, true);
        col.getColumn().setMoveable(false);

        col = new TableViewerColumn(fViewer, SWT.LEFT);
        customTable.manageColumn(col.getColumn(), new CColumnLayoutData(CColumnLayoutData.Size.FILL, 25),
                "Username", null, false, true);
        col.getColumn().setMoveable(false);

        /* Content Provider */
        fViewer.setContentProvider(new IStructuredContentProvider() {
            public Object[] getElements(Object inputElement) {
                Set<CredentialsModelData> credentials = loadCredentials();
                return credentials.toArray();
            }

            public void dispose() {
            }

            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            }
        });

        /* Label Provider */
        fViewer.setLabelProvider(new CellLabelProvider() {
            @SuppressWarnings("unchecked")
            @Override
            public void update(ViewerCell cell) {
                CredentialsModelData data = (CredentialsModelData) cell.getElement();

                switch (cell.getColumnIndex()) {
                case 0:
                    cell.setText(data.getNormalizedLink().toString());
                    break;

                case 1:
                    cell.setText(data.getRealm());
                    break;

                case 2:
                    cell.setText(data.getUsername());
                    break;
                }
            }
        });

        /* Sorter */
        fViewer.setSorter(new ViewerSorter() {
            @SuppressWarnings("unchecked")
            @Override
            public int compare(Viewer viewer, Object e1, Object e2) {
                CredentialsModelData data1 = (CredentialsModelData) e1;
                CredentialsModelData data2 = (CredentialsModelData) e2;

                return data1.getNormalizedLink().toString().compareTo(data2.getNormalizedLink().toString());
            }
        });

        /* Set Dummy Input */
        fViewer.setInput(new Object());

        /* Offer Buttons to remove Credentials */
        fRemoveSelected = new Button(container, SWT.PUSH);
        fRemoveSelected.setText("&Remove");
        fRemoveSelected.setEnabled(false);
        fRemoveSelected.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onRemove();
            }
        });

        fRemoveAll = new Button(container, SWT.PUSH);
        fRemoveAll.setText("Remove &All");
        fRemoveAll.setEnabled(fViewer.getTable().getItemCount() > 0);
        fRemoveAll.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                onRemoveAll();
            }
        });

        setButtonLayoutData(fRemoveSelected);
        setButtonLayoutData(fRemoveAll);
        ((GridData) fRemoveAll.getLayoutData()).grabExcessHorizontalSpace = false;
        ((GridData) fRemoveAll.getLayoutData()).horizontalAlignment = SWT.BEGINNING;

        fViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                fRemoveSelected.setEnabled(!fViewer.getSelection().isEmpty());
            }
        });

        return container;
    }

    @SuppressWarnings("unchecked")
    private void onRemove() {
        IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection();
        List<?> credentialsToRemove = selection.toList();
        for (Object obj : credentialsToRemove) {
            CredentialsModelData data = (CredentialsModelData) obj;
            remove(data);
        }

        /* Update in UI */
        fViewer.refresh();
        fRemoveSelected.setEnabled(!fViewer.getSelection().isEmpty());
        fRemoveAll.setEnabled(fViewer.getTable().getItemCount() > 0);
    }

    private void onRemoveAll() {

        /* Ask for Confirmation first */
        ConfirmDeleteDialog dialog = new ConfirmDeleteDialog(getShell(), "Confirm Remove",
                "This action can not be undone", "Are you sure you want to remove all stored passwords?", null);
        if (dialog.open() != IDialogConstants.OK_ID)
            return;

        Set<CredentialsModelData> credentials = loadCredentials();
        for (CredentialsModelData data : credentials) {
            remove(data);
        }

        /* Update in UI */
        fViewer.refresh();
        fRemoveSelected.setEnabled(!fViewer.getSelection().isEmpty());
        fRemoveAll.setEnabled(fViewer.getTable().getItemCount() > 0);
    }

    private void remove(CredentialsModelData data) {

        /* Remove normalized link and realm */
        ICredentialsProvider provider = fConService.getCredentialsProvider(data.getNormalizedLink());
        if (provider != null) {
            try {
                provider.deleteAuthCredentials(data.getNormalizedLink(), data.getRealm());
            } catch (CredentialsException e) {
                Activator.getDefault().logError(e.getMessage(), e);
            }
        }

        /* Remove all other stored Credentials matching normalized link and realm */
        Collection<IBookMark> bookmarks = DynamicDAO.loadAll(IBookMark.class);
        for (IBookMark bookmark : bookmarks) {
            String realm = (String) bookmark.getProperty(Controller.BM_REALM_PROPERTY);

            URI feedLink = bookmark.getFeedLinkReference().getLink();
            URI normalizedLink = URIUtils.normalizeUri(feedLink, true);

            /*
             * If realm is null, then this bookmark successfully loaded due to another bookmark
             * that the user successfully authenticated to. If the realm is not null, then we
             * have to compare the realm to ensure that no credentials from the same host but
             * a different realm gets removed.
             */
            if ((realm == null || realm.equals(data.getRealm()))
                    && normalizedLink.equals(data.getNormalizedLink())) {
                provider = fConService.getCredentialsProvider(feedLink);
                if (provider != null) {
                    try {
                        provider.deleteAuthCredentials(feedLink, null); //Null as per contract in DefaultProtocolHandler
                        bookmark.removeProperty(Controller.BM_REALM_PROPERTY);
                    } catch (CredentialsException e) {
                        Activator.getDefault().logError(e.getMessage(), e);
                    }
                }
            }
        }
    }

    private Set<CredentialsModelData> loadCredentials() {
        Set<CredentialsModelData> credentials = new HashSet<CredentialsModelData>();

        Collection<IBookMark> bookmarks = DynamicDAO.loadAll(IBookMark.class);
        for (IBookMark bookmark : bookmarks) {
            String realm = (String) bookmark.getProperty(Controller.BM_REALM_PROPERTY);

            /* Only support sites with realms */
            if (!StringUtils.isSet(realm))
                continue;

            URI feedLink = bookmark.getFeedLinkReference().getLink();
            URI normalizedLink = URIUtils.normalizeUri(feedLink, true);

            try {
                ICredentials authCredentials = fConService.getAuthCredentials(normalizedLink, realm);
                if (authCredentials != null) {
                    CredentialsModelData data = new CredentialsModelData(authCredentials.getUsername(),
                            authCredentials.getPassword(), normalizedLink, realm);
                    credentials.add(data);
                }
            } catch (CredentialsException e) {
                Activator.getDefault().logError(e.getMessage(), e);
                showError();
                break;
            }
        }

        return credentials;
    }

    private void showError() {
        setErrorMessage("Wrong master password supplied.");

        fUseMasterPasswordCheck.setEnabled(false);
        fResetMasterPassword.setEnabled(false);
        fViewer.getTable().setEnabled(false);
    }

    private Composite createComposite(Composite parent) {
        Composite composite = new Composite(parent, SWT.NULL);
        GridLayout layout = new GridLayout(2, false);
        layout.marginWidth = 0;
        layout.marginHeight = 0;
        composite.setLayout(layout);
        composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
        composite.setFont(parent.getFont());
        return composite;
    }

    /*
     * @see org.eclipse.jface.preference.PreferencePage#performOk()
     */
    @Override
    public boolean performOk() {
        boolean oldUseMasterPassword = fGlobalScope.getBoolean(DefaultPreferences.USE_MASTER_PASSWORD);
        boolean newUseMasterPassword = fUseMasterPasswordCheck.getSelection();

        fGlobalScope.putBoolean(DefaultPreferences.USE_MASTER_PASSWORD, fUseMasterPasswordCheck.getSelection());

        /*
         * Hack: There does not seem to be any API to update the stored credentials in Equinox Secure Storage.
         * In order to enable/disable the master password, the workaround is to save all known credentials again.
         * The provider will automatically prompt for the new master password to use for the credentials.
         */
        if (oldUseMasterPassword != newUseMasterPassword)
            reSetAllCredentials();

        return super.performOk();
    }

    /*
     * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
     */
    @Override
    protected void performDefaults() {
        super.performDefaults();

        IPreferenceScope defaultScope = Owl.getPreferenceService().getDefaultScope();
        fUseMasterPasswordCheck.setSelection(defaultScope.getBoolean(DefaultPreferences.USE_MASTER_PASSWORD));
        fResetMasterPassword.setEnabled(fUseMasterPasswordCheck.getSelection());
    }

    @SuppressWarnings("restriction")
    private void reSetAllCredentials() {
        boolean clearedOnce = false; // Implementation Detail of PlatformCredentialsProvider
        Set<CredentialsModelData> credentials = loadCredentials();

        /* Add Dummy Credentials if no credentials present */
        CredentialsModelData dummyCredentials = null;
        if (credentials.isEmpty()) {
            try {
                dummyCredentials = new CredentialsModelData("", "", new URI(DUMMY_LINK), "");
                credentials.add(dummyCredentials);
            } catch (URISyntaxException e) {
                /* Should not happen */
            }
        }

        /* Write all Credentials into credential provider again */
        for (CredentialsModelData credential : credentials) {
            ICredentialsProvider credentialsProvider = Owl.getConnectionService()
                    .getCredentialsProvider(credential.fNormalizedLink);
            if (credentialsProvider != null) {

                /* Implementation Detail: Need to clear PlatformCredentialsProvider once if provided */
                if (!clearedOnce && credentialsProvider instanceof PlatformCredentialsProvider) {
                    ((PlatformCredentialsProvider) credentialsProvider).clear();
                    clearedOnce = true;
                }

                try {
                    credentialsProvider.setAuthCredentials(credential.toCredentials(), credential.fNormalizedLink,
                            credential.fRealm);
                } catch (CredentialsException e) {
                    Activator.getDefault().logError(e.getMessage(), e);
                }
            }
        }

        /* Delete Dummy Credentials Again */
        if (dummyCredentials != null) {
            ICredentialsProvider credentialsProvider = Owl.getConnectionService()
                    .getCredentialsProvider(dummyCredentials.fNormalizedLink);
            if (credentialsProvider != null) {
                try {
                    credentialsProvider.deleteAuthCredentials(dummyCredentials.fNormalizedLink,
                            dummyCredentials.fRealm);
                } catch (CredentialsException e) {
                    Activator.getDefault().logError(e.getMessage(), e);
                }
            }
        }
    }
}