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

org.apache.cayenne.crypto.CryptoModuleBuilder Maven / Gradle / Ivy

There is a newer version: 5.0-M1
Show 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.apache.cayenne.crypto;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.cayenne.access.jdbc.reader.RowReaderFactory;
import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
import org.apache.cayenne.crypto.batch.CryptoBatchTranslatorFactoryDecorator;
import org.apache.cayenne.crypto.cipher.CipherFactory;
import org.apache.cayenne.crypto.cipher.DefaultCipherFactory;
import org.apache.cayenne.crypto.key.JceksKeySource;
import org.apache.cayenne.crypto.key.KeySource;
import org.apache.cayenne.crypto.map.ColumnMapper;
import org.apache.cayenne.crypto.map.PatternColumnMapper;
import org.apache.cayenne.crypto.reader.CryptoRowReaderFactoryDecorator;
import org.apache.cayenne.crypto.transformer.DefaultTransformerFactory;
import org.apache.cayenne.crypto.transformer.TransformerFactory;
import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory;
import org.apache.cayenne.crypto.transformer.bytes.DefaultBytesTransformerFactory;
import org.apache.cayenne.crypto.transformer.value.DefaultValueTransformerFactory;
import org.apache.cayenne.crypto.transformer.value.ValueTransformerFactory;
import org.apache.cayenne.di.Binder;
import org.apache.cayenne.di.MapBuilder;
import org.apache.cayenne.di.Module;

/**
 * A builder of a Cayenne DI module that will contain all extension to Cayenne
 * runtime needed to enable encryption of certain data columns. Builder allows
 * to specify custom ciphers, as well as a strategy for discovering which
 * columns are encrypted.
 * 
 * @since 4.0
 */
public class CryptoModuleBuilder {

    private static final String DEFAULT_CIPHER_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_MODE = "CBC";
    private static final String DEFAULT_CIPHER_PADDING = "PKCS5Padding";

    private Class valueTransformerFactoryType;
    private Class bytesTransformerFactoryType;

    private String columnMapperPattern;
    private ColumnMapper columnMapper;
    private Class columnMapperType;

    private String cipherAlgoritm;
    private String cipherMode;
    private String cipherPadding;
    private Class cipherFactoryType;

    private URL keyStoreUrl;
    private String keyStoreUrlString;
    private File keyStoreFile;
    private Class keySourceType;
    private KeySource keySource;

    private String encryptionKeyAlias;
    private char[] keyPassword;

    private boolean compress;

    public CryptoModuleBuilder() {

        // init some sensible defaults that work in JVM without extra
        // packages...
        this.cipherAlgoritm = DEFAULT_CIPHER_ALGORITHM;
        this.cipherMode = DEFAULT_CIPHER_MODE;
        this.cipherPadding = DEFAULT_CIPHER_PADDING;

        this.cipherFactoryType = DefaultCipherFactory.class;
        this.keySourceType = JceksKeySource.class;

        this.columnMapperPattern = "^CRYPTO_";

        this.valueTransformerFactoryType = DefaultValueTransformerFactory.class;
        this.bytesTransformerFactoryType = DefaultBytesTransformerFactory.class;
    }

    public CryptoModuleBuilder cipherAlgorithm(String algorithm) {
        this.cipherAlgoritm = algorithm;
        return this;
    }

    public CryptoModuleBuilder cipherMode(String mode) {
        this.cipherMode = mode;
        return this;
    }

    public CryptoModuleBuilder cipherFactory(Class factoryType) {
        this.cipherFactoryType = factoryType;
        return this;
    }

    public CryptoModuleBuilder valueTransformer(Class factoryType) {
        this.valueTransformerFactoryType = factoryType;
        return this;
    }

    public CryptoModuleBuilder bytesTransformer(Class factoryType) {
        this.bytesTransformerFactoryType = factoryType;
        return this;
    }

    public CryptoModuleBuilder columnMapper(Class columnMapperType) {
        this.columnMapperPattern = null;
        this.columnMapperType = columnMapperType;
        this.columnMapper = null;
        return this;
    }

    public CryptoModuleBuilder columnMapper(ColumnMapper columnMapper) {
        this.columnMapperPattern = null;
        this.columnMapperType = null;
        this.columnMapper = columnMapper;
        return this;
    }

    public CryptoModuleBuilder columnMapper(String pattern) {
        this.columnMapperPattern = pattern;
        this.columnMapperType = null;
        this.columnMapper = null;
        return this;
    }

    /**
     * @param encryptionKeyAlias
     *            The name of the key in the keystore that should be used for
     *            encryption by default.
     */
    public CryptoModuleBuilder encryptionKeyAlias(String encryptionKeyAlias) {
        this.encryptionKeyAlias = encryptionKeyAlias;
        return this;
    }

    /**
     * Configures keystore parameters. The KeyStore must be of "jceks" type and
     * contain all needed secret keys for the target database. Currently all
     * keys must be protected with the same password.
     * 
     * @param file
     *            A file to load keystore from.
     * @param passwordForAllKeys
     *            A password that unlocks all keys in the keystore.
     * @param encryptionKeyAlias
     *            The name of the key in the keystore that should be used for
     *            encryption by default.
     */
    public CryptoModuleBuilder keyStore(File file, char[] passwordForAllKeys, String encryptionKeyAlias) {
        this.encryptionKeyAlias = encryptionKeyAlias;
        this.keyPassword = passwordForAllKeys;
        this.keyStoreUrl = null;
        this.keyStoreUrlString = null;
        this.keyStoreFile = file;
        return this;
    }

