#pragma once #include #include #include #include "json.hpp" #include "option.h" #include "store.h" struct api_key_t { uint32_t id; std::string value; std::string description; std::vector actions; std::vector collections; static const size_t PREFIX_LEN = 4; api_key_t() { } api_key_t(const std::string& value, const std::string& description, const std::vector& actions, const std::vector& collections): value(value), description(description), actions(actions), collections(collections) { } Option load(const std::string & json_str) { nlohmann::json key_obj; try { key_obj = nlohmann::json::parse(json_str); } catch(...) { return Option(500, "Error while parsing JSON string."); } id = key_obj["id"]; value = key_obj["value"]; description = key_obj["description"].get(); actions = key_obj["actions"].get>(); collections = key_obj["collections"].get>(); return Option(true); } static Option validate(const nlohmann::json & key_obj) { auto mandatory_keys = { "description", "actions", "collections" }; for(auto key: mandatory_keys) { if(key_obj.count(key) == 0) { return Option(400, std::string("Could not find a `") + key + "` key."); } } if(!key_obj["actions"].is_array() || key_obj["actions"].empty()) { return Option(400,"Wrong format for `actions`. It should be an array of string."); } if(!key_obj["collections"].is_array() || key_obj["collections"].empty()) { return Option(400,"Wrong format for `collections`. It should be an array of string."); } for(const nlohmann::json & item: key_obj["actions"]) { if(!item.is_string()) { return Option(400,"Wrong format for `actions`. It should be an array of string."); } } for(const nlohmann::json & item: key_obj["collections"]) { if(!item.is_string()) { return Option(400,"Wrong format for `collections`. It should be an array of string."); } } return Option(200); } nlohmann::json to_json() const { nlohmann::json obj; obj["id"] = id; obj["value"] = value; obj["description"] = description; obj["actions"] = actions; obj["collections"] = collections; return obj; } api_key_t& truncate_value() { value = value.substr(0, PREFIX_LEN); // return only first 4 chars return (*this); } }; class AuthManager { private: std::map api_keys; // stores key_value => key mapping Store *store; // Auto incrementing API KEY ID uint32_t next_api_key_id; // Using a $ prefix so that these meta keys stay above record entries in a lexicographically ordered KV store static constexpr const char* API_KEY_NEXT_ID_KEY = "$KN"; static constexpr const char* API_KEYS_PREFIX = "$KP"; uint32_t get_next_api_key_id(); static constexpr const char* DOCUMENTS_SEARCH_ACTION = "documents:search"; public: AuthManager() = default; Option init(Store *store); Option> list_keys(); Option get_key(uint32_t id, bool truncate_value = true); Option create_key(api_key_t& api_key); Option remove_key(uint32_t id); bool authenticate( const std::string& req_api_key, const std::string& action, const std::string& collection, std::map& params ); Option params_from_scoped_key( const std::string& scoped_api_key, const std::string& action, const std::string& collection ); static const size_t KEY_LEN = 32; static const size_t HMAC_BASE64_LEN = 44; };