Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

133 lines
4.7 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using MongoDB.Bson;
using MongoDB.Driver;
using Rms.Risk.Mango.Interfaces;
using Rms.Risk.Mango.Pivot.Core.MongoDb;
using Rms.Risk.Mango.Pivot.UI.Services;
namespace Rms.Risk.Mango.Services.Audit;
// ReSharper disable InconsistentNaming
public class AuditService(MongoDbConfigRecord _config, MongoDbSettings _settings, int _auditExpireDays, string? _databaseInstance = null) : IAuditService
// ReSharper restore InconsistentNaming
{
public const string AuditCollection = DatabaseStructureLoader.AuditCollection;
private readonly IMongoDatabase _database = MongoDbHelper.GetDatabase(_config, _settings, _databaseInstance);
public void PreCheck(BsonDocument command)
{
if (command.ToJson().Contains(AuditCollection))
throw new ApplicationException("Forbidden command");
}
public async Task Record(AuditRecord rec, CancellationToken token = default)
{
var commandType = rec.Command.ElementAt(0).Name ?? "";
if (MongoDbCommandHelper.IsReadOnlyCommand(commandType))
return;
string? shortError = null;
if (rec.Error != null)
{
var pos = rec.Error.IndexOf('\n');
if (pos >= 0)
{
shortError = rec.Error![..pos].Replace("\r", "");
shortError = shortError[..Math.Min(shortError.Length, 128)];
}
}
var doc = new BsonDocument(new Dictionary<string, object?>()
{
["_id"] = Guid.NewGuid().ToString("N"),
["expire-at"] = DateTime.UtcNow + TimeSpan.FromDays(_auditExpireDays),
["ts"] = rec.Timestamp,
["collection"] = rec.Command.ElementAt(0).Value,
["database"] = rec.DatabaseName,
["email"] = rec.Email,
["ticket"] = rec.Ticket,
["ok"] = rec.Success,
["error"] = shortError,
["commandType"] = commandType,
["command"] = rec.Command
});
await _database.GetCollection<BsonDocument>(AuditCollection).InsertOneAsync(doc, new (), token);
}
public async Task<List<AuditRecord>> Audit(DateTime startDate, DateTime endDate, CancellationToken token = default)
{
var filter = $@"{{
""$and"" : [
{{ ts : {{ ""$gte"" : ISODate(""{startDate:yyyy-MM-dd}T00:00:00"") }} }},
{{ ts : {{ ""$lte"" : ISODate(""{endDate:yyyy-MM-dd}T23:59:59"") }} }}
]
}}";
var findOptions = new FindOptions<BsonDocument, BsonDocument>()
{
BatchSize = 1000
};
var coll = _database.GetCollection<BsonDocument>(AuditCollection);
var cursor = await coll.FindAsync(filter, findOptions, token);
var list = new List<AuditRecord>();
while (await cursor.MoveNextAsync(token))
{
foreach (var doc in cursor.Current)
{
if (doc.IsBsonNull)
continue;
var rec = new AuditRecord
(
DatabaseName: GetStr(doc,"database"),
Timestamp : doc.GetValue("ts" , DateTime.MinValue).AsBsonDateTime.AsUniversalTime,
Email : GetStr(doc,"email" ),
Ticket : GetStr(doc,"ticket"),
Success : doc.GetValue("ok" , false).ToBoolean(),
Error : GetStr(doc,"error" ),
Command : BsonDocument.Parse(doc.GetValue("command", "").ToString() ?? "")
);
list.Add(rec);
}
}
return list;
}
private static string GetStr(BsonDocument doc, string name)
{
if (!doc.Contains(name) || doc[name].IsBsonNull )
return "";
return doc[name].ToString() ?? "";
}
//private static bool GetBool(BsonDocument doc, string name)
//{
// if (!doc.Contains(name) || doc[name].IsBsonNull )
// return false;
// return doc[name].ToBoolean();
//}
}