Change webmodule to have async return value

This commit is contained in:
Kenneth Skovhede
2026-01-14 09:49:56 +01:00
parent 201b5e1150
commit 6ac07531bd
11 changed files with 49 additions and 62 deletions
@@ -43,7 +43,7 @@ public class ListBackupsModule : IWebModule
}
/// <inheritdoc />
public IDictionary<string, string> Execute(IDictionary<string, string?> options)
public async Task<IDictionary<string, string>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
var opts = new Dictionary<string, string?>(options);
@@ -71,7 +71,7 @@ public class ListBackupsModule : IWebModule
using var backend = new DuplicatiBackend(url, opts);
return new Dictionary<string, string>()
{
{ "folders", JsonSerializer.Serialize( backend.ListBackupFolders(CancellationToken.None).ToArrayAsync().Await()) }
{ "folders", JsonSerializer.Serialize( await backend.ListBackupFolders(cancellationToken).ToArrayAsync(cancellationToken) ) }
};
}
@@ -53,7 +53,7 @@ public class GetApiKeyModule : IWebModule
];
/// <inheritdoc/>
public IDictionary<string, string> Execute(IDictionary<string, string?> options)
public async Task<IDictionary<string, string>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
options.TryGetValue("filen-operation", out var operation);
if (operation != "GetApiKey")
@@ -71,7 +71,7 @@ public class GetApiKeyModule : IWebModule
newOpts[key] = uri.QueryParameters[key];
var backend = new FilenBackend(url, newOpts);
var apiKey = backend.GetApiKey(CancellationToken.None).Await();
var apiKey = await backend.GetApiKey(cancellationToken).ConfigureAwait(false);
return new Dictionary<string, string> { { "api-key", apiKey ?? string.Empty } };
}
@@ -42,11 +42,11 @@ namespace Duplicati.Library.Backend.GoogleServices
#region IWebModule implementation
public IDictionary<string, string?> Execute(IDictionary<string, string?> options)
public Task<IDictionary<string, string?>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
var ct = Utility.Utility.ParseEnumOption(options.AsReadOnly(), KEY_CONFIGTYPE, DEFAULT_CONFIG_TYPE);
GetLookups().TryGetValue(ct.ToString(), out var dict);
return dict ?? new Dictionary<string, string?>();
return Task.FromResult(dict ?? new Dictionary<string, string?>());
}
public string Key => "gcs-getconfig";
@@ -38,11 +38,11 @@ namespace Duplicati.Library.Backend.OpenStack
{
}
public IDictionary<string, string?> Execute(IDictionary<string, string?> options)
public Task<IDictionary<string, string?>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
var ct = Utility.Utility.ParseEnumOption(options.AsReadOnly(), KEY_CONFIGTYPE, DEFAULT_CONFIG_TYPE);
GetLookups().TryGetValue(ct.ToString(), out var dict);
return dict ?? new Dictionary<string, string?>();
return Task.FromResult(dict ?? new Dictionary<string, string?>());
}
public string Key => "openstack-getconfig";
+2 -2
View File
@@ -43,11 +43,11 @@ namespace Duplicati.Library.Backend
#region IWebModule implementation
public IDictionary<string, string?> Execute(IDictionary<string, string?> options)
public Task<IDictionary<string, string?>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
var ct = Utility.Utility.ParseEnumOption(options.AsReadOnly(), KEY_CONFIGTYPE, DEFAULT_CONFIG_TYPE);
GetLookups().TryGetValue(ct.ToString(), out var dict);
return dict ?? new Dictionary<string, string?>();
return Task.FromResult(dict ?? new Dictionary<string, string?>());
}
public string Key => "s3-getconfig";
+17 -18
View File
@@ -76,7 +76,7 @@ namespace Duplicati.Library.Backend
new CommandLineArgument(KEY_PASSWORD, CommandLineArgument.ArgumentType.String, Strings.S3IAM.PasswordShort, Strings.S3IAM.PasswordLong)
]);
public IDictionary<string, string> Execute(IDictionary<string, string> options)
public async Task<IDictionary<string, string>> Execute(IDictionary<string, string> options, CancellationToken cancellationToken)
{
options.TryGetValue(KEY_OPERATION, out var operationstring);
options.TryGetValue(KEY_USERNAME, out var username);
@@ -98,12 +98,12 @@ namespace Duplicati.Library.Backend
ValidateArgument(username, KEY_USERNAME);
ValidateArgument(password, KEY_PASSWORD);
ValidateArgument(path, KEY_PATH);
return CreateUnprivilegedUser(username!, password!, path!);
return await CreateUnprivilegedUser(username!, password!, path!, cancellationToken).ConfigureAwait(false);
default:
ValidateArgument(username, KEY_USERNAME);
ValidateArgument(password, KEY_PASSWORD);
return CanCreateUser(username!, password!);
return await CanCreateUser(username!, password!, cancellationToken).ConfigureAwait(false);
}
}
@@ -138,7 +138,7 @@ namespace Duplicati.Library.Backend
}
private static bool DetermineIfCreateUserIsAllowed(User user, AmazonIdentityManagementServiceClient cl)
private static async Task<bool> DetermineIfCreateUserIsAllowed(User user, AmazonIdentityManagementServiceClient cl, CancellationToken cancellationToken)
{
var simulatePrincipalPolicy = new SimulatePrincipalPolicyRequest
{
@@ -146,13 +146,12 @@ namespace Duplicati.Library.Backend
ActionNames = new[] { "iam:CreateUser" }.ToList()
};
return cl.SimulatePrincipalPolicyAsync(simulatePrincipalPolicy)
.GetAwaiter().GetResult()
.EvaluationResults.First()
.EvalDecision == PolicyEvaluationDecisionType.Allowed;
return (await cl.SimulatePrincipalPolicyAsync(simulatePrincipalPolicy, cancellationToken).ConfigureAwait(false))
.EvaluationResults.First()
.EvalDecision == PolicyEvaluationDecisionType.Allowed;
}
private static IDictionary<string, string> GetCreateUserDict(User user, AmazonIdentityManagementServiceClient cl)
private static async Task<IDictionary<string, string>> GetCreateUserDict(User user, AmazonIdentityManagementServiceClient cl, CancellationToken cancellationToken)
{
var resultDict = new Dictionary<string, string>
{
@@ -164,7 +163,7 @@ namespace Duplicati.Library.Backend
try
{
resultDict["isroot"] = DetermineIfCreateUserIsAllowed(user, cl).ToString();
resultDict["isroot"] = (await DetermineIfCreateUserIsAllowed(user, cl, cancellationToken)).ToString();
}
catch (Exception ex)
{
@@ -180,13 +179,13 @@ namespace Duplicati.Library.Backend
return resultDict;
}
private static IDictionary<string, string> CanCreateUser(string awsid, string awskey)
private static async Task<IDictionary<string, string>> CanCreateUser(string awsid, string awskey, CancellationToken cancellationToken)
{
var cl = new AmazonIdentityManagementServiceClient(awsid, awskey);
User user;
try
{
user = cl.GetUserAsync().Await().User;
user = (await cl.GetUserAsync(cancellationToken).ConfigureAwait(false)).User;
}
catch (Exception ex)
{
@@ -198,10 +197,10 @@ namespace Duplicati.Library.Backend
};
}
return GetCreateUserDict(user, cl);
return await GetCreateUserDict(user, cl, cancellationToken);
}
private static IDictionary<string, string> CreateUnprivilegedUser(string awsid, string awskey, string path)
private static async Task<IDictionary<string, string>> CreateUnprivilegedUser(string awsid, string awskey, string path, CancellationToken cancellationToken)
{
var now = Utility.Utility.SerializeDateTime(DateTime.Now);
var username = string.Format("duplicati-autocreated-backup-user-{0}", now);
@@ -209,13 +208,13 @@ namespace Duplicati.Library.Backend
var policydoc = GeneratePolicyDoc(path);
var cl = new AmazonIdentityManagementServiceClient(awsid, awskey);
var user = cl.CreateUserAsync(new CreateUserRequest(username)).GetAwaiter().GetResult().User;
cl.PutUserPolicyAsync(new PutUserPolicyRequest(
var user = (await cl.CreateUserAsync(new CreateUserRequest(username), cancellationToken).ConfigureAwait(false)).User;
await cl.PutUserPolicyAsync(new PutUserPolicyRequest(
user.UserName,
policyname,
policydoc
)).GetAwaiter().GetResult();
var key = cl.CreateAccessKeyAsync(new CreateAccessKeyRequest { UserName = user.UserName }).GetAwaiter().GetResult().AccessKey;
)).ConfigureAwait(false);
var key = (await cl.CreateAccessKeyAsync(new CreateAccessKeyRequest { UserName = user.UserName }, cancellationToken).ConfigureAwait(false)).AccessKey;
return new Dictionary<string, string>
{
@@ -143,7 +143,7 @@ namespace Duplicati.Library.Backend
}
#region IWebModule implementation
public IDictionary<string, string> Execute(IDictionary<string, string> options)
public Task<IDictionary<string, string>> Execute(IDictionary<string, string> options, CancellationToken cancellationToken)
{
if (!options.TryGetValue(KEY_TYPE_NAME, out var keytype))
keytype = DEFAULT_KEYTYPE;
@@ -174,7 +174,7 @@ namespace Duplicati.Library.Backend
key.Modulus ?? []
};
return OutputKey(EncodeDER(privateEntries), EncodePEM(publicEntries), KEY_TEMPLATE_RSA, PUB_KEY_FORMAT_RSA, username);
return Task.FromResult(OutputKey(EncodeDER(privateEntries), EncodePEM(publicEntries), KEY_TEMPLATE_RSA, PUB_KEY_FORMAT_RSA, username));
}
else if (KEYTYPE_DSA.Equals(keytype, StringComparison.OrdinalIgnoreCase))
{
@@ -195,7 +195,7 @@ namespace Duplicati.Library.Backend
key.Y ?? []
};
return OutputKey(EncodeDER(privateEntries), EncodePEM(publicEntries), KEY_TEMPLATE_DSA, PUB_KEY_FORMAT_DSA, username);
return Task.FromResult(OutputKey(EncodeDER(privateEntries), EncodePEM(publicEntries), KEY_TEMPLATE_DSA, PUB_KEY_FORMAT_DSA, username));
}
else
{
+11 -11
View File
@@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend
#region IWebModule implementation
public IDictionary<string, string> Execute(IDictionary<string, string> options)
public async Task<IDictionary<string, string>> Execute(IDictionary<string, string> options, CancellationToken cancellationToken)
{
var res = new Dictionary<string, string>();
@@ -65,20 +65,20 @@ namespace Duplicati.Library.Backend
using (var connection = new SSHv2(url, (Dictionary<string, string?>)options))
{
var client = connection.CreateConnection(CancellationToken.None).Await();
var client = await connection.CreateConnection(cancellationToken).ConfigureAwait(false);
try
{
client.ChangeDirectory(SSH_FOLDER);
await client.ChangeDirectoryAsync(SSH_FOLDER, cancellationToken).ConfigureAwait(false);
}
catch
{
client.CreateDirectory(SSH_FOLDER);
await client.CreateDirectoryAsync(SSH_FOLDER, cancellationToken).ConfigureAwait(false);
client.ChangePermissions(SSH_FOLDER, SSH_FOLDER_PERMISSIONS);
client.ChangeDirectory(SSH_FOLDER);
await client.ChangeDirectoryAsync(SSH_FOLDER, cancellationToken).ConfigureAwait(false);
}
var sshfolder = client.ListDirectory(".").First(x => x.Name == ".");
client.ChangeDirectory("..");
var sshfolder = (await client.ListDirectoryAsync(".", cancellationToken).ToListAsync(cancellationToken)).First(x => x.Name == ".");
await client.ChangeDirectoryAsync("..", cancellationToken).ConfigureAwait(false);
if (!sshfolder.OwnerCanRead || !sshfolder.OwnerCanWrite)
client.ChangePermissions(SSH_FOLDER, SSH_FOLDER_PERMISSIONS);
@@ -86,12 +86,12 @@ namespace Duplicati.Library.Backend
var authorized_keys = "";
byte[]? authorized_keys_bytes = null;
var existing_authorized_keys = client.ListDirectory(SSH_FOLDER).Any(x => x.Name == AUTHORIZED_KEYS_FILE);
var existing_authorized_keys = (await client.ListDirectoryAsync(SSH_FOLDER, cancellationToken).ToListAsync(cancellationToken)).Any(x => x.Name == AUTHORIZED_KEYS_FILE);
if (existing_authorized_keys)
{
using (var ms = new MemoryStream())
{
client.DownloadFile(AUTHORIZED_KEYS_PATH, ms);
await client.DownloadFileAsync(AUTHORIZED_KEYS_PATH, ms, cancellationToken).ConfigureAwait(false);
authorized_keys_bytes = ms.ToArray();
authorized_keys = System.Text.Encoding.ASCII.GetString(authorized_keys_bytes);
}
@@ -120,12 +120,12 @@ namespace Duplicati.Library.Backend
{
var filename = AUTHORIZED_KEYS_PATH + ".backup-" + Utility.Utility.SerializeDateTime(DateTime.UtcNow);
using (var ms = new MemoryStream(authorized_keys_bytes))
client.UploadFile(ms, filename);
await client.UploadFileAsync(ms, filename, cancellationToken).ConfigureAwait(false);
client.ChangePermissions(filename, AUTHORIZED_KEYS_BACKUP_PERMISSIONS);
}
using (var ms = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(new_file)))
client.UploadFile(ms, AUTHORIZED_KEYS_PATH);
await client.UploadFileAsync(ms, AUTHORIZED_KEYS_PATH, cancellationToken).ConfigureAwait(false);
if (!existing_authorized_keys)
client.ChangePermissions(AUTHORIZED_KEYS_PATH, AUTHORIZED_KEYS_PERMISSIONS);
@@ -48,11 +48,11 @@ namespace Duplicati.Library.Backend.Storj
new CommandLineArgument(KEY_CONFIGTYPE, CommandLineArgument.ArgumentType.Enumeration, LC.L("The config to get"), LC.L("Provide different config values"), DEFAULT_CONFIG_TYPE_STR, Enum.GetNames(typeof(ConfigType)))
];
public IDictionary<string, string?> Execute(IDictionary<string, string?> options)
public Task<IDictionary<string, string?>> Execute(IDictionary<string, string?> options, CancellationToken cancellationToken)
{
var ct = Utility.Utility.ParseEnumOption(options.AsReadOnly(), KEY_CONFIGTYPE, DEFAULT_CONFIG_TYPE);
GetLookups().TryGetValue(ct.ToString(), out var dict);
return dict ?? new Dictionary<string, string?>();
return Task.FromResult(dict ?? new Dictionary<string, string?>());
}
public IDictionary<string, IDictionary<string, string?>> GetLookups()
+5 -17
View File
@@ -20,32 +20,20 @@
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Duplicati.Library.Interface
{
public interface IWebModule : IDynamicModule
public interface IWebModule : ICommonModule
{
/// <summary>
/// The module key, used to activate or deactivate the module on the commandline
/// </summary>
string Key { get; }
/// <summary>
/// A localized string describing the module with a friendly name
/// </summary>
string DisplayName { get; }
/// <summary>
/// A localized description of the module
/// </summary>
string Description { get; }
/// <summary>
/// Execute the specified command with the given options.
/// </summary>
/// <param name="options">The options to use</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>A list of output values</returns>
IDictionary<string, string> Execute(IDictionary<string, string> options);
Task<IDictionary<string, string>> Execute(IDictionary<string, string> options, CancellationToken cancellationToken);
/// <summary>
/// Returns lookup data from the module, if any.
@@ -63,7 +63,7 @@ public record WebModules : IEndpointV1
return new Dto.WebModuleOutputDto(
Status: "OK",
Result: m.Execute(options)
Result: await m.Execute(options, cancellationToken).ConfigureAwait(false)
);
}