aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Scheme.md16
-rw-r--r--include/DataView.hpp14
-rw-r--r--include/Database.hpp14
-rw-r--r--include/DatabaseNode.hpp31
-rw-r--r--include/DatabaseStorage.hpp10
-rw-r--r--include/Group.hpp16
-rw-r--r--include/LocalUser.hpp6
-rw-r--r--include/Permission.hpp46
-rw-r--r--include/RemoteUser.hpp6
-rw-r--r--include/User.hpp13
-rw-r--r--project.conf1
-rw-r--r--src/DataView.cpp10
-rw-r--r--src/Database.cpp192
-rw-r--r--src/DatabaseStorage.cpp43
-rw-r--r--src/Group.cpp17
-rw-r--r--src/Permission.cpp19
-rw-r--r--src/User.cpp26
-rw-r--r--tests/main.cpp12
18 files changed, 438 insertions, 54 deletions
diff --git a/Scheme.md b/Scheme.md
index 61fdce5..f573f73 100644
--- a/Scheme.md
+++ b/Scheme.md
@@ -4,15 +4,29 @@ Packet
packet structure version
timestamp
creator public key
+ admin group id (uuid, 16 bytes)
Body (Encrypted)
creator name
name
-# Add
+# Add data
Packet
creator public key
Content (Signed with creator private key, verify with creator public key)
Header
packet structure version
timestamp
+ operation type
Body (Encrypted with node encryption key)
data
+# Add user to group
+Packet
+ creator public key
+ Content (Signed with creator private key, verify with creator public key)
+ Header
+ packet structure version
+ timestamp
+ operation type
+ Body
+ name (of the user to add to group)
+ public key (of user to add to group)
+ group id (the group which the user should be added to)
diff --git a/include/DataView.hpp b/include/DataView.hpp
index c020f91..0ecf9fb 100644
--- a/include/DataView.hpp
+++ b/include/DataView.hpp
@@ -1,6 +1,8 @@
#pragma once
#include "types.hpp"
+#include "Hash.hpp"
+#include <unordered_map>
namespace odhtdb
{
@@ -9,8 +11,20 @@ namespace odhtdb
public:
DataView() : data(nullptr), size(0) {}
DataView(void *_data, usize _size) : data(_data), size(_size) {}
+ bool operator == (const DataView &other) const;
void *data;
usize size;
};
+
+ struct DataViewHasher
+ {
+ size_t operator()(const DataView &dataView) const
+ {
+ return fnvHash((const unsigned char*)dataView.data, dataView.size);
+ }
+ };
+
+ template <typename ValueType>
+ using DataViewMap = std::unordered_map<DataView, ValueType, DataViewHasher>;
}
diff --git a/include/Database.hpp b/include/Database.hpp
index 1024fe0..64a381c 100644
--- a/include/Database.hpp
+++ b/include/Database.hpp
@@ -8,6 +8,8 @@
#include "utils.hpp"
#include "StagedObject.hpp"
#include "Signature.hpp"
+#include "Permission.hpp"
+#include "DatabaseNode.hpp"
#include <opendht/dhtrunner.h>
#include <vector>
#include <ntp/NtpClient.hpp>
@@ -44,6 +46,12 @@ namespace odhtdb
DatabaseAddException(const std::string &errMsg) : std::runtime_error(errMsg) {}
};
+ enum class DatabaseOperation : u8
+ {
+ ADD_DATA,
+ ADD_USER
+ };
+
struct DatabaseCreateRequest
{
DISABLE_COPY(DatabaseCreateRequest)
@@ -122,7 +130,9 @@ namespace odhtdb
// Throws DatabaseCreateException on failure.
std::unique_ptr<DatabaseCreateResponse> create(const std::string &ownerName, const std::string &nodeName);
// Throws DatabaseAddException on failure
- void add(const std::unique_ptr<DatabaseCreateResponse> &nodeInfo, DataView dataToAdd);
+ void addData(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, DataView dataToAdd);
+ // Throws PermissionDeniedException if user @userToPerformActionWith is not allowed to add user @userToAdd to group @groupToAddUserTo
+ void addUserToGroup(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, const std::string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo);
void commit();
private:
// Throws CommitCreateException on failure
@@ -131,7 +141,7 @@ namespace odhtdb
void commitStagedAddObject(const std::unique_ptr<StagedObject> &stagedObject);
ntp::NtpTimestamp getSyncedTimestampUtc() const;
DatabaseCreateRequest deserializeCreateRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const std::shared_ptr<char*> encryptionKey);
- DatabaseAddRequest deserializeAddRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const std::shared_ptr<char*> encryptionKey);
+ void deserializeAddRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const std::shared_ptr<char*> encryptionKey);
bool listenCreateData(std::shared_ptr<dht::Value> value, const Hash &hash, const std::shared_ptr<char*> encryptionKey);
bool listenAddData(std::shared_ptr<dht::Value> value, const Hash &hash, const std::shared_ptr<char*> encryptionKey);
private:
diff --git a/include/DatabaseNode.hpp b/include/DatabaseNode.hpp
new file mode 100644
index 0000000..3ca4be3
--- /dev/null
+++ b/include/DatabaseNode.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Hash.hpp"
+#include <memory>
+
+namespace odhtdb
+{
+ class DatabaseNode
+ {
+ public:
+ DatabaseNode(const std::shared_ptr<char*> &_encryptionKey, const std::shared_ptr<Hash> &_nodeHash) :
+ encryptionKey(_encryptionKey),
+ nodeHash(_nodeHash)
+ {
+
+ }
+
+ const std::shared_ptr<char*> getNodeEncryptionKey() const
+ {
+ return encryptionKey;
+ }
+
+ const std::shared_ptr<Hash> getRequestHash() const
+ {
+ return nodeHash;
+ }
+ private:
+ std::shared_ptr<char*> encryptionKey;
+ std::shared_ptr<Hash> nodeHash;
+ };
+}
diff --git a/include/DatabaseStorage.hpp b/include/DatabaseStorage.hpp
index fd29050..ee4d2ad 100644
--- a/include/DatabaseStorage.hpp
+++ b/include/DatabaseStorage.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include "types.hpp"
#include "Hash.hpp"
#include "DataView.hpp"
#include "Signature.hpp"
@@ -65,6 +66,8 @@ namespace odhtdb
void addToQuarantine(const Signature::PublicKey &creatorPublicKey, u64 timestamp, const u8 *data, usize dataSize);
+ void addUser(User *user, const Hash &hash);
+
// Returns nullptr if no storage with provided hash exists
const DatabaseStorageObjectList* getStorage(const Hash &hash) const;
@@ -73,10 +76,17 @@ namespace odhtdb
// Returns nullptr if no user with public key exists
const User* getUserByPublicKey(const Signature::PublicKey &userPublicKey) const;
+
+ // Returns nullptr if a group with id @groupId doesn't exist
+ Group* getGroupById(uint8_t groupId[16]);
+
+ // Update storage state (remove quarantine objects if they are too old, etc)
+ void update();
private:
DatabaseStorageMap storageMap;
DatabaseStorageQuarantineMap quarantineStorageMap;
Signature::MapPublicKey<Hash> userPublicKeyNodeMap;
Signature::MapPublicKey<const User*> publicKeyUserMap;
+ DataViewMap<Group*> groupByIdMap;
};
}
diff --git a/include/Group.hpp b/include/Group.hpp
index a8dcf83..315961d 100644
--- a/include/Group.hpp
+++ b/include/Group.hpp
@@ -1,5 +1,8 @@
#pragma once
+#include "types.hpp"
+#include "DataView.hpp"
+#include "Permission.hpp"
#include <string>
#include <vector>
#include <stdexcept>
@@ -17,19 +20,26 @@ namespace odhtdb
}
};
+
+
class Group
{
+ friend class User;
public:
- Group(const std::string &name);
+ Group(const std::string &name, uint8_t id[16], const Permission &permission);
~Group();
- void addUser(const User *user);
-
const std::string& getName() const;
+ DataView getId() const;
+ const Permission& getPermission() const;
const std::vector<const User*>& getUsers() const;
private:
+ void addUser(const User *user);
+ private:
std::string name;
+ uint8_t id[16];
+ Permission permission;
std::vector<const User*> users;
};
}
diff --git a/include/LocalUser.hpp b/include/LocalUser.hpp
index 04f483d..d60cb38 100644
--- a/include/LocalUser.hpp
+++ b/include/LocalUser.hpp
@@ -7,9 +7,9 @@ namespace odhtdb
class LocalUser : public User
{
public:
- static LocalUser* create(const Signature::KeyPair &keyPair, const std::string &name)
+ static LocalUser* create(const Signature::KeyPair &keyPair, const std::string &name, Group *group)
{
- return new LocalUser(keyPair, name);
+ return new LocalUser(keyPair, name, group);
}
const Signature::PublicKey& getPublicKey() const override
@@ -22,7 +22,7 @@ namespace odhtdb
return keyPair.getPrivateKey();
}
private:
- LocalUser(const Signature::KeyPair &_keyPair, const std::string &name) : User(name), keyPair(_keyPair) {}
+ LocalUser(const Signature::KeyPair &_keyPair, const std::string &name, Group *group) : User(name, group), keyPair(_keyPair) {}
private:
Signature::KeyPair keyPair;
};
diff --git a/include/Permission.hpp b/include/Permission.hpp
new file mode 100644
index 0000000..1ae2642
--- /dev/null
+++ b/include/Permission.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "types.hpp"
+#include <initializer_list>
+#include <stdexcept>
+
+namespace odhtdb
+{
+ class PermissionDeniedException : public std::runtime_error
+ {
+ public:
+ PermissionDeniedException(const std::string &errMsg) : std::runtime_error(errMsg) {}
+ };
+
+ enum class PermissionType : u32
+ {
+ ADD_DATA = (1 << 0),
+ ADD_USER_SAME_LEVEL = (1 << 1),
+ ADD_USER_LOWER_LEVEL = (1 << 2),
+ ADD_GROUP = (1 << 3),
+ REMOVE_GROUP = (1 << 4)
+ };
+
+ const PermissionType ALL_PERMISSION_TYPES = (PermissionType)0xFFFFFFFF;
+
+ const u8 PERMISSION_LEVEL_ADMIN = 0;
+ const u8 PERMISSION_LEVEL_MODERATOR = 1;
+ const u8 PERMISSION_LEVEL_REGULAR_USER = 2;
+
+ class Permission
+ {
+ public:
+ // @permissionLevel is hierarchical access right. A group can only modify a group that has higher @permissionLevel value
+ Permission(u8 permissionLevel, std::initializer_list<PermissionType> permissions);
+
+ u8 getPermissionLevel() const { return permissionLevel; }
+ u32 getPermissionFlags() const { return permissionFlags; }
+ bool getFlag(PermissionType permissionType) const;
+ private:
+ u8 permissionLevel;
+ u32 permissionFlags;
+ };
+
+ static const Permission ADMIN_PERMISSION(PERMISSION_LEVEL_ADMIN, { ALL_PERMISSION_TYPES });
+ static const Permission REGULAR_USER_PERMISSION(PERMISSION_LEVEL_REGULAR_USER, { PermissionType::ADD_DATA });
+}
diff --git a/include/RemoteUser.hpp b/include/RemoteUser.hpp
index 770be61..181a4c4 100644
--- a/include/RemoteUser.hpp
+++ b/include/RemoteUser.hpp
@@ -7,9 +7,9 @@ namespace odhtdb
class RemoteUser : public User
{
public:
- static RemoteUser* create(const Signature::PublicKey &publicKey, const std::string &name)
+ static RemoteUser* create(const Signature::PublicKey &publicKey, const std::string &name, Group *group)
{
- return new RemoteUser(publicKey, name);
+ return new RemoteUser(publicKey, name, group);
}
const Signature::PublicKey& getPublicKey() const override
@@ -17,7 +17,7 @@ namespace odhtdb
return publicKey;
}
private:
- RemoteUser(const Signature::PublicKey &_publicKey, const std::string &name) : User(name), publicKey(_publicKey){}
+ RemoteUser(const Signature::PublicKey &_publicKey, const std::string &name, Group *group) : User(name, group), publicKey(_publicKey){}
private:
Signature::PublicKey publicKey;
};
diff --git a/include/User.hpp b/include/User.hpp
index ab5872a..fb37876 100644
--- a/include/User.hpp
+++ b/include/User.hpp
@@ -3,9 +3,12 @@
#include "Signature.hpp"
#include <string>
#include <stdexcept>
+#include <vector>
namespace odhtdb
{
+ class Group;
+
class UserNameTooLongException : public std::runtime_error
{
public:
@@ -21,15 +24,15 @@ namespace odhtdb
public:
virtual ~User(){}
+ void addToGroup(Group *group);
+
const std::string& getName() const { return name; }
+ const std::vector<Group*>& getGroups() const { return groups; }
virtual const Signature::PublicKey& getPublicKey() const = 0;
protected:
- User(const std::string &_name) : name(_name)
- {
- if(name.size() > 255)
- throw UserNameTooLongException(name);
- }
+ User(const std::string &name, Group *group);
private:
std::string name;
+ std::vector<Group*> groups;
};
}
diff --git a/project.conf b/project.conf
index c283a7a..432af7b 100644
--- a/project.conf
+++ b/project.conf
@@ -12,3 +12,4 @@ libsodium = "1.0.16"
ntpclient = "0.2.0"
sibs-serializer = "0.2.0"
boost-filesystem = "1.66.0"
+boost-uuid = "1.66.0"
diff --git a/src/DataView.cpp b/src/DataView.cpp
new file mode 100644
index 0000000..63c4f07
--- /dev/null
+++ b/src/DataView.cpp
@@ -0,0 +1,10 @@
+#include "../include/DataView.hpp"
+#include <cstring>
+
+namespace odhtdb
+{
+ bool DataView::operator == (const DataView &other) const
+ {
+ return size == other.size && memcmp(data, other.data, size) == 0;
+ }
+}
diff --git a/src/Database.cpp b/src/Database.cpp
index d01ff7d..fc1a69b 100644
--- a/src/Database.cpp
+++ b/src/Database.cpp
@@ -5,6 +5,7 @@
#include "../include/Encryption.hpp"
#include "../include/DhtKey.hpp"
#include "../include/bin2hex.hpp"
+#include <boost/uuid/uuid_generators.hpp>
#include <opendht.h>
#include <fmt/format.h>
#include <sodium/randombytes.h>
@@ -236,9 +237,11 @@ namespace odhtdb
unique_ptr<DatabaseCreateResponse> Database::create(const std::string &ownerName, const std::string &nodeName)
{
- LocalUser *nodeAdminUser = LocalUser::create(Signature::KeyPair(), ownerName);
- auto adminGroup = new Group("administrator");
- adminGroup->addUser(nodeAdminUser);
+ // TODO: Should this be declared static? is there any difference in behavior/performance?
+ boost::uuids::random_generator uuidGen;
+ auto adminGroupId = uuidGen();
+ auto adminGroup = new Group("administrator", adminGroupId.data, ADMIN_PERMISSION);
+ LocalUser *nodeAdminUser = LocalUser::create(Signature::KeyPair(), ownerName, adminGroup);
// Header
sibs::SafeSerializer serializer;
@@ -247,6 +250,7 @@ namespace odhtdb
u64 timestampMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull;
serializer.add(timestampMicroseconds);
serializer.add((u8*)nodeAdminUser->getPublicKey().getData(), PUBLIC_KEY_NUM_BYTES);
+ serializer.add(adminGroupId.data, adminGroupId.size());
// Encrypted body
sibs::SafeSerializer encryptedSerializer;
@@ -276,22 +280,72 @@ namespace odhtdb
}
}
- void Database::add(const unique_ptr<DatabaseCreateResponse> &nodeInfo, DataView dataToAdd)
+ void Database::addData(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, DataView dataToAdd)
{
sibs::SafeSerializer serializer;
serializer.add(DATABASE_ADD_PACKET_STRUCTURE_VERSION);
// TODO: Append fractions to get real microseconds time
u64 timestampMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull;
serializer.add(timestampMicroseconds);
+ serializer.add(DatabaseOperation::ADD_DATA);
- DataView encryptionKey(*nodeInfo->getNodeEncryptionKey(), KEY_BYTE_SIZE);
+ DataView encryptionKey(*nodeInfo.getNodeEncryptionKey(), KEY_BYTE_SIZE);
Encryption encryptedBody(dataToAdd, DataView(), encryptionKey);
DataView requestData = combine(serializer, encryptedBody);
- string signedRequestData = nodeInfo->getNodeAdminUser()->getPrivateKey().sign(requestData);
+ string signedRequestData = userToPerformActionWith->getPrivateKey().sign(requestData);
free(requestData.data);
- DataView stagedAddObject = combine(nodeInfo->getNodeAdminUser()->getPublicKey(), signedRequestData);
+ DataView stagedAddObject = combine(userToPerformActionWith->getPublicKey(), signedRequestData);
// TODO: Add add object to database storage here for local user
- stagedAddObjects.emplace_back(make_unique<StagedObject>(stagedAddObject, nodeInfo->getRequestHash()));
+ stagedAddObjects.emplace_back(make_unique<StagedObject>(stagedAddObject, nodeInfo.getRequestHash()));
+ }
+
+ Group* getGroupWithRightsToAddUserToGroup(const vector<Group*> &groups, Group *groupToAddUserTo)
+ {
+ for(auto group : groups)
+ {
+ const auto &groupPermission = group->getPermission();
+ if(groupPermission.getFlag(PermissionType::ADD_USER_LOWER_LEVEL) && groupPermission.getPermissionLevel() < groupToAddUserTo->getPermission().getPermissionLevel())
+ {
+ return group;
+ }
+ else if(groupPermission.getFlag(PermissionType::ADD_USER_SAME_LEVEL) && groupPermission.getPermissionLevel() == groupToAddUserTo->getPermission().getPermissionLevel())
+ {
+ return group;
+ }
+ }
+ return nullptr;
+ }
+
+ void Database::addUserToGroup(const DatabaseNode &nodeInfo, LocalUser *userToPerformActionWith, const std::string &userToAddName, const Signature::PublicKey &userToAddPublicKey, Group *groupToAddUserTo)
+ {
+ auto groupWithAddUserRights = getGroupWithRightsToAddUserToGroup(userToPerformActionWith->getGroups(), groupToAddUserTo);
+ if(!groupWithAddUserRights)
+ {
+ string errMsg = "The user ";
+ errMsg += userToPerformActionWith->getName();
+ errMsg += " does not belong to any group that is allowed to add an user to the group ";
+ errMsg += groupToAddUserTo->getName();
+ throw PermissionDeniedException(errMsg);
+ }
+
+ sibs::SafeSerializer serializer;
+ serializer.add(DATABASE_ADD_PACKET_STRUCTURE_VERSION);
+ // TODO: Append fractions to get real microseconds time
+ u64 timestampMicroseconds = ((u64)getSyncedTimestampUtc().seconds) * 1000000ull;
+ serializer.add(timestampMicroseconds);
+ serializer.add(DatabaseOperation::ADD_USER);
+
+ assert(userToAddName.size() <= 255);
+ serializer.add((u8)userToAddName.size());
+ serializer.add((u8*)userToAddName.data(), userToAddName.size());
+ serializer.add((u8*)userToAddPublicKey.getData(), PUBLIC_KEY_NUM_BYTES);
+ serializer.add((uint8_t*)groupToAddUserTo->getId().data, groupToAddUserTo->getId().size);
+
+ DataView requestData { serializer.getBuffer().data(), serializer.getBuffer().size() };
+ string signedRequestData = userToPerformActionWith->getPrivateKey().sign(requestData);
+ DataView stagedAddObject = combine(userToPerformActionWith->getPublicKey(), signedRequestData);
+ // TODO: Add add object to database storage here for local user
+ stagedAddObjects.emplace_back(make_unique<StagedObject>(stagedAddObject, nodeInfo.getRequestHash()));
}
void Database::commit()
@@ -389,12 +443,15 @@ namespace odhtdb
deserializer.extract((u8*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
Signature::PublicKey userPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ uint8_t adminGroupId[16];
+ deserializer.extract(adminGroupId, 16);
+
if(deserializer.getSize() < NONCE_BYTE_SIZE)
throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)");
- auto adminGroup = new Group("administrator");
- auto creatorUser = RemoteUser::create(userPublicKey, "ENCRYPTED USER NAME"); // Username is encrypted, we dont know it...
- adminGroup->addUser(creatorUser);
+ auto adminGroup = new Group("administrator", adminGroupId, ADMIN_PERMISSION);
+ // TODO: Username is encrypted, we dont know it... unless we have encryption key, in which case we should modify the user name and set it
+ auto creatorUser = RemoteUser::create(userPublicKey, "ENCRYPTED USER NAME", adminGroup);
databaseStorage.createStorage(hash, adminGroup, creationDate, value->data.data(), value->data.size());
u8 nonce[NONCE_BYTE_SIZE];
@@ -415,16 +472,26 @@ namespace odhtdb
return { creationDate, adminGroup, move(name) };
}
+
+ bool isUserAllowedToAddData(const User *user)
+ {
+ for(Group *group : user->getGroups())
+ {
+ if(group->getPermission().getFlag(PermissionType::ADD_DATA))
+ return true;
+ }
+ return false;
+ }
- DatabaseAddRequest Database::deserializeAddRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const shared_ptr<char*> encryptionKey)
+ void Database::deserializeAddRequest(const std::shared_ptr<dht::Value> &value, const Hash &hash, const shared_ptr<char*> encryptionKey)
{
sibs::SafeDeserializer deserializer(value->data.data(), value->data.size());
char creatorPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
deserializer.extract((u8*)creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
- Signature::PublicKey userPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ Signature::PublicKey creatorPublicKey(creatorPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
DataView signedData((void*)deserializer.getBuffer(), deserializer.getSize());
- string unsignedData = userPublicKey.unsign(signedData);
+ string unsignedData = creatorPublicKey.unsign(signedData);
sibs::SafeDeserializer deserializerUnsigned((u8*)unsignedData.data(), unsignedData.size());
u16 packetStructureVersion = deserializerUnsigned.extract<u16>();
@@ -443,29 +510,98 @@ namespace odhtdb
if(creationDate > timestampMicroseconds)
throw sibs::DeserializeException("Packet is from the future");
- if(deserializerUnsigned.getSize() < NONCE_BYTE_SIZE)
- throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)");
+ DatabaseOperation operation = deserializerUnsigned.extract<DatabaseOperation>();
- const Hash *node = databaseStorage.getNodeByUserPublicKey(userPublicKey);
+ const Hash *node = databaseStorage.getNodeByUserPublicKey(creatorPublicKey);
if(!node)
{
// The user (public key) could belong to a node but we might not have retrieved the node info yet since data may
// not be retrieved in order.
// Data in quarantine is processed when 'create' packet is received or removed after 60 seconds
- databaseStorage.addToQuarantine(userPublicKey, creationDate, value->data.data(), value->data.size());
+ databaseStorage.addToQuarantine(creatorPublicKey, creationDate, value->data.data(), value->data.size());
throw RequestQuarantineException();
}
- auto creatorUser = databaseStorage.getUserByPublicKey(userPublicKey);
- // TODO: Verify there isn't already data with same timestamp for this node. Same for quarantine
+ auto creatorUser = databaseStorage.getUserByPublicKey(creatorPublicKey);
+ // TODO: Verify there isn't already data with same timestamp for this node. Same for quarantine.
+ // TODO: We might receive 'add' data packet before 'create'. If that happens, we should put it in quarantine and process it later.
databaseStorage.appendStorage(*node, creatorUser, creationDate, value->data.data(), value->data.size());
- u8 nonce[NONCE_BYTE_SIZE];
- deserializerUnsigned.extract(nonce, NONCE_BYTE_SIZE);
- DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize());
- Decryption decryptedBody(dataToDecrypt, DataView(nonce, NONCE_BYTE_SIZE), DataView(*encryptionKey, KEY_BYTE_SIZE));
-
- return { creationDate, creatorUser, move(decryptedBody) };
+ if(operation == DatabaseOperation::ADD_DATA)
+ {
+ if(deserializerUnsigned.getSize() < NONCE_BYTE_SIZE)
+ throw sibs::DeserializeException("Unsigned encrypted body is too small (unable to extract nonce)");
+
+ u8 nonce[NONCE_BYTE_SIZE];
+ deserializerUnsigned.extract(nonce, NONCE_BYTE_SIZE);
+ DataView dataToDecrypt((void*)deserializerUnsigned.getBuffer(), deserializerUnsigned.getSize());
+ Decryption decryptedBody(dataToDecrypt, DataView(nonce, NONCE_BYTE_SIZE), DataView(*encryptionKey, KEY_BYTE_SIZE));
+
+ if(!isUserAllowedToAddData(creatorUser))
+ {
+ // 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);
+ }
+
+ printf("Got add object, timestamp: %zu, data: %.*s\n", creationDate, decryptedBody.getDecryptedText().size, decryptedBody.getDecryptedText().data);
+ }
+ else if(operation == DatabaseOperation::ADD_USER)
+ {
+
+ u8 nameLength = deserializerUnsigned.extract<u8>();
+ string name;
+ name.resize(nameLength);
+ deserializerUnsigned.extract((u8*)&name[0], nameLength);
+
+ char userToAddPublicKeyRaw[PUBLIC_KEY_NUM_BYTES];
+ deserializerUnsigned.extract((u8*)userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+ Signature::PublicKey userToAddPublicKey(userToAddPublicKeyRaw, PUBLIC_KEY_NUM_BYTES);
+
+ uint8_t groupId[16];
+ deserializerUnsigned.extract(groupId, 16);
+
+ auto group = databaseStorage.getGroupById(groupId);
+ if(group)
+ {
+ auto user = RemoteUser::create(userToAddPublicKey, name, group);
+ databaseStorage.addUser(user, hash);
+
+ 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);
+ }
+
+ printf("Got add user object, timestamp: %zu, user added: %.*s\n", creationDate, nameLength, name.c_str());
+ }
+ else
+ {
+ throw sibs::DeserializeException("TODO: Add to quarantine? You can receive ADD_USER packet before you receive ADD_GROUP");
+ }
+ }
+ else
+ {
+ string errMsg = "Got unexpected operation: ";
+ errMsg += to_string((u8)operation);
+ throw sibs::DeserializeException(errMsg);
+ }
}
bool Database::listenCreateData(std::shared_ptr<dht::Value> value, const Hash &hash, const shared_ptr<char*> encryptionKey)
@@ -490,8 +626,8 @@ namespace odhtdb
printf("Got add data\n");
try
{
- DatabaseAddRequest addObject = deserializeAddRequest(value, hash, encryptionKey);
- printf("Got add object, timestamp: %zu\n", addObject.timestamp);
+ deserializeAddRequest(value, hash, encryptionKey);
+ //printf("Got add object, timestamp: %zu\n", addObject.timestamp);
}
catch (RequestQuarantineException &e)
{
diff --git a/src/DatabaseStorage.cpp b/src/DatabaseStorage.cpp
index 638eac0..7c86d18 100644
--- a/src/DatabaseStorage.cpp
+++ b/src/DatabaseStorage.cpp
@@ -1,6 +1,6 @@
#include "../include/DatabaseStorage.hpp"
-#include "../include/Group.hpp"
#include "../include/User.hpp"
+#include "../include/Group.hpp"
#include <cstring>
#include <chrono>
@@ -8,6 +8,8 @@ using namespace std;
namespace odhtdb
{
+ const u64 QUARANTINE_STORAGE_TIME_MICROSECONDS = 60 * 1.0e6;
+
DatabaseStorageObject::DatabaseStorageObject(DataView &_data, u64 _timestamp, const Signature::PublicKey &_creatorPublicKey) :
data(_data), createdTimestamp(_timestamp), creatorPublicKey(_creatorPublicKey)
{
@@ -37,11 +39,11 @@ namespace odhtdb
databaseStorageObjectList->data = DataView(new u8[dataSize], dataSize);
memcpy(databaseStorageObjectList->data.data, data, dataSize);
storageMap[hash] = databaseStorageObjectList;
+ groupByIdMap[creatorGroup->getId()] = creatorGroup;
for(auto user : creatorGroup->getUsers())
{
- userPublicKeyNodeMap[user->getPublicKey()] = hash;
- publicKeyUserMap[user->getPublicKey()] = user;
+ addUser((User*)user, hash);
}
}
@@ -69,6 +71,12 @@ namespace odhtdb
quarantineStorageMap[creatorPublicKey].emplace_back(databaseQuarantineStorageObject);
}
+ void DatabaseStorage::addUser(User *user, const Hash &hash)
+ {
+ userPublicKeyNodeMap[user->getPublicKey()] = hash;
+ publicKeyUserMap[user->getPublicKey()] = user;
+ }
+
const DatabaseStorageObjectList* DatabaseStorage::getStorage(const Hash &hash) const
{
auto it = storageMap.find(hash);
@@ -93,4 +101,33 @@ namespace odhtdb
return it->second;
return nullptr;
}
+
+ Group* DatabaseStorage::getGroupById(uint8_t groupId[16])
+ {
+ auto it = groupByIdMap.find(DataView(groupId, 16));
+ if(it != groupByIdMap.end())
+ return it->second;
+ return nullptr;
+ }
+
+ void DatabaseStorage::update()
+ {
+ 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;
+ }
+ }
}
diff --git a/src/Group.cpp b/src/Group.cpp
index 93a0688..12b50ed 100644
--- a/src/Group.cpp
+++ b/src/Group.cpp
@@ -1,15 +1,18 @@
#include "../include/Group.hpp"
#include "../include/User.hpp"
+#include <cstring>
using namespace std;
namespace odhtdb
{
- Group::Group(const string &_name) :
- name(_name)
+ Group::Group(const string &_name, uint8_t _id[16], const Permission &_permission) :
+ name(_name),
+ permission(_permission)
{
if(name.size() > 255)
throw GroupNameTooLongException(name);
+ memcpy(id, _id, 16);
}
Group::~Group()
@@ -26,6 +29,16 @@ namespace odhtdb
{
return name;
}
+
+ DataView Group::getId() const
+ {
+ return { (void*)id, 16 };
+ }
+
+ const Permission& Group::getPermission() const
+ {
+ return permission;
+ }
const vector<const User*>& Group::getUsers() const
{
diff --git a/src/Permission.cpp b/src/Permission.cpp
new file mode 100644
index 0000000..de0a747
--- /dev/null
+++ b/src/Permission.cpp
@@ -0,0 +1,19 @@
+#include "../include/Permission.hpp"
+
+namespace odhtdb
+{
+ Permission::Permission(u8 _permissionLevel, std::initializer_list<PermissionType> permissions) :
+ permissionLevel(_permissionLevel)
+ {
+ permissionFlags = 0;
+ for(auto permission : permissions)
+ {
+ permissionFlags |= (u32)permission;
+ }
+ }
+
+ bool Permission::getFlag(PermissionType permissionType) const
+ {
+ return (permissionFlags & (u32)permissionType) != 0;
+ }
+}
diff --git a/src/User.cpp b/src/User.cpp
new file mode 100644
index 0000000..e2017ff
--- /dev/null
+++ b/src/User.cpp
@@ -0,0 +1,26 @@
+#include "../include/User.hpp"
+#include "../include/Group.hpp"
+
+namespace odhtdb
+{
+ User::User(const std::string &_name, Group *group) : name(_name)
+ {
+ if(name.size() > 255)
+ throw UserNameTooLongException(name);
+
+ if(group)
+ {
+ groups.emplace_back(group);
+ group->addUser(this);
+ }
+ }
+
+ void User::addToGroup(Group *group)
+ {
+ if(group)
+ {
+ groups.emplace_back(group);
+ group->addUser(this);
+ }
+ }
+}
diff --git a/tests/main.cpp b/tests/main.cpp
index 0218c8c..4d3bcc3 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -83,16 +83,20 @@ void testEncryption()
int main()
{
printf("Starting tests...\n");
- LocalUser *localUser = LocalUser::create(Signature::KeyPair(), "dec05eba");
+ LocalUser *localUser = LocalUser::create(Signature::KeyPair(), "dec05eba", nullptr);
testSignData(localUser);
testEncryption();
testHash();
// TODO: Setup local bootstrap node for tests
Database database("bootstrap.ring.cx", 4222, "storage");
- auto databaseCreateResponse = database.create("dec05eba", "latenight");
- const char *data = "hello, world!";
- database.add(databaseCreateResponse, DataView{ (void*)data, strlen(data) });
+ auto databaseCreateResponse = database.create("adminUserName", "latenight");
+ DatabaseNode databaseNode(databaseCreateResponse->getNodeEncryptionKey(), databaseCreateResponse->getRequestHash());
+ auto adminUser = (LocalUser*)databaseCreateResponse->getNodeAdminUser();
+ database.addData(databaseNode, adminUser, DataView{ (void*)"hello, world!", 13 });
+ database.addUserToGroup(databaseNode, adminUser, localUser->getName(), localUser->getPublicKey(), adminUser->getGroups()[0]);
+ localUser->addToGroup(adminUser->getGroups()[0]);
+ database.addData(databaseNode, localUser, DataView{ (void*)"hello, aaald!", 13 });
database.commit();
database.seed(databaseCreateResponse->getRequestHash(), databaseCreateResponse->getNodeEncryptionKey());