From 6099ec04bd0d98b9e75f5b55b1215c94ccf20202 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 11 Mar 2018 00:12:37 +0100 Subject: Add operation to allow users to be added to group WARNING! Lazy implementation everywhere, does not handle out-of-order packets --- src/Database.cpp | 192 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 164 insertions(+), 28 deletions(-) (limited to 'src/Database.cpp') 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 #include #include #include @@ -236,9 +237,11 @@ namespace odhtdb unique_ptr 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 &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(stagedAddObject, nodeInfo->getRequestHash())); + stagedAddObjects.emplace_back(make_unique(stagedAddObject, nodeInfo.getRequestHash())); + } + + Group* getGroupWithRightsToAddUserToGroup(const vector &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(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 &value, const Hash &hash, const shared_ptr encryptionKey) + void Database::deserializeAddRequest(const std::shared_ptr &value, const Hash &hash, const shared_ptr 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(); @@ -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(); - 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(); + 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 value, const Hash &hash, const shared_ptr 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) { -- cgit v1.2.3