Merge pull request #6414 from duplicati/feature/fix-serverutil-probing
Update ServerUtil probing
This commit is contained in:
@@ -193,58 +193,62 @@ public class Connection
|
||||
}
|
||||
|
||||
// If we can read the server database, try to create a signin token
|
||||
try
|
||||
// But don't try to look at the database if the password is already set.
|
||||
if (string.IsNullOrWhiteSpace(settings.Password))
|
||||
{
|
||||
var opts = new Dictionary<string, string>();
|
||||
if (settings.Key != null)
|
||||
opts["settings-encryption-key"] = settings.Key.Key;
|
||||
if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), DataFolderManager.SERVER_DATABASE_FILENAME)))
|
||||
try
|
||||
{
|
||||
string? cfg = null;
|
||||
using (var connection = Server.Program.GetDatabaseConnection(new ApplicationSettings(), opts, true))
|
||||
var opts = new Dictionary<string, string>();
|
||||
if (settings.Key != null)
|
||||
opts["settings-encryption-key"] = settings.Key.Key;
|
||||
if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), DataFolderManager.SERVER_DATABASE_FILENAME)))
|
||||
{
|
||||
cfg = connection.ApplicationSettings.JWTConfig;
|
||||
if (settings.HostUrl.Scheme == "https" && connection.ApplicationSettings.ServerSSLCertificate != null && trustedCertificateHashes.Count == 0)
|
||||
string? cfg = null;
|
||||
using (var connection = Server.Program.GetDatabaseConnection(new ApplicationSettings(), opts, true, false))
|
||||
{
|
||||
var selfSignedCertHash = connection.ApplicationSettings.ServerSSLCertificate?.FirstOrDefault(x => x.HasPrivateKey)?.GetCertHashString();
|
||||
if (!string.IsNullOrWhiteSpace(selfSignedCertHash))
|
||||
trustedCertificateHashes.Add(selfSignedCertHash);
|
||||
cfg = connection.ApplicationSettings.JWTConfig;
|
||||
if (settings.HostUrl.Scheme == "https" && connection.ApplicationSettings.ServerSSLCertificate != null && trustedCertificateHashes.Count == 0)
|
||||
{
|
||||
var selfSignedCertHash = connection.ApplicationSettings.ServerSSLCertificate?.FirstOrDefault(x => x.HasPrivateKey)?.GetCertHashString();
|
||||
if (!string.IsNullOrWhiteSpace(selfSignedCertHash))
|
||||
trustedCertificateHashes.Add(selfSignedCertHash);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(cfg))
|
||||
{
|
||||
var signinjwt = new JWTTokenProvider(
|
||||
JsonSerializer.Deserialize<JWTConfig>(cfg)
|
||||
?? throw new InvalidOperationException("Failed to deserialize JWTConfig")
|
||||
).CreateSigninToken("server-cli");
|
||||
|
||||
var responseTask = client.PostAsync("auth/signin", JsonContent.Create(new { SigninToken = signinjwt, RememberMe = obtainRefreshToken }));
|
||||
var (accessToken, refreshToken) = await ParseAuthResponse(responseTask);
|
||||
if (string.IsNullOrWhiteSpace(accessToken))
|
||||
throw new InvalidOperationException("Failed to get access token");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(refreshToken))
|
||||
(settings with { RefreshToken = refreshToken }).Save(console);
|
||||
|
||||
return CreateConnectionWithClient(client, accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(cfg))
|
||||
else if (!string.IsNullOrWhiteSpace(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)))
|
||||
{
|
||||
var signinjwt = new JWTTokenProvider(
|
||||
JsonSerializer.Deserialize<JWTConfig>(cfg)
|
||||
?? throw new InvalidOperationException("Failed to deserialize JWTConfig")
|
||||
).CreateSigninToken("server-cli");
|
||||
|
||||
var responseTask = client.PostAsync("auth/signin", JsonContent.Create(new { SigninToken = signinjwt, RememberMe = obtainRefreshToken }));
|
||||
var (accessToken, refreshToken) = await ParseAuthResponse(responseTask);
|
||||
if (string.IsNullOrWhiteSpace(accessToken))
|
||||
throw new InvalidOperationException("Failed to get access token");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(refreshToken))
|
||||
(settings with { RefreshToken = refreshToken }).Save(console);
|
||||
|
||||
return CreateConnectionWithClient(client, accessToken);
|
||||
if (console != null)
|
||||
console.AppendConsoleMessage($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}");
|
||||
else
|
||||
Console.WriteLine($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}");
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)))
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (console != null)
|
||||
console.AppendConsoleMessage($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}");
|
||||
console.AppendConsoleMessage($"Failed to obtain a signin token: {ex.Message}");
|
||||
else
|
||||
Console.WriteLine($"No database found in {DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ProbeOnly)}");
|
||||
Console.WriteLine($"Failed to obtain a signin token: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (console != null)
|
||||
console.AppendConsoleMessage($"Failed to obtain a signin token: {ex.Message}");
|
||||
else
|
||||
Console.WriteLine($"Failed to obtain a signin token: {ex.Message}");
|
||||
}
|
||||
|
||||
// Otherwise, we need a password to log in
|
||||
try
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Duplicati.GUI.TrayIcon
|
||||
if (File.Exists(Path.Combine(DataFolderManager.GetDataFolder(DataFolderManager.AccessMode.ReadWritePermissionSet), DataFolderManager.SERVER_DATABASE_FILENAME)))
|
||||
{
|
||||
passwordSource = PasswordSource.Database;
|
||||
databaseConnection = Server.Program.GetDatabaseConnection(new ApplicationSettings(), options, true);
|
||||
databaseConnection = Server.Program.GetDatabaseConnection(new ApplicationSettings(), options, true, false);
|
||||
|
||||
if (databaseConnection != null)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Duplicati.Library.Common.IO;
|
||||
using Duplicati.Library.Interface;
|
||||
using Duplicati.Library.Utility;
|
||||
|
||||
namespace Duplicati.Library.AutoUpdater;
|
||||
@@ -95,9 +96,9 @@ public static class DataFolderManager
|
||||
{
|
||||
// Trigger portable mode, if the flag is set
|
||||
PORTABLE_MODE = ParseBoolSlim(ExtractOptionSlim(PORTABLE_MODE_OPTION));
|
||||
|
||||
|
||||
string dataFolder = string.Empty;
|
||||
|
||||
|
||||
// The environment variable is a legacy setting
|
||||
var envOverride = Environment.GetEnvironmentVariable(DATAFOLDER_ENV_NAME);
|
||||
|
||||
@@ -142,17 +143,23 @@ public static class DataFolderManager
|
||||
Logging.Log.WriteWarningMessage(LOGTAG, "FailedToSetPermissions", ex, "Failed to set permissions for {0}: {1}", dataFolder, ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (mode == AccessMode.ReadWritePermissionSet)
|
||||
{
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
try
|
||||
{
|
||||
if (mode == AccessMode.ReadWritePermissionSet)
|
||||
Directory.CreateDirectory(dataFolder);
|
||||
try
|
||||
{
|
||||
SystemIO.IO_OS.DirectorySetPermissionUserRWOnly(dataFolder);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log.WriteWarningMessage(LOGTAG, "FailedToSetPermissions", ex, "Failed to set permissions for {0}: {1}", dataFolder, ex.Message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Log.WriteWarningMessage(LOGTAG, "FailedToSetPermissions", ex, "Failed to set permissions for {0}: {1}", dataFolder, ex.Message);
|
||||
throw new UserInformationException($"Failed to create data folder {dataFolder}", "FailedToCreateDataFolder", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +174,7 @@ public static class DataFolderManager
|
||||
|
||||
if (mode == AccessMode.ReadWritePermissionSet && !File.Exists(Path.Combine(dataFolder, MACHINE_FILE)))
|
||||
File.WriteAllText(Path.Combine(dataFolder, MACHINE_FILE), AutoUpdateSettings.UpdateMachineFileText(InstallID));
|
||||
|
||||
|
||||
return dataFolder;
|
||||
}
|
||||
|
||||
@@ -227,7 +234,7 @@ public static class DataFolderManager
|
||||
&& !value.Equals("no", StringComparison.OrdinalIgnoreCase)
|
||||
&& !value.Equals("off", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The unique machine installation ID
|
||||
/// </summary>
|
||||
|
||||
+22
-20
@@ -197,7 +197,7 @@ namespace Duplicati.Server
|
||||
ISchedulerService scheduler = null;
|
||||
try
|
||||
{
|
||||
var connection = GetDatabaseConnection(applicationSettings, commandlineOptions, silentConsole);
|
||||
var connection = GetDatabaseConnection(applicationSettings, commandlineOptions, silentConsole, true);
|
||||
|
||||
if (!connection.ApplicationSettings.FixedInvalidBackupId)
|
||||
connection.FixInvalidBackupId();
|
||||
@@ -736,7 +736,7 @@ namespace Duplicati.Server
|
||||
throw new Exception("Server invoked with --help");
|
||||
}
|
||||
|
||||
public static Connection GetDatabaseConnection(IApplicationSettings applicationSettings, Dictionary<string, string> commandlineOptions, bool silentConsole)
|
||||
public static Connection GetDatabaseConnection(IApplicationSettings applicationSettings, Dictionary<string, string> commandlineOptions, bool silentConsole, bool changeDbEncryption)
|
||||
{
|
||||
// Emit a warning if the database is stored in the Windows folder
|
||||
if (Util.IsPathUnderWindowsFolder(applicationSettings.DataFolder))
|
||||
@@ -786,29 +786,26 @@ namespace Duplicati.Server
|
||||
if (requireDbEncryptionKey && !(hasValidEncryptionKey || disableDbEncryption))
|
||||
throw new UserInformationException(Strings.Program.DatabaseEncryptionKeyRequired(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME, DISABLE_DB_ENCRYPTION_OPTION), "RequireDbEncryptionKey");
|
||||
|
||||
if (!hasValidEncryptionKey)
|
||||
var hasEncryptedFields = false;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var hasEncryptedFields = false;
|
||||
using (var cmd = con.CreateCommand(@$"SELECT ""Value"" FROM ""Option"" WHERE ""Name"" = @Name AND ""BackupID"" = @BackupId"))
|
||||
hasEncryptedFields = Library.Utility.Utility.ParseBool(cmd
|
||||
.SetParameterValue("@Name", Database.ServerSettings.CONST.ENCRYPTED_FIELDS)
|
||||
.SetParameterValue("@BackupId", Connection.SERVER_SETTINGS_ID).ExecuteScalar()?.ToString(), false);
|
||||
using (var cmd = con.CreateCommand(@$"SELECT ""Value"" FROM ""Option"" WHERE ""Name"" = @Name AND ""BackupID"" = @BackupId"))
|
||||
hasEncryptedFields = Library.Utility.Utility.ParseBool(cmd
|
||||
.SetParameterValue("@Name", Database.ServerSettings.CONST.ENCRYPTED_FIELDS)
|
||||
.SetParameterValue("@BackupId", Connection.SERVER_SETTINGS_ID).ExecuteScalar()?.ToString(), false);
|
||||
|
||||
if (hasEncryptedFields)
|
||||
{
|
||||
Log.WriteWarningMessage(LOGTAG, "EncryptionKeyMissing", null, Strings.Program.EncryptionKeyMissing(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME));
|
||||
if (!silentConsole)
|
||||
Console.WriteLine(Strings.Program.EncryptionKeyMissing(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME));
|
||||
}
|
||||
}
|
||||
catch
|
||||
if (hasEncryptedFields && !hasValidEncryptionKey)
|
||||
{
|
||||
// Ignore errors here, as we are just checking for a potential issue
|
||||
// Only negative effect is that we do not show a potentially helpful warning
|
||||
Log.WriteWarningMessage(LOGTAG, "EncryptionKeyMissing", null, Strings.Program.EncryptionKeyMissing(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME));
|
||||
if (!silentConsole)
|
||||
Console.WriteLine(Strings.Program.EncryptionKeyMissing(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors here, as we are just checking for a potential issue
|
||||
// Only negative effect is that we do not show a potentially helpful warning
|
||||
}
|
||||
|
||||
if (!hasValidEncryptionKey && !disableDbEncryption)
|
||||
{
|
||||
@@ -826,6 +823,11 @@ namespace Duplicati.Server
|
||||
Console.WriteLine(Strings.Program.BlacklistedEncryptionKey(EncryptedFieldHelper.ENVIROMENT_VARIABLE_NAME, DISABLE_DB_ENCRYPTION_OPTION));
|
||||
}
|
||||
|
||||
// If the database is not encrypted, and we are not changing the encryption
|
||||
// don't pass the key, as that would cause the database to be encrypted
|
||||
if (!hasEncryptedFields && !changeDbEncryption && hasValidEncryptionKey)
|
||||
encKey = null;
|
||||
|
||||
return new Connection(con, disableDbEncryption, encKey, applicationSettings.DataFolder, applicationSettings.StartOrStopUsageReporter);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Duplicati.UnitTest
|
||||
}
|
||||
|
||||
byte[] jsonByteArray;
|
||||
using (var con = Program.GetDatabaseConnection(new ApplicationSettings(), advancedOptions, true))
|
||||
using (var con = Program.GetDatabaseConnection(new ApplicationSettings(), advancedOptions, true, false))
|
||||
jsonByteArray = BackupImportExportHandler.ExportToJSON(con, backup, null);
|
||||
|
||||
// The username should not have the '%40' converted to '@' since the import code
|
||||
|
||||
Reference in New Issue
Block a user