blob: 6a21741e74f13e1d9fa65c9de965e42aca93c2ba [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/smb_client/smb_service.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/json/json_reader.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/file_system_provider/fake_provided_file_system.h"
#include "chrome/browser/chromeos/file_system_provider/fake_registry.h"
#include "chrome/browser/chromeos/file_system_provider/icon_set.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system_info.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/smb_client/smb_file_system_id.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_smb_provider_client.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/browser/extension_registry.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AllOf;
using ::testing::Field;
using ::testing::Invoke;
using ::testing::Ne;
using ::testing::WithArg;
namespace chromeos {
namespace smb_client {
namespace {
const ProviderId kProviderId = ProviderId::CreateFromNativeId("smb");
constexpr char kTestUser[] = "foobar";
constexpr char kTestDomain[] = "EXAMPLE.COM";
constexpr char kSharePath[] = "\\\\server\\foobar";
constexpr char kMountPath[] = "smb://server/foobar";
void SaveMountResult(SmbMountResult* out, SmbMountResult result) {
*out = result;
}
class MockSmbProviderClient : public chromeos::FakeSmbProviderClient {
public:
MockSmbProviderClient()
: FakeSmbProviderClient(true /* should_run_synchronously */) {}
MOCK_METHOD4(Mount,
void(const base::FilePath& share_path,
const MountOptions& options,
base::ScopedFD password_fd,
SmbProviderClient::MountCallback callback));
MOCK_METHOD2(SetupKerberos,
void(const std::string& account_id,
SmbProviderClient::SetupKerberosCallback callback));
};
} // namespace
class SmbServiceTest : public testing::Test {
protected:
SmbServiceTest()
: task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD) {
profile_manager_ = std::make_unique<TestingProfileManager>(
TestingBrowserProcess::GetGlobal());
EXPECT_TRUE(profile_manager_->SetUp());
std::unique_ptr<FakeChromeUserManager> user_manager_temp =
std::make_unique<FakeChromeUserManager>();
profile_ = profile_manager_->CreateTestingProfile("test-user@example.com");
user_manager_temp->AddUser(
AccountId::FromUserEmail(profile_->GetProfileUserName()));
ad_profile_ =
profile_manager_->CreateTestingProfile("ad-test-user@example.com");
user_manager_temp->AddUserWithAffiliationAndTypeAndProfile(
AccountId::AdFromUserEmailObjGuid(ad_profile_->GetProfileUserName(),
"abc"),
false, user_manager::UserType::USER_TYPE_ACTIVE_DIRECTORY, ad_profile_);
// Run pending async tasks resulting from profile construction to ensure
// these are complete before the test begins.
base::RunLoop().RunUntilIdle();
user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(user_manager_temp));
mock_client_ = new MockSmbProviderClient;
// The mock needs to be marked as leaked because ownership is passed to
// DBusThreadManager.
testing::Mock::AllowLeak(mock_client_);
chromeos::DBusThreadManager::GetSetterForTesting()->SetSmbProviderClient(
base::WrapUnique(mock_client_));
}
~SmbServiceTest() override {}
void CreateFspRegistry(TestingProfile* profile) {
// Create FSP service.
registry_ = new file_system_provider::FakeRegistry;
file_system_provider::Service::Get(profile)->SetRegistryForTesting(
base::WrapUnique(registry_));
}
void CreateService(TestingProfile* profile) {
SmbService::DisableShareDiscoveryForTesting();
// Create smb service.
smb_service_ = std::make_unique<SmbService>(
profile, std::make_unique<base::SimpleTestTickClock>());
}
void ExpectInvalidUrl(const std::string& url) {
SmbMountResult result = SmbMountResult::SUCCESS;
smb_service_->CallMount({} /* options */, base::FilePath(url),
"" /* username */, "" /* password */,
false /* use_chromad_kerberos */,
false /* should_open_file_manager_after_mount */,
false /* save_credentials */,
base::BindOnce(&SaveMountResult, &result));
EXPECT_EQ(result, SmbMountResult::INVALID_URL);
}
void ExpectInvalidSsoUrl(const std::string& url) {
SmbMountResult result = SmbMountResult::SUCCESS;
smb_service_->CallMount({} /* options */, base::FilePath(url),
"" /* username */, "" /* password */,
true /* use_chromad_kerberos */,
false /* should_open_file_manager_after_mount */,
false /* save_credentials */,
base::BindOnce(&SaveMountResult, &result));
EXPECT_EQ(result, SmbMountResult::INVALID_SSO_URL);
}
void WaitForSetupComplete() {
{
base::RunLoop run_loop;
smb_service_->OnSetupCompleteForTesting(run_loop.QuitClosure());
run_loop.Run();
}
{
// Share gathering needs to complete at least once before a share can be
// mounted.
base::RunLoop run_loop;
smb_service_->GatherSharesInNetwork(
base::DoNothing(),
base::BindLambdaForTesting(
[&run_loop](const std::vector<SmbUrl>& shares_gathered,
bool done) {
if (done) {
run_loop.Quit();
}
}));
run_loop.Run();
}
}
content::BrowserTaskEnvironment
task_environment_; // Included so tests magically don't crash.
TestingProfile* profile_ = nullptr; // Not owned.
TestingProfile* ad_profile_ = nullptr; // Not owned.
std::unique_ptr<TestingProfileManager> profile_manager_;
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
MockSmbProviderClient* mock_client_; // Owned by DBusThreadManager.
std::unique_ptr<SmbService> smb_service_;
std::unique_ptr<file_system_provider::Service> fsp_service_;
file_system_provider::FakeRegistry* registry_; // Owned by |fsp_service_|.
// Extension Registry and Registry needed for fsp_service.
std::unique_ptr<extensions::ExtensionRegistry> extension_registry_;
};
TEST_F(SmbServiceTest, InvalidUrls) {
CreateService(profile_);
ExpectInvalidUrl("");
ExpectInvalidUrl("foo");
ExpectInvalidUrl("\\foo");
ExpectInvalidUrl("\\\\foo");
ExpectInvalidUrl("\\\\foo\\");
ExpectInvalidUrl("file://foo/bar");
ExpectInvalidUrl("smb://foo");
ExpectInvalidUrl("smb://user@password:foo");
ExpectInvalidUrl("smb:\\\\foo\\bar");
ExpectInvalidUrl("//foo/bar");
}
TEST_F(SmbServiceTest, InvalidSsoUrls) {
CreateService(profile_);
ExpectInvalidSsoUrl("\\\\192.168.1.1\\foo");
ExpectInvalidSsoUrl("\\\\[0:0:0:0:0:0:0:1]\\foo");
ExpectInvalidSsoUrl("\\\\[::1]\\foo");
ExpectInvalidSsoUrl("smb://192.168.1.1/foo");
ExpectInvalidSsoUrl("smb://[0:0:0:0:0:0:0:1]/foo");
ExpectInvalidSsoUrl("smb://[::1]/foo");
}
TEST_F(SmbServiceTest, Mount) {
CreateFspRegistry(profile_);
CreateService(profile_);
WaitForSetupComplete();
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(
base::FilePath(kMountPath),
AllOf(Field(&SmbProviderClient::MountOptions::username, kTestUser),
Field(&SmbProviderClient::MountOptions::workgroup, ""),
Field(&SmbProviderClient::MountOptions::ntlm_enabled, true),
Field(&SmbProviderClient::MountOptions::skip_connect, false),
Field(&SmbProviderClient::MountOptions::save_password, false)),
_, _))
.WillOnce(
WithArg<3>(Invoke([](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
})));
smb_service_->Mount(
{}, base::FilePath(kSharePath), kTestUser, "password",
false /* use_chromad_kerberos */,
false /* should_open_file_manager_after_mount */,
false /* save_credentials */,
base::BindLambdaForTesting([&run_loop](SmbMountResult result) {
EXPECT_EQ(SmbMountResult::SUCCESS, result);
run_loop.Quit();
}));
run_loop.Run();
// If |save_credentials| is false, then the username should not be saved in
// the file system id.
const std::string file_system_id =
registry_->file_system_info()->file_system_id();
EXPECT_FALSE(IsKerberosChromadFileSystemId(file_system_id));
EXPECT_FALSE(GetUserFromFileSystemId(file_system_id));
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, MountSaveCredentials) {
CreateFspRegistry(profile_);
CreateService(profile_);
WaitForSetupComplete();
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(
base::FilePath(kMountPath),
AllOf(Field(&SmbProviderClient::MountOptions::username, kTestUser),
Field(&SmbProviderClient::MountOptions::workgroup, ""),
Field(&SmbProviderClient::MountOptions::ntlm_enabled, true),
Field(&SmbProviderClient::MountOptions::skip_connect, false),
Field(&SmbProviderClient::MountOptions::save_password, true),
Field(&SmbProviderClient::MountOptions::account_hash, Ne(""))),
_, _))
.WillOnce(
WithArg<3>(Invoke([](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
})));
smb_service_->Mount(
{}, base::FilePath(kSharePath), kTestUser, "password",
false /* use_chromad_kerberos */,
false /* should_open_file_manager_after_mount */,
true /* save_credentials */,
base::BindLambdaForTesting([&run_loop](SmbMountResult result) {
EXPECT_EQ(SmbMountResult::SUCCESS, result);
run_loop.Quit();
}));
run_loop.Run();
const std::string file_system_id =
registry_->file_system_info()->file_system_id();
EXPECT_FALSE(IsKerberosChromadFileSystemId(file_system_id));
base::Optional<std::string> saved_user =
GetUserFromFileSystemId(file_system_id);
ASSERT_TRUE(saved_user);
EXPECT_EQ(*saved_user, kTestUser);
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, Remount) {
file_system_provider::MountOptions mount_options(
CreateFileSystemId(base::FilePath(kSharePath),
false /* is_kerberos_chromad */),
"Foo");
ProvidedFileSystemInfo file_system_info(
kProviderId, mount_options, base::FilePath(kSharePath),
false /* configurable */, false /* watchable */,
extensions::SOURCE_NETWORK, chromeos::file_system_provider::IconSet());
CreateFspRegistry(profile_);
registry_->RememberFileSystem(file_system_info, {});
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(base::FilePath(kMountPath),
AllOf(Field(&SmbProviderClient::MountOptions::skip_connect, true),
Field(&SmbProviderClient::MountOptions::restore_password,
false)),
_, _))
.WillOnce(WithArg<3>(
Invoke([&run_loop](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
run_loop.Quit();
})));
CreateService(profile_);
run_loop.Run();
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, Remount_ActiveDirectory) {
file_system_provider::MountOptions mount_options(
CreateFileSystemId(base::FilePath(kSharePath),
true /* is_kerberos_chromad */),
"Foo");
ProvidedFileSystemInfo file_system_info(
kProviderId, mount_options, base::FilePath(kSharePath),
false /* configurable */, false /* watchable */,
extensions::SOURCE_NETWORK, chromeos::file_system_provider::IconSet());
CreateFspRegistry(ad_profile_);
registry_->RememberFileSystem(file_system_info, {});
base::RunLoop run_loop;
EXPECT_CALL(*mock_client_, SetupKerberos(_, _))
.WillOnce(WithArg<1>(
Invoke([](SmbProviderClient::SetupKerberosCallback callback) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), true));
})));
EXPECT_CALL(
*mock_client_,
Mount(
base::FilePath(kMountPath),
AllOf(
Field(&SmbProviderClient::MountOptions::username, "ad-test-user"),
Field(&SmbProviderClient::MountOptions::workgroup, kTestDomain),
Field(&SmbProviderClient::MountOptions::skip_connect, true),
Field(&SmbProviderClient::MountOptions::restore_password, false)),
_, _))
.WillOnce(WithArg<3>(
Invoke([&run_loop](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
run_loop.Quit();
})));
CreateService(ad_profile_);
run_loop.Run();
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, Remount_SavedUser) {
file_system_provider::MountOptions mount_options(
CreateFileSystemIdForUser(base::FilePath(kSharePath),
base::StrCat({kTestUser, "@", kTestDomain})),
"Foo");
ProvidedFileSystemInfo file_system_info(
kProviderId, mount_options, base::FilePath(kSharePath),
false /* configurable */, false /* watchable */,
extensions::SOURCE_NETWORK, chromeos::file_system_provider::IconSet());
CreateFspRegistry(profile_);
registry_->RememberFileSystem(file_system_info, {});
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(
base::FilePath(kMountPath),
AllOf(Field(&SmbProviderClient::MountOptions::username, kTestUser),
Field(&SmbProviderClient::MountOptions::workgroup, kTestDomain),
Field(&SmbProviderClient::MountOptions::skip_connect, true),
Field(&SmbProviderClient::MountOptions::restore_password, true),
Field(&SmbProviderClient::MountOptions::account_hash, Ne(""))),
_, _))
.WillOnce(WithArg<3>(
Invoke([&run_loop](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
run_loop.Quit();
})));
CreateService(profile_);
run_loop.Run();
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, Remount_SavedInvalidUser) {
file_system_provider::MountOptions mount_options(
CreateFileSystemIdForUser(
base::FilePath(kSharePath),
base::StrCat({kTestUser, "@", kTestDomain, "@", kTestDomain})),
"Foo");
ProvidedFileSystemInfo file_system_info(
kProviderId, mount_options, base::FilePath(kSharePath),
false /* configurable */, false /* watchable */,
extensions::SOURCE_NETWORK, chromeos::file_system_provider::IconSet());
CreateFspRegistry(profile_);
registry_->RememberFileSystem(file_system_info, {});
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(base::FilePath(kMountPath),
AllOf(Field(&SmbProviderClient::MountOptions::username, ""),
Field(&SmbProviderClient::MountOptions::workgroup, ""),
Field(&SmbProviderClient::MountOptions::skip_connect, true),
Field(&SmbProviderClient::MountOptions::restore_password,
false)),
_, _))
.WillOnce(WithArg<3>(
Invoke([&run_loop](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
run_loop.Quit();
})));
CreateService(profile_);
run_loop.Run();
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
TEST_F(SmbServiceTest, Premount) {
const char kPremountPath[] = "smb://server/foobar";
const char kPreconfiguredShares[] =
"[{\"mode\":\"pre_mount\",\"share_url\":\"\\\\\\\\server\\\\foobar\"}]";
auto parsed_shares = base::JSONReader::Read(kPreconfiguredShares);
ASSERT_TRUE(parsed_shares);
profile_->GetPrefs()->Set(prefs::kNetworkFileSharesPreconfiguredShares,
*parsed_shares);
base::RunLoop run_loop;
EXPECT_CALL(
*mock_client_,
Mount(base::FilePath(kPremountPath),
AllOf(Field(&SmbProviderClient::MountOptions::username, ""),
Field(&SmbProviderClient::MountOptions::workgroup, ""),
Field(&SmbProviderClient::MountOptions::skip_connect, true)),
_, _))
.WillOnce(WithArg<3>(
Invoke([&run_loop](SmbProviderClient::MountCallback callback) {
std::move(callback).Run(smbprovider::ErrorType::ERROR_OK, 7);
run_loop.Quit();
})));
CreateFspRegistry(profile_);
CreateService(profile_);
run_loop.Run();
// Because the mock is potentially leaked, expectations needs to be manually
// verified.
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(mock_client_));
}
} // namespace smb_client
} // namespace chromeos