aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorijon <ijon@yandex-team.com>2023-03-13 13:06:42 +0300
committerijon <ijon@yandex-team.com>2023-03-13 13:06:42 +0300
commitb289c1b08d7c71b12149c22feb733cd79c9f0143 (patch)
tree20a5f80663ea19b879d64bc1cc69e4f485a2f858
parentd2e129c8bc361ffd7f75b24ce1903eeb06613d3a (diff)
downloadydb-b289c1b08d7c71b12149c22feb733cd79c9f0143.tar.gz
schemeshard, auditlog: add cloud ids and acl/user attrs modification details
Add new audit record fields: - component -- (="schemeshard") - {cloud,folder,resource}_id -- Cloud specific database binding - new_owner, acl_add, acl_remove -- ACL change (if applicable) - user_attrs_add, user_attrs_remove -- user attributes change (if applicable)
-rw-r--r--ydb/core/tx/schemeshard/schemeshard__operation.cpp95
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.cpp68
-rw-r--r--ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.h6
3 files changed, 155 insertions, 14 deletions
diff --git a/ydb/core/tx/schemeshard/schemeshard__operation.cpp b/ydb/core/tx/schemeshard/schemeshard__operation.cpp
index 5aeb859aea4..4fb08611b4f 100644
--- a/ydb/core/tx/schemeshard/schemeshard__operation.cpp
+++ b/ydb/core/tx/schemeshard/schemeshard__operation.cpp
@@ -36,39 +36,106 @@ std::tuple<TMaybe<NACLib::TUserToken>, bool> ParseUserToken(const TString& token
return std::make_tuple(result, parseError);
}
-TString RenderPaths(const TVector<TString>& paths) {
+TString GeneralStatus(NKikimrScheme::EStatus actualStatus) {
+ switch(actualStatus) {
+ case NKikimrScheme::EStatus::StatusAlreadyExists:
+ case NKikimrScheme::EStatus::StatusSuccess:
+ return "SUCCESS";
+ case NKikimrScheme::EStatus::StatusAccepted:
+ //TODO: reclassify StatusAccepted as IN-PROCCESS when
+ // logging of operation completion points will be added
+ // return "IN-PROCCESS";
+ return "SUCCESS";
+ default:
+ return "ERROR";
+ }
+}
+
+TString RenderList(const TVector<TString>& list) {
auto result = TStringBuilder();
- result << "[" << JoinStrings(paths.begin(), paths.end(), ", ") << "]";
+ result << "[" << JoinStrings(list.begin(), list.end(), ", ") << "]";
return result;
}
+std::tuple<TString, TString, TString> GetDatabaseCloudIds(const TPath &databasePath) {
+ if (databasePath.IsEmpty()) {
+ return {};
+ }
+ Y_VERIFY(databasePath->IsDomainRoot());
+ auto getAttr = [&databasePath](const TString &name) -> TString {
+ if (databasePath.Base()->UserAttrs->Attrs.contains(name)) {
+ return databasePath.Base()->UserAttrs->Attrs.at(name);
+ }
+ return {};
+ };
+ return std::make_tuple(
+ getAttr("cloud_id"),
+ getAttr("folder_id"),
+ getAttr("database_id")
+ );
+}
+
+TPath DatabasePathFromWorkingDir(TSchemeShard* SS, const TString &opWorkingDir) {
+ auto databasePath = TPath::Resolve(opWorkingDir, SS);
+ if (!databasePath.IsResolved()) {
+ databasePath.RiseUntilFirstResolvedParent();
+ }
+ //NOTE: operation working dir is usually set to a path of some database/subdomain,
+ // so the next lines is only a safety measure
+ if (!databasePath.IsEmpty() && !databasePath->IsDomainRoot()) {
+ databasePath = TPath::Init(databasePath.GetPathIdForDomain(), SS);
+ }
+ return databasePath;
+}
+
void AuditLogModifySchemeTransaction(const NKikimrScheme::TEvModifySchemeTransaction& request, const NKikimrScheme::TEvModifySchemeTransactionResult& response, TSchemeShard* SS, const TString& userSID) {
- // Each TEvModifySchemeTransaction.Transaction is a self sufficient operation and should be logged independently
- // (even if it was packed into a single TxProxy transaction with some other operations).
+ static const TString SchemeshardComponentName = "schemeshard";
- //NOTE: UserSIDNone couldn't be an empty string as "subject" field is a required one,
- // but AUDIT_PART() skips any part with an empty value
+ //NOTE: EmptyValue couldn't be an empty string as AUDIT_PART() skips parts with an empty values
static const TString EmptyValue = "{none}";
+ // Each TEvModifySchemeTransaction.Transaction is a self sufficient operation and should be logged independently
+ // (even if it was packed into a single TxProxy transaction with some other operations).
for (const auto& operation : request.GetTransaction()) {
auto logEntry = MakeAuditLogFragment(operation);
- auto databasePath = TPath::Resolve(operation.GetWorkingDir(), SS);
- if (!databasePath.IsResolved()) {
- databasePath.RiseUntilFirstResolvedParent();
- }
-
+ TPath databasePath = DatabasePathFromWorkingDir(SS, operation.GetWorkingDir());
+ auto [cloud_id, folder_id, database_id] = GetDatabaseCloudIds(databasePath);
auto peerName = request.GetPeerName();
AUDIT_LOG(
- AUDIT_PART("txId", std::to_string(request.GetTxId()))
+ AUDIT_PART("component", SchemeshardComponentName)
+ AUDIT_PART("tx_id", std::to_string(request.GetTxId()))
AUDIT_PART("remote_address", (!peerName.empty() ? peerName : EmptyValue) )
AUDIT_PART("subject", (!userSID.empty() ? userSID : EmptyValue))
AUDIT_PART("database", (!databasePath.IsEmpty() ? databasePath.GetDomainPathString() : EmptyValue))
AUDIT_PART("operation", logEntry.Operation)
- AUDIT_PART("paths", RenderPaths(logEntry.Paths), !logEntry.Paths.empty())
- AUDIT_PART("status", NKikimrScheme::EStatus_Name(response.GetStatus()))
+ AUDIT_PART("paths", RenderList(logEntry.Paths), !logEntry.Paths.empty())
+ AUDIT_PART("status", GeneralStatus(response.GetStatus()))
+ AUDIT_PART("detailed_status", NKikimrScheme::EStatus_Name(response.GetStatus()))
AUDIT_PART("reason", response.GetReason(), response.HasReason())
+
+ AUDIT_PART("cloud_id", cloud_id, !cloud_id.empty());
+ AUDIT_PART("folder_id", folder_id, !folder_id.empty());
+ AUDIT_PART("resource_id", database_id, !database_id.empty());
+
+ // Additionally:
+
+ // ModifyACL.
+ // Technically, non-empty ModifyACL field could come with any ModifyScheme operation.
+ // In practice, ModifyACL will get processed only by:
+ // 1. explicit operation ESchemeOpModifyACL -- to modify ACL on a path
+ // 2. ESchemeOpMkDir or ESchemeOpCreate* operations -- to set rights to newly created paths/entities
+ // 3. ESchemeOpCopyTable -- to be checked against acl size limit, not to be applied in any way
+ AUDIT_PART("new_owner", logEntry.NewOwner, !logEntry.NewOwner.empty());
+ AUDIT_PART("acl_add", RenderList(logEntry.ACLAdd), !logEntry.ACLAdd.empty());
+ AUDIT_PART("acl_remove", RenderList(logEntry.ACLRemove), !logEntry.ACLRemove.empty());
+
+ // AlterUserAttributes.
+ // 1. explicit operation ESchemeOpAlterUserAttributes -- to modify user attributes on a path
+ // 2. ESchemeOpMkDir or some ESchemeOpCreate* operations -- to set user attributes for newly created paths/entities
+ AUDIT_PART("user_attrs_add", RenderList(logEntry.UserAttrsAdd), !logEntry.UserAttrsAdd.empty());
+ AUDIT_PART("user_attrs_remove", RenderList(logEntry.UserAttrsRemove), !logEntry.UserAttrsRemove.empty());
);
}
}
diff --git a/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.cpp b/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.cpp
index 6044c00fa81..b24db727fba 100644
--- a/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.cpp
+++ b/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.cpp
@@ -480,14 +480,82 @@ TVector<TString> ExtractChangingPaths(const NKikimrSchemeOp::TModifyScheme& tx)
return result;
}
+TString ExtractNewOwner(const NKikimrSchemeOp::TModifyScheme& tx) {
+ bool hasNewOwner = tx.HasModifyACL() && tx.GetModifyACL().HasNewOwner();
+ if (hasNewOwner) {
+ return tx.GetModifyACL().GetNewOwner();
+ }
+ return {};
+}
+
+struct TChange {
+ TVector<TString> Add;
+ TVector<TString> Remove;
+};
+
+TChange ExtractACLChange(const NKikimrSchemeOp::TModifyScheme& tx) {
+ bool hasACL = tx.HasModifyACL() && tx.GetModifyACL().HasDiffACL();
+ if (hasACL) {
+ TChange result;
+
+ NACLib::TDiffACL diff(tx.GetModifyACL().GetDiffACL());
+ for (const auto& i : diff.GetDiffACE()) {
+ auto diffType = static_cast<NACLib::EDiffType>(i.GetDiffType());
+ const NACLibProto::TACE& ace = i.GetACE();
+ switch (diffType) {
+ case NACLib::EDiffType::Add:
+ result.Add.push_back(NACLib::TACL::ToString(ace));
+ break;
+ case NACLib::EDiffType::Remove:
+ result.Remove.push_back(NACLib::TACL::ToString(ace));
+ break;
+ }
+ }
+
+ return result;
+ }
+ return {};
+}
+
+TChange ExtractUserAttrChange(const NKikimrSchemeOp::TModifyScheme& tx) {
+ bool hasUserAttrs = tx.HasAlterUserAttributes() && (tx.GetAlterUserAttributes().UserAttributesSize() > 0);
+ if (hasUserAttrs) {
+ TChange result;
+ auto str = TStringBuilder();
+
+ for (const auto& i : tx.GetAlterUserAttributes().GetUserAttributes()) {
+ const auto& key = i.GetKey();
+ const auto& value = i.GetValue();
+ if (value.empty()) {
+ result.Remove.push_back(key);
+ } else {
+ str.clear();
+ str << key << ": " << "value";
+ result.Add.push_back(str);
+ }
+ }
+
+ return result;
+ }
+ return {};
+}
+
+
} // anonymous namespace
namespace NKikimr::NSchemeShard {
TAuditLogFragment MakeAuditLogFragment(const NKikimrSchemeOp::TModifyScheme& tx) {
+ auto [aclAdd, aclRemove] = ExtractACLChange(tx);
+ auto [userAttrsAdd, userAttrsRemove] = ExtractUserAttrChange(tx);
return {
.Operation = DefineUserOperationName(tx.GetOperationType()),
.Paths = ExtractChangingPaths(tx),
+ .NewOwner = ExtractNewOwner(tx),
+ .ACLAdd = aclAdd,
+ .ACLRemove = aclRemove,
+ .UserAttrsAdd = userAttrsAdd,
+ .UserAttrsRemove = userAttrsRemove,
};
}
diff --git a/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.h b/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.h
index 57874086b48..af93dc15c80 100644
--- a/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.h
+++ b/ydb/core/tx/schemeshard/schemeshard_audit_log_fragment.h
@@ -2,6 +2,7 @@
#include <util/generic/string.h>
#include <util/generic/vector.h>
+#include <util/generic/maybe.h>
namespace NKikimrSchemeOp {
class TModifyScheme;
@@ -12,6 +13,11 @@ namespace NKikimr::NSchemeShard {
struct TAuditLogFragment {
TString Operation;
TVector<TString> Paths;
+ TString NewOwner;
+ TVector<TString> ACLAdd;
+ TVector<TString> ACLRemove;
+ TVector<TString> UserAttrsAdd;
+ TVector<TString> UserAttrsRemove;
};
TAuditLogFragment MakeAuditLogFragment(const NKikimrSchemeOp::TModifyScheme& tx);