aboutsummaryrefslogtreecommitdiff
path: root/include/odhtdb/DatabaseStorage.hpp
blob: 8d559cd6cc3974594dea194c7ddcd137064d0b77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#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 <vector>
#include <stdexcept>
#include <boost/filesystem/path.hpp>
#include <sibs/SafeDeserializer.hpp>
#include <functional>
#include <sibs/DirectConnection.hpp>
#include <mutex>

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<void(const DataView rawData)>;
    using FetchNodeAddDataRawCallbackFunc = std::function<void(const DataView rawData, const DataView creatorPublicKey, u64 actionCounter)>;
    using FetchNodeUserActionGapsCallbackFunc = std::function<void(const DataView userPublicKey, u64 start, u64 range)>;
    using FetchNodeUserLatestActionCounterCallbackFunc = std::function<void(const DataView userPublicKey, u64 latestActionCounter)>;
    
    struct StoredNodeInfo
    {
        std::shared_ptr<OwnedByteArray> nodeEncryptionKey;
        std::shared_ptr<Signature::KeyPair> 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<StoredNodeInfo> 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<bool, std::shared_ptr<OwnedByteArray>> getNodeDecryptionKey(const Hash &nodeHash);
        void setNodeDecryptionKey(const Hash &nodeHash, const DataView &decryptionKey);
        
        const std::vector<std::shared_ptr<sibs::DirectConnectionPeer>>& getRemotePeers() const;
        void setRemotePeers(const std::vector<std::shared_ptr<sibs::DirectConnectionPeer>> &remoteNodes);
        
        std::vector<OwnedByteArray> 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<OwnedByteArray> decryptionKey);
        bool decryptNodeData(const Hash &nodeHash, const std::shared_ptr<OwnedByteArray> 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<OwnedByteArray> 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<OwnedByteArray> 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<std::shared_ptr<sibs::DirectConnectionPeer>> remotePeers;
        std::recursive_mutex databaseOperationCallbackMutex;
    };
}