133 lines
4.7 KiB
C#
133 lines
4.7 KiB
C#
/*
|
||
* 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();
|
||
//}
|
||
} |