    /**
     * Configures keystore parameters. The KeyStore must be of "jceks" type and
     * contain all needed secret keys for the target database. Currently all
     * keys must be protected with the same password.
     * 
     * @param url
     *            A URL to load keystore from.
     * @param passwordForAllKeys
     *            A password that unlocks all keys in the keystore.
     * @param encryptionKeyAlias
     *            The name of the key in the keystore that should be used for
     *            encryption by default.
     */
    public CryptoModuleBuilder keyStore(String url, char[] passwordForAllKeys, String encryptionKeyAlias) {
        this.encryptionKeyAlias = encryptionKeyAlias;
        this.keyPassword = passwordForAllKeys;
        this.keyStoreUrl = null;
        this.keyStoreUrlString = url;
        this.keyStoreFile = null;
        return this;
    }

    /**
     * Configures keystore parameters. The KeyStore must be of "jceks" type and
     * contain all needed secret keys for the target database. Currently all
     * keys must be protected with the same password.
     * 
     * @param url
     *            A URL to load keystore from.
     * @param passwordForAllKeys
     *            A password that unlocks all keys in the keystore.
     * @param encryptionKeyAlias
     *            The name of the key in the keystore that should be used for
     *            encryption by default.
     */
    public CryptoModuleBuilder keyStore(URL url, char[] passwordForAllKeys, String encryptionKeyAlias) {
        this.encryptionKeyAlias = encryptionKeyAlias;
        this.keyPassword = passwordForAllKeys;
        this.keyStoreUrl = url;
        this.keyStoreUrlString = null;
        this.keyStoreFile = null;
        return this;
    }

    public CryptoModuleBuilder keySource(Class type) {
        this.keySourceType = type;
        this.keySource = null;
        return this;
    }

    public CryptoModuleBuilder keySource(KeySource keySource) {
        this.keySourceType = null;
        this.keySource = keySource;
        return this;
    }

    public CryptoModuleBuilder compress() {
        this.compress = true;
        return this;
    }

    /**
     * Produces a module that can be used to start Cayenne runtime.
     */
    public Module build() {

        if (valueTransformerFactoryType == null) {
            throw new IllegalStateException("'ValueTransformerFactory' is not initialized");
        }

        if (columnMapperType == null && columnMapper == null && columnMapperPattern == null) {
            throw new IllegalStateException("'ColumnMapper' is not initialized");
        }

        if (cipherFactoryType == null) {
            throw new IllegalStateException("'CipherFactory' is not initialized");
        }

        return new Module() {

            @Override
            public void configure(Binder binder) {

                String keyStoreUrl = null;
                if (CryptoModuleBuilder.this.keyStoreUrl != null) {
                    keyStoreUrl = CryptoModuleBuilder.this.keyStoreUrl.toExternalForm();
                } else if (CryptoModuleBuilder.this.keyStoreUrlString != null) {
                    keyStoreUrl = CryptoModuleBuilder.this.keyStoreUrlString;
                } else if (keyStoreFile != null) {
                    try {
                        keyStoreUrl = keyStoreFile.toURI().toURL().toExternalForm();
                    } catch (MalformedURLException e) {
                        throw new IllegalStateException("Invalid keyStore file", e);
                    }
                }

                // String properties
                MapBuilder props = binder. bindMap(CryptoConstants.PROPERTIES_MAP)
                        .put(CryptoConstants.CIPHER_ALGORITHM, cipherAlgoritm)
                        .put(CryptoConstants.CIPHER_MODE, cipherMode)
                        .put(CryptoConstants.CIPHER_PADDING, cipherPadding);

                if (keyStoreUrl != null) {
                    props.put(CryptoConstants.KEYSTORE_URL, keyStoreUrl);
                }

                if (encryptionKeyAlias != null) {
                    props.put(CryptoConstants.ENCRYPTION_KEY_ALIAS, encryptionKeyAlias);
                }

                if (compress) {
                    props.put(CryptoConstants.COMPRESSION, "true");
                }

                // char[] credentials... stored as char[] to potentially allow
                // wiping them clean in memory...
                MapBuilder creds = binder. bindMap(CryptoConstants.CREDENTIALS_MAP);

                if (keyPassword != null) {
                    creds.put(CryptoConstants.KEY_PASSWORD, keyPassword);
                }

                binder.bind(CipherFactory.class).to(cipherFactoryType);
                binder.bind(TransformerFactory.class).to(DefaultTransformerFactory.class);
                binder.bind(ValueTransformerFactory.class).to(valueTransformerFactoryType);
                binder.bind(BytesTransformerFactory.class).to(bytesTransformerFactoryType);

                if (keySource != null) {
                    binder.bind(KeySource.class).toInstance(keySource);
                } else {
                    binder.bind(KeySource.class).to(keySourceType);
                }

                if (columnMapperPattern != null) {
                    binder.bind(ColumnMapper.class).toInstance(new PatternColumnMapper(columnMapperPattern));
                } else if (columnMapperType != null) {
                    binder.bind(ColumnMapper.class).to(columnMapperType);
                } else {
                    binder.bind(ColumnMapper.class).toInstance(columnMapper);
                }

                binder.decorate(BatchTranslatorFactory.class).before(CryptoBatchTranslatorFactoryDecorator.class);
                binder.decorate(RowReaderFactory.class).before(CryptoRowReaderFactoryDecorator.class);
            }
        };
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy