Implement CrackerFactory

The idea with this was to get away from the singleton pattern, since it
makes tests difficult and fragile. Dependency injection would have been
an option too, but for simplicity's sake, I chose the factory method.
There were also some strange errors on Windows that I've corrected,
so the program should run on Windows now as well.
This commit is contained in:
William Brawner 2018-04-18 20:56:37 -05:00
parent 81db6720c9
commit 599e155490
8 changed files with 63 additions and 63 deletions

View file

@ -1,4 +1,5 @@
#Sun Apr 15 14:52:35 CDT 2018
# suppress inspection "UnusedProperty" for whole file
#Wed Apr 18 08:19:13 CDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View file

@ -16,6 +16,7 @@
package com.wbrawner.keecrack.cli;
import com.wbrawner.keecrack.lib.Code;
import com.wbrawner.keecrack.lib.CrackerFactory;
import com.wbrawner.keecrack.lib.KeeCrack;
import com.wbrawner.keecrack.lib.view.CrackingView;
import net.sourceforge.argparse4j.ArgumentParsers;
@ -71,7 +72,7 @@ public class Main {
Namespace res = parser.parseArgs(args);
isVerbose = res.getBoolean("verbose");
isIncremental = res.getBoolean("incremental");
KeeCrack keeCrack = KeeCrack.getInstance();
KeeCrack keeCrack = new CrackerFactory().getCracker(true);
keeCrack.setCrackingView(new CLICrackingView());
String databasePath = res.getString("database");

View file

@ -16,6 +16,7 @@
package com.wbrawner.keecrack.gui;
import com.wbrawner.keecrack.lib.Code;
import com.wbrawner.keecrack.lib.CrackerFactory;
import com.wbrawner.keecrack.lib.KeeCrack;
import com.wbrawner.keecrack.lib.view.CrackingView;
import javafx.application.Platform;
@ -41,10 +42,11 @@ public class CrackingController implements Initializable, CrackingView {
private Label timeElapsed;
private Stage stage;
private KeeCrack keeCrack;
@Override
public void initialize(URL location, ResourceBundle resources) {
final KeeCrack keeCrack = KeeCrack.getInstance();
keeCrack = new CrackerFactory().getCracker(true);
keeCrack.setCrackingView(this);
new Thread(keeCrack::attack).start();
}
@ -88,7 +90,6 @@ public class CrackingController implements Initializable, CrackingView {
}
private void onClose() {
KeeCrack keeCrack = KeeCrack.getInstance();
keeCrack.abort();
keeCrack.setCrackingView(null);
}

View file

@ -16,6 +16,7 @@
package com.wbrawner.keecrack.gui;
import com.wbrawner.keecrack.lib.Code;
import com.wbrawner.keecrack.lib.CrackerFactory;
import com.wbrawner.keecrack.lib.KeeCrack;
import com.wbrawner.keecrack.lib.view.FormView;
import javafx.fxml.FXML;
@ -48,7 +49,6 @@ public class MainController implements Initializable, FormView {
private TextField wordlist;
@FXML
private Button crackButton;
@FXML
private ToggleGroup wordlistType;
@FXML
private RadioButton wordlistFile;
@ -57,10 +57,10 @@ public class MainController implements Initializable, FormView {
@Override
public void initialize(URL location, ResourceBundle resources) {
keeCrack = KeeCrack.getInstance();
keeCrack = new CrackerFactory().getCracker(true);
keeCrack.setFormView(this);
database.setOnMouseClicked(event -> {
if (KeeCrack.getInstance().isCracking()) {
if (keeCrack.isCracking()) {
return;
}
@ -69,7 +69,7 @@ public class MainController implements Initializable, FormView {
});
key.setOnMouseClicked(event -> {
if (KeeCrack.getInstance().isCracking()) {
if (keeCrack.isCracking()) {
return;
}
@ -81,7 +81,7 @@ public class MainController implements Initializable, FormView {
crackButton.setOnMouseClicked(event -> {
try {
if (KeeCrack.getInstance().isCracking()) {
if (keeCrack.isCracking()) {
return;
}
Stage stage = new Stage();
@ -107,13 +107,16 @@ public class MainController implements Initializable, FormView {
e.printStackTrace();
}
});
if (wordlistType == null)
wordlistType = new ToggleGroup();
wordlistFile.setToggleGroup(wordlistType);
wordlistPattern.setToggleGroup(wordlistType);
wordlistType.selectedToggleProperty().addListener((observableValue, oldToggle, newToggle) ->
updateWordListHandler());
if (keeCrack.getDatabaseFile() != null)
onDatabaseFileSet(keeCrack.getDatabaseFile().getName());
if (keeCrack.getKeyFile() != null)
onKeyFileSet(keeCrack.getKeyFile().getName());
if (keeCrack.getWordListName() != null)
onWordListSet(keeCrack.getWordListName());
}
@ -127,7 +130,7 @@ public class MainController implements Initializable, FormView {
new FileChooser.ExtensionFilter(description, extensions)
);
Stage stage = new Stage();
stage.setOnHidden(e -> KeeCrack.getInstance().setFormView(null));
stage.setOnHidden(e -> keeCrack.setFormView(null));
return fileChooser.showOpenDialog(stage);
}
@ -137,7 +140,7 @@ public class MainController implements Initializable, FormView {
wordlist.setEditable(false);
wordlist.setCursor(Cursor.HAND);
wordlist.setOnMouseClicked(event -> {
if (KeeCrack.getInstance().isCracking()) {
if (keeCrack.isCracking()) {
return;
}
File wordlistFile = getFile("Text Files", "txt");

View file

@ -69,12 +69,8 @@
<HBox.margin>
<Insets left="20.0"/>
</HBox.margin>
<toggleGroup>
<ToggleGroup fx:id="wordlistType"/>
</toggleGroup>
</RadioButton>
<RadioButton fx:id="wordlistFile" selected="true" text="From File"
toggleGroup="wordlistType">
<RadioButton fx:id="wordlistFile" selected="true" text="From File">
<HBox.margin>
<Insets left="20.0"/>
</HBox.margin>

View file

@ -0,0 +1,36 @@
/*
* Copyright 2018 William Brawner
*
* 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.wbrawner.keecrack.lib;
/**
* Class responsible for acquiring a {@link KeeCrack} instance
*/
public class CrackerFactory {
private static final Object singletonLock = new Object();
private static KeeCrack singleton;
public KeeCrack getCracker(boolean asSingleton) {
if (asSingleton) {
synchronized (singletonLock) {
if (singleton == null) {
singleton = new KeeCrack();
}
return singleton;
}
}
return new KeeCrack();
}
}

View file

@ -31,11 +31,11 @@ import java.util.concurrent.atomic.AtomicReference;
/**
* The main class responsible for handling the brute forcing of the KeePass database. You do not contruct the
* KeeCrack instance directly, but rather call {@link #getInstance()}. To begin, you should set the form view and
* cracking view with {@link #setFormView(FormView)} and {@link #setCrackingView(CrackingView)} respectively. These
* views will be responsible for displaying information like error messages and status updates. The cracking will
* work without these, though it's highly recommended to set them prior to beginning. The database and wordlist must
* be set, while the key file is also optional. If either of the required parameters are missing, the
* KeeCrack instance directly, but rather call {@link CrackerFactory#getCracker(boolean)}. To begin, you should set the
* form view and cracking view with {@link #setFormView(FormView)} and {@link #setCrackingView(CrackingView)}
* respectively. These views will be responsible for displaying information like error messages and status updates.
* The cracking will work without these, though it's highly recommended to set them prior to beginning. The database
* and wordlist must be set, while the key file is also optional. If either of the required parameters are missing, the
* {@link #attack()} operation will abort, sending either {@link Code#ERROR_INVALID_DATABASE_FILE} or
* {@link Code#ERROR_INVALID_WORD_LIST}, respectively. The word list can either be a pattern, in which case
* incremental guessing will take place, or a file, in which case each line of the file will be considered a password
@ -49,7 +49,6 @@ import java.util.concurrent.atomic.AtomicReference;
* first parameter will be null.
*/
public class KeeCrack {
private static final AtomicReference<KeeCrack> singleton = new AtomicReference<>(null);
private final Object keyFileLock = new Object();
private final AtomicBoolean isCracking = new AtomicBoolean(false);
/**
@ -65,27 +64,10 @@ public class KeeCrack {
private WordList wordList;
private int guessCount = 0;
private KeeCrack() {
}
public static KeeCrack getInstance() {
if (singleton.get() == null) {
singleton.set(new KeeCrack());
}
return singleton.get();
}
/**
* Call this to reset the state of the KeeCrack instance. Note that you will need to set the views again after
* calling this
* To get an instance of the class, use the {@link CrackerFactory}
*/
public void reset() {
setDatabaseFile(null);
setKeyFile(null);
setWordListFile(null);
setCrackingView(null);
setFormView(null);
KeeCrack() {
}
public void abort() {

View file

@ -21,13 +21,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.time.Duration;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ -41,30 +37,14 @@ public class KeeCrackTest {
public void setUp() {
mockCrackingView = mock(CrackingView.class);
mockFormView = mock(FormView.class);
keeCrack = KeeCrack.getInstance();
keeCrack = new CrackerFactory().getCracker(false);
}
@After
public void tearDown() {
keeCrack.reset();
Utils.rmdir(Utils.getTmpDir());
}
@Test
public void resetTest() {
keeCrack.setDatabaseFile(new File("Database"));
keeCrack.setKeyFile(new File("Keyfile"));
keeCrack.setWordListPattern("Some pattern");
assertNotNull(keeCrack.getDatabaseFile());
assertNotNull(keeCrack.getKeyFile());
assertNotNull(keeCrack.getWordList());
keeCrack.reset();
assertNull(keeCrack.getDatabaseFile());
assertNull(keeCrack.getKeyFile());
assertNull(keeCrack.getWordList());
assertFalse(keeCrack.isCracking());
}
@Test
public void abortTest() throws IOException {
keeCrack.setDatabaseFile(Utils.getDatabase("123456.kdbx"));