com.intellij.ide.passwordSafe.impl.providers.masterKey.PasswordDatabase.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.ide.passwordSafe.impl.providers.masterKey.PasswordDatabase.java

Source

/*
 * Copyright 2000-2012 JetBrains s.r.o.
 *
 * 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.intellij.ide.passwordSafe.impl.providers.masterKey;

import com.intellij.ide.passwordSafe.impl.providers.ByteArrayWrapper;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.diagnostic.Logger;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * The password database. The internal component for {@link MasterKeyPasswordSafe}.
 */
@State(name = "PasswordDatabase", storages = { @Storage(file = StoragePathMacros.APP_CONFIG + "/security.xml") })
public class PasswordDatabase implements PersistentStateComponent<PasswordDatabase.State> {
    /**
     * The name of logger
     */
    private final static Logger LOG = Logger.getInstance(PasswordDatabase.class.getName());
    /**
     * The password database
     */
    private transient final Map<ByteArrayWrapper, byte[]> myDatabase = new HashMap<ByteArrayWrapper, byte[]>();
    /**
     * OS-specific information about master password
     */
    private transient byte[] myMasterPasswordInfo;

    /**
     * Clear the password database
     */
    public void clear() {
        synchronized (myDatabase) {
            myDatabase.clear();
        }
    }

    /**
     * @return true if the database is empty
     */
    public boolean isEmpty() {
        synchronized (myDatabase) {
            return myDatabase.isEmpty();
        }
    }

    /**
     * Put password in the database
     *
     * @param key   the encrypted key
     * @param value the encrypted value
     */
    public void put(byte[] key, byte[] value) {
        synchronized (myDatabase) {
            myDatabase.put(new ByteArrayWrapper(key), value);
        }
    }

    /**
     * Get all entries in the database
     *
     * @param copy the copy to use
     */
    public void copyTo(Map<ByteArrayWrapper, byte[]> copy) {
        synchronized (myDatabase) {
            copy.putAll(myDatabase);
        }
    }

    /**
     * Put all entries to the database
     *
     * @param copy the copy to use
     */
    public void putAll(Map<ByteArrayWrapper, byte[]> copy) {
        synchronized (myDatabase) {
            myDatabase.putAll(copy);
        }
    }

    /**
     * Get password from the database
     *
     * @param key the encrypted key
     * @return the encrypted value or null
     */
    public byte[] get(byte[] key) {
        synchronized (myDatabase) {
            return myDatabase.get(new ByteArrayWrapper(key));
        }

    }

    /**
     * Remove password from the database
     *
     * @param key the encrypted key
     */
    public void remove(byte[] key) {
        synchronized (myDatabase) {
            myDatabase.remove(new ByteArrayWrapper(key));
        }
    }

    /**
     * {@inheritDoc}
     */
    public State getState() {
        TreeMap<ByteArrayWrapper, byte[]> sorted;
        String pi;
        synchronized (myDatabase) {
            pi = toHex(myMasterPasswordInfo);
            sorted = new TreeMap<ByteArrayWrapper, byte[]>(myDatabase);
        }
        String[][] db = new String[2][sorted.size()];
        int i = 0;
        for (Map.Entry<ByteArrayWrapper, byte[]> e : sorted.entrySet()) {
            db[0][i] = toHex(e.getKey().unwrap());
            db[1][i] = toHex(e.getValue());
            i++;
        }
        State s = new State();
        s.PASSWORDS = db;
        s.MASTER_PASSWORD_INFO = pi;
        return s;
    }

    /**
     * Covert bytes to hex
     *
     * @param bytes bytes to convert
     * @return hex representation
     */
    @Nullable
    private static String toHex(byte[] bytes) {
        return bytes == null ? null : new String(Hex.encodeHex(bytes));
    }

    /**
     * Covert hex to bytes
     *
     * @param hex string to convert
     * @return bytes representation
     * @throws DecoderException if invalid data encountered
     */
    @Nullable
    private static byte[] fromHex(String hex) throws DecoderException {
        return hex == null ? null : Hex.decodeHex(hex.toCharArray());
    }

    /**
     * {@inheritDoc}
     */
    public void loadState(State state) {
        String[][] db = state.PASSWORDS;
        String pi = state.MASTER_PASSWORD_INFO;
        synchronized (myDatabase) {
            try {
                myMasterPasswordInfo = fromHex(pi);
                if (myMasterPasswordInfo == null) {
                    myMasterPasswordInfo = new byte[0];
                }
            } catch (DecoderException e) {
                myMasterPasswordInfo = new byte[0];
            }
            myDatabase.clear();
            if (db[0].length != db[1].length) {
                LOG.warn("The password database is in inconsistent state, ignoring it: " + db[0].length + " != "
                        + db[1].length);
            }
            int n = db[0].length;
            for (int i = 0; i < n; i++) {
                try {
                    byte[] key = fromHex(db[0][i]);
                    byte[] value = fromHex(db[1][i]);
                    if (key != null && value != null) {
                        myDatabase.put(new ByteArrayWrapper(key), value);
                    }
                } catch (DecoderException e) {
                    // skip the entry
                }
            }
        }
    }

    /**
     * @return the object over which database is synchronized
     */
    Object getDbLock() {
        return myDatabase;
    }

    /**
     * @return master password information
     */
    byte[] getPasswordInfo() {
        synchronized (myDatabase) {
            return myMasterPasswordInfo;
        }
    }

    /**
     * Set master password information
     *
     * @param bytes the bytes for the master password
     */
    public void setPasswordInfo(byte[] bytes) {
        synchronized (myDatabase) {
            myMasterPasswordInfo = bytes;
        }
    }

    /**
     * The state for passwords database
     */
    public static class State {
        /**
         * Information about master password (used in OS specific way)
         */
        public String MASTER_PASSWORD_INFO = "";
        /**
         * The password database
         */
        public String[][] PASSWORDS = new String[0][];
    }
}