/*************************************************************************
* Copyright 2009-2014 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.bootstrap;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import javax.crypto.Cipher;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.PersistenceContext;
import javax.persistence.Table;
import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.hibernate.annotations.Type;
import com.eucalyptus.component.auth.SystemCredentials;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.configurable.ConfigurableFieldType;
import com.eucalyptus.configurable.ConfigurableProperty;
import com.eucalyptus.configurable.ConfigurablePropertyException;
import com.eucalyptus.configurable.PropertyChangeListener;
import com.eucalyptus.crypto.Ciphers;
import com.eucalyptus.crypto.Crypto;
import com.eucalyptus.entities.AbstractPersistent;
import com.eucalyptus.entities.Transactions;
import com.eucalyptus.scripting.Groovyness;
@Entity
@PersistenceContext(name="eucalyptus_config")
@Table( name = "config_database" )
@ConfigurableClass(root = "services.database", description = "Parameters controlling database information.", singleton = true)
public class DatabaseInfo extends AbstractPersistent {
private static final Logger LOG = Logger.getLogger( DatabaseInfo.class );
@ConfigurableField( displayName = "append_only_host",
description = "host address of the backend database for append-only data",
initial = "localhost" )
@Column(name = "append_only_host")
private String appendOnlyHost = null;
@ConfigurableField( displayName = "append_only_port",
description = "port number of the backend database for append-only data",
changeListener = AppendOnlyPortChangeListener.class)
@Column(name = "append_only_port")
private String appendOnlyPort = null;
@ConfigurableField( displayName = "append_only_user",
description = "user name of the backend database for append-only data")
@Column(name = "append_only_user")
private String appendOnlyUser = null;
@ConfigurableField( displayName = "append_only_password",
description = "password of the backend database for append-only data",
type = ConfigurableFieldType.KEYVALUEHIDDEN)
@Column(name = "append_only_password")
@Type(type="org.hibernate.type.StringClobType")
@Lob
private String appendOnlyPassword = null;
@ConfigurableField( displayName = "append_only_ssl",
description = "ssl certificate to use when connecting to the backend database for append-only data",
type = ConfigurableFieldType.KEYVALUEHIDDEN)
@Column(name = "append_only_ssl_certificate")
@Type(type="org.hibernate.type.StringClobType")
@Lob
private String appendOnlySslCert = null;
public DatabaseInfo() { }
private static DatabaseInfo newDefault() {
final DatabaseInfo newInfo = new DatabaseInfo();
newInfo.appendOnlyHost = "localhost";
newInfo.appendOnlyPort = "";
newInfo.appendOnlyUser = "";
newInfo.appendOnlyPassword = "";
newInfo.appendOnlySslCert = "";
return newInfo;
}
public void setAppendOnlyHost(final String host){
this.appendOnlyHost = host;
}
public String getAppendOnlyHost(){
return this.appendOnlyHost;
}
public void setAppendOnlyPort(final String port){
this.appendOnlyPort = port;
}
public String getAppendOnlyPort(){
return this.appendOnlyPort;
}
public void setAppendOnlyUser(final String user){
this.appendOnlyUser = user;
}
public String getAppendOnlyUser(){
return this.appendOnlyUser;
}
public void setAppendOnlyPassword(final String password){
try{
final X509Certificate cloudCert = SystemCredentials.lookup(Eucalyptus.class).getCertificate();
final Cipher cipher = Ciphers.RSA_PKCS1.get();
cipher.init(Cipher.ENCRYPT_MODE, cloudCert.getPublicKey(), Crypto.getSecureRandomSupplier( ).get( ));
byte[] bencPassword = cipher.doFinal(password.getBytes());
final String encryptedPassword = new String(Base64.encode(bencPassword));
this.appendOnlyPassword = encryptedPassword;
}catch(final Exception ex){
LOG.error("Failed to encrypt the database password");
}
}
public String getAppendOnlyPassword(){
try{
final PrivateKey cloudPk = SystemCredentials.lookup(Eucalyptus.class).getPrivateKey();
final Cipher cipher = Ciphers.RSA_PKCS1.get();
cipher.init(Cipher.DECRYPT_MODE, cloudPk, Crypto.getSecureRandomSupplier().get( ));
byte[] decoded = Base64.decode(this.appendOnlyPassword.getBytes());
byte[] bdecPassword = cipher.doFinal(decoded);
final String password= new String(bdecPassword);
return password;
}catch(final Exception ex){
return null;
}
}
public void setAppendOnlySslCert(final String cert){
this.appendOnlySslCert = cert;
}
public String getAppendOnlySslCert() {
return this.appendOnlySslCert;
}
private void resetDatabase() {
if (this.appendOnlyHost == null || this.appendOnlyHost.length()<=0)
return;
else if (this.appendOnlyPort == null || this.appendOnlyPort.length()<=0)
return;
else if (this.appendOnlyUser == null || this.appendOnlyUser.length()<=0)
return;
else if (this.appendOnlyPassword == null || this.appendOnlyPassword.length()<=0)
return;
try{
Groovyness.run("setup_dbpool_remote.groovy");
}catch(final Exception ex) {
LOG.error("Failed to reset remote db pool", ex);
}
try{
Groovyness.run("setup_persistence_remote.groovy");
}catch(final Exception ex) {
LOG.error("Failed to reset persistence contexts", ex);
}
LOG.info(String.format("Remote databases are reset [%s:%s]", this.appendOnlyHost, this.appendOnlyPort));
}
public static DatabaseInfo getDatabaseInfo() {
DatabaseInfo conf = null;
try {
conf = Transactions.find(new DatabaseInfo());
} catch (Exception e) {
LOG.warn("Database information is not found. Loading defaults.");
try {
conf = Transactions.saveDirect(newDefault());
} catch (Exception e1) {
try {
conf = Transactions.find(new DatabaseInfo());
} catch (Exception e2) {
LOG.warn("Failed to persist and retrieve DatabaseInfo entity");
}
}
}
if (conf == null) {
conf = newDefault();
}
return conf;
}
public static final class AppendOnlyPortChangeListener implements PropertyChangeListener {
@Override
public void fireChange(ConfigurableProperty t, Object newValue)
throws ConfigurablePropertyException {
try{
int portNum = Integer.parseInt( (String) newValue);
if(portNum < 1 || portNum > 65535)
throw new Exception();
}catch(final Exception ex){
throw new ConfigurablePropertyException("Invalid port number");
}
}
}
}