Skip to content

Commit

Permalink
installer authentication tests
Browse files Browse the repository at this point in the history
  • Loading branch information
yao-msft committed Dec 23, 2024
1 parent 8e5dcf4 commit 45bd50d
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 1 deletion.
113 changes: 112 additions & 1 deletion src/AppInstallerCLITests/DownloadFlow.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include "TestHooks.h"
#include "AppInstallerRuntime.h"
#include "WorkflowCommon.h"
#include <Commands/DownloadCommand.h>

using namespace TestCommon;
using namespace AppInstaller;
using namespace AppInstaller::Authentication;
using namespace AppInstaller::CLI;

TEST_CASE("DownloadFlow_DownloadCommandProhibited", "[DownloadFlow][workflow]")
Expand All @@ -20,5 +24,112 @@ TEST_CASE("DownloadFlow_DownloadCommandProhibited", "[DownloadFlow][workflow]")

// Verify AppInfo is printed
REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_DOWNLOAD_COMMAND_PROHIBITED);
REQUIRE(downloadOutput.str().find(Resource::LocString(Resource::String::InstallerDownloadCommandProhibited).get()) != std::string::npos);
REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadCommandProhibited).get()) != std::string::npos);
}

AppInstaller::Utility::DownloadResult ValidateAzureBlobStorageAuthHeaders(
const std::string&,
const std::filesystem::path&,
AppInstaller::Utility::DownloadType,
AppInstaller::IProgressCallback&,
std::optional<AppInstaller::Utility::DownloadInfo> info)
{
REQUIRE(info);
REQUIRE(info->RequestHeaders.size() > 0);
REQUIRE(info->RequestHeaders[0].IsAuth);
REQUIRE(info->RequestHeaders[0].Name == "Authorization");
REQUIRE(info->RequestHeaders[0].Value == "Bearer TestToken");
REQUIRE_FALSE(info->RequestHeaders[1].IsAuth);
REQUIRE(info->RequestHeaders[1].Name == "x-ms-version");
// Not validating x-ms-version value

AppInstaller::Utility::DownloadResult result;
result.Sha256Hash = AppInstaller::Utility::SHA256::ConvertToBytes("65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B");
return result;
}

TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationSuccess", "[DownloadFlow][workflow]")
{
if (Runtime::IsRunningAsSystem())
{
WARN("Test does not support running as system. Skipped.");
return;
}

// Set authentication success result override
std::string expectedToken = "TestToken";
AuthenticationResult authResultOverride;
authResultOverride.Status = S_OK;
authResultOverride.Token = expectedToken;
TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride);

// Set auth header validation override
TestHook::SetDownloadResult_Function_Override downloadFunctionOverride({ &ValidateAzureBlobStorageAuthHeaders });

std::ostringstream downloadOutput;
TestContext context{ downloadOutput, std::cin };
auto previousThreadGlobals = context.SetForCurrentThread();
context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string());

DownloadCommand download({});
download.Execute(context);
INFO(downloadOutput.str());

// Verify success
REQUIRE_FALSE(context.IsTerminated());
REQUIRE(context.GetTerminationHR() == S_OK);
}

TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationNotSupported", "[DownloadFlow][workflow]")
{
if (Runtime::IsRunningAsSystem())
{
WARN("Test does not support running as system. Skipped.");
return;
}

// Set authentication failed result
AuthenticationResult authResultOverride;
authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED;
TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride);

std::ostringstream downloadOutput;
TestContext context{ downloadOutput, std::cin };
auto previousThreadGlobals = context.SetForCurrentThread();
context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string());

DownloadCommand download({});
download.Execute(context);
INFO(downloadOutput.str());

// Verify AppInfo is printed
REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_AUTHENTICATION_TYPE_NOT_SUPPORTED);
REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadAuthenticationNotSupported).get()) != std::string::npos);
}

