All Downloads are FREE. Search and download functionalities are using the official Maven repository.

es.gob.jmulticard.ui.passwordcallback.gui.InputPasswordSmartcardDialog Maven / Gradle / Ivy

/*
 * Controlador Java de la Secretaria de Estado de Administraciones Publicas
 * para el DNI electronico.
 *
 * El Controlador Java para el DNI electronico es un proveedor de seguridad de JCA/JCE
 * que permite el acceso y uso del DNI electronico en aplicaciones Java de terceros
 * para la realizacion de procesos de autenticacion, firma electronica y validacion
 * de firma. Para ello, se implementan las funcionalidades KeyStore y Signature para
 * el acceso a los certificados y claves del DNI electronico, asi como la realizacion
 * de operaciones criptograficas de firma con el DNI electronico. El Controlador ha
 * sido disenado para su funcionamiento independiente del sistema operativo final.
 *
 * Copyright (C) 2012 Direccion General de Modernizacion Administrativa, Procedimientos
 * e Impulso de la Administracion Electronica
 *
 * Este programa es software libre y utiliza un licenciamiento dual (LGPL 2.1+
 * o EUPL 1.1+), lo cual significa que los usuarios podran elegir bajo cual de las
 * licencias desean utilizar el codigo fuente. Su eleccion debera reflejarse
 * en las aplicaciones que integren o distribuyan el Controlador, ya que determinara
 * su compatibilidad con otros componentes.
 *
 * El Controlador puede ser redistribuido y/o modificado bajo los terminos de la
 * Lesser GNU General Public License publicada por la Free Software Foundation,
 * tanto en la version 2.1 de la Licencia, o en una version posterior.
 *
 * El Controlador puede ser redistribuido y/o modificado bajo los terminos de la
 * European Union Public License publicada por la Comision Europea,
 * tanto en la version 1.1 de la Licencia, o en una version posterior.
 *
 * Deberia recibir una copia de la GNU Lesser General Public License, si aplica, junto
 * con este programa. Si no, consultelo en .
 *
 * Deberia recibir una copia de la European Union Public License, si aplica, junto
 * con este programa. Si no, consultelo en .
 *
 * Este programa es distribuido con la esperanza de que sea util, pero
 * SIN NINGUNA GARANTiA; incluso sin la garantia implicita de comercializacion
 * o idoneidad para un proposito particular.
 */
package es.gob.jmulticard.ui.passwordcallback.gui;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;

import es.gob.jmulticard.CancelledOperationException;
import es.gob.jmulticard.ui.passwordcallback.Messages;

/** Componente diálogo que solicita una contraseña al usuario. */
public final class InputPasswordSmartcardDialog extends AbstractJAccessibilityCustomDialog implements ActionListener {

    /** UID. */
    private static final long serialVersionUID = 1L;

    private static final int PIN_MIN_LENGTH = 4;
    private static final int PIN_MAX_LENGTH = 16;

    /** Botón de aceptar. */
    private static JButton okButton = null;

    static JButton getOkButton() {
        return okButton;
    }

    /** Botón de cancelar. */
    private static JButton cancelButton = null;

    static JButton getCancelButton() {
        return cancelButton;
    }

    /** Texto para el botón */
    private static String cancellText = Messages.getString("PrincipalGUI.cancelar"); //$NON-NLS-1$

    /** Etiqueta con la informaciñn de la alerta. */
    private transient InfoLabel infoLabel = null;

    /** Panel de botones. */
    private transient JPanel buttonsPanel = null;

    /** Panel principal. */
    private transient JPanel mainPanel = null;

    /** Campo de texto o campo de contraseña. */
    private transient JSecurePasswordLabel component = null;

    /** Indica si se muestra o no la casilla de "No volver a preguntar" (cacheo del PIN). */
    private transient JCheckBox useCacheCheckBox = null;

    /** Etiqueta que contiene el icono de la alerta. */
    private transient final IconLabel iconLabel = new IconLabel();

    /** Respuesta al mensaje */
    private transient int answer;

    /** Constructor.
     * @param componentParent Componente padre para la modalidad.
     * @param modal true si el diálogo debe ser modal,
     *              false en caso contrario.
     * @param message Mensaje del diálogo.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el recurso de fichero de icono.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN. */
    private InputPasswordSmartcardDialog(final JDialog componentParent,
                         final boolean modal,
                         final String message,
                         final String title,
                         final String iconPath,
                         final boolean allowUseCache,
                         final boolean defaultUseCache) {
        super(componentParent, modal, true);
        initComponents(message, title, iconPath, allowUseCache, defaultUseCache);
        setLocationRelativeTo(componentParent);
        pack();
    }

