A basic cross-platform GUI for the Master Password generation algorithm.
This commit is contained in:
parent
585268eb0f
commit
b74bc79699
79
MasterPassword/Java/masterpassword-gui/pom.xml
Normal file
79
MasterPassword/Java/masterpassword-gui/pom.xml
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!-- PROJECT METADATA -->
|
||||
<parent>
|
||||
<groupId>com.lyndir.lhunath.masterpassword</groupId>
|
||||
<artifactId>masterpassword</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Master Password GUI</name>
|
||||
<description>A GUI interface to the Master Password algorithm</description>
|
||||
|
||||
<groupId>com.lyndir.lhunath.masterpassword</groupId>
|
||||
<artifactId>masterpassword-gui</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!-- BUILD CONFIGURATION -->
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.lyndir.lhunath.masterpassword.GUI</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<!-- DEPENDENCY MANAGEMENT -->
|
||||
<dependencies>
|
||||
|
||||
<!-- PROJECT REFERENCES -->
|
||||
<dependency>
|
||||
<groupId>com.lyndir.lhunath.masterpassword</groupId>
|
||||
<artifactId>masterpassword-algorithm</artifactId>
|
||||
<version>GIT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2008, Maarten Billemont
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.lyndir.lhunath.masterpassword;
|
||||
|
||||
import com.apple.eawt.*;
|
||||
import java.io.IOException;
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* <p> <i>Jun 10, 2008</i> </p>
|
||||
*
|
||||
* @author mbillemo
|
||||
*/
|
||||
public class GUI implements UnlockFrame.SignInCallback {
|
||||
|
||||
private UnlockFrame unlockFrame = new UnlockFrame( this );
|
||||
private PasswordFrame passwordFrame;
|
||||
|
||||
public static void main(final String[] args)
|
||||
throws IOException {
|
||||
|
||||
new GUI().open();
|
||||
}
|
||||
|
||||
public GUI() {
|
||||
|
||||
try {
|
||||
getClass().getClassLoader().loadClass( "com.apple.eawt.Application" );
|
||||
Application application = Application.getApplication();
|
||||
application.addAppEventListener( new AppForegroundListener() {
|
||||
|
||||
@Override
|
||||
public void appMovedToBackground(AppEvent.AppForegroundEvent arg0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appRaisedToForeground(AppEvent.AppForegroundEvent arg0) {
|
||||
open();
|
||||
}
|
||||
} );
|
||||
application.addAppEventListener( new AppReOpenedListener() {
|
||||
@Override
|
||||
public void appReOpened(AppEvent.AppReOpenedEvent arg0) {
|
||||
open();
|
||||
}
|
||||
} );
|
||||
}
|
||||
catch (ClassNotFoundException | NoClassDefFoundError ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private void open() {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (passwordFrame == null) {
|
||||
unlockFrame.setVisible( true );
|
||||
} else {
|
||||
passwordFrame.setVisible( true );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean signedIn(final String userName, final String masterPassword) {
|
||||
final byte[] key = MasterPassword.keyForPassword( masterPassword, userName );
|
||||
passwordFrame = new PasswordFrame( new User( userName, key ) );
|
||||
|
||||
open();
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package com.lyndir.lhunath.masterpassword;
|
||||
|
||||
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;
|
||||
|
||||
import com.lyndir.lhunath.masterpassword.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import javax.swing.event.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-06-08
|
||||
*/
|
||||
public class PasswordFrame extends JFrame implements DocumentListener {
|
||||
|
||||
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final User user;
|
||||
private final JPanel root;
|
||||
private final JTextField siteNameField;
|
||||
private final JComboBox<MPElementType> siteTypeField;
|
||||
private final JSpinner siteCounterField;
|
||||
private final JLabel passwordLabel;
|
||||
|
||||
public PasswordFrame(User user)
|
||||
throws HeadlessException {
|
||||
super( "Master Password" );
|
||||
this.user = user;
|
||||
|
||||
JLabel label;
|
||||
|
||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
|
||||
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
|
||||
|
||||
// User
|
||||
add( new JLabel( strf( "Generating passwords for: %s", user.getName() ) ), BorderLayout.NORTH );
|
||||
|
||||
// Site
|
||||
JPanel sitePanel = new JPanel();
|
||||
sitePanel.setLayout( new BoxLayout( sitePanel, BoxLayout.PAGE_AXIS ) );
|
||||
sitePanel.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
|
||||
add( sitePanel, BorderLayout.CENTER );
|
||||
|
||||
// Site Name
|
||||
sitePanel.add( new JLabel( "Site Name:", JLabel.LEADING ) );
|
||||
sitePanel.add( siteNameField = new JTextField() {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||
}
|
||||
} );
|
||||
siteNameField.getDocument().addDocumentListener( this );
|
||||
siteNameField.addActionListener( new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
updatePassword( new PasswordCallback() {
|
||||
@Override
|
||||
public void passwordGenerated(final String siteName, final String sitePassword) {
|
||||
StringSelection clipboardContents = new StringSelection( sitePassword );
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents( clipboardContents, null );
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
dispose();
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
// Site Type & Counter
|
||||
sitePanel.add( Components.boxLayout( BoxLayout.LINE_AXIS, //
|
||||
siteTypeField = new JComboBox<>( MPElementType.values() ),
|
||||
siteCounterField = new JSpinner( new SpinnerNumberModel( 1, 1, Integer.MAX_VALUE, 1 ) ) {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( 50, getPreferredSize().height );
|
||||
}
|
||||
} ) );
|
||||
siteTypeField.setSelectedItem( MPElementType.GeneratedLong );
|
||||
siteCounterField.addChangeListener( new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(final ChangeEvent e) {
|
||||
updatePassword( null );
|
||||
}
|
||||
} );
|
||||
|
||||
// Password
|
||||
add( passwordLabel = new JLabel( " ", JLabel.CENTER ), BorderLayout.SOUTH );
|
||||
passwordLabel.setFont( passwordLabel.getFont().deriveFont( 40f ) );
|
||||
|
||||
pack();
|
||||
setMinimumSize( getSize() );
|
||||
setPreferredSize( new Dimension( 600, getSize().height ) );
|
||||
pack();
|
||||
|
||||
setLocationByPlatform( true );
|
||||
setLocationRelativeTo( null );
|
||||
}
|
||||
|
||||
private void updatePassword(final PasswordCallback callback) {
|
||||
final MPElementType siteType = (MPElementType) siteTypeField.getSelectedItem();
|
||||
final String siteName = siteNameField.getText();
|
||||
final int siteCounter = (Integer) siteCounterField.getValue();
|
||||
|
||||
executor.submit( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final String sitePassword = MasterPassword.generateContent( siteType, siteName, user.getKey(), siteCounter );
|
||||
if (callback != null) {
|
||||
callback.passwordGenerated( siteName, sitePassword );
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
passwordLabel.setText( sitePassword );
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(final DocumentEvent e) {
|
||||
updatePassword( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
updatePassword( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
updatePassword( null );
|
||||
}
|
||||
|
||||
interface PasswordCallback {
|
||||
|
||||
void passwordGenerated(String siteName, String sitePassword);
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package com.lyndir.lhunath.masterpassword;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import com.lyndir.lhunath.masterpassword.util.Components;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-06-08
|
||||
*/
|
||||
public class UnlockFrame extends JFrame implements DocumentListener {
|
||||
|
||||
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final SignInCallback signInCallback;
|
||||
private final JPanel root;
|
||||
private final JLabel avatarView;
|
||||
private final JTextField userNameField;
|
||||
private final JTextField masterPasswordField;
|
||||
private final JButton signInButton;
|
||||
|
||||
public UnlockFrame(final SignInCallback signInCallback)
|
||||
throws HeadlessException {
|
||||
super( "Unlock Master Password" );
|
||||
this.signInCallback = signInCallback;
|
||||
|
||||
JLabel label;
|
||||
|
||||
setDefaultCloseOperation( DISPOSE_ON_CLOSE );
|
||||
setContentPane( root = new JPanel( new BorderLayout( 20, 20 ) ) );
|
||||
root.setBorder( new EmptyBorder( 20, 20, 20, 20 ) );
|
||||
|
||||
JPanel userAndPassword = new JPanel();
|
||||
userAndPassword.setLayout( new BoxLayout( userAndPassword, BoxLayout.PAGE_AXIS ) );
|
||||
userAndPassword.setBorder( new CompoundBorder( new EtchedBorder( EtchedBorder.RAISED ), new EmptyBorder( 8, 8, 8, 8 ) ) );
|
||||
add( userAndPassword, BorderLayout.CENTER );
|
||||
|
||||
// Avatar
|
||||
userAndPassword.add( Box.createVerticalGlue() );
|
||||
userAndPassword.add( avatarView = new JLabel( new ImageIcon( Resources.getResource( "media/Avatars/avatar-0.png" ) ) ) {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE );
|
||||
}
|
||||
} );
|
||||
userAndPassword.add( Box.createVerticalGlue() );
|
||||
|
||||
// User Name
|
||||
userAndPassword.add( label = new JLabel( "User Name:" ) );
|
||||
label.setHorizontalAlignment( SwingConstants.CENTER );
|
||||
label.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||
userAndPassword.add( userNameField = new JTextField() {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||
}
|
||||
} );
|
||||
userNameField.getDocument().addDocumentListener( this );
|
||||
userNameField.addActionListener( new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
trySignIn();
|
||||
}
|
||||
} );
|
||||
|
||||
// Master Password
|
||||
userAndPassword.add( label = new JLabel( "Master Password:" ) );
|
||||
label.setHorizontalAlignment( SwingConstants.CENTER );
|
||||
label.setVerticalAlignment( SwingConstants.BOTTOM );
|
||||
userAndPassword.add( masterPasswordField = new JPasswordField() {
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return new Dimension( Integer.MAX_VALUE, getPreferredSize().height );
|
||||
}
|
||||
} );
|
||||
masterPasswordField.getDocument().addDocumentListener( this );
|
||||
masterPasswordField.addActionListener( new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
trySignIn();
|
||||
}
|
||||
} );
|
||||
|
||||
// Sign In
|
||||
add( Components.boxLayout( BoxLayout.LINE_AXIS, Box.createGlue(), signInButton = new JButton( "Sign In" ), Box.createGlue() ),
|
||||
BorderLayout.SOUTH );
|
||||
signInButton.addActionListener( new AbstractAction() {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
trySignIn();
|
||||
}
|
||||
} );
|
||||
|
||||
checkSignIn();
|
||||
|
||||
pack();
|
||||
setMinimumSize( getSize() );
|
||||
setPreferredSize( new Dimension( 300, 300 ) );
|
||||
pack();
|
||||
|
||||
setLocationByPlatform( true );
|
||||
setLocationRelativeTo( null );
|
||||
}
|
||||
|
||||
private boolean checkSignIn() {
|
||||
String userName = userNameField.getText();
|
||||
String masterPassword = masterPasswordField.getText();
|
||||
|
||||
boolean enabled = !userName.isEmpty() && !masterPassword.isEmpty();
|
||||
signInButton.setEnabled( enabled );
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private void trySignIn() {
|
||||
if (!checkSignIn())
|
||||
return;
|
||||
|
||||
final String userName = userNameField.getText();
|
||||
final String masterPassword = masterPasswordField.getText();
|
||||
|
||||
userNameField.setEnabled( false );
|
||||
masterPasswordField.setEnabled( false );
|
||||
signInButton.setEnabled( false );
|
||||
signInButton.setText( "Signing In..." );
|
||||
|
||||
executor.submit( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final boolean success = signInCallback.signedIn( userName, masterPassword );
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (success) {
|
||||
dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
userNameField.setEnabled( true );
|
||||
masterPasswordField.setEnabled( true );
|
||||
signInButton.setText( "Sign In" );
|
||||
checkSignIn();
|
||||
}
|
||||
} );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(final DocumentEvent e) {
|
||||
checkSignIn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(final DocumentEvent e) {
|
||||
checkSignIn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(final DocumentEvent e) {
|
||||
checkSignIn();
|
||||
}
|
||||
|
||||
interface SignInCallback {
|
||||
|
||||
boolean signedIn(String userName, String masterPassword);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.lyndir.lhunath.masterpassword;
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-06-08
|
||||
*/
|
||||
public class User {
|
||||
private final String name;
|
||||
private final byte[] key;
|
||||
|
||||
public User(final String name, final byte[] key) {
|
||||
this.name = name;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.lyndir.lhunath.masterpassword.util;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
|
||||
|
||||
/**
|
||||
* @author lhunath, 2014-06-08
|
||||
*/
|
||||
public abstract class Components {
|
||||
|
||||
public static JComponent boxLayout(int axis, Component... components) {
|
||||
JPanel container = new JPanel();
|
||||
container.setLayout( new BoxLayout( container, axis ) );
|
||||
for (Component component : components)
|
||||
container.add( component );
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<configuration scan="true">
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<Pattern>%-8relative %22c{0} [%-5level] %msg%n</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<logger name="com.lyndir" level="TRACE" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
1
MasterPassword/Java/masterpassword-gui/src/main/resources/media
Symbolic link
1
MasterPassword/Java/masterpassword-gui/src/main/resources/media
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../../Resources/Media/
|
@ -21,6 +21,7 @@
|
||||
<modules>
|
||||
<module>masterpassword-algorithm</module>
|
||||
<module>masterpassword-cli</module>
|
||||
<module>masterpassword-gui</module>
|
||||
<!--module>masterpassword-android</module-->
|
||||
</modules>
|
||||
|
||||
|
@ -1876,7 +1876,7 @@
|
||||
);
|
||||
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
|
||||
INFOPLIST_FILE = "MasterPassword-Info.plist";
|
||||
PROVISIONING_PROFILE = "9FCC90BB-24CA-40E2-8565-860F95CCC039";
|
||||
PROVISIONING_PROFILE = "9BA628A2-36F5-438B-9F08-A2527CECC9F8";
|
||||
SKIP_INSTALL = NO;
|
||||
WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}";
|
||||
};
|
||||
@ -1896,7 +1896,7 @@
|
||||
);
|
||||
GCC_PREFIX_HEADER = "MasterPassword-Prefix.pch";
|
||||
INFOPLIST_FILE = "MasterPassword-Info.plist";
|
||||
PROVISIONING_PROFILE = "9FCC90BB-24CA-40E2-8565-860F95CCC039";
|
||||
PROVISIONING_PROFILE = "9BA628A2-36F5-438B-9F08-A2527CECC9F8";
|
||||
SKIP_INSTALL = NO;
|
||||
WRAPPER_NAME = "Master Password.${WRAPPER_EXTENSION}";
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user