TEST_CASE("DownloadFlow_DownloadWithInstallerAuthenticationFailed", "[DownloadFlow][workflow]")
{
if (Runtime::IsRunningAsSystem())
{
WARN("Test does not support running as system. Skipped.");
return;
}

// Set authentication failed result
AuthenticationResult authResultOverride;
authResultOverride.Status = APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED;
TestHook::SetAuthenticationResult_Override setAuthenticationResultOverride(authResultOverride);

std::ostringstream downloadOutput;
TestContext context{ downloadOutput, std::cin };
auto previousThreadGlobals = context.SetForCurrentThread();
context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("ManifestV1_10-InstallerAuthentication.yaml").GetPath().u8string());

DownloadCommand download({});
download.Execute(context);
INFO(downloadOutput.str());

// Verify AppInfo is printed
REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_AUTHENTICATION_FAILED);
REQUIRE(downloadOutput.str().find(CLI::Resource::LocString(CLI::Resource::String::InstallerDownloadAuthenticationFailed).get()) != std::string::npos);
}
37 changes: 37 additions & 0 deletions src/AppInstallerCLITests/TestHooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <AppInstallerTelemetry.h>
#include <AppInstallerRuntime.h>
#include <AppInstallerDownloader.h>
#include <winget/UserSettings.h>
#include <winget/Filesystem.h>
#include <winget/IconExtraction.h>
Expand Down Expand Up @@ -93,6 +94,16 @@ namespace AppInstaller

void SetLicensingHttpPipelineStage_Override(std::shared_ptr<web::http::http_pipeline_stage> value);
}

namespace Utility::TestHooks
{
void SetDownloadResult_Function_Override(std::function<DownloadResult(
const std::string& url,
const std::filesystem::path& dest,
DownloadType type,
IProgressCallback& progress,
std::optional<DownloadInfo> info)>* value);
}
}

namespace TestHook
Expand Down Expand Up @@ -301,4 +312,30 @@ namespace TestHook
AppInstaller::MSStore::TestHooks::SetLicensingHttpPipelineStage_Override(nullptr);
}
};

struct SetDownloadResult_Function_Override
{
SetDownloadResult_Function_Override(std::function<AppInstaller::Utility::DownloadResult(
const std::string& url,
const std::filesystem::path& dest,
AppInstaller::Utility::DownloadType type,
AppInstaller::IProgressCallback& progress,
std::optional<AppInstaller::Utility::DownloadInfo> info)> value) : m_downloadFunction(std::move(value))
{
AppInstaller::Utility::TestHooks::SetDownloadResult_Function_Override(&m_downloadFunction);
}

~SetDownloadResult_Function_Override()
{
AppInstaller::Utility::TestHooks::SetDownloadResult_Function_Override(nullptr);
}

private:
std::function<AppInstaller::Utility::DownloadResult(
const std::string& url,
const std::filesystem::path& dest,
AppInstaller::Utility::DownloadType type,
AppInstaller::IProgressCallback& progress,
std::optional<AppInstaller::Utility::DownloadInfo> info)> m_downloadFunction;
};
}
29 changes: 29 additions & 0 deletions src/AppInstallerCommonCore/Downloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ namespace AppInstaller::Utility
}
}

#ifndef AICLI_DISABLE_TEST_HOOKS
namespace TestHooks
{
static std::function<DownloadResult(
const std::string& url,
const std::filesystem::path& dest,
DownloadType type,
IProgressCallback& progress,
std::optional<DownloadInfo> info)>* s_Download_Function_Override = nullptr;

void SetDownloadResult_Function_Override(std::function<DownloadResult(
const std::string& url,
const std::filesystem::path& dest,
DownloadType type,
IProgressCallback& progress,
std::optional<DownloadInfo> info)>* value)
{
s_Download_Function_Override = value;
}
}
#endif

DownloadResult WinINetDownloadToStream(
const std::string& url,
std::ostream& dest,
Expand Down Expand Up @@ -320,6 +342,13 @@ namespace AppInstaller::Utility
IProgressCallback& progress,
std::optional<DownloadInfo> info)
{
#ifndef AICLI_DISABLE_TEST_HOOKS
if (TestHooks::s_Download_Function_Override)
{
return (*TestHooks::s_Download_Function_Override)(url, dest, type, progress, info);
}
#endif

THROW_HR_IF(E_INVALIDARG, url.empty());
THROW_HR_IF(E_INVALIDARG, dest.empty());

Expand Down

0 comments on commit 45bd50d

Please sign in to comment.