/*
*
* Panbox - encryption for cloud storage
* Copyright (C) 2014-2015 by Fraunhofer SIT and Sirrix AG
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*
* Additonally, third party code may be provided with notices and open source
* licenses from communities and third parties that govern the use of those
* portions, and any licenses granted hereunder do not alter any rights and
* obligations you may have under such open source licenses, however, the
* disclaimer of warranty and limitation of liability provisions of the GPLv3
* will apply to all the product.
*
*/
package org.panbox.desktop.common.identitymgmt.sqlightimpl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;
import org.apache.log4j.Logger;
import org.panbox.Settings;
import org.panbox.core.crypto.KeyConstants;
import org.panbox.core.identitymgmt.AbstractAddressbookManager;
import org.panbox.core.identitymgmt.AbstractIdentity;
import org.panbox.core.identitymgmt.AbstractIdentityManager;
import org.panbox.core.identitymgmt.CloudProviderInfo;
import org.panbox.core.identitymgmt.IAddressbook;
import org.panbox.core.identitymgmt.Identity;
public class IdentityManager extends AbstractIdentityManager {
public static String DB_FILE = Settings.getInstance().getIdentityPath();
private static String IDENTITYDB_CONNECT_STRING = "jdbc:sqlite:" + DB_FILE;
public static String KEYSTORE_PATH = Settings.getInstance()
.getKeystorePath();
private static IdentityManager idm = null;
private Connection connection = null;
private static final Logger logger = Logger.getLogger("org.panbox.common");
private IdentityManager() {
}
public static IdentityManager getInstance() {
if (idm == null) {
idm = new IdentityManager();
}
return idm;
}
/**
* Creates the initial set of DB tables for the SQLITE DB to store our
* identity and addressbook entries
*/
@Override
public void init(AbstractAddressbookManager aBooMgr) {
setAddressBookManager(aBooMgr);
reloadSettings();
// create a database connection
try {
connection = DriverManager.getConnection(IDENTITYDB_CONNECT_STRING);
} catch (SQLException e) {
logger.error("IdentityManager: Failure to connect to db: "
+ IDENTITYDB_CONNECT_STRING, e);
}
Statement statement;
try {
statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
statement
.executeUpdate("create table if not exists "
+ TABLE_IDENTITY
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, name string, firstname string, email string, keystorePath string)");
statement
.executeUpdate("create table if not exists "
+ TABLE_CLOUDPROVIDER
+ " (id INTEGER PRIMARY KEY AUTOINCREMENT, name string, username string, password string)");
statement.executeUpdate("create table if not exists "
+ TABLE_CLOUDPROVIDER_MAP
+ " (identityID integer, cloudProviderID integer)");
aBooMgr.init();
} catch (SQLException e) {
logger.error("IdentityManager: Failure on creating tables", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.error(
"IdentityManager: Failure to close db connection while creating tables",
e);
}
}
}
}
/**
* Stores the given Identity in a SQLITE DB and keys in a java keystore as a
* file
*
* @param id
* - The Identity to store
*/
@Override
public void storeMyIdentity(AbstractIdentity id) {
reloadSettings();
Statement statement;
try {
connection = DriverManager.getConnection(IDENTITYDB_CONNECT_STRING);
} catch (SQLException e) {
logger.error("IdentityManager: Failure to open db connection", e);
}
int storedIdentityID = id.getID();
String sql = "";
if (storedIdentityID > 0) // do update
{
sql = "update "
+ TABLE_IDENTITY
+ " set name=(?), firstname=(?), email=(?), keystorePath=(?) where id=(?)";
} else {
sql = "insert into " + TABLE_IDENTITY
+ " VALUES (NULL, (?), (?), (?), (?))";
}
PreparedStatement pStatement;
try {
pStatement = connection.prepareStatement(sql);
pStatement.setString(1, id.getName());
pStatement.setString(2, id.getFirstName());
pStatement.setString(3, id.getEmail());
pStatement.setString(4, KEYSTORE_PATH); // this will never be
// updated, if keystore is
// changed after identity
// was created
if (storedIdentityID > 0) {
pStatement.setInt(5, storedIdentityID);
}
pStatement.executeUpdate();
if (storedIdentityID < 0) {
ResultSet keys = pStatement.getGeneratedKeys();
storedIdentityID = keys.getInt("last_insert_rowid()");
id.setID(storedIdentityID);
}
statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
// insert/update cloudproviders
String sqlListCPIs = "select * from " + TABLE_CLOUDPROVIDER + ", "
+ TABLE_CLOUDPROVIDER_MAP + " where " + TABLE_CLOUDPROVIDER
+ ".id=" + TABLE_CLOUDPROVIDER_MAP
+ ".cloudProviderID and " + TABLE_CLOUDPROVIDER_MAP
+ ".identityID=" + storedIdentityID;
for (CloudProviderInfo cp : id.getCloudProviders().values()) {
// check if cp is in db
if (cp.getId() > 0) {
String upCPI = "update " + TABLE_CLOUDPROVIDER
+ " set name=(?), username=(?) where id=(?)";
PreparedStatement ps = connection.prepareStatement(upCPI);
ps.setString(1, cp.getProviderName());
ps.setString(2, cp.getUsername());
ps.setInt(3, cp.getId());
ps.execute();
} else {
statement.executeUpdate("insert into "
+ TABLE_CLOUDPROVIDER + " VALUES(NULL, \""
+ cp.getProviderName() + "\", \""
+ cp.getUsername() + "\", NULL" + ")");
ResultSet keys = statement.getGeneratedKeys();
int cpID = keys.getInt("last_insert_rowid()");
cp.setId(cpID);
statement.executeUpdate("insert into "
+ TABLE_CLOUDPROVIDER_MAP + " VALUES(\""
+ storedIdentityID + "\", \"" + cpID + "\")");
}
}
// check which cloudprovider need to be removed from db
ResultSet r = statement.executeQuery(sqlListCPIs);
LinkedList<Integer> toBeRemoved = new LinkedList<Integer>();
while (r.next()) {
boolean found = false;
int cId = r.getInt("id");
for (CloudProviderInfo cpi : id.getCloudProviders().values()) {
if (cpi.getId() == cId) {
found = true;
break;
}
}
if (!found) {
// delete this cpi from db
toBeRemoved.add(cId);
}
}
for (Integer cId : toBeRemoved) {
String sqlDel = "delete from " + TABLE_CLOUDPROVIDER
+ " where id=" + cId;
statement.execute(sqlDel);
sqlDel = "delete from " + TABLE_CLOUDPROVIDER_MAP
+ " where cloudProviderID=" + cId + " and identityID="
+ storedIdentityID;
statement.execute(sqlDel);
}
} catch (SQLException e) {
logger.error(
"IdentityManager: SQL error while storing the identity", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.error(
"IdentityManager: Failure to close db connection while storing the identity",
e);
}
}
}
// store contacts
if (null != getAddressBookManager()) {
getAddressBookManager().persistContacts(
id.getAddressbook().getContacts(), storedIdentityID);
} else {
System.err.println("IdentityManager: No AddressbookManager set");
}
// store keystore with private keys in file
File keyStoreFile = new File(KEYSTORE_PATH);
FileOutputStream fos;
try {
fos = new FileOutputStream(keyStoreFile);
Identity idCast = (Identity) id;
idCast.getKeyStore()
.store(fos, KeyConstants.OPEN_KEYSTORE_PASSWORD);
fos.close();
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
logger.error("IdentityManager: Failure to write keystore", e);
}
}
/**
* Loads our own Identity from a SQLITE DB and keys from a java keystore
* file
*
* @param addressbook
* - Plattform specific Implementation of addressbook
* @param aBookMgr
* - Plattform specific manager to load and store contacts
* @return - Our own Identity, or null if no identity has been stored yet
*/
@Override
public AbstractIdentity loadMyIdentity(IAddressbook addressbook) {
Identity id = new Identity(addressbook);
Statement statement;
// String keystorePath = ""; // this is now loaded from settings
try {
reloadSettings();
connection = DriverManager.getConnection(IDENTITYDB_CONNECT_STRING);
statement = connection.createStatement();
statement.setQueryTimeout(30); // set timeout to 30 sec.
ResultSet rs = statement.executeQuery("select * from "
+ TABLE_IDENTITY);
int idCount = 0;
while (rs.next()) {
// we only support ONE ID
if (idCount >= 1) {
throw new RuntimeException(
"More than one ID found - Not supported, DB corrupted");
}
// read the result set
int identityID = rs.getInt("id");
id.setName(rs.getString("name"));
id.setFirstName(rs.getString("firstname"));
id.setEmail(rs.getString("email"));
// keystorePath = rs.getString("keystorePath"); // this is now
// loaded from settings
ResultSet rsCPs = statement.executeQuery("select * from "
+ TABLE_CLOUDPROVIDER + ", " + TABLE_CLOUDPROVIDER_MAP
+ " where " + TABLE_CLOUDPROVIDER_MAP
+ ".identityID=\"" + identityID + "\" and "
+ TABLE_CLOUDPROVIDER_MAP + ".cloudProviderID = "
+ TABLE_CLOUDPROVIDER + ".id");
while (rsCPs.next()) {
CloudProviderInfo cpi = new CloudProviderInfo(
rsCPs.getString("name"),
rsCPs.getString("username"));
cpi.setId(rsCPs.getInt("id"));
id.addCloudProvider(cpi);
}
id.setID(identityID);
getAddressBookManager().loadContacts(id);
idCount++;
}
if (idCount == 0) {
// could not load identity (table is empty)
return null;
}
} catch (SQLException e) {
throw new RuntimeException("Cannot access Identity.db", e);
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.error(
"IdentityManager: Failure to close db connection while loading the identity",
e);
}
}
}
// load keystore from file
KeyStore store = null;
try {
store = KeyStore.getInstance(KeyConstants.KEYSTORE_TYPE);
} catch (KeyStoreException e1) {
logger.error("IdentityManager: Failure instantiate keystore of type: " + KeyConstants.KEYSTORE_TYPE, e1);
}
File keystoreFile = new File(KEYSTORE_PATH);
if (keystoreFile.exists()) {
FileInputStream fis;
try {
fis = new FileInputStream(keystoreFile);
store.load(fis, KeyConstants.OPEN_KEYSTORE_PASSWORD);
fis.close();
} catch (NoSuchAlgorithmException | CertificateException
| IOException e) {
logger.error("IdentityManager: Failure to load keystore from file " + KEYSTORE_PATH, e);
}
id.setKeyStore(store);
} else {
logger.error("IdentityManager: No Keystore found -> returning null as identity!");
return null; // no keystore -> no identity
}
return id;
}
private void reloadSettings() {
DB_FILE = Settings.getInstance().getIdentityPath();
KEYSTORE_PATH = Settings.getInstance().getKeystorePath();
IDENTITYDB_CONNECT_STRING = "jdbc:sqlite:" + DB_FILE;
}
}