aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2018-03-11 00:12:37 +0100
committerdec05eba <dec05eba@protonmail.com>2020-08-18 23:25:46 +0200
commit6099ec04bd0d98b9e75f5b55b1215c94ccf20202 (patch)
tree9a551e8e723cde057610d6071587bc76b4a6af19 /src
parent0e62cb8e5ed06d906ad84321cdda22acfcc952c9 (diff)
Add operation to allow users to be added to group
WARNING! Lazy implementation everywhere, does not handle out-of-order packets
Diffstat (limited to 'src')
-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
6 files changed, 274 insertions, 33 deletions
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);
+ }
+ }
+}