/*
 * Copyright (C) 2003-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (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.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.kernel.security;

import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.SessionManager;
import com.arsdigita.util.UncheckedWrapperException;

import java.security.SecureRandom;
import java.math.BigInteger;

import org.apache.log4j.Logger;

/**
 * Provides access to the key store in which the secret is stored.
 *
 * @author  Vadim Nasardinov (vadimn@redhat.com)
 * @since   2003-12-18
 * @version $Revision: #6 $ $DateTime: 2004/04/07 16:07:11 $
 **/
final class Store implements KeyStorage {
    final static Store INSTANCE = new Store();

    private final static Logger s_log = Logger.getLogger(Store.class);

    private final static String TYPE = "com.arsdigita.kernel.security.KeyStore";
    private final static String ID    = "id";
    private final static String OWNER = "owner";
    private final static String STORE = "store";

    private final static String OWNER_VALUE = "kernel.security";
    private final static BigInteger ID_VALUE = new BigInteger("0");

    private byte[] m_secret;

    Store() {}

    static byte[] newKey() {
        byte[] key = new byte[Initializer.SECRET_KEY_BYTES];
        new SecureRandom().nextBytes(key);
        return key;
    }

    public synchronized void init() {
        if ( hasBeenInitialized() ) {
            throw new UncheckedWrapperException
                ("key store had been initialized");
        }
        init(ID_VALUE, OWNER_VALUE, newKey());
    }

    private boolean hasBeenInitialized() {
        DataCollection dc = SessionManager.getSession().retrieve(TYPE);
        dc.addEqualsFilter(ID, ID_VALUE);
        boolean result = dc.size() > 0;
        dc.close();
        return result;
    }


    /**
     * This method is exposed as package-scoped solely for the purpose of
     * white-box unit-testing.
     *
     * @throws NullPointerException if any of the parameters is null.
     **/
    void init(BigInteger id, String owner, byte[] store) {
        if ( id == null ) { throw new NullPointerException("id"); }
        if ( owner == null ) { throw new NullPointerException("owner"); }
        if ( store == null ) { throw new NullPointerException("store"); }
        if ( store.length < 1 ) {
            throw new IllegalArgumentException("empty store");
        }

        DataObject dobj = SessionManager.getSession().create(TYPE);
        dobj.set(ID, id);
        dobj.set(OWNER, owner);
        dobj.set(STORE, store);
        dobj.save();
    }

    synchronized byte[] loadSecret() {
        if ( m_secret != null ) { return m_secret; }

        DataObject dobj = SessionManager.getSession().retrieve
            (new OID(TYPE, ID_VALUE));
        m_secret = (byte[]) dobj.get(STORE);
        if ( m_secret == null ) {
            throw new IllegalStateException
                ("the store is null");
        }
        if ( m_secret.length != Initializer.SECRET_KEY_BYTES ) {
            throw new IllegalArgumentException
                ("wrong length. expected=" + Initializer.SECRET_KEY_BYTES +
                 ", but got " + m_secret.length);
        }
        return m_secret;
    }
}
