aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorijon <ijon@ydb.tech>2023-10-26 16:35:59 +0300
committerijon <ijon@ydb.tech>2023-10-26 17:02:08 +0300
commit4f440816e221a84402c1a4bc06c2fb9e3bb168e8 (patch)
tree4416b5a9546cbeaa630002a57a601973c3744479
parente50bfe724b357597d9528251a5be829f6c0d7cc0 (diff)
downloadydb-4f440816e221a84402c1a4bc06c2fb9e3bb168e8.tar.gz
auditlog: add cloud-ids
Log cloud specific database ids: - cloud_id - folder_id - database_id as resource_id KIKIMR-18688
-rw-r--r--ydb/core/grpc_services/audit_dml_operations.cpp31
-rw-r--r--ydb/core/grpc_services/audit_dml_operations.h2
-rw-r--r--ydb/core/grpc_services/grpc_request_check_actor.h2
-rw-r--r--ydb/tests/functional/audit/test_auditlog.py57
4 files changed, 88 insertions, 4 deletions
diff --git a/ydb/core/grpc_services/audit_dml_operations.cpp b/ydb/core/grpc_services/audit_dml_operations.cpp
index 9bd3ac8df4..682bb8944d 100644
--- a/ydb/core/grpc_services/audit_dml_operations.cpp
+++ b/ydb/core/grpc_services/audit_dml_operations.cpp
@@ -38,16 +38,45 @@ namespace {
}
ctx->AddAuditLogPart("commit_tx", ToString(tx_control.commit_tx()));
}
+
+ std::tuple<TString, TString, TString> GetDatabaseCloudIds(const std::vector<std::pair<TString, TString>>& databaseAttrs) {
+ if (databaseAttrs.empty()) {
+ return {};
+ }
+ auto getAttr = [&d = databaseAttrs](const TString &name) -> TString {
+ const auto& found = std::find_if(d.begin(), d.end(), [name](const auto& item) { return item.first == name; });
+ if (found != d.end()) {
+ return found->second;
+ }
+ return {};
+ };
+ return std::make_tuple(
+ getAttr("cloud_id"),
+ getAttr("folder_id"),
+ getAttr("database_id")
+ );
+ }
}
namespace NKikimr::NGRpcService {
-void AuditContextStart(IRequestCtxBase* ctx, const TString& database, const TString& userSID) {
+void AuditContextStart(IRequestCtxBase* ctx, const TString& database, const TString& userSID, const std::vector<std::pair<TString, TString>>& databaseAttrs) {
ctx->AddAuditLogPart("remote_address", NKikimr::NAddressClassifier::ExtractAddress(ctx->GetPeerName()));
ctx->AddAuditLogPart("subject", userSID);
ctx->AddAuditLogPart("database", database);
ctx->AddAuditLogPart("operation", ctx->GetRequestName());
ctx->AddAuditLogPart("start_time", TInstant::Now().ToString());
+
+ auto [cloud_id, folder_id, database_id] = GetDatabaseCloudIds(databaseAttrs);
+ if (cloud_id) {
+ ctx->AddAuditLogPart("cloud_id", cloud_id);
+ }
+ if (folder_id) {
+ ctx->AddAuditLogPart("folder_id", folder_id);
+ }
+ if (database_id) {
+ ctx->AddAuditLogPart("resource_id", database_id);
+ }
}
void AuditContextEnd(IRequestCtxBase* ctx) {
diff --git a/ydb/core/grpc_services/audit_dml_operations.h b/ydb/core/grpc_services/audit_dml_operations.h
index 875623e877..77547bf740 100644
--- a/ydb/core/grpc_services/audit_dml_operations.h
+++ b/ydb/core/grpc_services/audit_dml_operations.h
@@ -39,7 +39,7 @@ class IRequestCtx;
// AuditContextAppend() specializations extract specific info from request (and result) protos.
//
-void AuditContextStart(IRequestCtxBase* ctx, const TString& database, const TString& userSID);
+void AuditContextStart(IRequestCtxBase* ctx, const TString& database, const TString& userSID, const std::vector<std::pair<TString, TString>>& databaseAttrs);
void AuditContextEnd(IRequestCtxBase* ctx);
template <class TProtoRequest>
diff --git a/ydb/core/grpc_services/grpc_request_check_actor.h b/ydb/core/grpc_services/grpc_request_check_actor.h
index a78c0151ae..472a2ac34d 100644
--- a/ydb/core/grpc_services/grpc_request_check_actor.h
+++ b/ydb/core/grpc_services/grpc_request_check_actor.h
@@ -361,7 +361,7 @@ private:
const bool dmlAuditEnabled = requestBaseCtx->IsAuditable() && IsAuditEnabledFor(userSID);
if (dmlAuditEnabled) {
- AuditContextStart(requestBaseCtx, databaseName, userSID);
+ AuditContextStart(requestBaseCtx, databaseName, userSID, Attributes_);
requestBaseCtx->SetAuditLogHook([requestBaseCtx](ui32 status, const TAuditLogParts& parts) {
AuditContextEnd(requestBaseCtx);
AuditLog(status, parts);
diff --git a/ydb/tests/functional/audit/test_auditlog.py b/ydb/tests/functional/audit/test_auditlog.py
index 3e9e221124..b9b01fa5a9 100644
--- a/ydb/tests/functional/audit/test_auditlog.py
+++ b/ydb/tests/functional/audit/test_auditlog.py
@@ -94,6 +94,28 @@ def alter_database_audit_settings(cluster, database_path, enable_dml_audit=None,
ydbcli_db_schema_exec(cluster, alter_proto)
+def alter_user_attrs(cluster, path, **kwargs):
+ if not kwargs:
+ return
+
+ user_attrs = ['''UserAttributes { Key: "%s" Value: "%s" }''' % (k, v) for k, v in kwargs.items()]
+
+ alter_proto = r'''ModifyScheme {
+ OperationType: ESchemeOpAlterUserAttributes
+ WorkingDir: "%s"
+ AlterUserAttributes {
+ PathName: "%s"
+ %s
+ }
+ }''' % (
+ os.path.dirname(path),
+ os.path.basename(path),
+ '\n'.join(user_attrs),
+ )
+
+ ydbcli_db_schema_exec(cluster, alter_proto)
+
+
class CaptureFileOutput:
def __init__(self, filename):
self.filename = filename
@@ -189,9 +211,9 @@ def execute_data_query(pool, text):
QUERIES = [
r'''insert into `{table_path}` (id, value) values (100, 100), (101, 101)''',
+ r'''delete from `{table_path}` where id = 100 or id = 101''',
r'''select id from `{table_path}`''',
r'''update `{table_path}` set value = 0 where id = 1''',
- r'''delete from `{table_path}` where id = 2''',
r'''replace into `{table_path}` (id, value) values (2, 3), (3, 3)''',
r'''upsert into `{table_path}` (id, value) values (4, 4), (5, 5)''',
]
@@ -210,6 +232,19 @@ def test_single_dml_query_logged(query_template, prepared_test_env, _client_sess
print(capture_audit.captured, file=sys.stderr)
assert query_text in capture_audit.captured
+ assert '"query_text"' in capture_audit.captured
+ assert '"remote_address"' in capture_audit.captured
+ assert '"subject"' in capture_audit.captured
+ assert '"database"' in capture_audit.captured
+ assert '"operation"' in capture_audit.captured
+ assert '"start_time"' in capture_audit.captured
+ assert '"end_time"' in capture_audit.captured
+
+ # absent cloud-ids are not logged
+ assert '"cloud_id"' not in capture_audit.captured
+ assert '"folder_id"' not in capture_audit.captured
+ assert '"resource_id"' not in capture_audit.captured
+
def test_dml_begin_commit_logged(prepared_test_env, _client_session_pool_with_auth_root):
table_path, capture_audit = prepared_test_env
@@ -310,3 +345,23 @@ def test_dml_requests_logged_when_sid_is_unexpected(ydb_cluster, _database, prep
execute_data_query(pool, query_text)
print(capture_audit.captured, file=sys.stderr)
assert query_text in capture_audit.captured
+
+
+@pytest.mark.parametrize('attrs', [
+ dict(cloud_id='cloud-id-A', folder_id='folder-id-B', database_id='database-id-C'),
+ dict(folder_id='folder-id-B'),
+])
+def test_cloud_ids_are_logged(ydb_cluster, _database, prepared_test_env, _client_session_pool_with_auth_root, attrs):
+ database_path = _database
+ table_path, capture_audit = prepared_test_env
+
+ alter_user_attrs(ydb_cluster, database_path, **attrs)
+
+ pool = _client_session_pool_with_auth_root
+ with capture_audit:
+ execute_data_query(pool, fr'''update `{table_path}` set value = 0 where id = 1''')
+ print(capture_audit.captured, file=sys.stderr)
+
+ for k, v in attrs.items():
+ name = k if k != 'database_id' else 'resource_id'
+ assert fr'''"{name}":"{v}"''' in capture_audit.captured