2024-02-28 15:45:30 +01:00
// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2020-08-23 14:25:34 -07:00
2019-01-17 19:52:41 -08:00
using System ;
2019-01-16 20:27:02 -08:00
using System.Collections.Generic ;
using System.IO ;
2020-08-23 14:25:34 -07:00
using Duplicati.Server ;
using Duplicati.Server.Database ;
2020-08-23 20:25:11 -07:00
using Duplicati.Server.Serializable ;
using Duplicati.Server.Serialization ;
2020-08-23 14:25:34 -07:00
using Duplicati.Server.Serialization.Interface ;
using Duplicati.Server.WebServer.RESTMethods ;
using NUnit.Framework ;
using Backup = Duplicati . Server . Database . Backup ;
2019-01-16 20:27:02 -08:00
namespace Duplicati.UnitTest
{
2022-05-06 21:46:17 -07:00
public class ImportExportTests : BasicSetupHelper
2019-01-16 20:27:02 -08:00
{
private string serverDatafolder ;
[OneTimeSetUp]
public void Setup ()
{
this . serverDatafolder = Path . Combine ( Path . GetTempPath (), Path . GetRandomFileName ());
Directory . CreateDirectory ( this . serverDatafolder );
}
[OneTimeTearDown]
public void TearDown ()
{
try
{
Directory . Delete ( this . serverDatafolder , true );
}
catch
{
2019-01-17 19:21:32 -08:00
// If there's an exception, let the OS deal with cleaning up the temp folder.
2019-01-16 20:27:02 -08:00
}
}
2020-08-23 14:25:34 -07:00
private IBackup CreateBackup ( string name , string username , string password , Dictionary < string , string > metadata )
2020-08-23 14:24:27 -07:00
{
return new Backup
{
Description = "Mock Backup Description" ,
Filters = new IFilter [ 0 ],
ID = "1" ,
Metadata = metadata ,
Name = name ,
2020-08-23 14:25:34 -07:00
Settings = new [] { new Setting { Name = "passphrase" , Value = "12345" }},
Sources = new [] { "Mock Backup Source" },
Tags = new [] { "Tags" },
2020-08-23 14:24:27 -07:00
TargetURL = $"file:///mock_backup_target?auth-username={username}&auth-password={password}"
};
}
2020-08-23 20:25:11 -07:00
[Test]
[Category("ImportExport")]
[TestCase(true)]
[TestCase(false)]
public void ExportToJSONEncoding ( bool removePasswords )
{
Dictionary < string , string > advancedOptions = new Dictionary < string , string > {{ "server-datafolder" , this . serverDatafolder }};
string usernameKey = "auth-username" ;
string passwordKey = "auth-password" ;
string username = @"user%40email.com" ;
string password = @"abcde12345!@#$%/\" ;
IBackup backup = this . CreateBackup ( "backup" , username , password , new Dictionary < string , string >());
if ( removePasswords )
{
Server . WebServer . RESTMethods . Backup . RemovePasswords ( backup );
}
Assert . That ( backup . TargetURL , Does . Contain ( $"{usernameKey}={username}" ));
if ( removePasswords )
{
Assert . That ( backup . TargetURL , Does . Not . Contain ( passwordKey ));
Assert . That ( backup . TargetURL , Does . Not . Contain ( password ));
}
else
{
Assert . That ( backup . TargetURL , Does . Contain ( passwordKey ));
Assert . That ( backup . TargetURL , Does . Contain ( password ));
}
byte [] jsonByteArray ;
using ( Program . DataConnection = Program . GetDatabaseConnection ( advancedOptions ))
{
jsonByteArray = Server . WebServer . RESTMethods . Backup . ExportToJSON ( backup , null );
}
// The username should not have the '%40' converted to '@' since the import code
// cannot handle it (see issue #3619).
string json = System . Text . Encoding . Default . GetString ( jsonByteArray );
Assert . That ( json , Does . Not . Contain ( "user@email.com" ));
ImportExportStructure importedConfiguration = Serializer . Deserialize < ImportExportStructure >( new StreamReader ( new MemoryStream ( jsonByteArray )));
Assert . AreEqual ( backup . Description , importedConfiguration . Backup . Description );
Assert . AreEqual ( backup . Name , importedConfiguration . Backup . Name );
Assert . AreEqual ( backup . TargetURL , importedConfiguration . Backup . TargetURL );
}
2019-01-16 20:27:02 -08:00
[Test]
[Category("ImportExport")]
public void RoundTrip ()
{
2020-08-23 14:25:34 -07:00
Dictionary < string , string > metadata = new Dictionary < string , string > {{ "SourceFilesCount" , "1" }};
Dictionary < string , string > advancedOptions = new Dictionary < string , string > {{ "server-datafolder" , this . serverDatafolder }};
using ( Program . DataConnection = Program . GetDatabaseConnection ( advancedOptions ))
2019-01-16 20:27:02 -08:00
{
2019-01-17 19:41:55 -08:00
// Unencrypted file, don't import metadata.
2019-01-16 20:27:02 -08:00
string unencryptedWithoutMetadata = Path . Combine ( this . serverDatafolder , Path . GetRandomFileName ());
2020-08-23 14:24:27 -07:00
File . WriteAllBytes ( unencryptedWithoutMetadata , Server . WebServer . RESTMethods . Backup . ExportToJSON ( this . CreateBackup ( "unencrypted without metadata" , "user" , "password" , metadata ), null ));
2020-08-23 14:25:34 -07:00
Backups . ImportBackup ( unencryptedWithoutMetadata , false , () => null , advancedOptions );
Assert . AreEqual ( 1 , Program . DataConnection . Backups . Length );
Assert . AreEqual ( 0 , Program . DataConnection . Backups [ 0 ]. Metadata . Count );
2019-01-16 20:27:02 -08:00
2019-01-17 19:41:55 -08:00
// Unencrypted file, import metadata.
2019-01-16 20:27:02 -08:00
string unencryptedWithMetadata = Path . Combine ( this . serverDatafolder , Path . GetRandomFileName ());
2020-08-23 14:24:27 -07:00
File . WriteAllBytes ( unencryptedWithMetadata , Server . WebServer . RESTMethods . Backup . ExportToJSON ( this . CreateBackup ( "unencrypted with metadata" , "user" , "password" , metadata ), null ));
2020-08-23 14:25:34 -07:00
Backups . ImportBackup ( unencryptedWithMetadata , true , () => null , advancedOptions );
Assert . AreEqual ( 2 , Program . DataConnection . Backups . Length );
Assert . AreEqual ( metadata . Count , Program . DataConnection . Backups [ 1 ]. Metadata . Count );
2019-01-16 20:27:02 -08:00
2019-01-17 19:41:55 -08:00
// Encrypted file, don't import metadata.
2019-01-16 20:27:02 -08:00
string encryptedWithoutMetadata = Path . Combine ( this . serverDatafolder , Path . GetRandomFileName ());
string passphrase = "abcde" ;
2020-08-23 14:24:27 -07:00
File . WriteAllBytes ( encryptedWithoutMetadata , Server . WebServer . RESTMethods . Backup . ExportToJSON ( this . CreateBackup ( "encrypted without metadata" , "user" , "password" , metadata ), passphrase ));
2020-08-23 14:25:34 -07:00
Backups . ImportBackup ( encryptedWithoutMetadata , false , () => passphrase , advancedOptions );
Assert . AreEqual ( 3 , Program . DataConnection . Backups . Length );
Assert . AreEqual ( 0 , Program . DataConnection . Backups [ 2 ]. Metadata . Count );
2019-01-16 20:27:02 -08:00
2019-01-17 19:41:55 -08:00
// Encrypted file, import metadata.
2019-01-16 20:27:02 -08:00
string encryptedWithMetadata = Path . Combine ( this . serverDatafolder , Path . GetRandomFileName ());
2020-08-23 14:24:27 -07:00
File . WriteAllBytes ( encryptedWithMetadata , Server . WebServer . RESTMethods . Backup . ExportToJSON ( this . CreateBackup ( "encrypted with metadata" , "user" , "password" , metadata ), passphrase ));
2020-08-23 14:25:34 -07:00
Backups . ImportBackup ( encryptedWithMetadata , true , () => passphrase , advancedOptions );
Assert . AreEqual ( 4 , Program . DataConnection . Backups . Length );
Assert . AreEqual ( metadata . Count , Program . DataConnection . Backups [ 3 ]. Metadata . Count );
2019-01-17 19:52:41 -08:00
// Encrypted file, incorrect passphrase.
2020-08-23 14:25:34 -07:00
Assert . Throws ( Is . InstanceOf < Exception >(), () => Backups . ImportBackup ( encryptedWithMetadata , true , () => passphrase + " " , advancedOptions ));
2019-01-16 20:27:02 -08:00
}
}
}
2020-08-23 14:25:34 -07:00
}