From efad7a87f35fb40e628e273304892cf90f3252d4 Mon Sep 17 00:00:00 2001 From: Daniel Lyubomirov <daniel.lyubomirov@vereign.com> Date: Wed, 1 Jul 2020 16:55:37 +0300 Subject: [PATCH] [17] docs --- cpp/src/csandbox.cc | 11 +++- cpp/src/vereign/bytes/view_dump.hh | 62 +++++++++++++++-- cpp/src/vereign/fs/util.hh | 64 +++++++++++++++++- cpp/src/vereign/grpc/error_code.hh | 13 ++++ cpp/src/vereign/grpc/identity_api.hh | 50 ++++++++++++++ cpp/src/vereign/identity/provider.cc | 2 +- cpp/src/vereign/identity/provider.hh | 46 ++++++++++++- cpp/src/vereign/kvstore/lock.hh | 28 ++++++++ cpp/src/vereign/kvstore/sqlite_storage.hh | 73 +++++++++++++++++++++ cpp/src/vereign/kvstore/storage.hh | 59 +++++++++++++++++ cpp/src/vereign/service/identity_service.hh | 49 ++++++++++++++ 11 files changed, 447 insertions(+), 10 deletions(-) diff --git a/cpp/src/csandbox.cc b/cpp/src/csandbox.cc index 44b33e6..7038293 100644 --- a/cpp/src/csandbox.cc +++ b/cpp/src/csandbox.cc @@ -1,8 +1,17 @@ - +#include "vereign/crypto/rand.hh" +#include <vereign/bytes/view.hh> +#include <vereign/bytes/buffer.hh> +#include <vereign/bytes/view_dump.hh> +#include <vereign/fs/path.hh> +#include <vereign/fs/util.hh> +#include <iostream> auto main(int argc, char** argv) -> int { argc = 0; argv = nullptr; + auto path = vereign::fs::TempFilePath(vereign::fs::path::Join("tmp", "foo"), "test_db_"); + + std::cout << path << std::endl; return 0; } diff --git a/cpp/src/vereign/bytes/view_dump.hh b/cpp/src/vereign/bytes/view_dump.hh index 5f27088..47625f4 100644 --- a/cpp/src/vereign/bytes/view_dump.hh +++ b/cpp/src/vereign/bytes/view_dump.hh @@ -9,7 +9,64 @@ namespace vereign::bytes { namespace detail { +class ViewDump; +} +/** + * Returns a view dump object that can write the view dump into std::ostream. + * + * The dump is the good old hexadecimal format. + * + * Example: + * @code + * std::string input{"foo bar"}; + * std::cout << bytes::dump(bytes::View(input)) << std::endl; + * + * // Output: + * // 0x0000: 666f 6f20 6261 72 foo bar + * + * auto buf = crypto::Rand(32); + * std::cout << bytes::dump(buf.View()) << std::endl; + * + * // Output: + * // 0x0000: 1666 855a 650a 7549 ed0f ec01 4d87 09bf .f.Ze.uI....M... + * // 0x0010: e644 dbc8 7943 37b4 185c dbab 4977 ff3f .D..yC7..\..Iw.? + * + * @endcode + */ +auto dump(View buf) -> detail::ViewDump; + +/** + * Returns a dump object that can write the buffer dump into std::ostream. + * + * The dump is the good old hexadecimal format. + * + * Example: + * @code + auto buf = bytes::Buffer{bytes::View("foo bar")}; + std::cout << bytes::dump(buf) << std::endl; + * + * // Output: + * // 0x0000: 666f 6f20 6261 72 foo bar + * + * auto buf = crypto::Rand(32); + * std::cout << bytes::dump(buf) << std::endl; + * + * // Output: + * // 0x0000: 1666 855a 650a 7549 ed0f ec01 4d87 09bf .f.Ze.uI....M... + * // 0x0010: e644 dbc8 7943 37b4 185c dbab 4977 ff3f .D..yC7..\..Iw.? + * + * @endcode + */ +auto dump(const Buffer& buf) -> detail::ViewDump; + +namespace detail { + +/** + * Helper for dumping a view to a std::ostream. + * + * Users typically will use the bytes::dump functions. + */ class ViewDump { public: auto operator=(const ViewDump&) -> ViewDump& = delete; @@ -27,11 +84,8 @@ private: private: View buf_; }; -} // namespace detail - -auto dump(View buf) -> detail::ViewDump; -auto dump(const Buffer& buf) -> detail::ViewDump; +} // namespace detail } // namespace vereign::bytes diff --git a/cpp/src/vereign/fs/util.hh b/cpp/src/vereign/fs/util.hh index caa2f29..c4e9d3d 100644 --- a/cpp/src/vereign/fs/util.hh +++ b/cpp/src/vereign/fs/util.hh @@ -28,6 +28,9 @@ private: std::string path_; }; +/** + * A RAII guard that deletes all files and directories recursively. + */ class RemoveAllGuard { public: RemoveAllGuard(std::string path); @@ -39,13 +42,70 @@ private: std::string path_; }; -auto BoostPath(std::string_view path) -> boost::filesystem::path; - +/** + * Generates a file path usable as temporary file. + * + * For a temp file path under the system tmp dir use the other TempFilePath overload. + * + * @code + * auto path = fs::TempFilePath("/tmp/foo", "test_db_"); + * std::cout << path << std::endl; + * + * // Output: + * // /tmp/foo/test_db_bh0vcr0jbz + * @endcode + * + * @param dir The directory of the temp file path. + * @param prefix A prefix to prepend to the temp file name. + * @returns a file path usable as temporary file. + */ auto TempFilePath(std::string_view dir, std::string_view prefix) -> std::string; + +/** + * Generates a file path usable as temporary file under the system temporary directory. + * + * It tries to detect the correct temp dir under different operation systems. + * For Linux this is `/tmp` and for Windows this is `C:\Users\<username>\AppData\Local\Temp`. + * + * @code + * auto path = fs::TempFilePath("test_db_"); + * std::cout << path << std::endl; + * + * // Output: + * // /tmp/test_db_bh0vcr0jbz + * @endcode + * + * @param prefix A prefix to prepend to the temp file name. + * @returns a file path usable as temporary file. + */ auto TempFilePath(std::string_view prefix) -> std::string; +/** + * Creates a temporary sub directory under the system temporary directory. + * + * It tries to detect the correct temp dir under different operation systems. + * For Linux this is `/tmp` and for Windows this is `C:\Users\<username>\AppData\Local\Temp`. + * + * @code + * auto path = fs::TempDir("test_db_"); + * std::cout << path << std::endl; + * + * // Output: + * // /tmp/test_db_bh0vcr0jbz + * @endcode + * + * @param prefix A prefix to prepend to the temp dir name. + * @returns a the path of the created temporary directory. + */ auto TempDir(std::string_view prefix) -> std::string; +/** + * Returns current user home directory. + * + * On Windows this is `C:\Users\<username>\AppData\Local`. + * + * @returns the user's home directory full path. + */ auto HomePath() -> std::string; } // namespace vereign::fs diff --git a/cpp/src/vereign/grpc/error_code.hh b/cpp/src/vereign/grpc/error_code.hh index a64de2f..2c5c26e 100644 --- a/cpp/src/vereign/grpc/error_code.hh +++ b/cpp/src/vereign/grpc/error_code.hh @@ -8,6 +8,13 @@ namespace vereign::grpc { static constexpr const char* ClientErrorStatus = "Vereign Client Library Error"; +/** + * Error codes returned into the gRPC API response `code` field on various failures. + * + * These are errors that happen inside the Vereign Client Library. + * The errors that happen inside the Vereign Restful API are in the standard HTTP status code range + * below 600. + */ enum class ErrorCode : uint64_t { ClientError = 1000, UnexpectedError = 1001, @@ -16,6 +23,12 @@ enum class ErrorCode : uint64_t { InvalidIdentity = 1004 }; +/** + * Convert the error code to string. + * + * @param ec The error code. + * @returns the error code integer as string. + */ inline auto ErrorCodeAsString(ErrorCode ec) -> std::string { return std::to_string(uint64_t(ec)); } diff --git a/cpp/src/vereign/grpc/identity_api.hh b/cpp/src/vereign/grpc/identity_api.hh index 5e40c26..6083548 100644 --- a/cpp/src/vereign/grpc/identity_api.hh +++ b/cpp/src/vereign/grpc/identity_api.hh @@ -11,21 +11,53 @@ namespace vereign::grpc { +/** + * Implementation of the gRPC `vereign::client_library::IdentityAPI::Service` service. + * + * Inherits all the API implementations from the generated gen::IdentityAPI and adds some + * additional implementations. + * + * IdentityAPI is a thin layer on top of the service::IdentityService. + */ template <class VereignService> class IdentityAPI final : public gen::IdentityAPI<VereignService> { public: + // API service name. static constexpr const char* Name = gen::IdentityAPI<VereignService>::Name; using VereignServiceType = VereignService; using VereignServicePtr = std::unique_ptr<VereignService>; + /** + * Constructs IdentityAPI instance. + * + * @param service The client library Identity service. + */ IdentityAPI(VereignServicePtr&& service) : gen::IdentityAPI<VereignService>{std::move(service)} {} + // disable copying IdentityAPI(const IdentityAPI&) = delete; auto operator=(const IdentityAPI&) -> IdentityAPI& = delete; + /** + * Registers a new device. + * + * req.pin is required only under Linux. + * + * Under windows the system cypto storage is used. + * When the device is registered a master key is created and the user will be asked for his + * consent by showing a dialog window. + * + * Unexpected error codes: + * - ErrorCode::ClientError Error that happen inside the Vereign Client Library + * - ErrorCode::UnexpectedError Should never happen. + * + * Error codes of interest: + * - ErrorCode::InvalidPinCode The pin code is invalid, currently during the registration an empty + * pin code is considered invalid. + */ auto LoginWithNewDevice( ::grpc::ServerContext* ctx, const client_library::LoginFormNewDevice* req, @@ -55,6 +87,24 @@ public: return ::grpc::Status::OK; } + /** + * Login with already registered device. + * + * req.pin is required only under Linux. + * + * Under windows the system cypto storage is used. + * When the device is registered a master key is created and the user will be asked for his + * consent by showing a dialog window. + * + * Unexpected error codes: + * - ErrorCode::ClientError Error that happen inside the Vereign Client Library + * - ErrorCode::UnexpectedError Should never happen. + * + * Error codes of interest: + * - ErrorCode::DeviceNotRegistered The device is not registered. + * - ErrorCode::InvalidPinCode The pin code is invalid and the crypto storage cannot be unlocked. + * - ErrorCode::InvalidIdentity Under windows if for some reason the RSA master key has been changed. + */ auto LoginWithPreviouslyAddedDevice( ::grpc::ServerContext* ctx, const client_library::LoginFormPreviousAddedDevice* req, diff --git a/cpp/src/vereign/identity/provider.cc b/cpp/src/vereign/identity/provider.cc index dd19a5f..94f761b 100644 --- a/cpp/src/vereign/identity/provider.cc +++ b/cpp/src/vereign/identity/provider.cc @@ -1,6 +1,6 @@ -#include "vereign/crypto/digest.hh" #include <vereign/identity/provider.hh> +#include <vereign/crypto/digest.hh> #include <vereign/crypto/bio.hh> #include <vereign/crypto/rsa.hh> #include <vereign/encoding/base64.hh> diff --git a/cpp/src/vereign/identity/provider.hh b/cpp/src/vereign/identity/provider.hh index 80e1720..60003b4 100644 --- a/cpp/src/vereign/identity/provider.hh +++ b/cpp/src/vereign/identity/provider.hh @@ -7,20 +7,62 @@ namespace vereign::identity { +/** + * Identity provider that manages the locally stored user identity. + * + * All public methods are thread safe. + */ class Provider { public: + /** + * Creates Provider instance. + * + * @param storage The crypto storage used for read/write identity properties. + */ Provider(kvstore::CryptoStorage& storage); + /** + * Default constructor. + * + * Does nothing. + */ + ~Provider(); + // disable copying Provider(const kvstore::Storage&) = delete; auto operator=(const kvstore::Storage&) -> Provider& = delete; + /** + * Replaces the current identity. + * + * @param pin Required only under Linux. The pin code used for derivation of the crypto storage + * master key. + * + * @returns The base64 encoded PEM encoded identity public key. + */ auto ResetIdentity(const std::string& pin) -> std::string; + + /** + * Loads the local identity. + * + * @param pin Required only under Linux. The pin code used for derivation of the crypto storage + * master key. + * + * @returns The base64 encoded PEM encoded identity public key. + */ auto LoadIdentity(const std::string& pin) -> std::string; + + /** + * Retrieve identity public key. + * + * @returns The base64 encoded PEM encoded identity public key. + */ auto GetIdentityPublicKeyBase64() -> std::string; - auto GetDeviceHash() -> std::string; - ~Provider(); + /** + * @returns base64 encoded SHA1 hash of the identity public key. + */ + auto GetDeviceHash() -> std::string; private: std::mutex mu_; diff --git a/cpp/src/vereign/kvstore/lock.hh b/cpp/src/vereign/kvstore/lock.hh index 38d0d47..1989b1d 100644 --- a/cpp/src/vereign/kvstore/lock.hh +++ b/cpp/src/vereign/kvstore/lock.hh @@ -6,12 +6,40 @@ namespace vereign::kvstore { +/** + * Lock guard used for lock/unlock Storage within a scope. + * + * When the Lock is constructed it locks the Storage, and when it is destroyed it unlock it. + */ class Lock { public: + /** + * Creates a Lock and locks the Storage. + * + * @param storage The storage to lock. + * @throws LockError when the lock is held by another process. + */ explicit Lock(Storage& storage); + + /** + * Creates a Lock and locks the Storage. + * + * If the lock is not possible it retries `retry_count` and sleeps between retries `sleep_interval`. + * + * @param storage The storage to lock. + * @param retry_count How many times to retry if the lock is held by another process. + * @param sleep_interval How many time to sleep between retries. + * + * @throws LockError If the lock could not be held after `retry_count` retries. + */ Lock(Storage& storage, int retry_count, std::chrono::milliseconds sleep_interval); + + /** + * Unlocks the storage. + */ ~Lock() noexcept; + // copying is disabled. Lock(const Lock&) = delete; auto operator=(const Lock&) -> Lock& = delete; diff --git a/cpp/src/vereign/kvstore/sqlite_storage.hh b/cpp/src/vereign/kvstore/sqlite_storage.hh index b07f39f..6f7d139 100644 --- a/cpp/src/vereign/kvstore/sqlite_storage.hh +++ b/cpp/src/vereign/kvstore/sqlite_storage.hh @@ -6,23 +6,96 @@ namespace vereign::kvstore { +/** + * Sqlite implementation of the kvstore::Storage interface. + */ class SqliteStorage : public Storage { public: + /** + * Creates SqliteStorage instance. + * + * @param db_path Full path to the sqlite database file. + */ SqliteStorage(const std::string& db_path); + + /** + * Closes the connection with the database. + */ ~SqliteStorage() override; + // disable copying SqliteStorage(const SqliteStorage&) = delete; auto operator=(const SqliteStorage&) -> SqliteStorage& = delete; + /** + * Locks the storage. + * + * The lock must be recursive, meaning that may be called multiple times. + * Storage::Unlock must be called the same number of times the Storage::Lock was called. + * + * Use kvstore::Lock for lock guard and lock with retrials. + * + * @throws LockError when the lock is held by another process. + */ void Lock() override; + + /** + * Locks the storage. + * + * The lock must be recursive, meaning that may be called multiple times. + * Storage::Unlock must be called the same number of times the Storage::Lock was called. + * + * Use kvstore::Lock for lock guard and lock with retrials. + */ void Unlock() override; + /** + * Deletes all the values in the storage. + * + * @throws sqlite::Error on failure. + */ void DeleteAll() override; + /** + * Store bytes value into the storage. + * + * @param key The key under which the value will be stored. + * @param value The bytes that will be stored. + * + * @throws sqlite::Error on failure. + */ void PutBytes(const std::string& key, bytes::View value) override; + + /** + * Retrieve bytes value from the storage. + * + * @param key The key of the value that will be retrieved. + * @param value Buffer where the value will be returned. + * + * @throws ValueNotFoundError when the key does not exist. + * @throws sqlite::Error on failure. + */ void GetBytes(const std::string& key, bytes::Buffer& value) override; + /** + * Store int64_t value into the storage. + * + * @param key The key under which the value will be stored. + * @param value The value that will be stored. + * + * @throws sqlite::Error on failure. + */ void PutInt64(const std::string& key, int64_t value) override; + + /** + * Retrieve int64_t value from the storage. + * + * @param key The key of the value that will be retrieved. + * @param value Buffer where the value will be returned. + * + * @throws ValueNotFoundError when the key does not exist. + * @throws sqlite::Error on failure. + */ auto GetInt64(const std::string& key) -> int64_t override; private: diff --git a/cpp/src/vereign/kvstore/storage.hh b/cpp/src/vereign/kvstore/storage.hh index a190bd5..2e8c553 100644 --- a/cpp/src/vereign/kvstore/storage.hh +++ b/cpp/src/vereign/kvstore/storage.hh @@ -5,21 +5,80 @@ namespace vereign::kvstore { +// The vereign RSA master key name. constexpr const auto VereignKeyName = std::string_view{"vereign_key"}; +/** + * Key/value storage interface. + */ class Storage { public: + /** + * Locks the storage. + * + * The lock must be recursive, meaning that may be called multiple times. + * Storage::Unlock must be called the same number of times the Storage::Lock was called. + * + * Use kvstore::Lock for lock guard and lock with retrials. + * + * @throws LockError when the lock is held by another process. + */ virtual void Lock() = 0; + + /** + * Locks the storage. + * + * The lock must be recursive, meaning that may be called multiple times. + * Storage::Unlock must be called the same number of times the Storage::Lock was called. + * + * Use kvstore::Lock for lock guard and lock with retrials. + */ virtual void Unlock() = 0; + /** + * Deletes all the values in the storage. + */ virtual void DeleteAll() = 0; + /** + * Store bytes value into the storage. + * + * @param key The key under which the value will be stored. + * @param value The bytes that will be stored. + */ virtual void PutBytes(const std::string& key, bytes::View value) = 0; + + /** + * Retrieve bytes value from the storage. + * + * @param key The key of the value that will be retrieved. + * @param value Buffer where the value will be returned. + * + * @throws ValueNotFoundError when the key does not exist. + */ virtual void GetBytes(const std::string& key, bytes::Buffer& value) = 0; + /** + * Store int64_t value into the storage. + * + * @param key The key under which the value will be stored. + * @param value The value that will be stored. + */ virtual void PutInt64(const std::string& key, int64_t value) = 0; + + /** + * Retrieve int64_t value from the storage. + * + * @param key The key of the value that will be retrieved. + * @param value Buffer where the value will be returned. + * + * @throws ValueNotFoundError when the key does not exist. + */ virtual auto GetInt64(const std::string& key) -> int64_t = 0; + /** + * Destroy and cleanup. + */ virtual ~Storage() = default; }; diff --git a/cpp/src/vereign/service/identity_service.hh b/cpp/src/vereign/service/identity_service.hh index 51a77b4..11de3e0 100644 --- a/cpp/src/vereign/service/identity_service.hh +++ b/cpp/src/vereign/service/identity_service.hh @@ -23,24 +23,73 @@ using Result = restapi::PostResult<Request, Response>; class IdentityService : public gen::IdentityService { public: + /** + * Creates IdentityService instance. + * + * @param client_session HTTP client used for communicating with the Vereign Restful API. + * @param identity_provider Local identity provider (manager). + */ IdentityService( restapi::ClientSession& client_session, identity::Provider& identity_provider ); + // disable copying IdentityService(const IdentityService&) = delete; auto operator=(const IdentityService&) -> IdentityService& = delete; + /** + * Login with existing identity public key. + * + * This API is for test purposes only. It is not exposed under the gRPC API, and thus is not + * accessible by the integrators. + * + * **WARN: do not use this in production code** + */ void LoginWithExistingPubKey( const client_library::LoginWithExistingPubKeyForm* req, client_library::EmptyResponse* resp ); + /** + * Registers a new device. + * + * req.pin is required only under Linux. + * + * Under windows the system cypto storage is used. + * When the device is registered a master key is created and the user will be asked for his + * consent by showing a dialog window. + * + * @param req Login request. + * @param resp Operation response. + * + * @throws kvstore::InvalidPinCodeError Only under Linux. Thrown when the provided pin is invalid, + * currently that is when the pin is empty. + */ void LoginWithNewDevice( const client_library::LoginFormNewDevice* req, client_library::LoginFormNewDeviceResponse* resp ); + /** + * Login with already registered device. + * + * req.pin is required only under Linux. + * + * Under windows the system cypto storage is used. + * When the device is registered a master key is created and the user will be asked for his + * consent by showing a dialog window. + * + * @param req Login request. + * @param resp Operation response. + * + * @throws kvstore::StorageNotInitializedError when the crypto storage is empty, meaning that + * the device is not registered. + * @throws kvstore::InvalidPinCodeError under Linux, when the provided pin is invalid, meaning + * that the pin does not match the pin used during the registration. + * @throws kvstore::InvalidIdentityError under windows, when for some reason the RSA master key + * has been changed. + */ void LoginWithPreviouslyAddedDevice( const client_library::LoginFormPreviousAddedDevice* req, client_library::EmptyResponse* resp -- GitLab