Skip to content

Commit ca69565

Browse files
committed
Add databaseIsUnlocked method to KeepassProxyAccess, improve use of Optionals
1 parent c7199bd commit ca69565

File tree

7 files changed

+243
-113
lines changed

7 files changed

+243
-113
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ pom.xml.versionsBackup
2222
.idea/**/libraries/
2323
*.iml
2424

25+
/.idea/copilot/
26+
/.idea/inspectionProfiles/

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
[![License](https://img.shields.io/github/license/purejava/keepassxc-proxy-access.svg)](https://github.com/purejava/keepassxc-proxy-access/blob/master/LICENSE)
88
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?hosted_button_id=XVX9ZM7WE4ANL)
99

10-
A Java library to access KeePassXC via its build-in proxy. Requires KeePassXC 2.6.0 or newer.
10+
A Java library to access KeePassXC via its built-in proxy. Requires KeePassXC 2.6.0 or newer.
1111

1212
# Dependency
1313
Add `keepassxc-proxy-access` as a dependency to your project.

src/main/java/org/keepassxc/Connection.java

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public abstract class Connection implements AutoCloseable {
2727
private final PropertyChangeSupport support;
2828

2929
private TweetNaclFast.Box box;
30-
private Optional<Credentials> credentials;
30+
private Credentials credentials;
3131
private final String clientID;
3232
private static final int nonceLength = 24;
3333
private byte[] nonce;
@@ -87,7 +87,6 @@ public Connection() {
8787
new Random().nextBytes(array);
8888
clientID = b64encode(array);
8989
nonce = TweetNaclFast.randombytes(nonceLength);
90-
credentials = Optional.empty();
9190
support = new PropertyChangeSupport(this);
9291
scheduler = Executors.newSingleThreadScheduledExecutor();
9392
}
@@ -116,18 +115,18 @@ public void run() {
116115
if (!isSignal(response)) LOG.trace("Response added to queue: {}", response);
117116
queue.offer(response);
118117
errorCount = 0;
119-
} else {
120-
errorCount++;
121-
if (errorCount > MAX_ERROR_COUNT) {
122-
LOG.info("Too much errors - stopping MessagePublisher");
123-
doStop();
124-
try {
125-
terminateConnection();
126-
} catch (IOException e) {
127-
LOG.error(e.toString(), e.getCause());
128-
}
129-
reconnect();
118+
continue;
119+
}
120+
errorCount++;
121+
if (errorCount > MAX_ERROR_COUNT) {
122+
LOG.info("Too much errors - stopping MessagePublisher");
123+
doStop();
124+
try {
125+
terminateConnection();
126+
} catch (IOException e) {
127+
LOG.error(e.toString(), e.getCause());
130128
}
129+
reconnect();
131130
}
132131
}
133132
LOG.debug("MessagePublisher stopped");
@@ -270,8 +269,13 @@ private synchronized byte[] sendEncryptedMessage(Map<String, Object> msg) throws
270269
throw new IllegalStateException(NOT_CONNECTED);
271270
}
272271

273-
var publicKey = credentials.orElseThrow(() -> new IllegalStateException(KEYEXCHANGE_MISSING)).getServerPublicKey();
274-
var keyPair = credentials.orElseThrow(() -> new IllegalStateException(KEYEXCHANGE_MISSING)).getOwnKeypair();
272+
var publicKey = Optional.ofNullable(credentials).orElseThrow(
273+
() -> new IllegalStateException(KEYEXCHANGE_MISSING)
274+
).getServerPublicKey();
275+
276+
var keyPair = Optional.ofNullable(credentials).orElseThrow(
277+
() -> new IllegalStateException(KEYEXCHANGE_MISSING)
278+
).getOwnKeypair();
275279

276280
if (msg.containsKey("triggerUnlock") && msg.get("triggerUnlock").equals("true")) {
277281
msg.remove("triggerUnlock");
@@ -386,11 +390,11 @@ protected void changePublicKeys() throws IOException, KeepassProxyAccessExceptio
386390
var publicKey = b64decode(response.getString("publicKey").getBytes());
387391
box = new TweetNaclFast.Box(publicKey, keyPair.getSecretKey());
388392

389-
if (credentials.isEmpty()) {
390-
setCredentials(Optional.of(new Credentials()));
393+
if (Optional.ofNullable(credentials).isEmpty()) {
394+
setCredentials(null);
391395
}
392-
credentials.orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setOwnKeypair(keyPair);
393-
credentials.orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setServerPublicKey(publicKey);
396+
Optional.ofNullable(credentials).orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setOwnKeypair(keyPair);
397+
Optional.ofNullable(credentials).orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setServerPublicKey(publicKey);
394398
support.firePropertyChange("credentialsCreated", null, credentials);
395399

396400
}
@@ -404,7 +408,8 @@ protected void changePublicKeys() throws IOException, KeepassProxyAccessExceptio
404408
*/
405409
public void associate() throws IOException, KeepassProxyAccessException {
406410
var idKeyPair = TweetNaclFast.Box.keyPair();
407-
var keyPair = credentials.orElseThrow(() -> new IllegalStateException(KEYEXCHANGE_MISSING)).getOwnKeypair();
411+
var keyPair = Optional.ofNullable(credentials).orElseThrow(
412+
() -> new IllegalStateException(KEYEXCHANGE_MISSING)).getOwnKeypair();
408413

409414
// Send associate request
410415
var nonce = sendEncryptedMessage(Map.of(
@@ -427,8 +432,12 @@ public void associate() throws IOException, KeepassProxyAccessException {
427432
LOG.error(e.toString(), e.getCause());
428433
}
429434
assert response != null;
430-
credentials.orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setAssociateId(response.getString("id"));
431-
credentials.orElseThrow(() -> new IllegalStateException(MISSING_CLASS)).setIdKeyPublicKey(idKeyPair.getPublicKey());
435+
Optional.ofNullable(credentials).orElseThrow(
436+
() -> new IllegalStateException(MISSING_CLASS)).setAssociateId(response.getString("id"));
437+
438+
Optional.ofNullable(credentials).orElseThrow(
439+
() -> new IllegalStateException(MISSING_CLASS)).setIdKeyPublicKey(idKeyPair.getPublicKey());
440+
432441
support.firePropertyChange("associated", null, credentials);
433442
};
434443
scheduler.schedule(lookupResponse, RESPONSE_DELAY_MS, TimeUnit.MILLISECONDS);
@@ -438,16 +447,20 @@ public void associate() throws IOException, KeepassProxyAccessException {
438447
/**
439448
* Request for receiving the database hash (SHA256) of the current active KeePassXC database.
440449
*
441-
* @return The database hash of the current active KeePassXC database.
450+
* @return The database hash of the current active KeePassXC database. Empty if the database is locked.
442451
* @throws IOException Retrieving the hash failed due to technical reasons.
443452
* @throws KeepassProxyAccessException It was impossible to get the hash.
444453
*/
445-
public String getDatabasehash() throws IOException, KeepassProxyAccessException {
454+
public Optional<String> getDatabasehash() throws IOException, KeepassProxyAccessException {
446455
// Send get-databasehash request
447456
var nonce = sendEncryptedMessage(Map.of("action", Message.GET_DATABASE_HASH.action));
448457
var response = getEncryptedResponseAndDecrypt(Message.GET_DATABASE_HASH.action, nonce);
449458

450-
return response.getString("hash");
459+
if (response.has("hash") && !response.getString("hash").isBlank()) {
460+
return Optional.of(response.getString("hash"));
461+
}
462+
463+
return Optional.empty();
451464
}
452465

453466
/**
@@ -459,15 +472,15 @@ public String getDatabasehash() throws IOException, KeepassProxyAccessException
459472
* @throws IOException Retrieving the hash failed due to technical reasons.
460473
* @throws KeepassProxyAccessException It was impossible to get the hash.
461474
*/
462-
public String getDatabasehash(boolean triggerUnlock) throws IOException, KeepassProxyAccessException {
475+
public Optional<String> getDatabasehash(boolean triggerUnlock) throws IOException, KeepassProxyAccessException {
463476
// Send get-databasehash request with triggerUnlock, if needed
464477
var map = new HashMap<String, Object>(); // Map.of can't be used here, because we need a mutable object
465478
map.put("action", Message.GET_DATABASE_HASH.action);
466479
map.put("triggerUnlock", Boolean.toString(triggerUnlock));
467480
var nonce = sendEncryptedMessage(map);
468481
var response = getEncryptedResponseAndDecrypt(Message.GET_DATABASE_HASH.action, nonce);
469482

470-
return response.getString("hash");
483+
return Optional.ofNullable(response.getString("hash"));
471484
}
472485

473486
/**
@@ -832,15 +845,15 @@ private JSONArray checkKeysList(List<Map<String, String>> list) throws KeepassPr
832845
}
833846

834847
// Getters and Setters
835-
public String getIdKeyPairPublicKey() {
836-
return credentials.map(value -> b64encode(value.getIdKeyPublicKey())).orElse("");
848+
public Optional<String> getIdKeyPairPublicKey() {
849+
return Optional.ofNullable(credentials).map(value -> b64encode(value.getIdKeyPublicKey()));
837850
}
838851

839-
public String getAssociateId() {
840-
return credentials.map(Credentials::getAssociateId).orElse("");
852+
public Optional<String> getAssociateId() {
853+
return Optional.ofNullable(this.credentials).flatMap(Credentials::getAssociateId);
841854
}
842855

843-
public void setCredentials(Optional<Credentials> credentials) {
856+
public void setCredentials(Credentials credentials) {
844857
this.credentials = credentials;
845858
}
846859

src/main/java/org/purejava/Credentials.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@ public class Credentials implements Serializable {
1515

1616
private byte[] serverPublicKey;
1717

18-
private transient Optional<String> associateId;
18+
private transient String associateId;
1919
private String aID;
2020

21-
private transient Optional<byte[]> idKeyPublicKey;
21+
private transient byte[] idKeyPublicKey;
2222
private byte[] idKeyPub;
2323

24-
public Credentials() {
25-
this.associateId = Optional.empty();
26-
this.idKeyPublicKey = Optional.empty();
27-
}
28-
2924
@Serial
3025
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
3126
ois.defaultReadObject();
@@ -38,7 +33,7 @@ private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IO
3833
@Serial
3934
private void writeObject(ObjectOutputStream oos) throws IOException {
4035
secretKey = ownKeypair.getSecretKey();
41-
aID = getAssociateId();
36+
aID = getAssociateId().orElse(null);
4237
idKeyPub = getIdKeyPublicKey();
4338
oos.defaultWriteObject();
4439
}
@@ -60,27 +55,32 @@ public void setServerPublicKey(byte[] serverPublicKey) {
6055
this.serverPublicKey = serverPublicKey;
6156
}
6257

63-
public String getAssociateId() {
64-
return associateId.isEmpty() ? "" : associateId.get();
58+
public Optional<String> getAssociateId() {
59+
if (associateId == null || associateId.isEmpty())
60+
return Optional.empty();
61+
62+
return Optional.of(associateId);
6563
}
6664

6765
public void setAssociateId(String associateId) {
6866
if (associateId.isEmpty()) {
69-
this.associateId = Optional.empty();
70-
} else {
71-
this.associateId = Optional.of(associateId);
67+
this.associateId = null;
68+
return;
7269
}
70+
71+
this.associateId = associateId;
7372
}
7473

7574
public byte[] getIdKeyPublicKey() {
76-
return idKeyPublicKey.isEmpty() ? new byte[]{} : idKeyPublicKey.get();
75+
return Optional.ofNullable(idKeyPublicKey).isEmpty() ? new byte[]{} : idKeyPublicKey;
7776
}
7877

7978
public void setIdKeyPublicKey(byte[] idKeyPublicKey) {
8079
if (idKeyPublicKey.length == 0) {
81-
this.idKeyPublicKey = Optional.empty();
82-
} else {
83-
this.idKeyPublicKey = Optional.of(idKeyPublicKey);
80+
this.idKeyPublicKey = null;
81+
return;
8482
}
83+
84+
this.idKeyPublicKey = idKeyPublicKey;
8585
}
8686
}

0 commit comments

Comments
 (0)