diff --git a/.gitignore b/.gitignore index b13651d19..4a1d79fca 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,6 @@ languages/java/src/main/java/com/bitwarden/sdk/schema languages/js/sdk-client/src/schemas.ts languages/python/bitwarden_sdk/schemas.py support/schemas + +# Cmake build files +languages/cpp/cmake-build-debug diff --git a/languages/cpp/CMakeBuild.md b/languages/cpp/CMakeBuild.md index cb148069e..b75da5c17 100644 --- a/languages/cpp/CMakeBuild.md +++ b/languages/cpp/CMakeBuild.md @@ -3,7 +3,6 @@ ## Introduction Cmake is used to build the C++ Bitwarden client library. Output should be placed in the build directory. - The output contains two dynamic libraries: - The C++ client `BitwardenClient` @@ -20,7 +19,8 @@ See how to use these libraries in the [example use guide](./examples/ExampleUse. ## Build Commands -One should be in the root directory of the C++ wrapper (the same level where is CMakeLists.txt placed). Paths of the three libraries should be placed inside the cmake build command: +One should be in the root directory of the C++ wrapper (the same level where is CMakeLists.txt placed). Paths of the +three libraries should be placed inside the cmake build command: ```bash mkdir -p build @@ -29,6 +29,16 @@ cmake .. -DNLOHMANN=/path/to/include/nlohmann -DBOOST=/path/to/include/boost -DT cmake --build . ``` +## IDE Support + +You may need to manually set the CMake `TARGET` variable for your IDE. For CLion, add the following to the CMake options +settings: + +```bash +# macOS example +-DTARGET=../../target/release/libbitwarden_c.dylib +``` + ## Example ### macOS diff --git a/languages/cpp/README.md b/languages/cpp/README.md index 23b59ac76..fb714a20e 100644 --- a/languages/cpp/README.md +++ b/languages/cpp/README.md @@ -22,9 +22,10 @@ bitwardenSettings.set_identity_url(""); ```c++ std::string accessToken = ""; +std::string stateFile = ""; // Optional - argument in BitwardenClient BitwardenClient bitwardenClient = BitwardenClient(bitwardenSettings); -bitwardenClient.accessTokenLogin(accessToken); +bitwardenClient.loginAccessToken(accessToken, stateFile); ``` ### Create new project @@ -32,6 +33,7 @@ bitwardenClient.accessTokenLogin(accessToken); ```c++ boost::uuids::uuid organizationUuid = boost::uuids::string_generator()(""); ProjectResponse projectResponseCreate = bitwardenClient.createProject(organizationUuid, "TestProject"); +boost::uuids::uuid projectId = boost::uuids::string_generator()(projectResponseCreate.get_id()); ``` ### List all projects @@ -43,21 +45,19 @@ ProjectsResponse projectResponseList = bitwardenClient.listProjects(organization ### Get project details ```c++ -boost::uuids::uuid projectId = boost::uuids::string_generator()(projectResponseCreate.get_id()); ProjectResponse projectResponseGet = bitwardenClient.getProject(projectId); ``` ### Update project ```c++ -boost::uuids::uuid projectId = boost::uuids::string_generator()(projectResponseCreate.get_id()); -ProjectResponse projectResponseUpdate = bitwardenClient.updateProject(projectId, organizationUuid, "TestProjectUpdated"); +ProjectResponse projectResponseUpdate = bitwardenClient.updateProject(organizationUuid, projectId, "TestProjectUpdated"); ``` ### Delete projects ```c++ -SecretsDeleteResponse secretsDeleteResponse = bitwardenClient.deleteSecrets({secretId}); +ProjectsDeleteResponse projectsDeleteResponse = bitwardenClient.deleteProjects({projectId}); ``` ### Add new secret @@ -66,7 +66,8 @@ SecretsDeleteResponse secretsDeleteResponse = bitwardenClient.deleteSecrets({sec std::string key = "key"; std::string value = "value"; std::string note = "note"; -SecretResponse secretResponseCreate = bitwardenClient.createSecret(key, value, note, organizationUuid, {projectId}); +SecretResponse secretResponseCreate = bitwardenClient.createSecret(organizationUuid, key, value, note, {projectId}); +boost::uuids::uuid secretId = boost::uuids::string_generator()(secretResponseCreate.get_id()); ``` ### List secrets @@ -77,14 +78,28 @@ SecretIdentifiersResponse secretIdentifiersResponse = bitwardenClient.listSecret ### Get secret details -``` -boost::uuids::uuid secretId = boost::uuids::string_generator()(secretResponseCreate.get_id()); +```c++ SecretResponse secretResponseGet = bitwardenClient.getSecret(secretId); ``` +### Get multiple secrets by ids + +```c++ +std::vector secretIds = {secretId, secretId2}; +SecretsResponse secretsResponseGet = bitwardenClient.getSecrets(secretIds); +``` + ### Update secret + +```c++ +SecretResponse secretResponseUpdate = bitwardenClient.updateSecret(organizationUuid, secretId, "key2", "value2", "note2", {projectId}); +``` + +### Sync secrets + ```c++ -SecretResponse secretResponseUpdate = bitwardenClient.updateSecret(secretId, "key2", "value2", "note2", organizationUuid, {projectId}); +std::chrono::system_clock::time_point lastSyncedDate = std::chrono::system_clock::now(); +SecretsSyncResponse secretsSyncResponse = bitwardenClient.sync(orgnizationUuid, lastSyncedDate); ``` # Delete secrets diff --git a/languages/cpp/examples/Wrapper.cpp b/languages/cpp/examples/Wrapper.cpp index bb53bf0c4..7790adfb0 100644 --- a/languages/cpp/examples/Wrapper.cpp +++ b/languages/cpp/examples/Wrapper.cpp @@ -1,14 +1,18 @@ #include "BitwardenClient.h" #include #include +#include int main() { // Retrieve access token and organization ID from environment variables - const char* accessTokenEnv = std::getenv("ACCESS_TOKEN"); - const char* organizationIdEnv = std::getenv("ORGANIZATION_ID"); + const char *accessTokenEnv = std::getenv("ACCESS_TOKEN"); + const char *organizationIdEnv = std::getenv("ORGANIZATION_ID"); - const char* apiUrl = std::getenv("API_URL"); - const char* identityUrl = std::getenv("IDENTITY_URL"); + // Use optional state file for authentication + const char *stateFile = std::getenv("STATE_FILE"); + + const char *apiUrl = std::getenv("API_URL"); + const char *identityUrl = std::getenv("IDENTITY_URL"); if (!accessTokenEnv || !organizationIdEnv) { std::cerr << "Error: Environment variables ACCESS_TOKEN or ORGANIZATION_ID not set." << std::endl; @@ -18,57 +22,110 @@ int main() { std::string accessToken = accessTokenEnv; std::string organizationId = organizationIdEnv; - // Configuring the URLS is optional, remove them to use the default values + // Configuring the URLS is optional; if unset, use bitwarden.com BitwardenSettings bitwardenSettings; - bitwardenSettings.set_api_url(apiUrl); - bitwardenSettings.set_identity_url(identityUrl); + if (apiUrl != nullptr && identityUrl != nullptr) { + bitwardenSettings.set_api_url(apiUrl); + bitwardenSettings.set_identity_url(identityUrl); + } else { + std::cerr << "Info: API_URL and IDENTITY_URL are not set, using default values..." << std::endl; + } // Create a Bitwarden client instance - BitwardenClient bitwardenClient = BitwardenClient(bitwardenSettings); - // // Access token login - bitwardenClient.loginAccessToken(accessToken); - // Organization ID - boost::uuids::uuid organizationUuid = boost::uuids::string_generator()(organizationId); - - // // Create a new project + BitwardenClient bitwardenClient(bitwardenSettings); + + // Access token login + if (stateFile != nullptr) { + bitwardenClient.loginAccessToken(accessToken, stateFile); + } else { + bitwardenClient.loginAccessToken(accessToken); + } + + // Convert organization ID to UUID + boost::uuids::uuid organizationUuid = boost::uuids::string_generator()(organizationId); + + // Create a new project + std::cout << "Projects:\n"; ProjectResponse projectResponseCreate = bitwardenClient.createProject(organizationUuid, "NewTestProject"); boost::uuids::uuid projectId = boost::uuids::string_generator()(projectResponseCreate.get_id()); - + + std::cout << "\tcreateProject: '" << projectResponseCreate.get_name() << "'\n\n"; + // List projects ProjectsResponse projectResponseList = bitwardenClient.listProjects(organizationUuid); + std::cout << "\tlistProjects:\n"; + for (const ProjectResponse& project : projectResponseList.get_data()) { + std::cout << "\t\tID: '" << project.get_id() << "', Name: '" << project.get_name() << "'\n"; + } + std::cout << '\n'; // Get project details ProjectResponse projectResponseGet = bitwardenClient.getProject(projectId); + std::cout << "\tgetProject:\n\t\tID: '" << projectResponseGet.get_id() << "', Name: '" << projectResponseGet.get_name() << "'\n\n"; // Update project - ProjectResponse ProjectResponseUpdate = bitwardenClient.updateProject(projectId, organizationUuid, "NewTestProject2"); + ProjectResponse projectResponseUpdate = bitwardenClient.updateProject(organizationUuid, projectId, "NewTestProject2"); + std::cout << "\tupdateProject: '" << projectResponseUpdate.get_name() << "'\n\n"; // Secrets std::string key = "key"; std::string value = "value"; std::string note = "note"; + // Sync secrets + std::cout << "Secrets:\n"; + std::cout << "\tSyncing secrets...\n"; + SecretsSyncResponse secretsSyncResponse = bitwardenClient.sync(organizationUuid, {}); + std::chrono::system_clock::time_point lastSyncedDate = std::chrono::system_clock::now(); + std::cout << "\t\tSync has changes: '" << (secretsSyncResponse.get_has_changes() ? "true" : "false") << "'\n\n"; + + std::cout << "\tSyncing again to ensure no changes since last sync...\n"; + secretsSyncResponse = bitwardenClient.sync(organizationUuid, lastSyncedDate); + std::cout << "\t\tSync has changes: '" << (secretsSyncResponse.get_has_changes() ? "true" : "false") << "'\n\n"; + // Create a new secret - SecretResponse secretResponseCreate = bitwardenClient.createSecret(key, value, note, organizationUuid, {projectId}); + SecretResponse secretResponseCreate = bitwardenClient.createSecret(organizationUuid, key, value, note, {projectId}); boost::uuids::uuid secretId = boost::uuids::string_generator()(secretResponseCreate.get_id()); + std::cout << "\tcreateSecret: '" << secretResponseCreate.get_key() << "'\n\n"; + // List secrets SecretIdentifiersResponse secretIdentifiersResponse = bitwardenClient.listSecrets(organizationUuid); + std::cout << "\tlistSecrets:\n"; + for (const SecretIdentifierResponse& secretIdentifier : secretIdentifiersResponse.get_data()) { + std::cout << "\t\tID: '" << secretIdentifier.get_id() << "'\n"; + } + std::cout << '\n'; // Get secret details SecretResponse secretResponseGet = bitwardenClient.getSecret(secretId); + std::cout << "\tgetSecret: '" << secretResponseGet.get_key() << "'\n\n"; + + // Get secrets by IDs + std::cout << "\tgetSecretsByIds:\n"; + SecretsResponse secretsResponseGetByIds = bitwardenClient.getSecretsByIds({secretId}); + for (const SecretResponse& secret : secretsResponseGetByIds.get_data()) { + std::cout << "\t\tID: '" << secret.get_id() << "', Key: '" << secret.get_key() << "'\n"; + } + std::cout << '\n'; // Update secret - key = "key2"; - value = "value2"; - note = "note2"; - SecretResponse responseForSecretResponseUpdate = bitwardenClient.updateSecret(secretId, key, value, note, organizationUuid, {projectId}); + key = "updated-key"; + value = "updated-value"; + note = "updated-note"; + SecretResponse responseForSecretResponseUpdate = bitwardenClient.updateSecret( + organizationUuid, secretId, key, value, note, {projectId}); + + std::cout << "\tupdateSecret: '" << responseForSecretResponseUpdate.get_key() << "'\n\n"; // Delete secrets + std::cout << "Deleting projects and secrets...\n"; SecretsDeleteResponse secretsDeleteResponse = bitwardenClient.deleteSecrets({secretId}); + std::cout << "\tdeleteSecrets: '" << secretsDeleteResponse.get_data()[0].get_id() << "'\n\n"; // Delete projects ProjectsDeleteResponse projectsDeleteResponse = bitwardenClient.deleteProjects({projectId}); + std::cout << "\tdeleteProjects: '" << projectsDeleteResponse.get_data()[0].get_id() << "'\n\n"; return 0; } diff --git a/languages/cpp/include/BitwardenClient.h b/languages/cpp/include/BitwardenClient.h index a5cf72475..2910c49b7 100644 --- a/languages/cpp/include/BitwardenClient.h +++ b/languages/cpp/include/BitwardenClient.h @@ -9,20 +9,22 @@ class BitwardenClient { public: - BitwardenClient(const BitwardenSettings& bitwardenSettings = BitwardenSettings()); + explicit BitwardenClient(const BitwardenSettings& bitwardenSettings = BitwardenSettings()); ~BitwardenClient(); - - void accessTokenLogin(const std::string& accessToken); + + void loginAccessToken(const std::string& accessToken, const std::string& stateFile = ""); ProjectResponse getProject(const boost::uuids::uuid& id); ProjectResponse createProject(const boost::uuids::uuid& organizationId, const std::string& name); - ProjectResponse updateProject(const boost::uuids::uuid& id, const boost::uuids::uuid& organizationId, const std::string& name); + ProjectResponse updateProject(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name); ProjectsDeleteResponse deleteProjects(const std::vector& ids); ProjectsResponse listProjects(const boost::uuids::uuid &organizationId); SecretResponse getSecret(const boost::uuids::uuid& id); - SecretResponse createSecret(const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds); - SecretResponse updateSecret(const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds); + SecretsResponse getSecretsByIds(const std::vector& ids); + SecretResponse createSecret(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); + SecretResponse updateSecret(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); SecretsDeleteResponse deleteSecrets(const std::vector& ids); SecretIdentifiersResponse listSecrets(const boost::uuids::uuid& organizationId); + SecretsSyncResponse sync(const boost::uuids::uuid &organizationId, const std::chrono::system_clock::time_point &lastSyncedDate); private: BitwardenLibrary* library; diff --git a/languages/cpp/include/Projects.h b/languages/cpp/include/Projects.h index 9bef19b9c..27511c327 100644 --- a/languages/cpp/include/Projects.h +++ b/languages/cpp/include/Projects.h @@ -10,7 +10,7 @@ class Projects { ProjectResponse get(const boost::uuids::uuid& id); ProjectResponse create(const boost::uuids::uuid& organizationId, const std::string& name); - ProjectResponse update(const boost::uuids::uuid& id, const boost::uuids::uuid& organizationId, const std::string& name); + ProjectResponse update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name); ProjectsDeleteResponse deleteProjects(const std::vector& ids); ProjectsResponse list(const boost::uuids::uuid& organizationId); diff --git a/languages/cpp/include/Secrets.h b/languages/cpp/include/Secrets.h index 024ec3692..5c5a3275c 100644 --- a/languages/cpp/include/Secrets.h +++ b/languages/cpp/include/Secrets.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include "CommandRunner.h" @@ -9,10 +11,12 @@ class Secrets { Secrets(CommandRunner* commandRunner); SecretResponse get(const boost::uuids::uuid& id); - SecretResponse create(const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds); - SecretResponse update(const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds); + SecretsResponse getByIds(const std::vector &ids); + SecretResponse create(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); + SecretResponse update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds); SecretsDeleteResponse deleteSecrets(const std::vector& ids); SecretIdentifiersResponse list(const boost::uuids::uuid& organizationId); + SecretsSyncResponse sync(const boost::uuids::uuid& organizationId, const boost::optional& lastSyncedDate); private: CommandRunner* commandRunner; diff --git a/languages/cpp/src/BitwardenClient.cpp b/languages/cpp/src/BitwardenClient.cpp index ce161a2bf..2d11977d4 100644 --- a/languages/cpp/src/BitwardenClient.cpp +++ b/languages/cpp/src/BitwardenClient.cpp @@ -50,10 +50,11 @@ BitwardenClient::~BitwardenClient() { } } -void BitwardenClient::loginAccessToken(const std::string& accessToken) { +void BitwardenClient::loginAccessToken(const std::string& accessToken, const std::string& stateFile) { Command command; AccessTokenLoginRequest accessTokenLoginRequest; accessTokenLoginRequest.set_access_token(accessToken); + accessTokenLoginRequest.set_state_file(stateFile); command.set_login_access_token(accessTokenLoginRequest); auto deserializer = [](const char* response) -> ResponseForApiKeyLoginResponse { @@ -84,11 +85,11 @@ ProjectResponse BitwardenClient::createProject(const boost::uuids::uuid& organiz return projects.create(organizationId, name); } -ProjectResponse BitwardenClient::updateProject(const boost::uuids::uuid& id, const boost::uuids::uuid& organizationId, const std::string& name){ +ProjectResponse BitwardenClient::updateProject(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name){ if (!isClientOpen) { throw std::runtime_error("Client is not open."); } - return projects.update(id, organizationId, name); + return projects.update(organizationId, id, name); } ProjectsDeleteResponse BitwardenClient::deleteProjects(const std::vector& ids) { @@ -114,18 +115,25 @@ SecretResponse BitwardenClient::getSecret(const boost::uuids::uuid& id){ return secrets.get(id); } -SecretResponse BitwardenClient::createSecret(const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds){ +SecretsResponse BitwardenClient::getSecretsByIds(const std::vector& ids){ if (!isClientOpen) { throw std::runtime_error("Client is not open."); } - return secrets.create(key, value, note, organizationId, projectIds); + return secrets.getByIds(ids); } -SecretResponse BitwardenClient::updateSecret(const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds){ +SecretResponse BitwardenClient::createSecret(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds){ if (!isClientOpen) { throw std::runtime_error("Client is not open."); } - return secrets.update(id, key, value, note, organizationId, projectIds); + return secrets.create(organizationId, key, value, note, projectIds); +} + +SecretResponse BitwardenClient::updateSecret(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds){ + if (!isClientOpen) { + throw std::runtime_error("Client is not open."); + } + return secrets.update(organizationId, id, key, value, note, projectIds); } SecretsDeleteResponse BitwardenClient::deleteSecrets(const std::vector& ids) { @@ -143,3 +151,10 @@ SecretIdentifiersResponse BitwardenClient::listSecrets(const boost::uuids::uuid return secrets.list(organizationId); } + +SecretsSyncResponse BitwardenClient::sync(const boost::uuids::uuid &organizationId, const std::chrono::system_clock::time_point &lastSyncedDate) { + if (!isClientOpen) { + throw std::runtime_error("Client is not open."); + } + return secrets.sync(organizationId, lastSyncedDate); +} diff --git a/languages/cpp/src/Projects.cpp b/languages/cpp/src/Projects.cpp index d0aa6ed49..b2fa1c688 100644 --- a/languages/cpp/src/Projects.cpp +++ b/languages/cpp/src/Projects.cpp @@ -67,7 +67,7 @@ ProjectResponse Projects::create(const boost::uuids::uuid& organizationId, const } } -ProjectResponse Projects::update(const boost::uuids::uuid& id, const boost::uuids::uuid& organizationId, const std::string& name) { +ProjectResponse Projects::update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& name) { Command command; ProjectsCommand projectsCommand; ProjectPutRequest projectPutRequest; diff --git a/languages/cpp/src/Secrets.cpp b/languages/cpp/src/Secrets.cpp index e153ea7f1..1fff9441f 100644 --- a/languages/cpp/src/Secrets.cpp +++ b/languages/cpp/src/Secrets.cpp @@ -1,8 +1,8 @@ #include "Secrets.h" #include #include -#include -#include +#include +#include Secrets::Secrets(CommandRunner* commandRunner) : commandRunner(commandRunner) {} @@ -13,6 +13,13 @@ auto secretsDeserializer = [](const std::string& response) -> ResponseForSecretR return secretResponse; }; +auto secretsByIdsDeserializer = [](const std::string& response) -> ResponseForSecretsResponse { + nlohmann::json jsonResponse = nlohmann::json::parse(response); + ResponseForSecretsResponse secretsResponse; + Bitwarden::Sdk::from_json(jsonResponse, secretsResponse); + return secretsResponse; +}; + auto deleteSecretsDeserializer = [](const std::string& response) -> ResponseForSecretsDeleteResponse { nlohmann::json jsonResponse = nlohmann::json::parse(response); ResponseForSecretsDeleteResponse deleteSecretsResponse; @@ -27,6 +34,13 @@ auto secretListDeserializer = [](const std::string& response) -> ResponseForSecr return listResponse; }; +auto secretsSyncDeserializer = [](const std::string& response) -> ResponseForSecretsSyncResponse { + nlohmann::json jsonResponse = nlohmann::json::parse(response); + ResponseForSecretsSyncResponse syncResponse; + Bitwarden::Sdk::from_json(jsonResponse, syncResponse); + return syncResponse; +}; + SecretResponse Secrets::get(const boost::uuids::uuid& id) { Command command; SecretsCommand secretsCommand; @@ -46,7 +60,29 @@ SecretResponse Secrets::get(const boost::uuids::uuid& id) { } } -SecretResponse Secrets::create(const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds) { +SecretsResponse Secrets::getByIds(const std::vector& ids) { + Command command; + SecretsCommand secretsCommand; + SecretsGetRequest secretsGetRequest; + + std::vector idsStr; + for (const auto& id : ids) { + idsStr.push_back(boost::uuids::to_string(id)); + } + secretsGetRequest.set_ids(idsStr); + + secretsCommand.set_get_by_ids(secretsGetRequest); + command.set_secrets(secretsCommand); + + try { + return commandRunner->runCommand(command, secretsByIdsDeserializer); + } catch (const std::exception& ex) { + std::cerr << "Error in getSecretsByIds: " << ex.what() << std::endl; + throw ex; + } +} + +SecretResponse Secrets::create(const boost::uuids::uuid& organizationId, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds) { Command command; SecretsCommand secretsCommand; SecretCreateRequest secretCreateRequest; @@ -75,7 +111,7 @@ SecretResponse Secrets::create(const std::string& key, const std::string& value, } } -SecretResponse Secrets::update(const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const boost::uuids::uuid& organizationId, const std::vector& projectIds) { +SecretResponse Secrets::update(const boost::uuids::uuid& organizationId, const boost::uuids::uuid& id, const std::string& key, const std::string& value, const std::string& note, const std::vector& projectIds) { Command command; SecretsCommand secretsCommand; SecretPutRequest secretPutRequest; @@ -147,3 +183,48 @@ SecretIdentifiersResponse Secrets::list(const boost::uuids::uuid& organizationId throw ex; } } + +SecretsSyncResponse Secrets::sync(const boost::uuids::uuid& organizationId, const boost::optional& lastSyncedDate) { + Command command; + SecretsCommand secretsCommand; + SecretsSyncRequest secretsSyncRequest; + + std::string orgIdStr = boost::uuids::to_string(organizationId); + secretsSyncRequest.set_organization_id(orgIdStr); + + if (lastSyncedDate.has_value()) { + auto timePoint = lastSyncedDate.value(); + + // Get time as time_t and milliseconds + auto timeT = std::chrono::system_clock::to_time_t(timePoint); + auto milliseconds = std::chrono::duration_cast(timePoint.time_since_epoch()) % 1000; + + // Convert to a tm struct + std::tm tm = *std::gmtime(&timeT); + + // Create a string stream to format the date and time + std::stringstream dateStream; + dateStream << std::put_time(&tm, "%Y-%m-%dT%H:%M:%S"); + + // Add milliseconds + dateStream << '.' << std::setw(3) << std::setfill('0') << milliseconds.count() << 'Z'; + + // Convert to string + std::string dateStr = dateStream.str(); + + // Set the last synced date + secretsSyncRequest.set_last_synced_date(dateStr); + } else { + secretsSyncRequest.set_last_synced_date(boost::none); + } + + secretsCommand.set_sync(secretsSyncRequest); + command.set_secrets(secretsCommand); + + try { + return commandRunner->runCommand(command, secretsSyncDeserializer); + } catch (const std::exception& ex) { + std::cerr << "Error in syncSecrets: " << ex.what() << std::endl; + throw ex; + } +}