blob: 6d58c0bbfa7a061791458804da050c59f17c9a67 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/webid/request_service.h"
#include "content/browser/webid/test/delegated_idp_network_request_manager.h"
#include "content/browser/webid/test/mock_api_permission_delegate.h"
#include "content/browser/webid/test/mock_auto_reauthn_permission_delegate.h"
#include "content/browser/webid/test/mock_identity_registry.h"
#include "content/browser/webid/test/mock_identity_request_dialog_controller.h"
#include "content/browser/webid/test/mock_idp_network_request_manager.h"
#include "content/browser/webid/test/mock_modal_dialog_view_delegate.h"
#include "content/browser/webid/test/mock_permission_delegate.h"
#include "content/public/common/content_features.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
#include "url/origin.h"
using ApiPermissionStatus =
content::FederatedIdentityApiPermissionContextDelegate::PermissionStatus;
using blink::mojom::RegisterIdpStatus;
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::StrictMock;
namespace content::webid {
namespace {
constexpr char kIdpUrl[] = "https://idp.example/";
class TestApiPermissionDelegate : public MockApiPermissionDelegate {
public:
ApiPermissionStatus GetApiPermissionStatus(
const url::Origin& origin) override {
return ApiPermissionStatus::GRANTED;
}
};
class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
public:
void FetchWellKnown(const GURL& provider,
FetchWellKnownCallback callback) override {
// Assume that the well-known file is not found for a registered IDP.
IdpNetworkRequestManager::WellKnown well_known;
FetchStatus fetch_status = {ParseStatus::kHttpNotFoundError, 404};
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), fetch_status, well_known));
}
void FetchConfig(const GURL& provider,
blink::mojom::RpMode rp_mode,
int idp_brand_icon_ideal_size,
int idp_brand_icon_minimum_size,
FetchConfigCallback callback) override {
IdpNetworkRequestManager::Endpoints endpoints;
endpoints.token = GURL("https://idp.example/token");
endpoints.accounts = GURL("https://idp.example/accounts");
IdentityProviderMetadata idp_metadata;
idp_metadata.config_url = provider;
idp_metadata.idp_login_url = GURL("https://idp.example/login");
idp_metadata.types = {"idp-type"};
FetchStatus fetch_status = {ParseStatus::kSuccess, 200};
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), fetch_status, endpoints,
idp_metadata));
}
};
} // namespace
class RequestServiceRegistryTest : public RenderViewHostImplTestHarness {
protected:
RequestServiceRegistryTest() = default;
~RequestServiceRegistryTest() override = default;
void SetUp() override {
RenderViewHostImplTestHarness::SetUp();
test_api_permission_delegate_ =
std::make_unique<TestApiPermissionDelegate>();
mock_permission_delegate_ =
std::make_unique<StrictMock<MockPermissionDelegate>>();
static_cast<TestWebContents*>(web_contents())
->NavigateAndCommit(GURL(kIdpUrl), ui::PAGE_TRANSITION_LINK);
mock_auto_reauthn_permission_delegate_ =
std::make_unique<NiceMock<MockAutoReauthnPermissionDelegate>>();
mock_identity_registry_ = std::make_unique<NiceMock<MockIdentityRegistry>>(
web_contents(), /*delegate=*/nullptr, GURL(kIdpUrl));
federated_auth_request_impl_ = &RequestService::CreateForTesting(
*main_test_rfh(), test_api_permission_delegate_.get(),
mock_auto_reauthn_permission_delegate_.get(),
mock_permission_delegate_.get(), mock_identity_registry_.get(),
request_remote_.BindNewPipeAndPassReceiver());
auto mock_dialog_controller =
std::make_unique<NiceMock<MockIdentityRequestDialogController>>();
federated_auth_request_impl_->SetDialogControllerForTests(
std::move(mock_dialog_controller));
std::unique_ptr<TestIdpNetworkRequestManager> network_request_manager =
std::make_unique<TestIdpNetworkRequestManager>();
federated_auth_request_impl_->SetNetworkManagerForTests(
std::move(network_request_manager));
}
void TearDown() override {
federated_auth_request_impl_ = nullptr;
RenderViewHostImplTestHarness::TearDown();
}
protected:
base::test::ScopedFeatureList feature_list_;
mojo::Remote<blink::mojom::FederatedAuthRequest> request_remote_;
raw_ptr<RequestService> federated_auth_request_impl_;
std::unique_ptr<TestApiPermissionDelegate> test_api_permission_delegate_;
std::unique_ptr<StrictMock<MockPermissionDelegate>> mock_permission_delegate_;
std::unique_ptr<NiceMock<MockAutoReauthnPermissionDelegate>>
mock_auto_reauthn_permission_delegate_;
std::unique_ptr<NiceMock<MockIdentityRegistry>> mock_identity_registry_;
};
// Test Registering an IdP successfully.
TEST_F(RequestServiceRegistryTest, RegistersIdPSuccessfully) {
GURL configURL = GURL(kIdpUrl);
static_cast<TestRenderFrameHost*>(main_test_rfh())->SimulateUserActivation();
auto controller =
std::make_unique<NiceMock<MockIdentityRequestDialogController>>();
EXPECT_CALL(*controller, RequestIdPRegistrationPermision(_, _))
.WillOnce(::testing::WithArg<1>(
[](base::OnceCallback<void(bool accepted)> callback) {
std::move(callback).Run(true);
}));
federated_auth_request_impl_->SetDialogControllerForTests(
std::move(controller));
feature_list_.InitAndEnableFeature(features::kFedCmIdPRegistration);
EXPECT_CALL(*mock_permission_delegate_, RegisterIdP(_)).WillOnce(Return());
base::RunLoop loop;
request_remote_->RegisterIdP(
std::move(configURL),
base::BindLambdaForTesting([&loop](RegisterIdpStatus result) {
EXPECT_EQ(RegisterIdpStatus::kSuccess, result);
loop.Quit();
}));
loop.Run();
}
// Test Registering denied without user activation.
TEST_F(RequestServiceRegistryTest, RegistersIdPDeniedWithoutUserActivation) {
GURL configURL = GURL(kIdpUrl);
auto controller =
std::make_unique<NiceMock<MockIdentityRequestDialogController>>();
federated_auth_request_impl_->SetDialogControllerForTests(
std::move(controller));
feature_list_.InitAndEnableFeature(features::kFedCmIdPRegistration);
base::RunLoop loop;
request_remote_->RegisterIdP(
std::move(configURL),
base::BindLambdaForTesting([&loop](RegisterIdpStatus result) {
EXPECT_EQ(RegisterIdpStatus::kErrorNoTransientActivation, result);
loop.Quit();
}));
loop.Run();
}
// Test Registering an IdP without the feature enabled.
TEST_F(RequestServiceRegistryTest, RegistersWithoutFeature) {
GURL configURL = GURL(kIdpUrl);
static_cast<TestRenderFrameHost*>(main_test_rfh())->SimulateUserActivation();
base::RunLoop loop;
request_remote_->RegisterIdP(
std::move(configURL),
base::BindLambdaForTesting([&loop](RegisterIdpStatus result) {
EXPECT_EQ(RegisterIdpStatus::kErrorFeatureDisabled, result);
loop.Quit();
}));
loop.Run();
}
// Test Registering a configURL of a different origin.
TEST_F(RequestServiceRegistryTest, RegistersCrossOriginNotAllowed) {
GURL configURL = GURL("https://another.example");
static_cast<TestRenderFrameHost*>(main_test_rfh())->SimulateUserActivation();
feature_list_.InitAndEnableFeature(features::kFedCmIdPRegistration);
base::RunLoop loop;
request_remote_->RegisterIdP(
std::move(configURL),
base::BindLambdaForTesting([&loop](RegisterIdpStatus result) {
EXPECT_EQ(RegisterIdpStatus::kErrorCrossOriginConfig, result);
loop.Quit();
}));
loop.Run();
}
// Test Unregistering an IdP without the feature enabled.
TEST_F(RequestServiceRegistryTest, UnregistersWithoutFeature) {
GURL configURL = GURL(kIdpUrl);
// no call to the mock_permission_delegate_ (which is a strict)
// mock) expected.
base::RunLoop loop;
request_remote_->UnregisterIdP(
std::move(configURL), base::BindLambdaForTesting([&loop](bool result) {
EXPECT_EQ(false, result);
loop.Quit();
}));
loop.Run();
}
// Test Unregistering an IdP with the feature enabled but for a different
// origin.
TEST_F(RequestServiceRegistryTest, UnregisterAcrossOrigin) {
GURL configURL = GURL("https://another.example");
feature_list_.InitAndEnableFeature(features::kFedCmIdPRegistration);
// no call to the mock_permission_delegate_ (which is a strict)
// mock) expected.
base::RunLoop loop;
request_remote_->UnregisterIdP(
std::move(configURL), base::BindLambdaForTesting([&loop](bool result) {
EXPECT_EQ(false, result);
loop.Quit();
}));
loop.Run();
}
// Test Unregistering an IdP Successfully.
TEST_F(RequestServiceRegistryTest, UnregistersIdP) {
GURL configURL = GURL(kIdpUrl);
feature_list_.InitAndEnableFeature(features::kFedCmIdPRegistration);
EXPECT_CALL(*mock_permission_delegate_, UnregisterIdP(_)).WillOnce(Return());
base::RunLoop loop;
request_remote_->UnregisterIdP(
std::move(configURL), base::BindLambdaForTesting([&loop](bool result) {
EXPECT_EQ(true, result);
loop.Quit();
}));
loop.Run();
}
} // namespace content::webid