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

org.netbeans.api.keyring.Keyring Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.netbeans.api.keyring;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressUtils;
import org.netbeans.spi.keyring.KeyringProvider;
import org.openide.util.Cancellable;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import org.openide.util.RequestProcessor;

/**
 * Client class for working with stored keys (such as passwords).
 * 

The key identifier should be unique for the whole application, * so qualify it with any prefixes as needed. *

Since 1.10 it is allowed to call methods of this class from even * dispatch thread. */ public class Keyring { // throughput 1 is intentional private static final RequestProcessor KEYRING_ACCESS = new RequestProcessor(Keyring.class); private static final long SAFE_DELAY = 70; private Keyring() {} private static final Logger LOG = Logger.getLogger("org.netbeans.modules.keyring"); private static KeyringProvider PROVIDER; private static synchronized KeyringProvider provider() { if (PROVIDER == null) { for (KeyringProvider p : Lookup.getDefault().lookupAll(KeyringProvider.class)) { if (p.enabled()) { PROVIDER = p; break; } } if (PROVIDER == null) { PROVIDER = new DummyKeyringProvider(); } LOG.log(Level.FINE, "Using provider: {0}", PROVIDER); } return PROVIDER; } private static synchronized char[] readImpl(String key) { LOG.log(Level.FINEST, "reading: {0}", key); return provider().read(key); } /** * Reads a key from the ring. *

* This method can be called from any thread. * All the changes done by previous calls to {@link #delete(java.lang.String)} * or {@link #save(java.lang.String, char[], java.lang.String)} methods * are guaranteed to be visible by subsequent calls to this method. * * @param key the identifier of the key * @return its value if found (you may null out its elements), else null if not present */ @NbBundle.Messages("MSG_KeyringAccess=Requesting keyring access") @CheckForNull public static char[] read(@NonNull final String key) { Parameters.notNull("key", key); try { final Future result = KEYRING_ACCESS.submit(new Callable() { @Override public char[] call() throws Exception { return Keyring.readImpl(key); } }); if (SwingUtilities.isEventDispatchThread()) { if (!result.isDone()) { try { // lets wait in awt to avoid flashing dialogs return result.get(SAFE_DELAY, TimeUnit.MILLISECONDS); } catch (TimeoutException ex) { // show progress dialog return ProgressUtils.showProgressDialogAndRun( new ProgressRunnable(result), Bundle.MSG_KeyringAccess(), false); } } } return result.get(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch (ExecutionException ex) { LOG.log(Level.INFO, null, ex); } return null; } private static synchronized void saveImpl(String key, char[] password, String description) { LOG.log(Level.FINEST, "saving: {0}", key); provider().save(key, password, description); Arrays.fill(password, (char) 0); } /** * Saves a key to the ring. * If it could not be saved, does nothing. * If the key already existed, overwrites the password. *

* This method can be called from any thread. * The changes done by multiple calls to {@link #delete(java.lang.String)} * or {@link #save(java.lang.String, char[], java.lang.String)} methods * are guaranteed to be processed in order in which they were called. * * @param key a key identifier * @param password the password or other sensitive information associated with the key * (its contents will be nulled out by end of call) * @param description a user-visible description of the key (may be null) */ public static void save(@NonNull final String key, @NonNull final char[] password, @NullAllowed final String description) { Parameters.notNull("key", key); Parameters.notNull("password", password); KEYRING_ACCESS.post(new Runnable() { @Override public void run() { Keyring.saveImpl(key, password, description); } }); } private static synchronized void deleteImpl(String key) { LOG.log(Level.FINEST, "deleting: {0}", key); provider().delete(key); } /** * Deletes a key from the ring. * If the key was not in the ring to begin with, does nothing. *

* This method can be called from any thread. * The changes done by multiple calls to {@link #delete(java.lang.String)} * or {@link #save(java.lang.String, char[], java.lang.String)} methods * are guaranteed to be processed in order in which they were called. * * @param key a key identifier */ public static void delete(@NonNull final String key) { Parameters.notNull("key", key); KEYRING_ACCESS.post(new Runnable() { @Override public void run() { Keyring.deleteImpl(key); } }); } private static class DummyKeyringProvider implements KeyringProvider { public @Override boolean enabled() { return true; } // prefer byte[] to make passwords less readable in heap dumps: private final Map passwords = new HashMap(); public @Override char[] read(String key) { byte[] pwd = passwords.get(key); return pwd != null ? bytes2Chars(pwd) : null; } public @Override void save(String key, char[] password, String description) { passwords.put(key, chars2Bytes(password)); } public @Override void delete(String key) { passwords.remove(key); } } // XXX copied from org.netbeans.modules.keyring.impl.Utils: private static byte[] chars2Bytes(char[] chars) { byte[] bytes = new byte[chars.length * 2]; for (int i = 0; i < chars.length; i++) { bytes[i * 2] = (byte) (chars[i] / 256); bytes[i * 2 + 1] = (byte) (chars[i] % 256); } return bytes; } private static char[] bytes2Chars(byte[] bytes) { char[] result = new char[bytes.length / 2]; for (int i = 0; i < result.length; i++) { result[i] = (char) (((int) bytes[i * 2]) * 256 + (int) bytes[i * 2 + 1]); } return result; } private static class ProgressRunnable implements org.netbeans.api.progress.ProgressRunnable, Cancellable { private final Future task; public ProgressRunnable(Future task) { this.task = task; } @Override public T run(ProgressHandle handle) { try { return task.get(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } catch (ExecutionException ex) { LOG.log(Level.INFO, null, ex); } return null; } @Override public boolean cancel() { return task.cancel(true); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy