389 lines
14 KiB
Plaintext
389 lines
14 KiB
Plaintext
@using Microsoft.Extensions.Options
|
|
@using MongoDB.Driver
|
|
@using Rms.Risk.Mango.Pivot.Core.MongoDb
|
|
@using Rms.Risk.Mango.Services.Context
|
|
@using Rms.Service.Bootstrap.Security
|
|
|
|
@inject IDatabaseConfigurationService DatabaseConfig
|
|
@inject IUserSession UserSession
|
|
@inject IJSRuntime JsRuntime
|
|
@inject IPasswordManager PasswordManager
|
|
@inject IOptions<DbMangoSettings> Settings
|
|
@inject IUserService UserService
|
|
|
|
@*
|
|
* 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.
|
|
*@
|
|
|
|
<style>
|
|
.full-width {
|
|
width: 408px !important;
|
|
}
|
|
</style>
|
|
|
|
<EditForm EditContext="EditContext">
|
|
<div class="d-flex flex-row fit-content">
|
|
<div class="fit-content mr-3">
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="Configuration name" @bind-Value="Name" Icon="icon-document-blank-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="Contacts" @bind-Value="Value.Contacts" Icon="icon-mail-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="mr-2" Name="Database" @bind-Value="Value.Config.MongoDbDatabase" Icon="icon-annotate-sm" />
|
|
<FormItemCheckBox Class="mr-2" Name="Use TLS" @bind-Value="Value.Config.UseTls" Icon="icon-wrench-outline-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="URL" @bind-Value="Value.Config.MongoDbUrl" Icon="icon-wrench-outline-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemCheckBox Class="mr-2" Name="Direct connection" @bind-Value="Value.Config.DirectConnection" Icon="icon-wrench-outline-sm"/>
|
|
<FormItemCheckBox Class="mr-2" Name="Shard access" @bind-Value="Value.Config.AllowShardAccess" Icon="icon-wrench-outline-sm"/>
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="mr-2" Name="Auth database" @bind-Value="AuthDatabase" Icon="icon-cog-sm" />
|
|
<FormItemText Class="mr-2" Name="Auth method" @bind-Value="AuthMethod" Icon="icon-cog-sm" />
|
|
</div>
|
|
|
|
</div>
|
|
<div class="fit-content">
|
|
<div class="d-flex flex-column">
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="mr-2" Name="User" @bind-Value="UserUser" Icon="icon-wrench-outline-sm" />
|
|
<FormItemPassword Class="mr-2" Name="Password" @bind-Value="UserPassword" Icon="icon-unlock-outline-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="mr-2" Name="Admin User" @bind-Value="AdminUser" Icon="icon-wrench-outline-sm" />
|
|
<FormItemPassword Class="mr-2" Name="Admin Password" @bind-Value="AdminPassword" Icon="icon-unlock-outline-sm" />
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="LDAP Read group" @bind-Value="Value.Groups.ReadOnly" Icon="icon-layers-sm" />
|
|
</div>
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="LDAP Write group" @bind-Value="Value.Groups.ReadWrite" Icon="icon-layers-sm" />
|
|
</div>
|
|
<div class="form-row fit-content">
|
|
<FormItemText Class="full-width" Name="LDAP Admin group" @bind-Value="Value.Groups.Admin" Icon="icon-layers-sm" />
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row fit-content">
|
|
<FormButton Name="Update" OnClick="Update" Icon="icon-document-upload-sm" />
|
|
<FormButton Name="Delete" OnClick="Delete" Icon="icon-trash-sm" />
|
|
<FormButton Name="" OnClick="Copy" Icon="icon-duplicate-document" />
|
|
<FormButton Name="" OnClick="Paste" Icon="icon-paste" />
|
|
<FormButton Name="Test" OnClick="Test" Icon="icon-flame" />
|
|
</div>
|
|
|
|
</EditForm>
|
|
|
|
@code {
|
|
[CascadingParameter] public IModalService Modal { get; set; } = null!;
|
|
|
|
[Parameter] public string Name { get; set; } = "New connection";
|
|
[Parameter] public DatabasesConfig.DatabaseConfig Value { get; set; } = new();
|
|
[Parameter] public EventCallback OnRefresh { get; set; }
|
|
|
|
private EditContext EditContext => _editContext!;
|
|
|
|
private EditContext? _editContext;
|
|
|
|
private string UserUser
|
|
{
|
|
get => Value.Config.Auth?.User ?? "";
|
|
set
|
|
{
|
|
Value.Config.Auth ??= new();
|
|
Value.Config.Auth.User = value;
|
|
}
|
|
}
|
|
|
|
private string UserPassword
|
|
{
|
|
get => Value.Config.Auth?.Password ?? "";
|
|
set
|
|
{
|
|
Value.Config.Auth ??= new();
|
|
Value.Config.Auth.Password = value;
|
|
}
|
|
}
|
|
|
|
private string AuthDatabase
|
|
{
|
|
get => Value.Config.Auth?.AuthDatabase ?? "";
|
|
set
|
|
{
|
|
Value.Config.Auth ??= new();
|
|
Value.Config.Auth.AuthDatabase = value;
|
|
}
|
|
}
|
|
|
|
private string AuthMethod
|
|
{
|
|
get => Value.Config.Auth?.Method ?? "";
|
|
set
|
|
{
|
|
Value.Config.Auth ??= new();
|
|
Value.Config.Auth.Method = value;
|
|
}
|
|
}
|
|
|
|
private string AdminUser
|
|
{
|
|
get => Value.Config.AdminAuth?.User ?? "";
|
|
set
|
|
{
|
|
Value.Config.AdminAuth ??= new();
|
|
Value.Config.AdminAuth.User = value;
|
|
}
|
|
}
|
|
|
|
private string AdminPassword
|
|
{
|
|
get => Value.Config.AdminAuth?.Password ?? "";
|
|
set
|
|
{
|
|
Value.Config.AdminAuth ??= new();
|
|
Value.Config.AdminAuth.Password = value;
|
|
}
|
|
}
|
|
|
|
protected override void OnInitialized()
|
|
{
|
|
_editContext = new(this);
|
|
}
|
|
|
|
private Task<string?> CanExecuteCommand()
|
|
=> Shell.CanExecuteCommand(UserSession, InvokeAsync, Modal);
|
|
|
|
|
|
private async Task Update()
|
|
{
|
|
var ticket = await CanExecuteCommand();
|
|
if (string.IsNullOrWhiteSpace(ticket))
|
|
return;
|
|
|
|
var res = await ModalDialogUtils.ShowConfirmationDialog(
|
|
Modal,
|
|
"Update configuration",
|
|
$"Are you sure want to update configuration for {Name}?"
|
|
);
|
|
if (res.Cancelled)
|
|
return;
|
|
|
|
try
|
|
{
|
|
PrepareForSaving();
|
|
await DatabaseConfig.Update(Name, Value, UserSession.User.GetEmail());
|
|
await ModalDialogUtils.ShowInfoDialog(Modal, "Success", $"Configuration {Name} was updated successfully.");
|
|
|
|
// exception in Audit() call can lead to 2 dialogs shown in total: Success in config change + error recording audit
|
|
try
|
|
{
|
|
await Audit(ticket, "update", Name);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex2);
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try
|
|
{
|
|
await Audit(ticket, "update", Name, ex);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex2);
|
|
}
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex);
|
|
}
|
|
|
|
await OnRefresh.InvokeAsync();
|
|
}
|
|
|
|
private async Task Audit(string ticket, string action, string configName, Exception? exception = null)
|
|
{
|
|
var doc = new BsonDocument
|
|
{
|
|
["dbMangoOnboarding"] = exception?.Message ?? "Success",
|
|
["action"] = action,
|
|
["configName"] = configName,
|
|
["ticket"] = ticket
|
|
};
|
|
|
|
var auditRecord = new AuditRecord(UserSession.Database, DateTime.UtcNow, UserService.GetEmail(), ticket, exception == null, doc, exception?.Message);
|
|
await UserSession.Audit.Record(auditRecord);
|
|
}
|
|
|
|
|
|
private async Task Delete()
|
|
{
|
|
var ticket = await CanExecuteCommand();
|
|
if (string.IsNullOrWhiteSpace(ticket))
|
|
return;
|
|
|
|
var res = await ModalDialogUtils.ShowConfirmationDialog(
|
|
Modal,
|
|
"Delete configuration",
|
|
$"Are you sure want to delete configuration for {Name}? (Note that pre-configured databases can't be deleted.)"
|
|
);
|
|
if (res.Cancelled)
|
|
return;
|
|
|
|
try
|
|
{
|
|
await DatabaseConfig.Delete(Name, UserSession.User.GetEmail());
|
|
|
|
await ModalDialogUtils.ShowInfoDialog(Modal, "Success", $"Configuration {Name} was deleted successfully.");
|
|
try
|
|
{
|
|
await Audit(ticket, "delete", Name);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex2);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try
|
|
{
|
|
await Audit(ticket, "delete", Name, ex);
|
|
}
|
|
catch (Exception ex2)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex2);
|
|
}
|
|
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex);
|
|
}
|
|
|
|
await OnRefresh.InvokeAsync();
|
|
}
|
|
|
|
private void PrepareForSaving()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Value.Config.Auth?.Password) || string.IsNullOrWhiteSpace(Value.Config.Auth?.User))
|
|
Value.Config.Auth = null;
|
|
if (string.IsNullOrWhiteSpace(Value.Config.AdminAuth?.Password) || string.IsNullOrWhiteSpace(Value.Config.AdminAuth?.User))
|
|
Value.Config.AdminAuth = null;
|
|
}
|
|
|
|
private class ClipboardFormat
|
|
{
|
|
public string Name { get; set; } = "";
|
|
public DatabasesConfig.DatabaseConfig Value { get; set; } = new();
|
|
}
|
|
|
|
private async Task Copy()
|
|
{
|
|
try
|
|
{
|
|
PrepareForSaving();
|
|
|
|
var obj = new ClipboardFormat
|
|
{
|
|
Name = Name,
|
|
Value = Value
|
|
};
|
|
|
|
var json = JsonUtils.ToJson(obj, new() {WriteIndented = true});
|
|
var clone = JsonUtils.FromJson<ClipboardFormat>(json);
|
|
if (clone == null)
|
|
return;
|
|
|
|
if (!string.IsNullOrWhiteSpace(clone.Value.Config.Auth?.Password))
|
|
clone.Value.Config.Auth.Password = PasswordManager.EncryptPassword(clone.Value.Config.Auth.Password);
|
|
if (!string.IsNullOrWhiteSpace(clone.Value.Config.AdminAuth?.Password))
|
|
clone.Value.Config.AdminAuth.Password = PasswordManager.EncryptPassword(clone.Value.Config.AdminAuth.Password);
|
|
|
|
json = JsonUtils.ToJson(clone, new() { WriteIndented = true });
|
|
|
|
await JsRuntime.InvokeVoidAsync("DashboardUtils.CopyToClipboard", json);
|
|
await ModalDialogUtils.ShowInfoDialog(Modal, "Copied", $"Configuration {Name} was copied to clipboard.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex);
|
|
}
|
|
}
|
|
|
|
private async Task Paste()
|
|
{
|
|
try
|
|
{
|
|
var json = await JsRuntime.InvokeAsync<string>("DashboardUtils.PasteFromClipboard");
|
|
|
|
var clone = JsonUtils.FromJson<ClipboardFormat>(json);
|
|
if (clone == null)
|
|
return;
|
|
|
|
if (clone.Value.Config.Auth?.Password.StartsWith('*') ?? false)
|
|
clone.Value.Config.Auth.Password = PasswordManager.DecryptPassword(clone.Value.Config.Auth.Password);
|
|
if (clone.Value.Config.AdminAuth?.Password.StartsWith('*') ?? false)
|
|
clone.Value.Config.AdminAuth.Password = PasswordManager.DecryptPassword(clone.Value.Config.AdminAuth.Password);
|
|
|
|
Name = clone.Name;
|
|
Value = clone.Value;
|
|
|
|
await InvokeAsync(StateHasChanged);
|
|
|
|
await ModalDialogUtils.ShowInfoDialog(Modal, "Copied", $"Configuration {Name} was successfully parsed. You still need to save it!");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex);
|
|
}
|
|
}
|
|
|
|
private async Task Test()
|
|
{
|
|
try
|
|
{
|
|
var databaseInstance = !string.IsNullOrWhiteSpace(Value.Config.MongoDbDatabase) ? Value.Config.MongoDbDatabase : "admin";
|
|
|
|
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
|
var db = MongoDbHelper.GetDatabase(Value.Config, Settings.Value.Settings, databaseInstance);
|
|
_ = await db.RunCommandAsync(
|
|
new BsonDocumentCommand<BsonDocument>(BsonDocument.Parse( @"{ ""ping"" : 1 }")),
|
|
cancellationToken: cts.Token);
|
|
|
|
await ModalDialogUtils.ShowInfoDialog(Modal, "Success", $"Successfully connected to {Name}.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", ex);
|
|
}
|
|
}
|
|
}
|