#include "../include/odhtdb/DatabaseStorage.hpp" #include "../include/odhtdb/RemoteUser.hpp" #include "../include/odhtdb/LocalUser.hpp" #include "../include/odhtdb/LocalUserEncrypted.hpp" #include "../include/odhtdb/Group.hpp" #include "../include/odhtdb/FileUtils.hpp" #include "../include/odhtdb/bin2hex.hpp" #include "../include/odhtdb/PasswordHash.hpp" #include "../include/odhtdb/Log.hpp" #include "../include/odhtdb/Database.hpp" #include "../include/odhtdb/sql/SqlQuery.hpp" #include #include #include #include #include #include using namespace std; namespace odhtdb { enum StorageType : u8 { STORAGE_TYPE_CREATE, STORAGE_TYPE_APPEND }; const u64 QUARANTINE_STORAGE_TIME_MICROSECONDS = 60 * 1.0e6; const u16 STORAGE_VERSION = 3; DatabaseStorageObject::DatabaseStorageObject(const Hash &_requestHash, DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) : requestHash(_requestHash), data(_data), createdTimestamp(_timestamp), creatorPublicKey(_creatorPublicKey) { } DatabaseStorageQuarantineObject::DatabaseStorageQuarantineObject(DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) : data(_data), createdTimestamp(_timestamp), creatorPublicKey(_creatorPublicKey) { auto time = chrono::high_resolution_clock::now().time_since_epoch(); storedTimestamp = chrono::duration_cast(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), 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; } catch(FileException &e) { if(metadataLoaded) { string errMsg = "Failed to load storage, reason: "; errMsg += e.what(); throw DatabaseStorageCorrupt(errMsg); } else { Log::warn("Failed to load storage meta data, reason: %s. Ignoring... (new storage probably)", e.what()); sibs::SafeSerializer metadataSerializer; metadataSerializer.add(STORAGE_VERSION); randombytes_buf(passwordSalt, PASSWORD_SALT_LEN); metadataSerializer.add(passwordSalt, PASSWORD_SALT_LEN); //string passwordSaltStr((const char*)passwordSalt, PASSWORD_SALT_LEN); identity = dht::crypto::generateIdentity(); dht::Blob privateKeyData = identity.first->serialize(); metadataSerializer.add((u16)privateKeyData.size()); metadataSerializer.add(privateKeyData.data(), privateKeyData.size()); dht::Blob certificateData; identity.second->pack(certificateData); metadataSerializer.add((u16)certificateData.size()); metadataSerializer.add(certificateData.data(), certificateData.size()); fileAppend(metadataFilePath, { metadataSerializer.getBuffer().data(), metadataSerializer.getBuffer().size() }); } } catch(sibs::DeserializeException &e) { string errMsg = "Failed to load storage, reason: "; errMsg += e.what(); throw DatabaseStorageCorrupt(errMsg); } } void DatabaseStorage::cleanup() { 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); } DatabaseStorage::~DatabaseStorage() { cleanup(); } void DatabaseStorage::loadNode(const Hash &nodeHash) { if(database->onCreateNodeCallbackFunc) { SqlQuery nodeQuery(sqliteDb, "SELECT timestamp, creatorPublicKey, adminGroupId From Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) }); while(nodeQuery.next()) { 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); } } 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); const DataView requestHashRaw = nodeQuery.getBlob(1); Hash requestHash; memcpy(requestHash.getData(), requestHashRaw.data, HASH_BYTE_SIZE); DatabaseOperation operation = (DatabaseOperation)nodeQuery.getInt(2); u64 timestamp = nodeQuery.getInt64(3); const DataView creatorPublicKeyRaw = nodeQuery.getBlob(4); Signature::PublicKey creatorPublicKey((const char*)creatorPublicKeyRaw.data, creatorPublicKeyRaw.size); if(operation == DatabaseOperation::ADD_DATA) { if(!database->onAddNodeCallbackFunc) continue; 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); throw DatabaseStorageException(errMsg); } // 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); } } } void DatabaseStorage::loadMetadataFromFile() { OwnedMemory metadataFileContent = fileGetContent(metadataFilePath); sibs::SafeDeserializer deserializer((u8*)metadataFileContent.data, metadataFileContent.size); u16 storageVersion = deserializer.extract(); if(storageVersion != STORAGE_VERSION) throw std::runtime_error("Wrong storage version!"); deserializer.extract(passwordSalt, PASSWORD_SALT_LEN); u16 privateKeySize = deserializer.extract(); dht::Blob privateKeyRaw; privateKeyRaw.resize(privateKeySize); deserializer.extract(&privateKeyRaw[0], privateKeySize); identity.first = make_shared(privateKeyRaw); u16 certificateSize = deserializer.extract(); dht::Blob certificateRaw; certificateRaw.resize(certificateSize); deserializer.extract(&certificateRaw[0], certificateSize); identity.second = make_shared(certificateRaw); assert(deserializer.empty()); } static void sqlite_step_rollback_on_failure(sqlite3 *db, sqlite3_stmt *stmt, const char *description) { int rc = sqlite3_step(stmt); if(rc != SQLITE_DONE) { 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); } } void DatabaseStorage::bindCheckError(int sqliteBindResult) { if(sqliteBindResult != SQLITE_OK) { string errMsg = "Failed to bind param, error code: "; errMsg += to_string(sqliteBindResult); sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0); throw DatabaseStorageException(errMsg); } } i64 DatabaseStorage::getNodeRowId(const Hash &nodeHash) { sqlite3_reset(selectNodeIdStatement); sqlite3_clear_bindings(selectNodeIdStatement); int rc; rc = sqlite3_bind_blob(selectNodeIdStatement, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_step(selectNodeIdStatement); if(rc != SQLITE_ROW) { 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); } i64 DatabaseStorage::getNodeAddDataRowId(const Hash &requestHash) { sqlite3_reset(selectNodeAddDataIdStatement); sqlite3_clear_bindings(selectNodeAddDataIdStatement); int rc; rc = sqlite3_bind_blob(selectNodeAddDataIdStatement, 1, requestHash.getData(), requestHash.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_step(selectNodeAddDataIdStatement); if(rc != SQLITE_ROW) { 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); } bool DatabaseStorage::doesNodeExist(const Hash &nodeHash) const { SqlQuery query(sqliteDb, "SELECT id FROM Node WHERE nodeHash = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) }); return query.next(); } bool DatabaseStorage::doesDataExist(const Hash &requestHash) const { SqlQuery query(sqliteDb, "SELECT id FROM NodeAddData WHERE requestHash = ?", { DataView(requestHash.getData(), requestHash.getSize()) }); return query.next(); } void DatabaseStorage::createStorage(const Hash &hash, Group *creatorGroup, u64 timestamp, const void *data, usize size) { assert(creatorGroup->getUsers().size() == 1); User *creator = (User*)creatorGroup->getUsers()[0]; sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0); { 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()); } { 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); auto nodeDecryptionKeyResult = getNodeDecryptionKey(hash); if(nodeDecryptionKeyResult.first) decryptNodeData(hash, nodeDecryptionKeyResult.second, &creator->getPublicKey(), creatorGroup->getId(), timestamp); } 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) { sqlite3_exec(sqliteDb, "BEGIN", 0, 0, 0); { 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"); } i64 nodeRowId = getNodeRowId(nodeHash); i64 nodeAddRowId = getNodeAddDataRowId(dataHash); Signature::PublicKey userToAddPublicKey; u8 groupToAddUserTo[GROUP_ID_LENGTH]; if(operation == DatabaseOperation::ADD_DATA) { 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"); } else if(operation == DatabaseOperation::ADD_USER) { 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; } } else { sqlite3_exec(sqliteDb, "ROLLBACK", 0, 0, 0); throw std::runtime_error("Unexpected operation type"); } { 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 nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash); if(nodeDecryptionKeyResult.first) { 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); } sqlite3_exec(sqliteDb, "COMMIT", 0, 0, 0); } void DatabaseStorage::addGroup(const Hash &nodeHash, Group *group) { sqlite3_reset(insertGroupStmt); sqlite3_clear_bindings(insertGroupStmt); int rc; rc = sqlite3_bind_blob(insertGroupStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_bind_blob(insertGroupStmt, 2, group->getId().data, GROUP_ID_LENGTH, SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_bind_int(insertGroupStmt, 3, group->getPermission().getPermissionLevel()); bindCheckError(rc); rc = sqlite3_bind_int64(insertGroupStmt, 4, group->getPermission().getPermissionFlags()); bindCheckError(rc); 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()); } void DatabaseStorage::addUserToGroup(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId) { sqlite3_reset(insertNodeUserGroupAssocStmt); sqlite3_clear_bindings(insertNodeUserGroupAssocStmt); int rc; rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_bind_blob(insertNodeUserGroupAssocStmt, 3, groupId.data, groupId.size, SQLITE_STATIC); bindCheckError(rc); 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); int rc; rc = sqlite3_bind_blob(insertUserStmt, 1, nodeHash.getData(), nodeHash.getSize(), SQLITE_STATIC); bindCheckError(rc); rc = sqlite3_bind_blob(insertUserStmt, 2, userPublicKey.getData(), userPublicKey.getSize(), SQLITE_STATIC); bindCheckError(rc); sqlite_step_rollback_on_failure(sqliteDb, insertUserStmt, "insert data into NodeUser"); addUserToGroup(nodeHash, userPublicKey, groupId); Log::debug("Created user %s in node %s", userPublicKey.toString().c_str(), nodeHash.toString().c_str()); } void DatabaseStorage::fetchNodeRaw(const Hash &nodeHash, FetchNodeRawCallbackFunc callbackFunc) { SqlQuery query(sqliteDb, "SELECT data FROM NodeRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) }); while(query.next()) { const DataView data = query.getBlob(0); callbackFunc(data); } } void DatabaseStorage::fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc) { SqlQuery query(sqliteDb, "SELECT data FROM NodeAddDataRaw WHERE node = ?", { DataView(nodeHash.getData(), nodeHash.getSize()) }); while(query.next()) { const DataView data = query.getBlob(0); callbackFunc(data); } } #if 0 bool DatabaseStorage::storeLocalUser(const string &username, const Signature::KeyPair &keyPair, const string &password) { auto it = nameLocalUsersMap.find(username); if(it != nameLocalUsersMap.end()) return false; OwnedMemory hashedPassword = hashPassword(DataView((void*)password.data(), password.size()), DataView((void*)passwordSalt, PASSWORD_SALT_LEN)); DataView privateKeyView((void*)keyPair.getPrivateKey().getData(), PRIVATE_KEY_NUM_BYTES); DataView hashedPasswordView(hashedPassword.data, hashedPassword.size); Encryption encryptedPrivateKey(privateKeyView, {}, hashedPasswordView); EncryptedPrivateKey userEncryptedPrivateKey; memcpy(userEncryptedPrivateKey.nonce, encryptedPrivateKey.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE); assert(sizeof(userEncryptedPrivateKey.encryptedPrivateKey) == encryptedPrivateKey.getCipherText().size); memcpy(userEncryptedPrivateKey.encryptedPrivateKey, encryptedPrivateKey.getCipherText().data, encryptedPrivateKey.getCipherText().size); LocalUserEncrypted *localUserEncrypted = LocalUserEncrypted::create(keyPair.getPublicKey(), userEncryptedPrivateKey); nameLocalUsersMap[username] = localUserEncrypted; sibs::SafeSerializer serializer; serializer.add((u8)username.size()); serializer.add((const u8*)username.data(), username.size()); serializer.add((const u8*)keyPair.getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES); serializer.add((const u8*)encryptedPrivateKey.getNonce().data, ENCRYPTION_NONCE_BYTE_SIZE); serializer.add((const u8*)encryptedPrivateKey.getCipherText().data, ENCRYPTION_CHECKSUM_BYTE_SIZE + PRIVATE_KEY_NUM_BYTES); fileAppend(localUsersFilePath, DataView(serializer.getBuffer().data(), serializer.getBuffer().size())); return true; } Signature::KeyPair DatabaseStorage::decryptLocalEncryptedUser(const string &username, const string &password) { auto localUserIt = nameLocalUsersMap.find(username); if(localUserIt == nameLocalUsersMap.end()) { string errMsg = "User "; errMsg += username; errMsg += " does not exist in local storage"; throw DatabaseStorageNoSuchLocalStorageUser(errMsg); } DataView passwordView((void*)password.data(), password.size()); DataView saltView((void*)passwordSalt, PASSWORD_SALT_LEN); try { auto privateKey = localUserIt->second->getPrivateKey().decrypt(passwordView, saltView); Signature::KeyPair keyPair(localUserIt->second->getPublicKey(), privateKey); return keyPair; } catch(DecryptionException &e) { string errMsg = "Wrong password provided for user "; errMsg += username; errMsg += " in local storage ("; errMsg += e.what(); errMsg += ")"; throw DatabaseStorageWrongPassword(errMsg); } } #endif pair> DatabaseStorage::getNodeDecryptionKey(const Hash &nodeHash) { 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()); 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 decryptionKey = make_shared(decryptionKeyRawCopy, ENCRYPTION_KEY_BYTE_SIZE); return make_pair(true, decryptionKey); } void DatabaseStorage::setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKeyView) { auto nodeDecryptionKeyResult = getNodeDecryptionKey(nodeHash); bool nodeHasExistingEncryptionKey = nodeDecryptionKeyResult.first; sqlite3_reset(setNodeDecryptionKeyStmt); sqlite3_clear_bindings(setNodeDecryptionKeyStmt); 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); 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, nodeDecryptionKeyResult.second); } bool DatabaseStorage::decryptNodeData(const Hash &nodeHash, const shared_ptr decryptionKey) { sqlite3_reset(selectNodeStmt); sqlite3_clear_bindings(selectNodeStmt); 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) { string errMsg = "select node failed with error: "; errMsg += sqlite3_errmsg(sqliteDb); throw DatabaseStorageException(errMsg); } 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 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; while(true) { 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; } void DatabaseStorage::setNodeAddDataDecrypted(i64 rowId) { sqlite3_reset(setNodeAddDataDecryptedStmt); sqlite3_clear_bindings(setNodeAddDataDecryptedStmt); int rc; rc = sqlite3_bind_int(setNodeAddDataDecryptedStmt, 1, 1); bindCheckError(rc); rc = sqlite3_bind_int64(setNodeAddDataDecryptedStmt, 2, rowId); bindCheckError(rc); 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); int rc; rc = sqlite3_bind_blob(setNodeAddDataAdditionalDataStmt, 1, decryptedData.data, decryptedData.size, SQLITE_STATIC); bindCheckError(rc); 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 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()) { // 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; } 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 decryptionKey) { SqlQuery queryGroupToAdd(sqliteDb, "SELECT permissionLevel FROM NodeGroup WHERE groupId = ?", { groupToAddUserTo }); if(!queryGroupToAdd.next()) { // 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; } 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()) { // 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; } const dht::crypto::Identity& DatabaseStorage::getIdentity() const { return identity; } void DatabaseStorage::update() { auto time = chrono::high_resolution_clock::now().time_since_epoch(); auto timeMicroseconds = chrono::duration_cast(time).count(); } }