aboutsummaryrefslogtreecommitdiff
path: root/src/Database.cpp
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/Database.cpp
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/Database.cpp')
-rw-r--r--src/Database.cpp192
1 files changed, 164 insertions, 28 deletions
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)
{