Merge pull request #1475 from krunal1313/autodelete_expired_keys

remove expired API keys when specified
This commit is contained in:
Kishore Nallan 2024-01-10 14:10:52 +05:30 committed by GitHub
commit cfeab44614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 75 additions and 3 deletions

View File

@ -17,6 +17,7 @@ struct api_key_t {
std::vector<std::string> actions;
std::vector<std::string> collections;
uint64_t expires_at;
bool autodelete;
static constexpr const size_t PREFIX_LEN = 4;
static constexpr const uint64_t FAR_FUTURE_TIMESTAMP = 64723363199; // year 4020
@ -26,8 +27,9 @@ struct api_key_t {
}
api_key_t(const std::string &value, const std::string &description, const std::vector<std::string> &actions,
const std::vector<std::string> &collections, uint64_t expires_at) :
value(value), description(description), actions(actions), collections(collections), expires_at(expires_at) {
const std::vector<std::string> &collections, uint64_t expires_at, bool autodel=false) :
value(value), description(description), actions(actions), collections(collections), expires_at(expires_at),
autodelete(autodel) {
}
@ -45,6 +47,7 @@ struct api_key_t {
description = key_obj["description"].get<std::string>();
actions = key_obj["actions"].get<std::vector<std::string>>();
collections = key_obj["collections"].get<std::vector<std::string>>();
autodelete = key_obj["autodelete"].get<bool>();
// handle optional fields
@ -67,6 +70,7 @@ struct api_key_t {
obj["actions"] = actions;
obj["collections"] = collections;
obj["expires_at"] = expires_at;
obj["autodelete"] = autodelete;
return obj;
}
@ -119,6 +123,7 @@ private:
static bool regexp_match(const std::string& value, const std::string& regexp);
void remove_expired_keys();
public:
static const size_t GENERATED_KEY_LEN = 32;
@ -144,4 +149,6 @@ public:
static bool add_item_to_params(std::map<std::string, std::string> &req_params,
const nlohmann::detail::iteration_proxy_value<nlohmann::json::iterator>& item,
bool overwrite);
void do_housekeeping();
};

View File

@ -416,4 +416,25 @@ bool AuthManager::add_item_to_params(std::map<std::string, std::string>& req_par
return true;
}
void AuthManager::remove_expired_keys() {
const Option<std::vector<api_key_t>>& keys_op = list_keys();
if(!keys_op.ok()) {
LOG(ERROR) << keys_op.error();
return;
}
const std::vector<api_key_t>& keys = keys_op.get();
for(const auto& key : keys) {
if(key.autodelete && (uint64_t(std::time(0)) > key.expires_at)) {
LOG(INFO) << "Deleting expired key " << key.value;
auto delete_op = remove_key(key.id);
if(!delete_op.ok()) {
LOG(ERROR) << delete_op.error();
}
}
}
}
void AuthManager::do_housekeeping() {
remove_expired_keys();
}

View File

@ -1787,6 +1787,10 @@ bool post_create_key(const std::shared_ptr<http_req>& req, const std::shared_ptr
req_json["expires_at"] = api_key_t::FAR_FUTURE_TIMESTAMP;
}
if(req_json.count("autodelete") == 0) {
req_json["autodelete"] = false;
}
const std::string &rand_key = (req_json.count("value") != 0) ?
req_json["value"].get<std::string>() : req->metadata;
@ -1795,7 +1799,8 @@ bool post_create_key(const std::shared_ptr<http_req>& req, const std::shared_ptr
req_json["description"].get<std::string>(),
req_json["actions"].get<std::vector<std::string>>(),
req_json["collections"].get<std::vector<std::string>>(),
req_json["expires_at"].get<uint64_t>()
req_json["expires_at"].get<uint64_t>(),
req_json["autodelete"].get<bool>()
);
const Option<api_key_t>& api_key_op = auth_manager.create_key(api_key);

View File

@ -48,6 +48,9 @@ void HouseKeeper::run() {
LOG(INFO) << "Ran housekeeping for " << coll_names.size() << " collections.";
}
//do housekeeping for authmanager
CollectionManager::get_instance().getAuthManager().do_housekeeping();
prev_hnsw_repair_s = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}

View File

@ -464,4 +464,40 @@ TEST_F(AuthManagerTest, ValidateBadKeyProperties) {
validate_op = api_key_t::validate(key_obj4);
ASSERT_FALSE(validate_op.ok());
ASSERT_STREQ("Key description must be a string.", validate_op.error().c_str());
}
TEST_F(AuthManagerTest, AutoDeleteKeysOnExpiry) {
auto list_op = auth_manager.list_keys();
ASSERT_TRUE(list_op.ok());
ASSERT_EQ(0, list_op.get().size());
//regular key(future ts)
api_key_t api_key1("abcd", "test key 1", {"read", "write"}, {"collection1", "collection2"}, FUTURE_TS);
//key is expired (past ts)
uint64_t PAST_TS = uint64_t(std::time(0)) - 100;
api_key_t api_key2("wxyz", "test key 2", {"admin"}, {"*"}, PAST_TS, true);
auto insert_op = auth_manager.create_key(api_key1);
ASSERT_TRUE(insert_op.ok());
ASSERT_EQ(4, insert_op.get().value.size());
insert_op = auth_manager.create_key(api_key2);
ASSERT_TRUE(insert_op.ok());
ASSERT_EQ(4, insert_op.get().value.size());
list_op = auth_manager.list_keys();
ASSERT_TRUE(list_op.ok());
auto keys = list_op.get();
ASSERT_EQ(2, keys.size());
ASSERT_EQ("abcd", keys[0].value);
ASSERT_EQ("wxyz", keys[1].value);
auth_manager.do_housekeeping();
list_op = auth_manager.list_keys();
ASSERT_TRUE(list_op.ok());
keys = list_op.get();
ASSERT_EQ(1, keys.size());
ASSERT_EQ("abcd", keys[0].value);
}