diff --git a/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/FreedesktopKeyringBackend.java b/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/FreedesktopKeyringBackend.java index d205391..051d36f 100755 --- a/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/FreedesktopKeyringBackend.java +++ b/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/FreedesktopKeyringBackend.java @@ -32,6 +32,8 @@ import java.util.List; import java.util.Map; +import org.freedesktop.secret.simple.SimpleCollection; + import com.github.javakeyring.BackendNotSupportedException; import com.github.javakeyring.PasswordAccessException; import com.github.javakeyring.internal.KeyringBackend; diff --git a/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/SimpleCollection.java b/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/SimpleCollection.java deleted file mode 100755 index 8efebb8..0000000 --- a/java-keyring/src/main/java/com/github/javakeyring/internal/freedesktop/SimpleCollection.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright © 2019, Java Keyring - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.github.javakeyring.internal.freedesktop; - -import java.io.IOException; -import java.security.AccessControlException; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; - -import org.freedesktop.dbus.DBusPath; -import org.freedesktop.dbus.ObjectPath; -import org.freedesktop.dbus.connections.impl.DBusConnection; -import org.freedesktop.dbus.exceptions.DBusException; -import org.freedesktop.dbus.types.Variant; -import org.freedesktop.secret.Collection; -import org.freedesktop.secret.Item; -import org.freedesktop.secret.Pair; -import org.freedesktop.secret.Prompt; -import org.freedesktop.secret.Secret; -import org.freedesktop.secret.Service; -import org.freedesktop.secret.Session; -import org.freedesktop.secret.Static; -import org.freedesktop.secret.TransportEncryption; -import org.freedesktop.secret.interfaces.Prompt.Completed; -import org.gnome.keyring.InternalUnsupportedGuiltRiddenInterface; - -/** - * A clone of {@link org.freedesktop.secret.simple.SimpleCollection}, with the prompting capability removed. - */ -public final class SimpleCollection implements AutoCloseable { - - private TransportEncryption encryption = null; - private Service service = null; - private Session session = null; - private Prompt prompt = null; - private InternalUnsupportedGuiltRiddenInterface withoutPrompt = null; - - private Collection collection; - private Secret encrypted = null; - - /** - * The default collection. - * @throws IOException if dbus collection can not be accessed. - */ - public SimpleCollection() throws IOException { - init(); - ObjectPath path = Static.Convert.toObjectPath(Static.ObjectPaths.DEFAULT_COLLECTION); - this.collection = new Collection(path, service); - unlock(); - } - - /* - * A user specified collection. - * - * @param label The displayable label of the collection - *

- * NOTE: The 'label' of a collection may differ from the 'id' of - * a collection. The 'id' is assigned by the Secret Service and - * used in the DBus object path of a collection or item. - *

- *

- * A SimpleCollection can't handle collections with the same - * label, but different ids correctly. - *

- * @param password Password of the collection - * @throws IOException if dbus collection can not be accessed. - * - /* - @SuppressWarnings("rawtypes") - public SimpleCollection(String label, CharSequence password) throws IOException { - init(); - - if (exists(label)) { - ObjectPath path = getCollectionPath(label); - this.collection = new Collection(path, service); - } else { - DBusPath path = null; - Map properties = Collection.createProperties(label); - - if (password == null) { - Pair response = service.createCollection(properties); - if (!"/".equals(response.a.getPath())) { - path = response.a; - } - performPrompt(response.b); - } else { - try { - encrypted = encryption.encrypt(password); - path = withoutPrompt.createWithMasterPassword(properties, encrypted); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException - | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex) { - // log.error(e.toString(), e.getCause()); - } - } - - if (path == null) { - try { - Thread.sleep(100L); - } catch (InterruptedException ex) { - // log.error(e.toString(), e.getCause()); - } - Service.CollectionCreated cc = service.getSignalHandler().getLastHandledSignal(Service.CollectionCreated.class); - path = cc.collection; - } - - this.collection = new Collection(path, service); - } - - unlock(); - } - */ - - private void init() throws IOException { - try { - encryption = new TransportEncryption(); - encryption.initialize(); - encryption.openSession(); - encryption.generateSessionKey(); - service = encryption.getService(); - session = service.getSession(); - prompt = new Prompt(service); - withoutPrompt = new InternalUnsupportedGuiltRiddenInterface(service); - } catch (DBusException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidKeySpecException - | InvalidKeyException ex) { - // log.error(e.toString(), e.getCause()); - throw new IOException(ex.toString(), ex.getCause()); - } - } - - /* - private Map getLabels() { - List collections = service.getCollections(); - - Map labels = new HashMap<>(); - for (ObjectPath path : collections) { - Collection collection = new Collection(path, service, null); - labels.put(path, collection.getLabel()); - } - - return labels; - } - - private boolean exists(String label) { - Map labels = getLabels(); - return labels.values().contains(label); - } - - private ObjectPath getCollectionPath(String label) { - Map labels = getLabels(); - - ObjectPath path = null; - for (Map.Entry entry : labels.entrySet()) { - ObjectPath currentPath = entry.getKey(); - String currentLabel = entry.getValue(); - if (label.equals(currentLabel)) { - path = currentPath; - break; - } - } - return path; - } - */ - - /* - private final boolean isDefault() { - List defaults = Arrays.asList(null, "login", "session", "default"); - return defaults.contains(collection.getId()); - } - */ - - private void performPrompt(ObjectPath path) { - if (!("/".equals(path.getPath()))) { - prompt.await(path); - } - } - - private void unlock() { - if (collection.isLocked()) { - if (encrypted == null) { - Pair, ObjectPath> response = service.unlock(Arrays.asList(collection.getPath())); - performPrompt(response.b); - } else { - withoutPrompt.unlockWithMasterPassword(collection.getPath(), encrypted); - } - } - } - - private Item getItem(String path) { - return new Item(Static.Convert.toObjectPath(path), service); - } - - /* - private void getUserPermission() throws AccessControlException { - if (isDefault() && enableUserPrompts) { - List lockable = Arrays.asList(collection.getPath()); - service.lock(lockable); - try { - Thread.sleep(250L); - } catch (InterruptedException ex) { - //fail silently? - } - Pair, ObjectPath> response = service.unlock(lockable); - performPrompt(response.b); - if (collection.isLocked()) { - throw new AccessControlException( - "One may not read all passwords from a default collection without permission."); - } - } - } - */ - - /** - * Clears the private key of the transport encryption and the passphrase of the - * collection. - */ - public void clear() { - if (encryption != null) { - encryption.clear(); - } - if (encrypted != null) { - encrypted.clear(); - } - } - - @Override - public void close() throws Exception { - clear(); - if (service != null) { - DBusConnection connection = service.getConnection(); - if (connection != null) { - service.getConnection().close(); - } - connection.close(); - } - } - - /** - * Delete this collection. - * @throws AccessControlException if user is prompted and refuses. - * - */ - /* - public void delete() throws AccessControlException { - if (!isDefault()) { - ObjectPath promptPath = collection.delete(); - performPrompt(promptPath); - } else { - throw new AccessControlException("Default collections may not be deleted with the simple API."); - } - } - */ - - /** - * Creates an item with the provided properties in this collection. - * - * @param label The displayable label of the new item - * @param password The password of the new item - * @param attributes The attributes of the new item - * - * @return DBus object path - * - * @throws IllegalArgumentException if inputs are null - */ - @SuppressWarnings("rawtypes") - public String createItem(String label, CharSequence password, Map attributes) - throws IllegalArgumentException { - - if (password == null) { - throw new IllegalArgumentException("The password may not be null."); - } - if (label == null) { - throw new IllegalArgumentException("The label of the password may not be null."); - } - - unlock(); - - DBusPath item = null; - final Map properties = Item.createProperties(label, attributes); - try (final Secret secret = encryption.encrypt(password)) { - Pair response = collection.createItem(properties, secret, false); - item = response.a; - if ("/".equals(item.getPath())) { - Completed completed = prompt.await(response.b); - if (!completed.dismissed) { - Collection.ItemCreated ic = collection.getSignalHandler().getLastHandledSignal(Collection.ItemCreated.class); - item = ic.item; - } - } - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException - | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex) { - // log.error(e.toString(), e.getCause()); - } - - return item.getPath(); - } - - /** - * Creates an item with the provided properties in this collection. - * - * @param label The displayable label of the new item - * @param password The password of the new item - * - * @return DBus object path - * - * @throws IllegalArgumentException if inputs are null - */ - public String createItem(String label, CharSequence password) throws IllegalArgumentException { - return createItem(label, password, null); - } - - /** - * Updates an item with the provided properties. - * - * @param objectPath The DBus object path of the item - * @param label The displayable label of the new item - * @param password The password of the new item - * @param attributes The attributes of the new item - * - * @throws IllegalArgumentException if inputs are null - */ - public void updateItem(String objectPath, String label, CharSequence password, Map attributes) - throws IllegalArgumentException { - - if (objectPath == null) { - throw new IllegalArgumentException("The object path of the item may not be null."); - } - - unlock(); - - Item item = getItem(objectPath); - - if (label != null) { - item.setLabel(label); - } - - if (attributes != null) { - item.setAttributes(attributes); - } - - if (password != null) { - try (Secret secret = encryption.encrypt(password)) { - item.setSecret(secret); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException - | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex) { - // log.error(e.toString(), e.getCause()); - } - } - } - - /** - * Get the displayable label of an item. - * - * @param objectPath The DBus object path of the item - * - * @return label - */ - /* - public String getLabel(String objectPath) { - unlock(); - return getItem(objectPath).getLabel(); - } - */ - - /** - * Get the user specified attributes of an item. - *

- * NOTE: - * The attributes can contain an additional 'xdg:schema' key-value pair. - *

- * - * @param objectPath The DBus object path of the item - * - * @return item attributes - */ - /* - public Map getAttributes(String objectPath) { - unlock(); - return getItem(objectPath).getAttributes(); - } - */ - - /** - * Get the object paths of items with given attributes. - * - * @param attributes The attributes of the secret - * - * @return object paths - */ - public List getItems(Map attributes) { - unlock(); - - List objects = collection.searchItems(attributes); - - if (objects != null && !objects.isEmpty()) { - return Static.Convert.toStrings(objects); - } else { - return null; - } - } - - /** - * Get the secret of the item. - * - * @param objectPath The DBus object path of the item - * - * @return plain chars - */ - public char[] getSecret(String objectPath) { - unlock(); - - final Item item = getItem(objectPath); - - char[] decrypted = null; - try (final Secret secret = item.getSecret(session.getPath())) { - decrypted = encryption.decrypt(secret); - } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException - | InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex) { - // log.error(e.toString(), e.getCause()); - } - return decrypted; - } - - /** - * Get the secrets from this collection. - * - * @return Mapping of DBus object paths and plain chars - * @throws AccessControlException if user is prompted and refuses. - */ - /* - public Map getSecrets() throws AccessControlException { - getUserPermission(); - - List items = collection.getItems(); - - Map passwords = new HashMap<>(); - for (ObjectPath item : items) { - String path = item.getPath(); - passwords.put(path, getSecret(path)); - } - - return passwords; - } - */ - - /** - * Delete an item from this collection. - * - * @param objectPath The DBus object path of the item - * @throws AccessControlException if user is prompted and refuses. - */ - public void deleteItem(String objectPath) throws AccessControlException { - //getUserPermission(); - - Item item = getItem(objectPath); - ObjectPath promptPath = item.delete(); - performPrompt(promptPath); - } - - /** - * Delete specified items from this collection. - * - * @param objectPaths The DBus object paths of the items - * @throws AccessControlException if user is prompted and refuses. - */ - /* - public void deleteItems(List objectPaths) throws AccessControlException { - for (String item : objectPaths) { - deleteItem(item); - } - } - */ -} diff --git a/java-keyring/src/main/java/com/github/javakeyring/internal/kde/KWalletBackend.java b/java-keyring/src/main/java/com/github/javakeyring/internal/kde/KWalletBackend.java index c0ec770..31236a2 100644 --- a/java-keyring/src/main/java/com/github/javakeyring/internal/kde/KWalletBackend.java +++ b/java-keyring/src/main/java/com/github/javakeyring/internal/kde/KWalletBackend.java @@ -32,6 +32,7 @@ import org.freedesktop.dbus.annotations.DBusInterfaceName; import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; import org.freedesktop.dbus.interfaces.DBusInterface; import java.io.IOException; @@ -45,7 +46,7 @@ public class KWalletBackend implements KeyringBackend { public KWalletBackend() throws BackendNotSupportedException { try { - connection = DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION); + connection = DBusConnectionBuilder.forSessionBus().build(); wallet = connection.getRemoteObject("org.kde.kwalletd5", "/modules/kwalletd5", KWallet.class, true); wallet.localWallet(); //attempt connection to wallet } catch (Exception e) { diff --git a/java-keyring/src/test/java/com/github/javakeyring/freedesktop/FreedesktopKeyringBackedTest.java b/java-keyring/src/test/java/com/github/javakeyring/freedesktop/FreedesktopKeyringBackedTest.java index 029c78c..2b163e2 100755 --- a/java-keyring/src/test/java/com/github/javakeyring/freedesktop/FreedesktopKeyringBackedTest.java +++ b/java-keyring/src/test/java/com/github/javakeyring/freedesktop/FreedesktopKeyringBackedTest.java @@ -67,7 +67,7 @@ public void testSetup() throws Exception { */ @Test public void testPasswordFlow() throws Exception { - assumeTrue(Platform.isLinux() && Keyring.create().getKeyringStorageType() == KeyringStorageType.GNOME_KEYRING); + //assumeTrue(Platform.isLinux() && Keyring.create().getKeyringStorageType() == KeyringStorageType.GNOME_KEYRING); FreedesktopKeyringBackend backend = new FreedesktopKeyringBackend(); catchThrowable(() -> backend.deletePassword(SERVICE, ACCOUNT)); checkExistenceOfPasswordEntry(backend);