aboutsummaryrefslogtreecommitdiff
path: root/src/DatabaseStorage.cpp
diff options
context:
space:
mode:
authordec05eba <0xdec05eba@gmail.com>2018-05-14 00:20:11 +0200
committerdec05eba <0xdec05eba@gmail.com>2018-05-14 00:27:29 +0200
commit9af086151e6d9d3fe88f9e3e21797812a3e701ba (patch)
treea11889f81b8f929c11dccbd2c1c3b3cd74fbb740 /src/DatabaseStorage.cpp
parentebff7aeafded4dd9d245dbcfc80d9c8d83fe1242 (diff)
Replace files with sqlite
Using sqlite because sqlite has transactions, storing/loading from files automatically, unloading data that is not accessed often. Removed cosmetic data (node name, username). They can be added using addData by the application that uses odhtdb instead. Database callback functions can now be called with stored data using database.loadNode function. TODO: Add local user storage (with password) back, it has been temorary disabled
Diffstat (limited to 'src/DatabaseStorage.cpp')
-rw-r--r--src/DatabaseStorage.cpp1503
1 files changed, 708 insertions, 795 deletions
diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp
index 0e7a1a8..501bd35 100644
--- a/src/DatabaseStorage.cpp
+++ b/src/DatabaseStorage.cpp
@@ -8,11 +8,13 @@
#include "../include/odhtdb/PasswordHash.hpp"
#include "../include/odhtdb/Log.hpp"
#include "../include/odhtdb/Database.hpp"
+#include "../include/odhtdb/sql/SqlQuery.hpp"
#include <cstring>
#include <chrono>
#include <boost/filesystem/convenience.hpp>
#include <sibs/SafeSerializer.hpp>
#include <sodium/randombytes.h>
+#include <sqlite3.h>
using namespace std;
@@ -24,15 +26,8 @@ namespace odhtdb
STORAGE_TYPE_APPEND
};
- enum class DecryptedDataType : u8
- {
- STORAGE_CREATE,
- STORAGE_ADD_DATA,
- STORAGE_ADD_USER
- };
-
const u64 QUARANTINE_STORAGE_TIME_MICROSECONDS = 60 * 1.0e6;
- const u16 STORAGE_VERSION = 2;
+ const u16 STORAGE_VERSION = 3;
DatabaseStorageObject::DatabaseStorageObject(const Hash &_requestHash, DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) :
requestHash(_requestHash),
@@ -50,30 +45,124 @@ namespace odhtdb
storedTimestamp = chrono::duration_cast<chrono::microseconds>(time).count();
}
+ static void sqlite_exec_checked(sqlite3 *db, const char *sql)
+ {
+ char *dbErrMsg = nullptr;
+ int rc = sqlite3_exec(db, sql, nullptr, nullptr, &dbErrMsg);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to run sqlite exec, error: ";
+ errMsg += dbErrMsg;
+ sqlite3_free(dbErrMsg);
+ throw DatabaseStorageException(errMsg);
+ }
+ }
+
+ static void sqlite_prepare_checked(sqlite3 *db, const char *sql, sqlite3_stmt **stmt)
+ {
+ int rc = sqlite3_prepare_v2(db, sql, -1, stmt, nullptr);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to run sqlite prepare statement, error: ";
+ errMsg += sqlite3_errmsg(db);
+ throw DatabaseStorageException(errMsg);
+ }
+ }
+
DatabaseStorage::DatabaseStorage(Database *_database, const boost::filesystem::path &storagePath) :
database(_database),
- groupsFilePath(storagePath / "groups"),
- usersFilePath(storagePath / "users"),
- dataFilePath(storagePath / "data"),
- metadataFilePath(storagePath / "metadata"),
- localUsersFilePath(storagePath / "local_users"),
- nodeDecryptionKeysFilePath(storagePath / "node_keys"),
- decryptedDataFilePath(storagePath / "decrypted_data")
+ sqliteDb(nullptr),
+ insertNodeStmt(nullptr),
+ insertUserStmt(nullptr),
+ insertGroupStmt(nullptr),
+ insertNodeAddDataStmt(nullptr),
+ setNodeDecryptionKeyStmt(nullptr),
+ getNodeDecryptionKeyStmt(nullptr),
+ selectNodeStmt(nullptr),
+ selectNodeAddDataByNodeStmt(nullptr),
+ selectNodeIdStatement(nullptr),
+ selectNodeAddDataIdStatement(nullptr),
+ insertNodeUserGroupAssocStmt(nullptr),
+ insertNodeRawStmt(nullptr),
+ insertNodeAddDataRawStmt(nullptr),
+ insertNodeAddDataAdditionalStmt(nullptr),
+ insertNodeAddUserDataStmt(nullptr),
+ selectNodeAddDataAdditionalStmt(nullptr),
+ selectNodeAddUserDataStmt(nullptr),
+ setNodeAddDataDecryptedStmt(nullptr),
+ setNodeAddDataAdditionalDataStmt(nullptr),
+ metadataFilePath(storagePath / "metadata")
+ {
+ try
+ {
+ init(storagePath);
+ }
+ catch(DatabaseStorageException &e)
+ {
+ cleanup();
+ throw e;
+ }
+ }
+
+ void DatabaseStorage::init(const boost::filesystem::path &storagePath)
{
boost::filesystem::create_directories(storagePath);
+ Log::debug("sqlite version: %s", sqlite3_libversion());
+
+ int rc = sqlite3_open((storagePath / "database.sqlite3").string().c_str(), &sqliteDb);
+ if(rc != SQLITE_OK)
+ {
+ string errMsg = "Failed to open database: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ sqlite_exec_checked(sqliteDb,
+ "CREATE TABLE IF NOT EXISTS Node(id INTEGER PRIMARY KEY, nodeHash BLOB UNIQUE NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, adminGroupId BLOB NOT NULL);"
+ "CREATE TABLE IF NOT EXISTS NodeUser(node BLOB NOT NULL, publicKey BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeGroup(node BLOB NOT NULL, groupId BLOB UNIQUE NOT NULL, permissionLevel INT NOT NULL, permissionFlags INTEGER NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeAddData(id INTEGER PRIMARY KEY, node BLOB NOT NULL, requestHash BLOB UNIQUE NOT NULL, operation INT NOT NULL, timestamp INTEGER NOT NULL, creatorPublicKey BLOB NOT NULL, decrypted INT NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash));"
+ "CREATE TABLE IF NOT EXISTS NodeAddDataAdditional(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
+ "CREATE TABLE IF NOT EXISTS NodeAddUserData(id INTEGER PRIMARY KEY, nodeAddDataId INTEGER NOT NULL, userToAddPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(nodeAddDataId) REFERENCES NodeAddData(id));"
+ "CREATE TABLE IF NOT EXISTS NodeDecryptionKey(node BLOB UNIQUE NOT NULL, decryptionKey BLOB NOT NULL);"
+ "CREATE TABLE IF NOT EXISTS NodeUserGroupAssoc(node BLOB NOT NULL, userPublicKey BLOB NOT NULL, groupId BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(nodeHash), FOREIGN KEY(userPublicKey) REFERENCES NodeUser(publicKey), FOREIGN KEY(groupId) REFERENCES NodeGroup(groupId));"
+
+ "CREATE TABLE IF NOT EXISTS NodeRaw(node INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id));"
+ "CREATE TABLE IF NOT EXISTS NodeAddDataRaw(node INTEGER NOT NULL, nodeAddData INTEGER NOT NULL, data BLOB NOT NULL, FOREIGN KEY(node) REFERENCES Node(id), FOREIGN KEY(nodeAddData) REFERENCES NodeAddData(id));"
+
+ "CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserInNode ON NodeUser(node, publicKey);"
+ "CREATE UNIQUE INDEX IF NOT EXISTS UniqueUserGroupAssoc ON NodeUserGroupAssoc(node, userPublicKey, groupId);");
+
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO Node(nodeHash, timestamp, creatorPublicKey, adminGroupId) VALUES(?, ?, ?, ?)", &insertNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUser(node, publicKey) VALUES(?, ?)", &insertUserStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeGroup(node, groupId, permissionLevel, permissionFlags) VALUES(?, ?, ?, ?)", &insertGroupStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddData(node, requestHash, operation, timestamp, creatorPublicKey, decrypted) VALUES(?, ?, ?, ?, ?, ?)", &insertNodeAddDataStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT OR REPLACE INTO NodeDecryptionKey(node, decryptionKey) VALUES(?, ?)", &setNodeDecryptionKeyStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT decryptionKey FROM NodeDecryptionKey WHERE node = ?", &getNodeDecryptionKeyStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeUserGroupAssoc(node, userPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeUserGroupAssocStmt);
+
+ sqlite_prepare_checked(sqliteDb, "SELECT timestamp, creatorPublicKey, adminGroupId FROM Node WHERE nodeHash = ?", &selectNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT id, requestHash, operation, timestamp, creatorPublicKey From NodeAddData WHERE node = ?", &selectNodeAddDataByNodeStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", &selectNodeIdStatement);
+ sqlite_prepare_checked(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", &selectNodeAddDataIdStatement);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeRaw(node, data) VALUES(?, ?)", &insertNodeRawStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataRaw(node, nodeAddData, data) VALUES(?, ?, ?)", &insertNodeAddDataRawStmt);
+
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddDataAdditional(nodeAddDataId, data) VALUES(?, ?)", &insertNodeAddDataAdditionalStmt);
+ sqlite_prepare_checked(sqliteDb, "INSERT INTO NodeAddUserData(nodeAddDataId, userToAddPublicKey, groupId) VALUES(?, ?, ?)", &insertNodeAddUserDataStmt);
+
+ sqlite_prepare_checked(sqliteDb, "SELECT data From NodeAddDataAdditional WHERE nodeAddDataId = ?", &selectNodeAddDataAdditionalStmt);
+ sqlite_prepare_checked(sqliteDb, "SELECT userToAddPublicKey, groupId From NodeAddUserData WHERE nodeAddDataId = ?", &selectNodeAddUserDataStmt);
+
+ sqlite_prepare_checked(sqliteDb, "UPDATE NodeAddData SET decrypted = ? WHERE id = ?", &setNodeAddDataDecryptedStmt);
+ sqlite_prepare_checked(sqliteDb, "UPDATE NodeAddDataAdditional SET data = ? WHERE nodeAddDataId = ?", &setNodeAddDataAdditionalDataStmt);
+
bool metadataLoaded = false;
try
{
loadMetadataFromFile();
metadataLoaded = true;
- loadGroupsFromFile();
- loadUsersFromFile();
- loadDataFromFile();
- loadLocalUsersFromFile();
- loadNodeDecryptionKeysFromFile();
- loadDecryptedDataFromFile();
- //loadQuarantineFromFile();
}
catch(FileException &e)
{
@@ -113,651 +202,474 @@ namespace odhtdb
}
}
- void DatabaseStorage::loadGroupsFromFile()
+ void DatabaseStorage::cleanup()
{
- if(!boost::filesystem::exists(groupsFilePath)) return;
-
- OwnedMemory groupsFileContent = fileGetContent(groupsFilePath);
- sibs::SafeDeserializer deserializer((u8*)groupsFileContent.data, groupsFileContent.size);
-
- while(!deserializer.empty())
- {
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- DataViewMap<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
-
- u8 groupNameSize = deserializer.extract<u8>();
- string groupName;
- groupName.resize(groupNameSize);
- deserializer.extract((u8*)&groupName[0], groupNameSize);
-
- u8 groupId[GROUP_ID_LENGTH];
- deserializer.extract(groupId, GROUP_ID_LENGTH);
-
- u8 permissionLevel = deserializer.extract<u8>();
- u32 permissionFlags = deserializer.extract<u32>();
-
- Group *group = new Group(groupName, groupId, { permissionLevel, permissionFlags });
- (*groupByIdMap)[group->getId()] = group;
-
- string groupIdStr = bin2hex((const char*)groupId, GROUP_ID_LENGTH);
- Log::debug("DatabaseStorage: Loaded group %s from file", groupIdStr.c_str());
- }
+ sqlite3_finalize(insertNodeStmt);
+ sqlite3_finalize(insertUserStmt);
+ sqlite3_finalize(insertGroupStmt);
+ sqlite3_finalize(insertNodeAddDataStmt);
+ sqlite3_finalize(setNodeDecryptionKeyStmt);
+ sqlite3_finalize(getNodeDecryptionKeyStmt);
+ sqlite3_finalize(insertNodeUserGroupAssocStmt);
+ sqlite3_finalize(selectNodeStmt);
+ sqlite3_finalize(selectNodeAddDataByNodeStmt);
+ sqlite3_finalize(selectNodeIdStatement);
+ sqlite3_finalize(selectNodeAddDataIdStatement);
+ sqlite3_finalize(insertNodeRawStmt);
+ sqlite3_finalize(insertNodeAddDataRawStmt);
+ sqlite3_finalize(insertNodeAddDataAdditionalStmt);
+ sqlite3_finalize(insertNodeAddUserDataStmt);
+ sqlite3_finalize(selectNodeAddDataAdditionalStmt);
+ sqlite3_finalize(selectNodeAddUserDataStmt);
+ sqlite3_finalize(setNodeAddDataDecryptedStmt);
+ sqlite3_finalize(setNodeAddDataAdditionalDataStmt);
+ sqlite3_close(sqliteDb);
}
- void DatabaseStorage::loadUsersFromFile()
+ DatabaseStorage::~DatabaseStorage()
{
- if(!boost::filesystem::exists(usersFilePath)) return;
-
- OwnedMemory usersFileContent = fileGetContent(usersFilePath);
- sibs::SafeDeserializer deserializer((u8*)usersFileContent.data, usersFileContent.size);
-
- while(!deserializer.empty())
+ cleanup();
+ }
+
+ void DatabaseStorage::loadNode(const Hash &nodeHash)
+ {
+ if(database->onCreateNodeCallbackFunc)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- Signature::MapPublicKey<User*> *publicKeyUserDataMap = nullptr;
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end())
+ SqlQuery nodeQuery(sqliteDb, "SELECT timestamp, creatorPublicKey, adminGroupId From Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(nodeQuery.next())
{
- publicKeyUserDataMap = new Signature::MapPublicKey<User*>();
- nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap;
+ u64 timestamp = nodeQuery.getInt64(0);
+ const DataView creatorPublicKeyRaw = nodeQuery.getBlob(1);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw.data, creatorPublicKeyRaw.size);
+ u8 adminGroupId[GROUP_ID_LENGTH];
+ const DataView adminGroupRaw = nodeQuery.getBlob(2);
+ memcpy(adminGroupId, adminGroupRaw.data, GROUP_ID_LENGTH);
+
+ const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, timestamp, &creatorPublicKey, DataView(adminGroupId, GROUP_ID_LENGTH));
+ database->onCreateNodeCallbackFunc(createNodeRequest);
}
- else
- publicKeyUserDataMap = publicKeyUserDataMapIt->second;
+ }
+
+ SqlQuery nodeQuery(sqliteDb, "SELECT id, requestHash, operation, timestamp, creatorPublicKey From NodeAddData WHERE node = ? AND decrypted = 1", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(nodeQuery.next())
+ {
+ i64 rowId = nodeQuery.getInt64(0);
- DataViewMap<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
+ const DataView requestHashRaw = nodeQuery.getBlob(1);
+ Hash requestHash;
+ memcpy(requestHash.getData(), requestHashRaw.data, HASH_BYTE_SIZE);
- u8 usernameSize = deserializer.extract<u8>();
- string username;
- username.resize(usernameSize);
- deserializer.extract((u8*)&username[0], usernameSize);
+ DatabaseOperation operation = (DatabaseOperation)nodeQuery.getInt(2);
- u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ u64 timestamp = nodeQuery.getInt64(3);
- User *user = RemoteUser::create(userPublicKey, username, nullptr);
+ const DataView creatorPublicKeyRaw = nodeQuery.getBlob(4);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw.data, creatorPublicKeyRaw.size);
- u8 numGroups = deserializer.extract<u8>();
- for(int i = 0; i < numGroups; ++i)
+ if(operation == DatabaseOperation::ADD_DATA)
{
- u8 groupId[GROUP_ID_LENGTH];
- deserializer.extract(groupId, GROUP_ID_LENGTH);
+ if(!database->onAddNodeCallbackFunc) continue;
+ sqlite3_reset(selectNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(selectNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddDataAdditionalStmt, 1, rowId);
+ bindCheckError(rc);
- auto groupIt = groupByIdMap->find(DataView(groupId, GROUP_ID_LENGTH));
- if(groupIt == groupByIdMap->end())
+ rc = sqlite3_step(selectNodeAddDataAdditionalStmt);
+ if(rc != SQLITE_ROW)
{
- string errMsg = "User group with id ";
- errMsg += bin2hex((const char*)groupId, GROUP_ID_LENGTH);
- errMsg += " does not exist";
- throw DatabaseStorageCorrupt(errMsg);
+ string errMsg = "select NodeAddDataAdditional failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
}
- user->addToGroup(groupIt->second);
+
+ // TODO: There is no need to allocate/deallocate several times, this can be moved outside the while loop
+ const void *decryptedDataRaw = sqlite3_column_blob(selectNodeAddDataAdditionalStmt, 0);
+ int decryptedDataSize = sqlite3_column_bytes(selectNodeAddDataAdditionalStmt, 0);
+ OwnedMemory decryptedData(new u8[decryptedDataSize], decryptedDataSize);
+ memcpy(decryptedData.data, decryptedDataRaw, decryptedDataSize);
+
+ const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &requestHash, timestamp, &creatorPublicKey, DataView(decryptedData.data, decryptedData.size));
+ database->onAddNodeCallbackFunc(addNodeRequest);
+ }
+ else if(operation == DatabaseOperation::ADD_USER)
+ {
+ if(!database->onAddUserCallbackFunc) continue;
+ sqlite3_reset(selectNodeAddUserDataStmt);
+ sqlite3_clear_bindings(selectNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddUserDataStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddUserDataStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddUserData failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ const void *userToAddPublicKeyRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 0);
+ Signature::PublicKey userToAddPublicKey((const char*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ const void *groupToAddUserToRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 1);
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
+ memcpy(groupToAddUserTo, groupToAddUserToRaw, GROUP_ID_LENGTH);
+
+ DatabaseAddUserRequest addUserRequest(&nodeHash, &requestHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH));
+ database->onAddUserCallbackFunc(addUserRequest);
}
-
- (*publicKeyUserDataMap)[user->getPublicKey()] = user;
}
}
- void DatabaseStorage::loadStorageCreate(sibs::SafeDeserializer &deserializer)
+ void DatabaseStorage::loadMetadataFromFile()
{
- u64 timestamp = deserializer.extract<u64>();
-
- u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- u8 groupId[GROUP_ID_LENGTH];
- deserializer.extract(groupId, GROUP_ID_LENGTH);
+ OwnedMemory metadataFileContent = fileGetContent(metadataFilePath);
+ sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size);
- u32 dataSize = deserializer.extract<u32>();
- u8 *data = new u8[dataSize];
- deserializer.extract(data, dataSize);
- u32 offsetToEncryptedData = deserializer.extract<u32>();
+ u16 storageVersion = deserializer.extract<u16>();
+ if(storageVersion != STORAGE_VERSION)
+ throw std::runtime_error("Wrong storage version!");
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ deserializer.extract(passwordSalt, PASSWORD_SALT_LEN);
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- string errMsg = "No groups exists in node ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
+ u16 privateKeySize = deserializer.extract<u16>();
+ dht::Blob privateKeyRaw;
+ privateKeyRaw.resize(privateKeySize);
+ deserializer.extract(&privateKeyRaw[0], privateKeySize);
+ identity.first = make_shared<dht::crypto::PrivateKey>(privateKeyRaw);
- auto groupIt = groupByIdMapIt->second->find(DataView(groupId, GROUP_ID_LENGTH));
- if(groupIt == groupByIdMapIt->second->end())
- {
- string errMsg = "User group with id ";
- errMsg += bin2hex((const char*)groupId, GROUP_ID_LENGTH);
- errMsg += " does not exist in node ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
+ u16 certificateSize = deserializer.extract<u16>();
+ dht::Blob certificateRaw;
+ certificateRaw.resize(certificateSize);
+ deserializer.extract(&certificateRaw[0], certificateSize);
+ identity.second = make_shared<dht::crypto::Certificate>(certificateRaw);
- DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(userPublicKey);
- databaseStorageObjectList->createdTimestamp = timestamp;
- databaseStorageObjectList->groups.push_back(groupIt->second);
- databaseStorageObjectList->data = DataView(data, dataSize);
- databaseStorageObjectList->offsetToEncryptedData = offsetToEncryptedData;
- storageMap[nodeHash] = databaseStorageObjectList;
+ assert(deserializer.empty());
}
- void DatabaseStorage::loadStorageAppend(sibs::SafeDeserializer &deserializer)
+ static void sqlite_step_rollback_on_failure(sqlite3 *db, sqlite3_stmt *stmt, const char *description)
{
- u64 timestamp = deserializer.extract<u64>();
-
- u8 creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- u32 dataSize = deserializer.extract<u32>();
- u8 *data = new u8[dataSize];
- deserializer.extract(data, dataSize);
-
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- Hash dataHash;
- deserializer.extract((u8*)dataHash.getData(), HASH_BYTE_SIZE);
-
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end())
+ int rc = sqlite3_step(stmt);
+ if(rc != SQLITE_DONE)
{
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found";
- throw DatabaseStorageCorrupt(errMsg);
+ string errMsg = description;
+ errMsg += " failed with error: ";
+ errMsg += sqlite3_errmsg(db);
+ sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+ if(rc == SQLITE_CONSTRAINT)
+ throw DatabaseStorageAlreadyExists(errMsg);
+ else
+ throw DatabaseStorageException(errMsg);
}
-
- DataView storageData { data, dataSize };
- DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(dataHash, storageData, timestamp, creatorPublicKey);
- storageIt->second->objects.push_back(databaseStorageObject);
- storedDataHash[dataHash] = databaseStorageObject;
}
- void DatabaseStorage::loadDataFromFile()
+ void DatabaseStorage::bindCheckError(int sqliteBindResult)
{
- if(!boost::filesystem::exists(dataFilePath)) return;
-
- OwnedMemory dataFileContent = fileGetContent(dataFilePath);
- sibs::SafeDeserializer deserializer((u8*)dataFileContent.data, dataFileContent.size);
-
- while(!deserializer.empty())
+ if(sqliteBindResult != SQLITE_OK)
{
- StorageType storageType = deserializer.extract<StorageType>();
- switch(storageType)
- {
- case STORAGE_TYPE_CREATE:
- loadStorageCreate(deserializer);
- break;
- case STORAGE_TYPE_APPEND:
- loadStorageAppend(deserializer);
- break;
- }
+ string errMsg = "Failed to bind param, error code: ";
+ errMsg += to_string(sqliteBindResult);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
}
- void DatabaseStorage::loadLocalUsersFromFile()
+ i64 DatabaseStorage::getNodeRowId(const Hash &nodeHash)
{
- if(!boost::filesystem::exists(localUsersFilePath)) return;
-
- OwnedMemory localUsersFileContent = fileGetContent(localUsersFilePath);
- sibs::SafeDeserializer deserializer((u8*)localUsersFileContent.data, localUsersFileContent.size);
+ sqlite3_reset(selectNodeIdStatement);
+ sqlite3_clear_bindings(selectNodeIdStatement);
- while(!deserializer.empty())
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeIdStatement, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeIdStatement);
+ if(rc != SQLITE_ROW)
{
- u8 usernameSize = deserializer.extract<u8>();
- string username;
- username.resize(usernameSize);
- deserializer.extract((u8*)&username[0], usernameSize);
-
- u8 userPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey((const char*)userPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- EncryptedPrivateKey encryptedPrivateKey;
- deserializer.extract(encryptedPrivateKey.nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- deserializer.extract(encryptedPrivateKey.encryptedPrivateKey, ENCRYPTION_CHECKSUM_BYTE_SIZE + PRIVATE_KEY_NUM_BYTES);
-
- nameLocalUsersMap[username] = LocalUserEncrypted::create(userPublicKey, encryptedPrivateKey, username);
+ string errMsg = "select Node id failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
+
+ return sqlite3_column_int64(selectNodeIdStatement, 0);
}
- void DatabaseStorage::loadNodeDecryptionKeysFromFile()
+ i64 DatabaseStorage::getNodeAddDataRowId(const Hash &requestHash)
{
- if(!boost::filesystem::exists(nodeDecryptionKeysFilePath)) return;
+ sqlite3_reset(selectNodeAddDataIdStatement);
+ sqlite3_clear_bindings(selectNodeAddDataIdStatement);
- OwnedMemory nodeKeysFileContent = fileGetContent(nodeDecryptionKeysFilePath);
- sibs::SafeDeserializer deserializer((u8*)nodeKeysFileContent.data, nodeKeysFileContent.size);
-
- while(!deserializer.empty())
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeAddDataIdStatement, 1, requestHash.getData(), requestHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddDataIdStatement);
+ if(rc != SQLITE_ROW)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- u8 *nodeKeyRaw = new u8[ENCRYPTION_KEY_BYTE_SIZE];
- deserializer.extract(nodeKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
- nodeDecryptionKeyMap[nodeHash] = make_shared<OwnedMemory>(nodeKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
+ string errMsg = "select NodeAddData id failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
}
+
+ return sqlite3_column_int64(selectNodeAddDataIdStatement, 0);
}
- void DatabaseStorage::loadDecryptedStorageCreate(sibs::SafeDeserializer &deserializer)
+ bool DatabaseStorage::doesNodeExist(const Hash &nodeHash) const
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end())
- {
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found";
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- u8 creatorNameLength = deserializer.extract<u8>();
- auto creator = getUserByPublicKey(nodeHash, storageIt->second->creatorPublicKey);
- if(!creator)
- {
- string errMsg = "User with public key ";
- errMsg += storageIt->second->creatorPublicKey.toString();
- errMsg += " does not exist in node with hash ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- creator->name.resize(creatorNameLength);
- deserializer.extract((u8*)&creator->name[0], creatorNameLength);
-
- u8 nodeNameLength = deserializer.extract<u8>();
- storageIt->second->nodeName.resize(nodeNameLength);
- deserializer.extract((u8*)&storageIt->second->nodeName[0], nodeNameLength);
- storageIt->second->isDecrypted = true;
+ SqlQuery query(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ return query.next();
}
- void DatabaseStorage::loadDecryptedStorageAddData(sibs::SafeDeserializer &deserializer)
+ bool DatabaseStorage::doesDataExist(const Hash &requestHash) const
{
- Hash requestHash;
- deserializer.extract((u8*)requestHash.getData(), HASH_BYTE_SIZE);
-
- auto storedDataIt = storedDataHash.find(requestHash);
- if(storedDataIt == storedDataHash.end())
- {
- string errMsg = "Database doesn't contain data with hash ";
- errMsg += requestHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
- }
-
- u32 decryptedDataSize = deserializer.extract<u32>();
- u8 *decryptedDataRaw = new u8[decryptedDataSize];
- deserializer.extract(decryptedDataRaw, decryptedDataSize);
-
- storedDataIt->second->decryptedObject.data.data = decryptedDataRaw;
- storedDataIt->second->decryptedObject.data.size = decryptedDataSize;
- storedDataIt->second->decryptedObject.operation = DatabaseOperation::ADD_DATA;
+ SqlQuery query(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", { DataView(requestHash.getData(), requestHash.getSize()) });
+ return query.next();
}
- void DatabaseStorage::loadDecryptedStorageAddUser(sibs::SafeDeserializer &deserializer)
+ void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const void *data, usize size)
{
- Hash nodeHash;
- deserializer.extract((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
-
- Hash requestHash;
- deserializer.extract((u8*)requestHash.getData(), HASH_BYTE_SIZE);
+ assert(creatorGroup->getUsers().size() == 1);
+ User *creator = (User*)creatorGroup->getUsers()[0];
- auto storedDataIt = storedDataHash.find(requestHash);
- if(storedDataIt == storedDataHash.end())
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
{
- string errMsg = "Database doesn't contain data with hash ";
- errMsg += requestHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
+ sqlite3_reset(insertNodeStmt);
+ sqlite3_clear_bindings(insertNodeStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeStmt, 1, hash.getData(), hash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeStmt, 2, timestamp);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeStmt, 3, creator->getPublicKey().getData(), creator->getPublicKey().getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeStmt, 4, creatorGroup->getId().data, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeStmt, "insert data into Node");
+ addGroup(hash, creatorGroup);
+ addUser(hash, creator->getPublicKey(), creatorGroup->getId());
}
-
- u8 addedUserPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract(addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey addedUserPublicKey((const char*)addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- auto addedUser = getUserByPublicKey(nodeHash, addedUserPublicKey);
- if(!addedUser)
{
- string errMsg = "User with public key ";
- errMsg += bin2hex((const char*)addedUserPublicKeyRaw, PUBLIC_KEY_NUM_BYTES).c_str();
- errMsg += " does not exist in node with hash ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageCorrupt(errMsg);
+ sqlite3_reset(insertNodeRawStmt);
+ sqlite3_clear_bindings(insertNodeRawStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeRawStmt, 1, getNodeRowId(hash));
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeRawStmt, 2, data, size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeRawStmt, "insert data into NodeRaw");
}
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
- u32 decryptedUserNameSize = deserializer.extract<u32>();
- addedUser->name.resize(decryptedUserNameSize);
- deserializer.extract((u8*)&addedUser->name[0], decryptedUserNameSize);
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(hash);
+ if(nodeDecryptionKeyResult.first)
+ decryptNodeData(hash, nodeDecryptionKeyResult.second, &creator->getPublicKey(), creatorGroup->getId(), timestamp);
}
- void DatabaseStorage::loadDecryptedDataFromFile()
+ void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView)
{
- if(!boost::filesystem::exists(decryptedDataFilePath)) return;
-
- OwnedMemory decryptedDataFileContent = fileGetContent(decryptedDataFilePath);
- sibs::SafeDeserializer deserializer((u8*)decryptedDataFileContent.data, decryptedDataFileContent.size);
-
- while(!deserializer.empty())
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
{
- DecryptedDataType decryptedDataType = deserializer.extract<DecryptedDataType>();
- switch(decryptedDataType)
- {
- case DecryptedDataType::STORAGE_CREATE:
- loadDecryptedStorageCreate(deserializer);
- break;
- case DecryptedDataType::STORAGE_ADD_DATA:
- loadDecryptedStorageAddData(deserializer);
- break;
- case DecryptedDataType::STORAGE_ADD_USER:
- loadDecryptedStorageAddUser(deserializer);
- break;
- }
+ sqlite3_reset(insertNodeAddDataStmt);
+ sqlite3_clear_bindings(insertNodeAddDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 2, dataHash.getData(), dataHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int(insertNodeAddDataStmt, 3, (u8)operation);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeAddDataStmt, 4, timestamp);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataStmt, 5, creatorPublicKey.getData(), creatorPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int(insertNodeAddDataStmt, 6, 0);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataStmt, "insert data into NodeAddData");
}
- }
-
- void DatabaseStorage::loadMetadataFromFile()
- {
- OwnedMemory metadataFileContent = fileGetContent(metadataFilePath);
- sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size);
- u16 storageVersion = deserializer.extract<u16>();
- if(storageVersion != STORAGE_VERSION)
- throw std::runtime_error("Wrong storage version!");
+ i64 nodeRowId = getNodeRowId(nodeHash);
+ i64 nodeAddRowId = getNodeAddDataRowId(dataHash);
- deserializer.extract(passwordSalt, PASSWORD_SALT_LEN);
- //string passwordSaltStr((const char*)passwordSalt, PASSWORD_SALT_LEN);
+ Signature::PublicKey userToAddPublicKey;
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
- u16 privateKeySize = deserializer.extract<u16>();
- dht::Blob privateKeyRaw;
- privateKeyRaw.resize(privateKeySize);
- deserializer.extract(&privateKeyRaw[0], privateKeySize);
- identity.first = make_shared<dht::crypto::PrivateKey>(privateKeyRaw);
-
- u16 certificateSize = deserializer.extract<u16>();
- dht::Blob certificateRaw;
- certificateRaw.resize(certificateSize);
- deserializer.extract(&certificateRaw[0], certificateSize);
- identity.second = make_shared<dht::crypto::Certificate>(certificateRaw);
-
- assert(deserializer.empty());
- }
-
- void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const u8 *data, usize dataSize, u32 offsetToEncryptedData)
- {
- if(storageMap.find(hash) != storageMap.end())
+ if(operation == DatabaseOperation::ADD_DATA)
{
- string errMsg = "Database storage with hash ";
- errMsg += hash.toString();
- errMsg += " already exists";
- throw DatabaseStorageAlreadyExists(errMsg);
+ sqlite3_reset(insertNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(insertNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddDataAdditionalStmt, 1, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataAdditionalStmt, 2, additionalDataView.data, additionalDataView.size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataAdditionalStmt, "insert data into NodeAddDataAdditional");
}
-
- addGroup(hash, creatorGroup);
- assert(creatorGroup->getUsers().size() == 1);
- User *creator = (User*)creatorGroup->getUsers()[0];
- addUser(hash, creator);
-
- DatabaseStorageObjectList *databaseStorageObjectList = new DatabaseStorageObjectList(creator->getPublicKey());
- databaseStorageObjectList->createdTimestamp = timestamp;
- databaseStorageObjectList->groups.push_back(creatorGroup);
- databaseStorageObjectList->data = DataView(new u8[dataSize], dataSize);
- memcpy(databaseStorageObjectList->data.data, data, dataSize);
- databaseStorageObjectList->offsetToEncryptedData = offsetToEncryptedData;
- storageMap[hash] = databaseStorageObjectList;
-
- sibs::SafeSerializer serializer;
- serializer.add(STORAGE_TYPE_CREATE);
- serializer.add(timestamp);
- serializer.add((u8*)creator->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
- serializer.add((u8*)creatorGroup->getId().data, GROUP_ID_LENGTH);
-
- serializer.add((u32)dataSize);
- serializer.add(data, dataSize);
- serializer.add(offsetToEncryptedData);
-
- serializer.add((u8*)hash.getData(), HASH_BYTE_SIZE);
-
- fileAppend(dataFilePath, { serializer.getBuffer().data(), serializer.getBuffer().size() });
-
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(hash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- decryptNodeData(hash, databaseStorageObjectList, nodeDecryptionKeyIt->second);
- }
-
- // TODO: Use encryptedDataView to remove duplicate of unsigning request, jumping straight to decrypting encrypted data and calling callback func
- void DatabaseStorage::appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, const User *creatorUser, u64 timestamp, const u8 *data, usize dataSize, const DataView &encryptedDataView)
- {
- auto it = storageMap.find(nodeHash);
- if(it == storageMap.end())
+ else if(operation == DatabaseOperation::ADD_USER)
{
- string errMsg = "Database storage with hash ";
- errMsg += nodeHash.toString();
- errMsg += " not found. Storage for a hash needs to be created before data can be appended to it";
- throw DatabaseStorageNotFound(errMsg);
+ try
+ {
+ sibs::SafeDeserializer deserializer((const u8*)additionalDataView.data, additionalDataView.size);
+ deserializer.extract((u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
+ deserializer.extract(groupToAddUserTo, GROUP_ID_LENGTH);
+
+ sqlite3_reset(insertNodeAddUserDataStmt);
+ sqlite3_clear_bindings(insertNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddUserDataStmt, 1, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddUserDataStmt, 2, userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddUserDataStmt, 3, groupToAddUserTo, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddUserDataStmt, "insert data into NodeAddUserData");
+ }
+ catch(sibs::DeserializeException &e)
+ {
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw e;
+ }
}
-
- auto storeDataHashResult = storedDataHash.find(dataHash);
- if(storeDataHashResult != storedDataHash.end())
+ else
{
- string errMsg = "Database already contains data with hash: ";
- errMsg += dataHash.toString();
- throw DatabaseStorageAlreadyExists(errMsg);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw std::runtime_error("Unexpected operation type");
}
- DataView storageData { new u8[dataSize], dataSize };
- memcpy(storageData.data, data, dataSize);
- DatabaseStorageObject *databaseStorageObject = new DatabaseStorageObject(dataHash, storageData, timestamp, creatorUser->getPublicKey());
- it->second->objects.push_back(databaseStorageObject);
- storedDataHash[dataHash] = databaseStorageObject;
-
- sibs::SafeSerializer serializer;
- serializer.add(STORAGE_TYPE_APPEND);
- serializer.add(timestamp);
- serializer.add((u8*)creatorUser->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
-
- serializer.add((u32)dataSize);
- serializer.add(data, dataSize);
-
- serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add((u8*)dataHash.getData(), HASH_BYTE_SIZE);
-
- fileAppend(dataFilePath, { serializer.getBuffer().data(), serializer.getBuffer().size() });
+ {
+ sqlite3_reset(insertNodeAddDataRawStmt);
+ sqlite3_clear_bindings(insertNodeAddDataRawStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(insertNodeAddDataRawStmt, 1, nodeRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(insertNodeAddDataRawStmt, 2, nodeAddRowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(insertNodeAddDataRawStmt, 3, data, size, SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeAddDataRawStmt, "insert data into NodeAddDataRaw");
+ }
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(nodeHash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- decryptNodeAppendedData(nodeHash, databaseStorageObject, nodeDecryptionKeyIt->second);
- }
-
- void DatabaseStorage::addToQuarantine(const Hash &dataHash, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const u8 *data, usize dataSize)
- {
- auto storeDataHashResult = storedDataHash.find(dataHash);
- if(storeDataHashResult != storedDataHash.end())
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash);
+ if(nodeDecryptionKeyResult.first)
{
- string errMsg = "Database already contains data with hash: ";
- errMsg += dataHash.toString();
- throw DatabaseStorageAlreadyExists(errMsg);
+ if(operation == DatabaseOperation::ADD_DATA)
+ decryptNodeAddData(nodeAddRowId, nodeHash, dataHash, timestamp, &creatorPublicKey, additionalDataView, nodeDecryptionKeyResult.second);
+ else if(operation == DatabaseOperation::ADD_USER)
+ decryptNodeAddUser(nodeAddRowId, nodeHash, dataHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH), nodeDecryptionKeyResult.second);
}
- DataView storageData { new u8[dataSize], dataSize };
- memcpy(storageData.data, data, dataSize);
- DatabaseStorageQuarantineObject *databaseQuarantineStorageObject = new DatabaseStorageQuarantineObject(storageData, timestamp, creatorPublicKey);
- quarantineStorageMap[creatorPublicKey].emplace_back(databaseQuarantineStorageObject);
- // TODO: Add quarantine object to storedDataHash
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
}
- bool DatabaseStorage::addGroup(const Hash &nodeHash, Group *group)
+ void DatabaseStorage::addGroup(const Hash &nodeHash, Group *group)
{
- DataViewMap<Group*> *groupByIdMap = nullptr;
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt == nodeGroupByIdMap.end())
- {
- groupByIdMap = new DataViewMap<Group*>();
- nodeGroupByIdMap[nodeHash] = groupByIdMap;
- }
- else
- groupByIdMap = groupByIdMapIt->second;
+ sqlite3_reset(insertGroupStmt);
+ sqlite3_clear_bindings(insertGroupStmt);
- if(groupByIdMap->find(group->getId()) != groupByIdMap->end())
- return false;
-
- (*groupByIdMap)[group->getId()] = group;
+ int rc;
+ rc = sqlite3_bind_blob(insertGroupStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- sibs::SafeSerializer serializer;
+ rc = sqlite3_bind_blob(insertGroupStmt, 2, group->getId().data, GROUP_ID_LENGTH, SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ rc = sqlite3_bind_int(insertGroupStmt, 3, group->getPermission().getPermissionLevel());
+ bindCheckError(rc);
- serializer.add((u8)group->getName().size());
- serializer.add((u8*)group->getName().data(), group->getName().size());
+ rc = sqlite3_bind_int64(insertGroupStmt, 4, group->getPermission().getPermissionFlags());
+ bindCheckError(rc);
- serializer.add((u8*)group->getId().data, GROUP_ID_LENGTH);
-
- serializer.add(group->getPermission().getPermissionLevel());
- serializer.add(group->getPermission().getPermissionFlags());
-
- fileAppend(groupsFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
-
+ sqlite_step_rollback_on_failure(sqliteDb, insertGroupStmt, "insert data into NodeGroup");
Log::debug("Created group %s in node %s", bin2hex((const char*)group->getId().data, GROUP_ID_LENGTH).c_str(), nodeHash.toString().c_str());
-
- return true;
}
- bool DatabaseStorage::addUser(const Hash &nodeHash, User *user)
+ void DatabaseStorage::addUserToGroup(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId)
{
- Signature::MapPublicKey<User*> *publicKeyUserDataMap = nullptr;
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt == nodePublicKeyUserDataMap.end())
- {
- publicKeyUserDataMap = new Signature::MapPublicKey<User*>();
- nodePublicKeyUserDataMap[nodeHash] = publicKeyUserDataMap;
- }
- else
- publicKeyUserDataMap = publicKeyUserDataMapIt->second;
+ sqlite3_reset(insertNodeUserGroupAssocStmt);
+ sqlite3_clear_bindings(insertNodeUserGroupAssocStmt);
- if(publicKeyUserDataMap->find(user->getPublicKey()) != publicKeyUserDataMap->end())
- return false;
+ int rc;
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- (*publicKeyUserDataMap)[user->getPublicKey()] = user;
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- // TODO: Instead of directly saving user to file, maybe user should be added to buffer and then save to file after we have several users (performance improvement)
+ rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 3, groupId.data, groupId.size, SQLITE_STATIC);
+ bindCheckError(rc);
- sibs::SafeSerializer serializer;
-
- serializer.add((u8*)nodeHash.getData(), HASH_BYTE_SIZE);
+ sqlite_step_rollback_on_failure(sqliteDb, insertNodeUserGroupAssocStmt, "insert data into NodeUserGroupAssoc");
+ }
+
+ void DatabaseStorage::addUser(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId)
+ {
+ sqlite3_reset(insertUserStmt);
+ sqlite3_clear_bindings(insertUserStmt);
- serializer.add((u8)user->getName().size());
- serializer.add((u8*)user->getName().data(), user->getName().size());
+ int rc;
+ rc = sqlite3_bind_blob(insertUserStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8*)user->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
+ rc = sqlite3_bind_blob(insertUserStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
- serializer.add((u8)user->getGroups().size());
- for(Group *group : user->getGroups())
- {
- serializer.add((u8*)group->getId().data, GROUP_ID_LENGTH);
- }
+ sqlite_step_rollback_on_failure(sqliteDb, insertUserStmt, "insert data into NodeUser");
- fileAppend(usersFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
-
- Log::debug("Created user %s in node %s", user->getPublicKey().toString().c_str(), nodeHash.toString().c_str());
+ addUserToGroup(nodeHash, userPublicKey, groupId);
- return true;
+ Log::debug("Created user %s in node %s", userPublicKey.toString().c_str(), nodeHash.toString().c_str());
}
- /*
- u64 DatabaseStorage::increaseUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey)
+ void DatabaseStorage::fetchNodeRaw(const Hash &nodeHash, FetchNodeRawCallbackFunc callbackFunc)
{
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
+ SqlQuery query(sqliteDb, "SELECT data FROM NodeRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(query.next())
{
- auto it = publicKeyUserDataMapIt->second->find(userPublicKey);
- if(it != publicKeyUserDataMapIt->second->end())
- {
- return ++it->second->actionCounter;
- }
-
- string errMsg = "User with id ";
- errMsg += userPublicKey.toString();
- errMsg += " doesn't exist in node ";
- errMsg += nodeHash.toString();
- throw DatabaseStorageException(errMsg);
- }
-
- string errMsg = "Node with id ";
- errMsg += nodeHash.toString();
- errMsg += " doesn't exist";
- throw DatabaseStorageException(errMsg);
- }
- */
-
- const DatabaseStorageObjectList* DatabaseStorage::getStorage(const Hash &hash) const
- {
- auto it = storageMap.find(hash);
- if(it != storageMap.end())
- return it->second;
- return nullptr;
- }
-
- const DataViewMap<Group*>* DatabaseStorage::getNodeGroups(const Hash &nodeHash)
- {
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt != nodeGroupByIdMap.end())
- return groupByIdMapIt->second;
- return nullptr;
- }
-
- Group* DatabaseStorage::getGroupById(const Hash &nodeHash, uint8_t groupId[GROUP_ID_LENGTH]) const
- {
- auto groupByIdMapIt = nodeGroupByIdMap.find(nodeHash);
- if(groupByIdMapIt != nodeGroupByIdMap.end())
- {
- auto it = groupByIdMapIt->second->find(DataView(groupId, GROUP_ID_LENGTH));
- if(it != groupByIdMapIt->second->end())
- return it->second;
+ const DataView data = query.getBlob(0);
+ callbackFunc(data);
}
- return nullptr;
}
- const Signature::MapPublicKey<User*>* DatabaseStorage::getNodeUsers(const Hash &nodeHash)
+ void DatabaseStorage::fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc)
{
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
- return publicKeyUserDataMapIt->second;
- return nullptr;
- }
-
- User* DatabaseStorage::getUserByPublicKey(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const
- {
- auto publicKeyUserDataMapIt = nodePublicKeyUserDataMap.find(nodeHash);
- if(publicKeyUserDataMapIt != nodePublicKeyUserDataMap.end())
+ SqlQuery query(sqliteDb, "SELECT data FROM NodeAddDataRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) });
+ while(query.next())
{
- auto it = publicKeyUserDataMapIt->second->find(userPublicKey);
- if(it != publicKeyUserDataMapIt->second->end())
- return it->second;
+ const DataView data = query.getBlob(0);
+ callbackFunc(data);
}
- return nullptr;
}
-
+#if 0
bool DatabaseStorage::storeLocalUser(const string &username, const Signature::KeyPair &keyPair, const string &password)
{
auto it = nameLocalUsersMap.find(username);
@@ -774,7 +686,7 @@ namespace odhtdb
assert(sizeof(userEncryptedPrivateKey.encryptedPrivateKey) == encryptedPrivateKey.getCipherText().size);
memcpy(userEncryptedPrivateKey.encryptedPrivateKey, encryptedPrivateKey.getCipherText().data, encryptedPrivateKey.getCipherText().size);
- LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey, username);
+ LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey);
nameLocalUsersMap[username] = localUserEncrypted;
sibs::SafeSerializer serializer;
@@ -817,271 +729,288 @@ namespace odhtdb
throw DatabaseStorageWrongPassword(errMsg);
}
}
-
- vector<NodeLocalUser> DatabaseStorage::getLocalNodeUsers(const Signature::KeyPair &keyPair)
- {
- vector<NodeLocalUser> localUsers;
-
- for(auto nodeIt : nodePublicKeyUserDataMap)
- {
- auto userIt = nodeIt.second->find(keyPair.getPublicKey());
- if(userIt != nodeIt.second->end())
- {
- User *user = userIt->second;
- if(user->getType() != User::Type::LOCAL)
- {
- LocalUser *localUser = LocalUser::create(keyPair, user->getName(), nullptr);
- for(Group *group : user->getGroups())
- {
- localUser->addToGroup(group);
- }
-
- (*nodeIt.second)[keyPair.getPublicKey()] = localUser;
- localUsers.push_back({ nodeIt.first, localUser });
- delete user;
- }
- else
- localUsers.push_back({ nodeIt.first, static_cast<LocalUser*>(user) });
- }
- }
-
- return localUsers;
- }
-
- DatabaseStorageObject* DatabaseStorage::getDataById(const Hash &dataHash)
- {
- auto storageIt = storedDataHash.find(dataHash);
- if(storageIt != storedDataHash.end())
- return storageIt->second;
- return nullptr;
- }
-
- std::pair<bool, std::shared_ptr<OwnedMemory>> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash)
+#endif
+ pair<bool, shared_ptr<OwnedMemory>> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash)
{
- auto nodeDecryptionKeyIt = nodeDecryptionKeyMap.find(nodeHash);
- if(nodeDecryptionKeyIt != nodeDecryptionKeyMap.end())
- return make_pair(true, nodeDecryptionKeyIt->second);
- return make_pair(false, make_shared<OwnedMemory>());
+ sqlite3_reset(getNodeDecryptionKeyStmt);
+ sqlite3_clear_bindings(getNodeDecryptionKeyStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(getNodeDecryptionKeyStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(getNodeDecryptionKeyStmt);
+ if(rc != SQLITE_ROW)
+ return make_pair(false, make_shared<OwnedMemory>());
+
+ const void *decryptionKeyRaw = sqlite3_column_blob(getNodeDecryptionKeyStmt, 0);
+ u8 *decryptionKeyRawCopy = new u8[ENCRYPTION_KEY_BYTE_SIZE];
+ memcpy(decryptionKeyRawCopy, decryptionKeyRaw, ENCRYPTION_KEY_BYTE_SIZE);
+ shared_ptr<OwnedMemory> decryptionKey = make_shared<OwnedMemory>(decryptionKeyRawCopy, ENCRYPTION_KEY_BYTE_SIZE);
+ return make_pair(true, decryptionKey);
}
void DatabaseStorage::setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKeyView)
{
- bool nodeHasExistingEncryptionKey = nodeDecryptionKeyMap.find(nodeHash) != nodeDecryptionKeyMap.end();
+ auto nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash);
+ bool nodeHasExistingEncryptionKey = nodeDecryptionKeyResult.first;
- char *decryptionKeyRaw = new char[decryptionKeyView.size];
- memcpy(decryptionKeyRaw, decryptionKeyView.data, decryptionKeyView.size);
- shared_ptr<OwnedMemory> decryptionKey = make_shared<OwnedMemory>(decryptionKeyRaw, decryptionKeyView.size);
- nodeDecryptionKeyMap[nodeHash] = decryptionKey;
+ sqlite3_reset(setNodeDecryptionKeyStmt);
+ sqlite3_clear_bindings(setNodeDecryptionKeyStmt);
- sibs::SafeSerializer serializer;
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)decryptionKeyView.data, ENCRYPTION_KEY_BYTE_SIZE);
- fileAppend(nodeDecryptionKeysFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
+ int rc;
+ rc = sqlite3_bind_blob(setNodeDecryptionKeyStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_blob(setNodeDecryptionKeyStmt, 2, decryptionKeyView.data, decryptionKeyView.size, SQLITE_STATIC);
+ bindCheckError(rc);
- auto storageIt = storageMap.find(nodeHash);
- if(storageIt == storageMap.end()) return;
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeDecryptionKeyStmt, "insert or replace node decryption key");
// When changing existing encryption key, do not decrypt the existing data as it has already been decrypted,
// the new key should only be used for new data
if(!nodeHasExistingEncryptionKey)
- decryptNodeData(nodeHash, storageIt->second, decryptionKey);
+ decryptNodeData(nodeHash, nodeDecryptionKeyResult.second);
}
- bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, DatabaseStorageObjectList *databaseCreateObject, const shared_ptr<OwnedMemory> decryptionKey)
+ bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, const shared_ptr<OwnedMemory> decryptionKey)
{
- sibs::SafeDeserializer deserializer((u8*)databaseCreateObject->data.data + databaseCreateObject->offsetToEncryptedData, databaseCreateObject->data.size - databaseCreateObject->offsetToEncryptedData);
-
- u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
- deserializer.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
-
- DataView dataToDecrypt((void*)deserializer.getBuffer(), deserializer.getSize());
- Decryption decryptedBody(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
- sibs::SafeDeserializer bodyDeserializer((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
-
- u8 creatorNameLength = bodyDeserializer.extract<u8>();
- string creatorName; // TODO: Add this user name to storage creator name
- creatorName.resize(creatorNameLength);
- bodyDeserializer.extract((u8*)&creatorName[0], creatorNameLength);
+ sqlite3_reset(selectNodeStmt);
+ sqlite3_clear_bindings(selectNodeStmt);
- u8 nameLength = bodyDeserializer.extract<u8>();
- string name;
- name.resize(nameLength);
- bodyDeserializer.extract((u8*)&name[0], nameLength);
-
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_CREATE);
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add(creatorNameLength);
- serializer.add((const u8*)creatorName.data(), creatorNameLength);
- serializer.add(nameLength);
- serializer.add((const u8*)name.data(), nameLength);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
-
- User *creator = getUserByPublicKey(nodeHash, databaseCreateObject->creatorPublicKey);
- if(!creator)
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeStmt);
+ if(rc == SQLITE_DONE)
+ return true;
+ else if(rc != SQLITE_ROW)
{
- Log::error("Creator with public key %s does not exist in node %s", databaseCreateObject->creatorPublicKey.toString().c_str(), nodeHash.toString().c_str());
- return false;
+ string errMsg = "select node failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ throw DatabaseStorageException(errMsg);
}
- creator->name = move(creatorName);
- databaseCreateObject->nodeName = name;
- databaseCreateObject->isDecrypted = true;
- Log::debug("Deserialized node create data, name: %s", name.c_str());
- const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, databaseCreateObject->createdTimestamp, creator, move(name));
+ u64 timestamp = sqlite3_column_int64(selectNodeStmt, 0);
+ const void *creatorPublicKeyRaw = sqlite3_column_blob(selectNodeStmt, 1);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ const void *adminGroupIdRaw = sqlite3_column_blob(selectNodeStmt, 2);
+ u8 adminGroup[GROUP_ID_LENGTH];
+ memcpy(adminGroup, adminGroupIdRaw, GROUP_ID_LENGTH);
+
+ return decryptNodeData(nodeHash, decryptionKey, &creatorPublicKey, DataView(adminGroup, GROUP_ID_LENGTH), timestamp);
+ }
+
+ bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, const shared_ptr<OwnedMemory> decryptionKey, const Signature::PublicKey *creatorPublicKey, const DataView &adminGroupId, u64 timestamp)
+ {
+ const DatabaseCreateNodeRequest createNodeRequest(&nodeHash, timestamp, creatorPublicKey, adminGroupId);
if(database->onCreateNodeCallbackFunc)
database->onCreateNodeCallbackFunc(createNodeRequest);
+ sqlite3_reset(selectNodeAddDataByNodeStmt);
+ sqlite3_clear_bindings(selectNodeAddDataByNodeStmt);
+
+ int rc;
+ rc = sqlite3_bind_blob(selectNodeAddDataByNodeStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC);
+ bindCheckError(rc);
+
+ sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0);
bool success = true;
- for(auto appendObject : databaseCreateObject->objects)
+ while(true)
{
- bool appendObjectResult = decryptNodeAppendedData(nodeHash, appendObject, decryptionKey);
- if(!appendObjectResult)
- success = false;
+ rc = sqlite3_step(selectNodeAddDataByNodeStmt);
+ if(rc == SQLITE_ROW)
+ {
+ i64 rowId = sqlite3_column_int64(selectNodeAddDataByNodeStmt, 0);
+
+ const void *requestHashRaw = sqlite3_column_blob(selectNodeAddDataByNodeStmt, 1);
+ Hash requestHash;
+ memcpy(requestHash.getData(), requestHashRaw, HASH_BYTE_SIZE);
+
+ DatabaseOperation operation = (DatabaseOperation)sqlite3_column_int(selectNodeAddDataByNodeStmt, 2);
+
+ u64 timestamp = sqlite3_column_int64(selectNodeAddDataByNodeStmt, 3);
+
+ const void *creatorPublicKeyRaw = sqlite3_column_blob(selectNodeAddDataByNodeStmt, 4);
+ Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ if(operation == DatabaseOperation::ADD_DATA)
+ {
+ sqlite3_reset(selectNodeAddDataAdditionalStmt);
+ sqlite3_clear_bindings(selectNodeAddDataAdditionalStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddDataAdditionalStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddDataAdditionalStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddDataAdditional failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ // TODO: There is no need to allocate/deallocate several times, this can be moved outside the while loop
+ const void *encryptedDataRaw = sqlite3_column_blob(selectNodeAddDataAdditionalStmt, 0);
+ int encryptedDataSize = sqlite3_column_bytes(selectNodeAddDataAdditionalStmt, 0);
+ OwnedMemory encryptedData(new u8[encryptedDataSize], encryptedDataSize);
+ memcpy(encryptedData.data, encryptedDataRaw, encryptedDataSize);
+
+ bool appendObjectResult = decryptNodeAddData(rowId, nodeHash, requestHash, timestamp, &creatorPublicKey, DataView(encryptedData.data, encryptedData.size), decryptionKey);
+ if(!appendObjectResult)
+ success = false;
+ }
+ else if(operation == DatabaseOperation::ADD_USER)
+ {
+ sqlite3_reset(selectNodeAddUserDataStmt);
+ sqlite3_clear_bindings(selectNodeAddUserDataStmt);
+
+ int rc;
+ rc = sqlite3_bind_int64(selectNodeAddUserDataStmt, 1, rowId);
+ bindCheckError(rc);
+
+ rc = sqlite3_step(selectNodeAddUserDataStmt);
+ if(rc != SQLITE_ROW)
+ {
+ string errMsg = "select NodeAddUserData failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
+
+ const void *userToAddPublicKeyRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 0);
+ Signature::PublicKey userToAddPublicKey((const char*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ const void *groupToAddUserToRaw = sqlite3_column_blob(selectNodeAddUserDataStmt, 1);
+ u8 groupToAddUserTo[GROUP_ID_LENGTH];
+ memcpy(groupToAddUserTo, groupToAddUserToRaw, GROUP_ID_LENGTH);
+
+ bool appendObjectResult = decryptNodeAddUser(rowId, nodeHash, requestHash, timestamp, &creatorPublicKey, &userToAddPublicKey, DataView(groupToAddUserTo, GROUP_ID_LENGTH), decryptionKey);
+ if(!appendObjectResult)
+ success = false;
+ }
+ }
+ else if(rc == SQLITE_DONE)
+ {
+ break;
+ }
+ else
+ {
+ string errMsg = "select NodeAddData by node failed with error: ";
+ errMsg += sqlite3_errmsg(sqliteDb);
+ sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0);
+ throw DatabaseStorageException(errMsg);
+ }
}
+ sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0);
+
return success;
}
- bool DatabaseStorage::decryptNodeAppendedData(const Hash &nodeHash, DatabaseStorageObject *databaseAppendObject, const shared_ptr<OwnedMemory> decryptionKey)
+ void DatabaseStorage::setNodeAddDataDecrypted(i64 rowId)
{
- sibs::SafeDeserializer deserializer((u8*)databaseAppendObject->data.data, databaseAppendObject->data.size);
- char creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializer.extract((u8*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey creatorPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ sqlite3_reset(setNodeAddDataDecryptedStmt);
+ sqlite3_clear_bindings(setNodeAddDataDecryptedStmt);
- DataView signedData((void*)deserializer.getBuffer(), deserializer.getSize());
- string unsignedData = creatorPublicKey.unsign(signedData);
- sibs::SafeDeserializer deserializerUnsigned((u8*)unsignedData.data(), unsignedData.size());
+ int rc;
+ rc = sqlite3_bind_int(setNodeAddDataDecryptedStmt, 1, 1);
+ bindCheckError(rc);
+
+ rc = sqlite3_bind_int64(setNodeAddDataDecryptedStmt, 2, rowId);
+ bindCheckError(rc);
- u16 packetStructureVersion = deserializerUnsigned.extract<u16>();
- u64 creationDate = deserializerUnsigned.extract<u64>();
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeAddDataDecryptedStmt, "set NodeAddData decrypted");
+ }
+
+ void DatabaseStorage::setNodeAddDataDecryptedData(i64 rowId, const DataView &decryptedData)
+ {
+ setNodeAddDataDecrypted(rowId);
+ sqlite3_reset(setNodeAddDataAdditionalDataStmt);
+ sqlite3_clear_bindings(setNodeAddDataAdditionalDataStmt);
- DatabaseOperation operation = deserializerUnsigned.extract<DatabaseOperation>();
+ int rc;
+ rc = sqlite3_bind_blob(setNodeAddDataAdditionalDataStmt, 1, decryptedData.data, decryptedData.size, SQLITE_STATIC);
+ bindCheckError(rc);
- auto creatorUser = getUserByPublicKey(nodeHash, creatorPublicKey);
- if(!creatorUser)
+ rc = sqlite3_bind_int64(setNodeAddDataAdditionalDataStmt, 2, rowId);
+ bindCheckError(rc);
+
+ sqlite_step_rollback_on_failure(sqliteDb, setNodeAddDataAdditionalDataStmt, "set NodeAddData decrypted");
+ }
+
+ bool DatabaseStorage::decryptNodeAddData(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const DataView &encryptedData, const shared_ptr<OwnedMemory> decryptionKey)
+ {
+ SqlQuery queryCreatorGroupWithRightsToAddData(sqliteDb,
+ "SELECT nodeGroup.rowid FROM NodeUserGroupAssoc AS userGroupAssoc"
+ " INNER JOIN NodeGroup AS nodeGroup ON nodeGroup.groupId = userGroupAssoc.groupId"
+ " WHERE userGroupAssoc.node = ? AND userGroupAssoc.userPublicKey = ? AND (nodeGroup.permissionFlags & ?) != 0",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)creatorPublicKey->getData(), creatorPublicKey->getSize()), (i64)PermissionType::ADD_DATA });
+ if(!queryCreatorGroupWithRightsToAddData.next())
{
- Log::error("User with public key %s does not exist in node %s", creatorPublicKey.toString().c_str(), nodeHash.toString().c_str());
+ // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
+ // or we haven't received the packet that modifies group with the permission to perform the operation.
+ // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
+ // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
+ // How to handle this?
+ Log::error("User %s is not allowed to add data to node %s", creatorPublicKey->toString().c_str(), nodeHash.toString().c_str());
return false;
}
-
- if(operation == DatabaseOperation::ADD_DATA)
+
+ sibs::SafeDeserializer deserializer((const u8*)encryptedData.data, encryptedData.size);
+
+ u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
+ deserializer.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
+ DataView dataToDecrypt((void*)deserializer.getBuffer(), deserializer.getSize());
+ Decryption decryptedBody(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
+ setNodeAddDataDecryptedData(rowId, decryptedBody.getDecryptedText());
+
+ Log::debug("Got add object, timestamp: %zu, data: %.*s", timestamp, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data);
+ const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &dataHash, timestamp, creatorPublicKey, decryptedBody.getDecryptedText());
+ if(database->onAddNodeCallbackFunc)
+ database->onAddNodeCallbackFunc(addNodeRequest);
+ return true;
+ }
+
+ bool DatabaseStorage::decryptNodeAddUser(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const Signature::PublicKey *userToAddPublicKey, const DataView &groupToAddUserTo, const shared_ptr<OwnedMemory> decryptionKey)
+ {
+ SqlQuery queryGroupToAdd(sqliteDb, "SELECT permissionLevel FROM NodeGroup WHERE groupId = ?", { groupToAddUserTo });
+ if(!queryGroupToAdd.next())
{
- if(deserializerUnsigned.getSize() < ENCRYPTION_NONCE_BYTE_SIZE)
- {
- Log::error("Unsigned encrypted body is too small (unable to extract nonce)");
- return false;
- }
-#if 0
- if(!creatorUser->isAllowedToPerformAction(PermissionType::ADD_DATA))
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- Log::error("User %s is not allowed to add data to node %s", creatorUser->getName().c_str(), nodeHash.toString().c_str());
- return false;
- }
-#endif
-
- u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
- deserializerUnsigned.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize());
- Decryption decryptedBody(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
-
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_ADD_DATA);
- serializer.add((const u8*)databaseAppendObject->requestHash.getData(), HASH_BYTE_SIZE);
- serializer.add((u32)decryptedBody.getDecryptedText().size);
- serializer.add((const u8*)decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
- databaseAppendObject->decryptedObject.data.data = new char[decryptedBody.getDecryptedText().size];
- memcpy(databaseAppendObject->decryptedObject.data.data, decryptedBody.getDecryptedText().data, decryptedBody.getDecryptedText().size);
- databaseAppendObject->decryptedObject.data.size = decryptedBody.getDecryptedText().size;
- databaseAppendObject->decryptedObject.operation = operation;
-
- Log::debug("Got add object, timestamp: %zu, data: %.*s", creationDate, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data);
- const DatabaseAddNodeRequest addNodeRequest(&nodeHash, &databaseAppendObject->requestHash, creationDate, creatorUser, decryptedBody.getDecryptedText());
- if(database->onAddNodeCallbackFunc)
- database->onAddNodeCallbackFunc(addNodeRequest);
- }
- else if(operation == DatabaseOperation::ADD_USER)
- {
- u8 nameLength = deserializerUnsigned.extract<u8>();
-
- u8 nonce[ENCRYPTION_NONCE_BYTE_SIZE];
- deserializerUnsigned.extract(nonce, ENCRYPTION_NONCE_BYTE_SIZE);
- DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength);
- Decryption decryptedUserName(dataToDecrypt, DataView(nonce, ENCRYPTION_NONCE_BYTE_SIZE), DataView(decryptionKey->data, ENCRYPTION_KEY_BYTE_SIZE));
-
- string username;
- username.resize(nameLength);
- assert(decryptedUserName.getDecryptedText().size == nameLength);
- memcpy(&username[0], decryptedUserName.getDecryptedText().data, nameLength);
-
- sibs::SafeDeserializer deserializerSkippedEncryptedData(deserializerUnsigned.getBuffer() + ENCRYPTION_CHECKSUM_BYTE_SIZE + nameLength, PUBLIC_KEY_NUM_BYTES + GROUP_ID_LENGTH);
-
- char userToAddPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
- deserializerSkippedEncryptedData.extract((u8*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userToAddPublicKey(userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
-
- uint8_t groupId[GROUP_ID_LENGTH];
- deserializerSkippedEncryptedData.extract(groupId, GROUP_ID_LENGTH);
-
- auto group = getGroupById(nodeHash, groupId);
- if(!group)
- {
- // TODO: Add to quarantine?
- Log::error("There is no group with id %s in node %s", bin2hex((const char*)groupId, GROUP_ID_LENGTH).c_str(), nodeHash.toString().c_str());
- return false;
- }
-
- auto user = getUserByPublicKey(nodeHash, userToAddPublicKey);
- if(!user)
- {
- Log::error("User to add with public key %s doesn't exist in node %s", userToAddPublicKey.toString().c_str(), nodeHash.toString().c_str());
- return false;
- }
-#if 0
- auto creatorUserGroupWithRights = getGroupWithRightsToAddUserToGroup(creatorUser->getGroups(), group);
- if(!creatorUserGroupWithRights)
- {
- // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
- // or we haven't received the packet that modifies group with the permission to perform the operation.
- // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
- // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
- // How to handle this?
- string errMsg = "User ";
- errMsg += creatorUser->getName();
- errMsg += " is not allowed to perform the operation: ";
- errMsg += to_string((u8)operation);
- throw PermissionDeniedException(errMsg);
- }
-#endif
- sibs::SafeSerializer serializer;
- serializer.add(DecryptedDataType::STORAGE_ADD_USER);
- serializer.add((const u8*)nodeHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)databaseAppendObject->requestHash.getData(), HASH_BYTE_SIZE);
- serializer.add((const u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
- serializer.add((u32)decryptedUserName.getDecryptedText().size);
- serializer.add((const u8*)decryptedUserName.getDecryptedText().data, decryptedUserName.getDecryptedText().size);
- fileAppend(decryptedDataFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size()));
- databaseAppendObject->decryptedObject.data.data = new char[decryptedUserName.getDecryptedText().size];
- memcpy(databaseAppendObject->decryptedObject.data.data, decryptedUserName.getDecryptedText().data, decryptedUserName.getDecryptedText().size);
- databaseAppendObject->decryptedObject.data.size = decryptedUserName.getDecryptedText().size;
- databaseAppendObject->decryptedObject.operation = operation;
-
- Log::debug("Got add user object, timestamp: %zu, user added: %.*s", creationDate, nameLength, username.c_str());
- DatabaseAddUserRequest addUserRequest(&nodeHash, &databaseAppendObject->requestHash, creationDate, creatorUser, user, group);
- if(database->onAddUserCallbackFunc)
- database->onAddUserCallbackFunc(addUserRequest);
+ // TODO: Add to quarantine?
+ Log::error("There is no group with id %s in node %s", bin2hex((const char*)groupToAddUserTo.data, groupToAddUserTo.size).c_str(), nodeHash.toString().c_str());
+ return false;
}
- else
+
+ int groupToAddPermissionLevel = queryGroupToAdd.getInt(0);
+
+ SqlQuery queryCreatorGroupWithRightsToAddUserToGroup(sqliteDb,
+ "SELECT nodeGroup.rowid FROM NodeUserGroupAssoc AS userGroupAssoc"
+ " INNER JOIN NodeGroup AS nodeGroup ON nodeGroup.groupId = userGroupAssoc.groupId"
+ " WHERE userGroupAssoc.node = ? AND userGroupAssoc.userPublicKey = ? AND (nodeGroup.permissionLevel = ? AND ((nodeGroup.permissionFlags & ?) != 0) OR (nodeGroup.permissionLevel > ? AND (nodeGroup.permissionFlags & ?) != 0))",
+ { DataView(nodeHash.getData(), nodeHash.getSize()), DataView((void*)creatorPublicKey->getData(), creatorPublicKey->getSize()), groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_SAME_LEVEL, groupToAddPermissionLevel, (i64)PermissionType::ADD_USER_HIGHER_LEVEL });
+ if(!queryCreatorGroupWithRightsToAddUserToGroup.next())
{
- Log::error("Got unexpected operation %d", operation);
- return false;
+ // TODO: User might have permission to perform operation, but we haven't got the packet that adds user to the group with the permission,
+ // or we haven't received the packet that modifies group with the permission to perform the operation.
+ // This also means that an user can be in a group that has permission to perform the operation and then later be removed from it,
+ // and remote peers would accept our request to perform operation if they haven't received the operation that removes the user from the group.
+ // How to handle this?
+ string errMsg = "User ";
+ errMsg += creatorPublicKey->toString();
+ errMsg += " is not allowed to perform the operation: ADD USER";
+ throw PermissionDeniedException(errMsg);
}
+
+ addUser(nodeHash, *userToAddPublicKey, groupToAddUserTo);
+ setNodeAddDataDecrypted(rowId);
+
+ Log::debug("Got add user object, timestamp: %zu, user added: %s", timestamp, userToAddPublicKey->toString().c_str());
+ DatabaseAddUserRequest addUserRequest(&nodeHash, &dataHash, timestamp, creatorPublicKey, userToAddPublicKey, groupToAddUserTo);
+ if(database->onAddUserCallbackFunc)
+ database->onAddUserCallbackFunc(addUserRequest);
return true;
}
@@ -1092,23 +1021,7 @@ namespace odhtdb
void DatabaseStorage::update()
{
- // TODO: Modify this to iterate backwards. Because list is sorted in order of timestamp, we can remove data in range
auto time = chrono::high_resolution_clock::now().time_since_epoch();
auto timeMicroseconds = chrono::duration_cast<chrono::microseconds>(time).count();
- for(auto mapIt = quarantineStorageMap.begin(); mapIt != quarantineStorageMap.end(); )
- {
- for(auto vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); )
- {
- if(timeMicroseconds - (*vecIt)->storedTimestamp > QUARANTINE_STORAGE_TIME_MICROSECONDS)
- vecIt = mapIt->second.erase(vecIt);
- else
- ++vecIt;
- }
-
- if(mapIt->second.empty())
- mapIt = quarantineStorageMap.erase(mapIt);
- else
- ++mapIt;
- }
}
}