diff options
author | ijon <ijon@ydb.tech> | 2023-10-26 16:35:59 +0300 |
---|---|---|
committer | ijon <ijon@ydb.tech> | 2023-10-26 17:02:08 +0300 |
commit | 4f440816e221a84402c1a4bc06c2fb9e3bb168e8 (patch) | |
tree | 4416b5a9546cbeaa630002a57a601973c3744479 | |
parent | e50bfe724b357597d9528251a5be829f6c0d7cc0 (diff) | |
download | ydb-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.cpp | 31 | ||||
-rw-r--r-- | ydb/core/grpc_services/audit_dml_operations.h | 2 | ||||
-rw-r--r-- | ydb/core/grpc_services/grpc_request_check_actor.h | 2 | ||||
-rw-r--r-- | ydb/tests/functional/audit/test_auditlog.py | 57 |
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 |