diff --git a/services/tunnelbroker/src/cxx_bridge.rs b/services/tunnelbroker/src/cxx_bridge.rs index 44092e4d9..d1ddad7b7 100644 --- a/services/tunnelbroker/src/cxx_bridge.rs +++ b/services/tunnelbroker/src/cxx_bridge.rs @@ -1,38 +1,51 @@ #[cxx::bridge] pub mod ffi { enum GRPCStatusCodes { Ok, Cancelled, Unknown, InvalidArgument, DeadlineExceeded, NotFound, AlreadyExists, PermissionDenied, ResourceExhausted, FailedPrecondition, Aborted, OutOfRange, Unimplemented, Internal, Unavailable, DataLoss, Unauthenticated, } struct GrpcResult { statusCode: GRPCStatusCodes, errorText: String, } struct SessionSignatureResult { toSign: String, grpcStatus: GrpcResult, } + struct NewSessionResult { + sessionID: String, + grpcStatus: GrpcResult, + } unsafe extern "C++" { include!("tunnelbroker/src/libcpp/Tunnelbroker.h"); pub fn initialize(); pub fn getConfigParameter(parameter: &str) -> Result; pub fn isSandbox() -> Result; pub fn sessionSignatureHandler(deviceID: &str) -> SessionSignatureResult; + pub fn newSessionHandler( + deviceID: &str, + publicKey: &str, + signature: &str, + deviceType: i32, + deviceAppVersion: &str, + deviceOS: &str, + notifyToken: &str, + ) -> NewSessionResult; } } diff --git a/services/tunnelbroker/src/libcpp/Tunnelbroker.cpp b/services/tunnelbroker/src/libcpp/Tunnelbroker.cpp index 60a856727..bebaf5e96 100644 --- a/services/tunnelbroker/src/libcpp/Tunnelbroker.cpp +++ b/services/tunnelbroker/src/libcpp/Tunnelbroker.cpp @@ -1,68 +1,150 @@ #include "Tunnelbroker.h" #include "AmqpManager.h" #include "AwsTools.h" #include "ConfigManager.h" +#include "CryptoTools.h" #include "DatabaseManager.h" #include "GlobalTools.h" #include "Tools.h" #include "rust/cxx.h" #include "tunnelbroker/src/cxx_bridge.rs.h" +#include + void initialize() { comm::network::tools::InitLogging("tunnelbroker"); comm::network::config::ConfigManager::getInstance().load(); Aws::InitAPI({}); // List of AWS DynamoDB tables to check if they are created and can be // accessed before any AWS API methods const std::list tablesList = { comm::network::config::ConfigManager::getInstance().getParameter( comm::network::config::ConfigManager::OPTION_DYNAMODB_SESSIONS_TABLE), comm::network::config::ConfigManager::getInstance().getParameter( comm::network::config::ConfigManager:: OPTION_DYNAMODB_SESSIONS_VERIFICATION_TABLE), comm::network::config::ConfigManager::getInstance().getParameter( comm::network::config::ConfigManager:: OPTION_DYNAMODB_SESSIONS_PUBLIC_KEY_TABLE), comm::network::config::ConfigManager::getInstance().getParameter( comm::network::config::ConfigManager:: OPTION_DYNAMODB_MESSAGES_TABLE)}; for (const std::string &table : tablesList) { if (!comm::network::database::DatabaseManager::getInstance() .isTableAvailable(table)) { throw std::runtime_error( "Error: AWS DynamoDB table '" + table + "' is not available"); } }; comm::network::AmqpManager::getInstance().init(); } rust::String getConfigParameter(rust::Str parameter) { return rust::String{ comm::network::config::ConfigManager::getInstance().getParameter( std::string{parameter})}; } bool isSandbox() { return comm::network::tools::isSandbox(); } SessionSignatureResult sessionSignatureHandler(rust::Str deviceID) { const std::string requestedDeviceID(deviceID); if (!comm::network::tools::validateDeviceID(requestedDeviceID)) { return SessionSignatureResult{ .grpcStatus = { .statusCode = GRPCStatusCodes::InvalidArgument, .errorText = "Format validation failed for deviceID: " + requestedDeviceID}}; } const std::string toSign = comm::network::tools::generateRandomString( comm::network::SIGNATURE_REQUEST_LENGTH); std::shared_ptr SessionSignItem = std::make_shared( toSign, requestedDeviceID); comm::network::database::DatabaseManager::getInstance().putSessionSignItem( *SessionSignItem); - return SessionSignatureResult{.toSign = toSign}; + return SessionSignatureResult{ + .toSign = toSign, .grpcStatus = {.statusCode = GRPCStatusCodes::Ok}}; +} + +NewSessionResult newSessionHandler( + rust::Str deviceID, + rust::Str publicKey, + rust::Str signature, + int32_t deviceType, + rust::Str deviceAppVersion, + rust::Str deviceOS, + rust::Str notifyToken) { + std::shared_ptr deviceSessionItem; + std::shared_ptr sessionSignItem; + std::shared_ptr publicKeyItem; + const std::string stringDeviceID{deviceID}; + if (!comm::network::tools::validateDeviceID(stringDeviceID)) { + return NewSessionResult{ + .grpcStatus = { + .statusCode = GRPCStatusCodes::InvalidArgument, + .errorText = "Format validation failed for deviceID"}}; + } + const std::string stringPublicKey{publicKey}; + const std::string newSessionID = comm::network::tools::generateUUID(); + try { + sessionSignItem = comm::network::database::DatabaseManager::getInstance() + .findSessionSignItem(stringDeviceID); + if (sessionSignItem == nullptr) { + return NewSessionResult{ + .grpcStatus = { + .statusCode = GRPCStatusCodes::NotFound, + .errorText = "Session signature request not found for deviceID"}}; + } + publicKeyItem = comm::network::database::DatabaseManager::getInstance() + .findPublicKeyItem(stringDeviceID); + if (publicKeyItem == nullptr) { + std::shared_ptr newPublicKeyItem = + std::make_shared( + stringDeviceID, stringPublicKey); + comm::network::database::DatabaseManager::getInstance().putPublicKeyItem( + *newPublicKeyItem); + } else if (stringPublicKey != publicKeyItem->getPublicKey()) { + return NewSessionResult{ + .grpcStatus = { + .statusCode = GRPCStatusCodes::PermissionDenied, + .errorText = "The public key doesn't match for deviceID"}}; + } + const std::string verificationMessage = sessionSignItem->getSign(); + if (!comm::network::crypto::rsaVerifyString( + stringPublicKey, verificationMessage, std::string{signature})) { + return NewSessionResult{ + .grpcStatus = { + .statusCode = GRPCStatusCodes::PermissionDenied, + .errorText = + "Signature for the verification message is not valid"}}; + } + comm::network::database::DatabaseManager::getInstance() + .removeSessionSignItem(stringDeviceID); + + deviceSessionItem = + std::make_shared( + newSessionID, + stringDeviceID, + stringPublicKey, + std::string{notifyToken}, + deviceType, + std::string{deviceAppVersion}, + std::string{deviceOS}); + comm::network::database::DatabaseManager::getInstance().putSessionItem( + *deviceSessionItem); + } catch (std::runtime_error &e) { + LOG(ERROR) << "gRPC: " + << "Error while processing 'NewSession' request: " << e.what(); + return NewSessionResult{ + .grpcStatus = { + .statusCode = GRPCStatusCodes::Internal, .errorText = e.what()}}; + } + return NewSessionResult{ + .sessionID = newSessionID, + .grpcStatus = {.statusCode = GRPCStatusCodes::Ok}}; } diff --git a/services/tunnelbroker/src/libcpp/Tunnelbroker.h b/services/tunnelbroker/src/libcpp/Tunnelbroker.h index 3cc894c80..a07f66fc8 100644 --- a/services/tunnelbroker/src/libcpp/Tunnelbroker.h +++ b/services/tunnelbroker/src/libcpp/Tunnelbroker.h @@ -1,9 +1,17 @@ #pragma once #include "rust/cxx.h" #include "tunnelbroker/src/cxx_bridge.rs.h" void initialize(); rust::String getConfigParameter(rust::Str parameter); bool isSandbox(); SessionSignatureResult sessionSignatureHandler(rust::Str deviceID); +NewSessionResult newSessionHandler( + rust::Str deviceID, + rust::Str publicKey, + rust::Str signature, + int32_t deviceType, + rust::Str deviceAppVersion, + rust::Str deviceOS, + rust::Str notifyToken); diff --git a/services/tunnelbroker/src/libcpp/test/DatabaseManagerTest.cpp b/services/tunnelbroker/src/libcpp/test/DatabaseManagerTest.cpp index 90779e415..270deb98f 100644 --- a/services/tunnelbroker/src/libcpp/test/DatabaseManagerTest.cpp +++ b/services/tunnelbroker/src/libcpp/test/DatabaseManagerTest.cpp @@ -1,462 +1,458 @@ #include "DatabaseManager.h" #include "ConfigManager.h" #include "Constants.h" #include "GlobalTools.h" #include "Tools.h" #include #include #include #include using namespace comm::network; class DatabaseManagerTest : public testing::Test { protected: virtual void SetUp() { config::ConfigManager::getInstance().load(); Aws::InitAPI({}); } virtual void TearDown() { Aws::ShutdownAPI({}); } }; TEST_F(DatabaseManagerTest, PutAndFoundMessageItemsStaticDataIsSame) { const database::MessageItem item( "bc0c1aa2-bf09-11ec-9d64-0242ac120002", "mobile:EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", "web:JouLWf84zqRIsjBdHLOcHS9M4eSCz7VF84wT1uOD83u1qxDAqmqI4swmxNINjuhd", "lYlNcO6RR4i9UW3G1DGjdJTRRGbqtPya2aj94ZRjIGZWoHwT5MB9ciAgnQf2VafYb9Tl" "8SZkX37tg4yZ9pOb4lqslY4g4h58OmWjumghVRvrPUZDalUuK8OLs1Qoengpu9wccxAk" "Bti2leDTNeiJDy36NnwS9aCIUc0ozsMvXfX1gWdBdmKbiRG1LvpNd6S7BNGG7Zly5zYj" "xz7s6ZUSDoFfZe3eJWQ15ngYhgMw1TsfbECnMVQTYvY6OyqWPBQi5wiftFcluoxor8G5" "RJ1NEDQq2q2FRfWjNHLhky92C2C7Nnfe4oVzSinfC1319uUkNLpSzI4MvEMi6g5Ukbl7" "iGhpnX7Hp4xpBL3h2IkvGviDRQ98UvW0ugwUuPxm1NOQpjLG5dPoqQ0jrMst0Bl5rgPw" "ajjNGsUWmp9r0ST0wRQXrQcY30PoSoqKSlCEgFMLzHWLrPQ86QFyCICismGSe7iBIqdD" "6d37StvXBzfJoZVU79UeOF2bFvb3DNoArEOe", "7s6ZUSDoFfZe3eJWQ15ngYhgMw1TsfbECnMVQTYvY6OyqWPBQi5wiftFcluoxor8"); const size_t currentTimestamp = tools::getCurrentTimestamp(); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putMessageItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findMessageItem( item.getToDeviceID(), item.getMessageID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getFromDeviceID(), foundItem->getFromDeviceID()); EXPECT_EQ(item.getToDeviceID(), foundItem->getToDeviceID()); EXPECT_EQ(item.getPayload(), foundItem->getPayload()); EXPECT_EQ(item.getBlobHashes(), foundItem->getBlobHashes()); EXPECT_EQ( (foundItem->getExpire() >= static_cast(std::time(0))) && (foundItem->getExpire() <= static_cast(std::time(0) + MESSAGE_RECORD_TTL)), true); EXPECT_EQ( foundItem->getCreatedAt() >= currentTimestamp && foundItem->getCreatedAt() <= tools::getCurrentTimestamp(), true); database::DatabaseManager::getInstance().removeMessageItem( item.getToDeviceID(), item.getMessageID()); } TEST_F(DatabaseManagerTest, PutAndFoundMessageItemsGeneratedDataIsSame) { const database::MessageItem item( tools::generateUUID(), "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), "web:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), tools::generateRandomString(256), tools::generateRandomString(256)); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putMessageItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findMessageItem( item.getToDeviceID(), item.getMessageID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getFromDeviceID(), foundItem->getFromDeviceID()) << "Generated FromDeviceID \"" << item.getFromDeviceID() << "\" differs from what is found in the database " << foundItem->getFromDeviceID(); EXPECT_EQ(item.getToDeviceID(), foundItem->getToDeviceID()) << "Generated ToDeviceID \"" << item.getToDeviceID() << "\" differs from what is found in the database " << foundItem->getToDeviceID(); EXPECT_EQ(item.getPayload(), foundItem->getPayload()) << "Generated Payload \"" << item.getPayload() << "\" differs from what is found in the database " << foundItem->getPayload(); EXPECT_EQ(item.getBlobHashes(), foundItem->getBlobHashes()) << "Generated BlobHashes \"" << item.getBlobHashes() << "\" differs from what is found in the database " << foundItem->getBlobHashes(); database::DatabaseManager::getInstance().removeMessageItem( item.getToDeviceID(), item.getMessageID()); } TEST_F(DatabaseManagerTest, BatchPutAndFoundMessagesItemsCountIsSame) { const std::string receiverID = "web:JouLWf84zqRIsjBdHLOcHS9M4eSCz7VF84wT1uOD83u1qxDAqmqI4swmxNINjuhd"; const size_t itemsSize = 29; std::vector messageItems; for (size_t i = 1; i <= itemsSize; ++i) { database::MessageItem item{ tools::generateUUID(), "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), receiverID, tools::generateRandomString(256), tools::generateRandomString(256)}; messageItems.push_back(item); } EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( messageItems[0].getTableName()), true); database::DatabaseManager::getInstance().putMessageItemsByBatch(messageItems); std::vector> foundItems = database::DatabaseManager::getInstance().findMessageItemsByReceiver( receiverID); EXPECT_EQ(foundItems.size(), itemsSize); for (std::shared_ptr messageItem : foundItems) { database::DatabaseManager::getInstance().removeMessageItem( messageItem->getToDeviceID(), messageItem->getMessageID()); } } TEST_F(DatabaseManagerTest, PutAndFoundDeviceSessionItemStaticDataIsSame) { const database::DeviceSessionItem item( "bc0c1aa2-bf09-11ec-9d64-0242ac120002", "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9Q9wodsQdZNynbTnC35hA4mFW" "mwZf9BhbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3ZRYG" "ECqgx4zqajPTzVt7EAOGaIh/dPyQ6x2Ul1GlkkSYXUhhixEzExGp9g84eCyVkbCB" "U3SK6SNKyR7anAXDVQIDAQAB", "hbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3", database::DeviceSessionItem::DeviceTypes::MOBILE, "ios:1.1.1", "iOS 99.99.99"); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putSessionItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getDeviceID(), foundItem->getDeviceID()); EXPECT_EQ(item.getPubKey(), foundItem->getPubKey()); EXPECT_EQ(item.getNotifyToken(), foundItem->getNotifyToken()); EXPECT_EQ(item.getDeviceType(), foundItem->getDeviceType()); EXPECT_EQ(item.getAppVersion(), foundItem->getAppVersion()); EXPECT_EQ(item.getDeviceOs(), foundItem->getDeviceOs()); database::DatabaseManager::getInstance().removeSessionItem( item.getSessionID()); } TEST_F(DatabaseManagerTest, PutAndFoundDeviceSessionItemGeneratedDataIsSame) { const database::DeviceSessionItem item( tools::generateUUID(), "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), tools::generateRandomString(451), tools::generateRandomString(64), database::DeviceSessionItem::DeviceTypes::MOBILE, tools::generateRandomString(12), tools::generateRandomString(12)); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putSessionItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getDeviceID(), foundItem->getDeviceID()) << "Generated DeviceID \"" << item.getDeviceID() << "\" differs from what is found in the database " << foundItem->getDeviceID(); EXPECT_EQ(item.getPubKey(), foundItem->getPubKey()) << "Generated PubKey \"" << item.getPubKey() << "\" differs from what is found in the database " << foundItem->getPubKey(); EXPECT_EQ(item.getNotifyToken(), foundItem->getNotifyToken()) << "Generated NotifyToken \"" << item.getNotifyToken() << "\" differs from what is found in the database " << foundItem->getNotifyToken(); EXPECT_EQ(item.getDeviceType(), foundItem->getDeviceType()) << "Generated DeviceType \"" << item.getDeviceType() << "\" differs from what is found in the database " << foundItem->getDeviceType(); EXPECT_EQ(item.getAppVersion(), foundItem->getAppVersion()) << "Generated AppVersion \"" << item.getAppVersion() << "\" differs from what is found in the database " << foundItem->getAppVersion(); EXPECT_EQ(item.getDeviceOs(), foundItem->getDeviceOs()) << "Generated DeviceOS \"" << item.getDeviceOs() << "\" differs from what is found in the database " << foundItem->getDeviceOs(); database::DatabaseManager::getInstance().removeSessionItem( item.getSessionID()); } TEST_F(DatabaseManagerTest, UpdateIsOnlineDeviceSessionItem) { const database::DeviceSessionItem item( "bc0c1aa2-bf09-11ec-9d64-0242ac120002", "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9Q9wodsQdZNynbTnC35hA4mFW" "mwZf9BhbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3ZRYG" "ECqgx4zqajPTzVt7EAOGaIh/dPyQ6x2Ul1GlkkSYXUhhixEzExGp9g84eCyVkbCB" "U3SK6SNKyR7anAXDVQIDAQAB", "hbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3", database::DeviceSessionItem::DeviceTypes::MOBILE, "ios:1.1.1", "iOS 99.99.99"); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putSessionItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_FALSE(foundItem->getIsOnline()); database::DatabaseManager::getInstance().updateSessionItemIsOnline( item.getSessionID(), true); foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_TRUE(foundItem->getIsOnline()); database::DatabaseManager::getInstance().removeSessionItem( item.getSessionID()); } TEST_F(DatabaseManagerTest, UpdateNotifTokenInDeviceSessionItem) { const database::DeviceSessionItem item( "bc0c1aa2-bf09-11ec-9d64-0242ac120002", "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9Q9wodsQdZNynbTnC35hA4mFW" "mwZf9BhbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3ZRYG" "ECqgx4zqajPTzVt7EAOGaIh/dPyQ6x2Ul1GlkkSYXUhhixEzExGp9g84eCyVkbCB" "U3SK6SNKyR7anAXDVQIDAQAB", "hbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3", database::DeviceSessionItem::DeviceTypes::MOBILE, "ios:1.1.1", "iOS 99.99.99"); - EXPECT_EQ( - database::DatabaseManager::getInstance().isTableAvailable( - item.getTableName()), - true); database::DatabaseManager::getInstance().putSessionItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(foundItem->getNotifyToken(), item.getNotifyToken()); const std::string newToken = "HDVRgx4zqajPTzVt7EAOGaIh/dPyQ6x2Ul1GlkkSYXUhhixEzExGp9g84eCyVGHT"; database::DatabaseManager::getInstance().updateSessionItemDeviceToken( item.getSessionID(), newToken); foundItem = database::DatabaseManager::getInstance().findSessionItem( item.getSessionID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(foundItem->getNotifyToken(), newToken); database::DatabaseManager::getInstance().removeSessionItem( item.getSessionID()); } TEST_F(DatabaseManagerTest, PutAndFoundSessionSignItemStaticDataIsSame) { const database::SessionSignItem item( "bB3OSLdKlY60KPBpw6VoGKX7Lmw3SA07FmNhnqnclvVeaxXueAQ0dpQSpiQTtlGn", "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm"); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putSessionSignItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionSignItem( item.getDeviceID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getSign(), foundItem->getSign()); database::DatabaseManager::getInstance().removeSessionSignItem( item.getDeviceID()); } TEST_F(DatabaseManagerTest, PutAndFoundSessionSignItemGeneratedDataIsSame) { const database::SessionSignItem item( tools::generateRandomString(SIGNATURE_REQUEST_LENGTH), "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH)); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putSessionSignItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findSessionSignItem( item.getDeviceID()); EXPECT_NE(foundItem, nullptr) << "Item with the key of deviceID \"" << item.getDeviceID() << "\" is not found"; EXPECT_EQ(item.getSign(), foundItem->getSign()) << "Generated signature value \"" << item.getSign() << "\" is not equal of \"" + foundItem->getSign() + "\" from the database value"; database::DatabaseManager::getInstance().removeSessionSignItem( item.getDeviceID()); } TEST_F(DatabaseManagerTest, PutAndFoundPublicKeyItemsStaticDataIsSame) { const database::PublicKeyItem item( "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9Q9wodsQdZNynbTnC35hA4mFW" "mwZf9BhbI93aGAwPF9au0eYsawRz0jtYi4lSFXC9KleyQDg+6J+UW1kiWvE3ZRYG" "ECqgx4zqajPTzVt7EAOGaIh/dPyQ6x2Ul1GlkkSYXUhhixEzExGp9g84eCyVkbCB" "U3SK6SNKyR7anAXDVQIDAQAB"); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putPublicKeyItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findPublicKeyItem( item.getDeviceID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getPublicKey(), foundItem->getPublicKey()); database::DatabaseManager::getInstance().removePublicKeyItem( item.getDeviceID()); } TEST_F(DatabaseManagerTest, PutAndFoundPublicKeyItemsGeneratedDataIsSame) { const database::PublicKeyItem item( "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), tools::generateRandomString(451)); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putPublicKeyItem(item); std::shared_ptr foundItem = database::DatabaseManager::getInstance().findPublicKeyItem( item.getDeviceID()); EXPECT_NE(foundItem, nullptr); EXPECT_EQ(item.getPublicKey(), foundItem->getPublicKey()) << "Generated PublicKey \"" << item.getPublicKey() << "\" differs from what is found in the database " << foundItem->getPublicKey(); database::DatabaseManager::getInstance().removePublicKeyItem( item.getDeviceID()); } TEST_F(DatabaseManagerTest, PutAndFoundByReceiverMessageItemsDataIsSame) { const std::string receiverID = "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm"; const database::MessageItem item( "bc0c1aa2-bf09-11ec-9d64-0242ac120002", "mobile:" "EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm", receiverID, "lYlNcO6RR4i9UW3G1DGjdJTRRGbqtPya2aj94ZRjIGZWoHwT5MB9ciAgnQf2VafYb9Tl" "8SZkX37tg4yZ9pOb4lqslY4g4h58OmWjumghVRvrPUZDalUuK8OLs1Qoengpu9wccxAk" "Bti2leDTNeiJDy36NnwS9aCIUc0ozsMvXfX1gWdBdmKbiRG1LvpNd6S7BNGG7Zly5zYj" "xz7s6ZUSDoFfZe3eJWQ15ngYhgMw1TsfbECnMVQTYvY6OyqWPBQi5wiftFcluoxor8G5" "RJ1NEDQq2q2FRfWjNHLhky92C2C7Nnfe4oVzSinfC1319uUkNLpSzI4MvEMi6g5Ukbl7" "iGhpnX7Hp4xpBL3h2IkvGviDRQ98UvW0ugwUuPxm1NOQpjLG5dPoqQ0jrMst0Bl5rgPw" "ajjNGsUWmp9r0ST0wRQXrQcY30PoSoqKSlCEgFMLzHWLrPQ86QFyCICismGSe7iBIqdD" "6d37StvXBzfJoZVU79UeOF2bFvb3DNoArEOe", "7s6ZUSDoFfZe3eJWQ15ngYhgMw1TsfbECnMVQTYvY6OyqWPBQi5wiftFcluoxor8"); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( item.getTableName()), true); database::DatabaseManager::getInstance().putMessageItem(item); std::vector> foundItems = database::DatabaseManager::getInstance().findMessageItemsByReceiver( receiverID); EXPECT_NE(foundItems.size(), 0); EXPECT_EQ(item.getFromDeviceID(), foundItems[0]->getFromDeviceID()); EXPECT_EQ(item.getToDeviceID(), foundItems[0]->getToDeviceID()); EXPECT_EQ(item.getPayload(), foundItems[0]->getPayload()); EXPECT_EQ(item.getBlobHashes(), foundItems[0]->getBlobHashes()); EXPECT_EQ( (foundItems[0]->getExpire() >= static_cast(std::time(0))) && (foundItems[0]->getExpire() <= static_cast(std::time(0) + MESSAGE_RECORD_TTL)), true); database::DatabaseManager::getInstance().removeMessageItem( item.getToDeviceID(), item.getMessageID()); } TEST_F(DatabaseManagerTest, RemoveMessageItemsInBatch) { const size_t randomStringSize = 256; const std::string receiverID = "mobile:EMQNoQ7b2ueEmQ4QsevRWlXxFCNt055y20T1PHdoYAQRt0S6TLzZWNM6XSvdWqxm"; const database::MessageItem messageFirstToRemove( tools::generateUUID(), "mobile:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), receiverID, tools::generateRandomString(randomStringSize), tools::generateRandomString(randomStringSize)); const database::MessageItem messageSecondToRemove( tools::generateUUID(), "web:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), receiverID, tools::generateRandomString(randomStringSize), tools::generateRandomString(randomStringSize)); const database::MessageItem messageThirdToNotRemove( tools::generateUUID(), "web:" + tools::generateRandomString(DEVICEID_CHAR_LENGTH), receiverID, tools::generateRandomString(randomStringSize), tools::generateRandomString(randomStringSize)); EXPECT_EQ( database::DatabaseManager::getInstance().isTableAvailable( messageFirstToRemove.getTableName()), true); database::DatabaseManager::getInstance().putMessageItem(messageFirstToRemove); database::DatabaseManager::getInstance().putMessageItem( messageSecondToRemove); database::DatabaseManager::getInstance().putMessageItem( messageThirdToNotRemove); std::vector> foundItems = database::DatabaseManager::getInstance().findMessageItemsByReceiver( receiverID); EXPECT_EQ(foundItems.size(), 3) << "Items count found by receiverID after insert is not equal to 3"; std::vector messageIDs = { messageFirstToRemove.getMessageID(), messageSecondToRemove.getMessageID()}; database::DatabaseManager::getInstance().removeMessageItemsByIDsForDeviceID( messageIDs, receiverID); foundItems = database::DatabaseManager::getInstance().findMessageItemsByReceiver( receiverID); // `messageThirdToNotRemove` must not be removed and must be persisted EXPECT_EQ(foundItems.size(), 1) << "Items found by receiverID is not equal to 1 after calling " "`removeMessageItemsByIDsForDeviceID`. The one message must be " "persisted."; database::DatabaseManager::getInstance().removeMessageItem( messageThirdToNotRemove.getToDeviceID(), messageThirdToNotRemove.getMessageID()); } diff --git a/services/tunnelbroker/src/server/mod.rs b/services/tunnelbroker/src/server/mod.rs index 98663232d..cdf267d7c 100644 --- a/services/tunnelbroker/src/server/mod.rs +++ b/services/tunnelbroker/src/server/mod.rs @@ -1,106 +1,136 @@ use super::constants; -use super::cxx_bridge::ffi::{sessionSignatureHandler, GRPCStatusCodes}; +use super::cxx_bridge::ffi::{ + newSessionHandler, sessionSignatureHandler, GRPCStatusCodes, +}; use anyhow::Result; use futures::Stream; use std::pin::Pin; use tonic::transport::Server; use tonic::{Request, Response, Status, Streaming}; use tunnelbroker::tunnelbroker_service_server::{ TunnelbrokerService, TunnelbrokerServiceServer, }; mod tools; mod tunnelbroker { tonic::include_proto!("tunnelbroker"); } #[derive(Debug, Default)] struct TunnelbrokerServiceHandlers {} #[tonic::async_trait] impl TunnelbrokerService for TunnelbrokerServiceHandlers { async fn session_signature( &self, request: Request, ) -> Result, Status> { let result = sessionSignatureHandler(&request.into_inner().device_id); if result.grpcStatus.statusCode != GRPCStatusCodes::Ok { return Err(tools::create_tonic_status( result.grpcStatus.statusCode, &result.grpcStatus.errorText, )); } Ok(Response::new(tunnelbroker::SessionSignatureResponse { to_sign: result.toSign, })) } async fn new_session( &self, - _request: Request, + request: Request, ) -> Result, Status> { - Err(Status::unimplemented("Not implemented yet")) + let inner_request = request.into_inner(); + let notify_token = inner_request.notify_token.unwrap_or(String::new()); + if !tunnelbroker::new_session_request::DeviceTypes::is_valid( + inner_request.device_type, + ) { + return Err(tools::create_tonic_status( + GRPCStatusCodes::InvalidArgument, + "Unsupported device type", + )); + }; + + let result = newSessionHandler( + &inner_request.device_id, + &inner_request.public_key, + &inner_request.signature, + inner_request.device_type, + &inner_request.device_app_version, + &inner_request.device_os, + ¬ify_token, + ); + if result.grpcStatus.statusCode != GRPCStatusCodes::Ok { + return Err(tools::create_tonic_status( + result.grpcStatus.statusCode, + &result.grpcStatus.errorText, + )); + } + Ok(Response::new(tunnelbroker::NewSessionResponse { + session_id: result.sessionID, + })) } type MessagesStreamStream = Pin< Box< dyn Stream> + Send, >, >; async fn messages_stream( &self, _request: Request>, ) -> Result, Status> { Err(Status::unimplemented("Not implemented yet")) } // These empty old API handlers are deprecated and should be removed. // They are implemented only to fix the building process. async fn check_if_primary_device_online( &self, _request: Request, ) -> Result, Status> { Err(Status::cancelled("Deprecated")) } async fn become_new_primary_device( &self, _request: Request, ) -> Result, Status> { Err(Status::cancelled("Deprecated")) } async fn send_pong( &self, _request: Request, ) -> Result, Status> { Err(Status::cancelled("Deprecated")) } async fn send( &self, _request: Request, ) -> Result, Status> { Err(Status::cancelled("Deprecated")) } type GetStream = Pin< Box> + Send>, >; async fn get( &self, _request: Request, ) -> Result, Status> { Err(Status::cancelled("Deprecated")) } } pub async fn run_grpc_server() -> Result<()> { let addr = format!("[::1]:{}", constants::GRPC_SERVER_PORT).parse()?; Server::builder() .add_service(TunnelbrokerServiceServer::new( TunnelbrokerServiceHandlers::default(), )) .serve(addr) .await?; Ok(()) }