    /** Constructor.
     * @param componentParent Componente padre para la modalidad.
     * @param modal true si el diálogo debe ser modal,
     *              false en caso contrario.
     * @param message Mensaje del diálogo.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el recurso de fichero de icono.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN. */
    private InputPasswordSmartcardDialog(final Component componentParent,
                         final boolean modal,
                         final String message,
                         final String title,
                         final String iconPath,
                         final boolean allowUseCache,
                         final boolean defaultUseCache) {
        super(true);
        setModal(modal);
        initComponents(message, title, iconPath, allowUseCache, defaultUseCache);
        setLocationRelativeTo(componentParent);
        pack();
    }

    /** Constructor.
     * @param componentParent Componente padre para la modalidad.
     * @param modal true si el diálogo debe ser modal,
     *              false en caso contrario.
     * @param message Mensaje del diálogo.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el recurso de fichero de icono.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN. */
    private InputPasswordSmartcardDialog(final JFrame componentParent,
                             final boolean modal,
                             final String message,
                             final String title,
                             final String iconPath,
                             final boolean allowUseCache,
                             final boolean defaultUseCache) {
        super(componentParent, modal, true);
        initComponents(message, title, iconPath, allowUseCache, defaultUseCache);
        setLocationRelativeTo(componentParent);
        pack();
    }

    /** Inicializa los componentes de la alerta.
     * @param message Mensaje del diálogo.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el recurso de fichero de icono.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN. */
    protected void initComponents(final String message,
    		                      final String title,
    		                      final String iconPath,
    		                      final boolean allowUseCache,
    		                      final boolean defaultUseCache) {
        // Se obtienen las dimensiones de maximizado
        final int maxWidth = getMaxWidth();
        final int maxHeight = getMaxHeight();

        // Se establece el tamano minimo
        setMinimumSize(new Dimension(getInitialWidth(), getInitialHeight()));
        setPreferredSize(new Dimension(getInitialWidth(), getInitialHeight()));
        // Se establece el tamano maximo
        setMaximumSize(new Dimension(maxWidth, maxHeight));

        // Dimensiones de la ventana en Windows y Linux
        if (GeneralConfig.isMaximized()) {
            // Se maximiza
            this.setBounds(0, 0, maxWidth, maxHeight);
        }
        else if (GeneralConfig.isBigFontSize() || GeneralConfig.isFontBold()) { // Se establece el tamano minimo en base a las opciones activas
		    setMinimumSize(new Dimension(AccesiblityConstants.CUSTOMDIALOG_FONT_INITIAL_WIDTH, AccesiblityConstants.CUSTOMDIALOG_FONT_INITIAL_HEIGHT));
		}

        setTitle(title);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        answer = JOptionPane.NO_OPTION;
        // Contenedor del dialogo
        final Container container = getContentPane();
        // Layout del contenedor
        container.setLayout(new GridBagLayout());

        // Panel con los datos del dialogo
        mainPanel = new JPanel(new GridBagLayout());

        // Restricciones para el panel de datos
        final GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.gridx = 0;
        c.gridy = 0;
        c.insets = new Insets(10, 5, 0, 10); // right padding
        c.weightx = 1.0;
        c.weighty = 1.0;

        // Icono del dialogo
        setIconLabel(iconPath);
        final JPanel iconPanel = new JPanel(new GridBagLayout());
        final GridBagConstraints consIconPanel = new GridBagConstraints();
        consIconPanel.fill = GridBagConstraints.BOTH;

        iconPanel.add(iconLabel, consIconPanel);

        // Se crea una etiqueta sencilla
        infoLabel = new InfoLabel(message);
        infoLabel.setHorizontalAlignment(SwingConstants.LEFT); // Se alinea a la izqda
        infoLabel.setVerticalAlignment(SwingConstants.CENTER); // Se alinea al centro el texto

        // Se anade la etiqueta al panel de informacion general
        mainPanel.add(infoLabel, c);


        // campo de password del dialogo
        component = new JSecurePasswordLabel(16);

        c.gridy++;
        c.insets = new Insets(2, 5, 2, 10); // right padding

        // Se anade el campo de texto al panel de informacion general
        mainPanel.add(component, c);

        // Si corresponde, se agrega la casilla de verificacion para el uso de la cache
        if (allowUseCache) {
        	useCacheCheckBox = new JCheckBox(Messages.getString("InputPasswordSmartcardDialog.useCache")); //$NON-NLS-1$
        	useCacheCheckBox.setSelected(defaultUseCache);

        	c.gridy++;
        	mainPanel.add(useCacheCheckBox, c);
        }

        // Panel de botones
        createMainButtonsPanel();
        final JPanel accessibilityButtonsPanel = createAccessibilityButtonsPanel();

        // Restricciones del contenedor general

        final GridBagConstraints cons = new GridBagConstraints();
        cons.fill = GridBagConstraints.BOTH;

        //Se anade el panel de botones relacionados con la accesibilidad
        cons.gridx = 0;
        cons.gridy = 0;
        cons.gridwidth = 2;
        cons.gridheight = 1;
        cons.weighty = 0.0;
        cons.weightx = 0.10;
        container.add(accessibilityButtonsPanel, cons);

        //se anade el panel de informacion
        cons.gridx = 2;
        cons.gridy = 0;
        cons.gridwidth = 6;
        cons.gridheight = 2;
        cons.weighty = 0.35;
        cons.weightx = 0.90;
        container.add(mainPanel, cons);

        //Se anade el icono
        cons.gridx = 0;
        cons.gridy = 1;
        cons.gridwidth = 2;
        cons.gridheight = 3;
        cons.weighty = 0.90;
        cons.weightx = 0.0;
        container.add(iconPanel, cons);

        //Se anade el panel de botones
        cons.gridx = 2;
        cons.gridy = 3;
        cons.gridwidth = 6;
        cons.gridheight = 1;
        cons.weighty = 0.65;
        cons.weightx = 0.0;
        container.add(buttonsPanel, cons);

        pack();
    }

