#pragma once #include "types.hpp" #include "Hash.hpp" #include "DataView.hpp" #include "Signature.hpp" #include "Encryption.hpp" #include "Group.hpp" #include "Permission.hpp" #include "OwnedMemory.hpp" #include "DatabaseOperation.hpp" #include "DatabaseOrder.hpp" #include "DatabaseNode.hpp" #include "sql/SqlQuery.hpp" #include "sql/SqlExec.hpp" #include #include #include #include #include #include #include class sqlite3; class sqlite3_stmt; namespace odhtdb { class Database; class DatabaseStorageException : public std::runtime_error { public: DatabaseStorageException(const std::string &errMsg) : std::runtime_error(errMsg) {} virtual ~DatabaseStorageException() {} }; class DatabaseStorageAlreadyExists : public DatabaseStorageException { public: DatabaseStorageAlreadyExists(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageNotFound : public DatabaseStorageException { public: DatabaseStorageNotFound(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageCorrupt : public DatabaseStorageException { public: DatabaseStorageCorrupt(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageWrongPassword : public DatabaseStorageException { public: DatabaseStorageWrongPassword(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; class DatabaseStorageNoSuchStoredUser : public DatabaseStorageException { public: DatabaseStorageNoSuchStoredUser(const std::string &errMsg) : DatabaseStorageException(errMsg) {} }; const int PASSWORD_SALT_LEN = 16; const int HASHED_PASSWORD_LEN = 32; using FetchNodeRawCallbackFunc = std::function; using FetchNodeAddDataRawCallbackFunc = std::function; using FetchNodeUserActionGapsCallbackFunc = std::function; using FetchNodeUserLatestActionCounterCallbackFunc = std::function; struct StoredNodeInfo { std::shared_ptr nodeEncryptionKey; std::shared_ptr userKeyPair; }; class DatabaseStorage { public: // Throws DatabaseStorageCorrupt if storage is corrupted. Throws DatabaseStorageException on other failures DatabaseStorage(Database *database, const boost::filesystem::path &storagePath); ~DatabaseStorage(); void loadNode(const Hash &nodeHash, DatabaseLoadOrder loadOrder); bool doesNodeExist(const Hash &nodeHash) const; bool doesDataExist(const Hash &requestHash) const; // Throws DatabaseStorageAlreadyExists if data with hash already exists void createStorage(const Hash &hash, const Signature::PublicKey &adminPublicKey, const DataView &adminGroupId, u64 timestamp, const void *data, usize size); // Throws DatabaseStorageNotFound if data with @nodeHash hash has not been created yet. // Throws DatabaseStorageAlreadyExists if same data has been added before (hash of @data, in @dataHash) void appendStorage(const Hash &nodeHash, const Hash &dataHash, DatabaseOperation operation, u64 newUserActionCounter, const Signature::PublicKey &creatorPublicKey, u64 timestamp, const void *data, usize size, const DataView &additionalDataView); // Throws DatabaseStorageAlreadyExists if group already exists in node void addGroup(const Hash &nodeHash, const DataView &groupId, const Permission &permissions); void addUserToGroup(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId); // Throws DatabaseStorageAlreadyExists is user already exists in node void addUser(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupId); void fetchNodeRaw(const Hash &nodeHash, FetchNodeRawCallbackFunc callbackFunc); void fetchNodeAddDataRaw(const Hash &nodeHash, FetchNodeAddDataRawCallbackFunc callbackFunc, DatabaseFetchOrder fetchOrder); void fetchNodeUserActionGaps(const Hash &nodeHash, FetchNodeUserActionGapsCallbackFunc callbackFunc); void fetchNodeUserLatestActionCounter(const Hash &nodeHash, FetchNodeUserLatestActionCounterCallbackFunc callbackFunc); bool isUserAllowedToAddDataInNode(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const; bool isUserAllowedToAddUserToGroupInNode(const Hash &nodeHash, const Signature::PublicKey &userPublicKey, const DataView &groupToAddUserTo) const; // Returns -1 on failure int getUserLowestPermissionLevel(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const; // Throws DatabaseStorageNotFound if user doesn't exist in node u64 getUserActionCounter(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const; bool doesStoredUserExist(const std::string &username) const; // Throws SqlExecException if user with same name already exists void storeUserWithoutNodes(const std::string &username, const std::string &password); // Username has to be either unique or if it's the same as existing one, then password has to match. // Node has to be unique for the user. // Throws DatabaseStorageWrongPassword or SqlExecException on failure (if username is not unique in node). void storeNodeInfoForUserEncrypted(const DatabaseNode &nodeInfo, const std::string &username, const std::string &password, const Signature::KeyPair &keyPair); // Returns nodes, node encryption key, public key and private key of encrypted user. // Throws DatabaseStorageWrongPassword if password for the stored user is wrong. // Throws DatabaseStorageNoSuchStoredUser if user doesn't exist. // Otherwise throw DatabaseStorageException on other errors. MapHash getStoredNodeUserInfoDecrypted(const std::string &username, const std::string &password) const; // Returns true and node decryption key if node exists and we have the decryption key, // otherwise return false and OwnedByteArray with data set to nullptr std::pair> getNodeDecryptionKey(const Hash &nodeHash); void setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKey); const std::vector>& getRemotePeers() const; void setRemotePeers(const std::vector> &remoteNodes); std::vector getUserGroups(const Hash &nodeHash, const Signature::PublicKey &userPublicKey) const; // Update storage state (remove quarantine objects if they are too old, etc) void update(); // Return number of bytes cleared int clearCache(); private: void init(const boost::filesystem::path &storagePath); void cleanup(); void bindCheckError(int sqliteBindResult); void loadMetadataFromFile(); void loadRemoteNodesFromFile(); bool decryptNodeData(const Hash &nodeHash, const std::shared_ptr decryptionKey); bool decryptNodeData(const Hash &nodeHash, const std::shared_ptr decryptionKey, const Signature::PublicKey *creatorPublicKey, const DataView &adminGroupId, u64 timestamp); bool decryptNodeAddData(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const DataView &encryptedData, const std::shared_ptr decryptionKey); bool decryptNodeAddUser(i64 rowId, const Hash &nodeHash, const Hash &dataHash, u64 timestamp, const Signature::PublicKey *creatorPublicKey, const Signature::PublicKey *userToAddPublicKey, const DataView &groupToAddUserTo, const std::shared_ptr decryptionKey); i64 getNodeRowId(const Hash &nodeHash); i64 getNodeAddDataRowId(const Hash &requestHash); void setNodeAddDataDecrypted(i64 rowId); void setNodeAddDataDecryptedData(i64 rowId, const DataView &decryptedData); // Throws DatabaseStorageNoSuchStoredUser or DatabaseStorageWrongPassword i64 getStoredUserId(const std::string &username, const DataView &hashedPassword) const; private: Database *database; sqlite3 *sqliteDb; sqlite3_stmt *insertNodeStmt; sqlite3_stmt *insertUserStmt; sqlite3_stmt *insertGroupStmt; sqlite3_stmt *insertNodeAddDataStmt; sqlite3_stmt *setNodeDecryptionKeyStmt; sqlite3_stmt *getNodeDecryptionKeyStmt; sqlite3_stmt *insertNodeUserGroupAssocStmt; sqlite3_stmt *selectNodeStmt; sqlite3_stmt *selectNodeAddDataByNodeStmt; sqlite3_stmt *selectNodeIdStatement; sqlite3_stmt *selectNodeAddDataIdStatement; sqlite3_stmt *insertNodeRawStmt; sqlite3_stmt *insertNodeAddDataRawStmt; sqlite3_stmt *insertNodeAddDataAdditionalStmt; sqlite3_stmt *insertNodeAddUserDataStmt; sqlite3_stmt *selectNodeAddDataAdditionalStmt; sqlite3_stmt *selectNodeAddUserDataStmt; sqlite3_stmt *setNodeAddDataDecryptedStmt; sqlite3_stmt *setNodeAddDataAdditionalDataStmt; boost::filesystem::path metadataFilePath; boost::filesystem::path remoteNodesFilePath; u8 passwordSalt[PASSWORD_SALT_LEN]; std::vector> remotePeers; std::recursive_mutex databaseOperationCallbackMutex; }; }