    /** Asigna el icono a la etiqueta.
     * @param iconPath Ruta hacia el recurso de fichero de icono. */
    protected void setIconLabel(final String iconPath) {
    	if (iconPath != null) {
	        // Segun el tipo de mensaje se selecciona el icono
	        final ImageIcon icon = new ImageIcon(
	    		InputPasswordSmartcardDialog.class.getResource(
					iconPath
				)
			);
	        final Dimension dimensionInicial = new Dimension(100, (int)(100f / icon.getIconWidth() * icon.getIconHeight()));
	        iconLabel.setOriginalIcon(icon);
	        iconLabel.setOriginalDimension(dimensionInicial);
	        iconLabel.setIcon(
        		new ImageIcon(
    				icon.getImage().getScaledInstance(
						dimensionInicial.width,
						dimensionInicial.height,
						Image.SCALE_SMOOTH
					)
				)
    		);
    	}
    }

    /** Panel que contiene los botones principales de las alerta. */
    void createMainButtonsPanel() {
        buttonsPanel = new JPanel(new GridBagLayout());

        // Restricciones para el panel de botones
        final GridBagConstraints consButtons = new GridBagConstraints();
        consButtons.fill = GridBagConstraints.NONE;
        consButtons.gridx = 0;
        consButtons.gridy = 0;
        consButtons.insets = new Insets(0, 10, 0, 10); // right padding
        consButtons.anchor = GridBagConstraints.CENTER;

        // OK button
        final JPanel okPanel = new JPanel();
        okButton = getButton(Messages.getString("PrincipalGUI.aceptar"), KeyEvent.VK_A); //$NON-NLS-1$
        okButton.addKeyListener(
    		new KeyListener() {

				@Override public void keyTyped(final KeyEvent arg0) { /* No necesario */ }
				@Override public void keyReleased(final KeyEvent arg0) { /* No necesario */ }

				@Override
				public void keyPressed(final KeyEvent ke) {
					if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
						getOkButton().doClick();
					}
					else if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
						getCancelButton().doClick();
					}
				}
			}
		);

        okPanel.add(okButton);
        buttonsPanel.add(okPanel, consButtons);

        okButton.addActionListener(this);

    }

    /** Muestra un diálogo de solicitud de contraseña.
     * @param componentParent Componente padre para la modalidad
     * @param modal true si se desea que el diálogo sea modal,
     *              false en caso contrario.
     * @param message Mensaje a mostrar.
     * @param mnemonic Atajo de teclado.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el icono del diálogo.
     * @return Contraseña introducida por el usuario.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN. */
    public static PasswordResult showInputPasswordDialog(final Component componentParent,
                                                 final boolean modal,
                                                 final String message,
                                                 final int mnemonic,
                                                 final String title,
                                                 final String iconPath,
                                                 final boolean allowUseCache,
                                                 final boolean defaultUseCache) {
    	final InputPasswordSmartcardDialog inputPasswordDialog = InputPasswordSmartcardDialog.getInstanceInputPasswordDialog(
    			componentParent,
    			modal,
    			message,
    			title,
    			iconPath,
    			allowUseCache,
    			defaultUseCache
    	);

        okButton.setEnabled(false);
        inputPasswordDialog.getRootPane().setDefaultButton(null);

        // Configuramos el componente de insercion de contrasenas
        inputPasswordDialog.component.addKeyListener(
    		new KeyListener() {

	            @Override
	            public void keyTyped(final KeyEvent arg0) { /* Vacio */}

	            @Override
	            public void keyReleased(final KeyEvent ke) {
	            	final int length = inputPasswordDialog.getComponent().getPasswordLength();
	                //Control de los botones aceptar/cancelar
	                if (length >= PIN_MIN_LENGTH && length <= PIN_MAX_LENGTH) {
	                    getOkButton().setEnabled(true);
	                	if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
	    					getOkButton().doClick();
	    				}
	                }
	                else {
	                    getOkButton().setEnabled(false);
	                }
	            }

	            @Override
	            public void keyPressed(final KeyEvent ke) {
					if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
						getCancelButton().doClick();
					}
	            }
	        }
		);
        inputPasswordDialog.component.addAncestorListener(new RequestFocusListener());
        AccesibilityUtils.remarcar(inputPasswordDialog.component);
        AccesibilityUtils.setContrastColor(inputPasswordDialog.component);
        AccesibilityUtils.setFontBold(inputPasswordDialog.component);
        inputPasswordDialog.component.getAccessibleContext().setAccessibleName(
    		message.replace(AccesiblityConstants.HTML_SALTO_LINEA, "") + "  ALT + " + mnemonic + ". " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		);

        // Etiqueta principal
        // Se relaciona la etiqueta con el componente
        inputPasswordDialog.infoLabel.setLabelFor(inputPasswordDialog.component);
        // Se asigna un atajo
        inputPasswordDialog.infoLabel.setDisplayedMnemonic(mnemonic);
        // Se muestra el atajo
        final String text = AccesibilityUtils.remarkMnemonic(inputPasswordDialog.infoLabel.getText(), mnemonic);
        inputPasswordDialog.infoLabel.setText(text);

        // Configuracion de accesibilidad de la casilla de cacheo
        if (inputPasswordDialog.useCacheCheckBox != null) {
        	inputPasswordDialog.useCacheCheckBox.addKeyListener(
    			new KeyListener() {

	    			@Override public void keyTyped(final KeyEvent arg0) { /* No necesario */ }
	    			@Override public void keyReleased(final KeyEvent arg0) { /* No necesario */ }

	    			@Override
	    			public void keyPressed(final KeyEvent ke) {
	    				if (ke.getKeyCode() == KeyEvent.VK_ENTER) {
	    					getOkButton().doClick();
	    				}
	    				else if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
	    					getCancelButton().doClick();
	    				}
	    			}
	    		}
			);

        	AccesibilityUtils.remarcar(inputPasswordDialog.useCacheCheckBox);
        	AccesibilityUtils.setContrastColor(inputPasswordDialog.useCacheCheckBox);
        	AccesibilityUtils.setFontBold(inputPasswordDialog.useCacheCheckBox);

            // Se muestra el atajo
        	inputPasswordDialog.useCacheCheckBox.setMnemonic('e');
            AccesibilityUtils.remarkMnemonic(inputPasswordDialog.useCacheCheckBox, 'e');
        }

        // Restricciones del panel de botones
        final GridBagConstraints cons = new GridBagConstraints();
        cons.insets = new Insets(0, 0, 0, 10); // right padding

        // Cancel button
        cancelButton = inputPasswordDialog.getButton(cancellText, KeyEvent.VK_C);
        final JPanel cancelPanel = new JPanel();
        cancelPanel.add(cancelButton);
        inputPasswordDialog.buttonsPanel.add(cancelPanel, cons);
        cancelButton.addActionListener(inputPasswordDialog);
        cancelButton.addKeyListener(
    		new KeyListener() {

				@Override public void keyTyped(final KeyEvent arg0) { /* No necesario */ }
				@Override public void keyReleased(final KeyEvent arg0) { /* No necesario */ }

				@Override
				public void keyPressed(final KeyEvent ke) {
					if (ke.getKeyCode() == KeyEvent.VK_ENTER || ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
						getCancelButton().doClick();
					}
				}
			}
		);

        inputPasswordDialog.infoLabel.setHorizontalAlignment(SwingConstants.LEFT); // Se centra el texto
        inputPasswordDialog.component.setVisible(true); // Se hace visible el campo de texto

        cancelButton.addActionListener(inputPasswordDialog);

        inputPasswordDialog.pack();
        inputPasswordDialog.setSize(inputPasswordDialog.getInitialWidth() + 1, inputPasswordDialog.getInitialHeight() + 1); // Hacemos un resize para forzar un repintado
        inputPasswordDialog.setVisible(true);

        // Control para saber si se ha pulsado el boton cancelar
        if (inputPasswordDialog.getAnswer() == JOptionPane.YES_OPTION) {
        	final char[] finalPin = inputPasswordDialog.component.getPassword();

        	// Por precaucion borramos el PIN y dejamos sus componentes relacionados
        	// listos para ser descartados
        	inputPasswordDialog.component.setText(""); //$NON-NLS-1$
        	inputPasswordDialog.component.setText(null);
        	inputPasswordDialog.component = null;
        	inputPasswordDialog.dispose();
        	System.gc();

        	// Recuperamos el valor del checkbox de usar cache si existe
        	boolean useCache = false;
        	if (inputPasswordDialog.useCacheCheckBox != null) {
        		useCache = inputPasswordDialog.useCacheCheckBox.isSelected();
        	}

            return new PasswordResult(finalPin, useCache);
        }
        throw new CancelledOperationException(
    		"La insercion de contrasena ha sido cancelada por el usuario" //$NON-NLS-1$
		);
    }

    /** Crea un botón.
     * Si el botón corresponde al de cancelar, se le asigna la tecla escape.
     * @param text Texto del botón.
     * @param mnemonic Atajo.
     * @return Botón creado. */
    private JButton getButton(final String text, final int mnemonic) {
        final JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        AccesibilityUtils.remarcar(button);
        AccesibilityUtils.setContrastColor(button);
        AccesibilityUtils.setFontBold(button);
        // Se comprueba si el boton es el de cancelar
        if (text.equalsIgnoreCase(cancellText)) {
            // Se asigna la tecla escape a dicho boton
            final String cancelKey = "cancel"; //$NON-NLS-1$
            getRootPane().getInputMap(
        		JComponent.WHEN_IN_FOCUSED_WINDOW
    		).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				cancelKey
			);
            getRootPane().getActionMap().put(cancelKey, new ButtonAbstractAction(getCancelButton()));
        }

        return button;
    }

    @Override
    public int getMinimumRelation() {
        return 7;
    }

    private int getAnswer() {
        return answer;
    }

    JSecurePasswordLabel getComponent() {
        return component;
    }

    /** Devuelve una instancia de CustomDialog.
     * @param componentParent Componente padre para la modalidad.
     * @param modal true si el diálogo debe ser modal,
     *              false en caso contrario.
     * @param message Mensaje del diálogo.
     * @param title Título del diálogo.
     * @param iconPath Ruta hacia el recurso de fichero de icono.
     * @param allowUseCache Hace mostrarse la casilla para seleccionar el cacheo del PIN.
     * @param defaultUseCache Valor por defecto de la opción de cacheo de PIN.
     * @return Instancia del diálogo. */
    static InputPasswordSmartcardDialog getInstanceInputPasswordDialog(final Component componentParent,
                                                                       final boolean modal,
                                                                       final String message,
                                                                       final String title,
                                                                       final String iconPath,
                                                                       final boolean allowUseCache,
                                                                       final boolean defaultUseCache) {
    	// Se chequea el tipo del componente padre
        return new InputPasswordSmartcardDialog(
    		componentParent,
    		modal,
    		message,
    		title,
    		iconPath,
	        allowUseCache,
	        defaultUseCache
        );
    }

    /** Acción correspondiente a los botones de las alertas. */
    @Override
    public void actionPerformed(final ActionEvent e) {

        if (e.getSource().equals(okButton)) {
            answer = JOptionPane.YES_OPTION;
        }
        else {
            answer = JOptionPane.CANCEL_OPTION;
        }
        setVisible(false);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy