910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/network/device_bound_session_manager.h"

#include "base/barrier_callback.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/functional/callback_helpers.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/canonical_cookie.h"
#include "net/device_bound_sessions/session_params.h"
#include "net/device_bound_sessions/session_service.h"
#include "services/network/cookie_manager.h"
#include "services/network/public/mojom/clear_data_filter.mojom.h"

namespace network {

// static
std::unique_ptr<DeviceBoundSessionManager> DeviceBoundSessionManager::Create(
    net::device_bound_sessions::SessionService* service,
    CookieManager* cookie_manager) {
  if (!service) {
    return nullptr;
  }

  return base::WrapUnique(
      new DeviceBoundSessionManager(service, cookie_manager));
}

DeviceBoundSessionManager::DeviceBoundSessionManager(
    net::device_bound_sessions::SessionService* service,
    CookieManager* cookie_manager)
    : service_(service), cookie_manager_(cookie_manager) {}

DeviceBoundSessionManager::~DeviceBoundSessionManager() = default;

void DeviceBoundSessionManager::AddReceiver(
    mojo::PendingReceiver<mojom::DeviceBoundSessionManager> receiver) {
  receivers_.Add(this, std::move(receiver));
}

void DeviceBoundSessionManager::GetAllSessions(
    DeviceBoundSessionManager::GetAllSessionsCallback callback) {
  service_->GetAllSessionsAsync(std::move(callback));
}

void DeviceBoundSessionManager::DeleteSession(
    net::device_bound_sessions::DeletionReason reason,
    const net::device_bound_sessions::SessionKey& session_key) {
  service_->DeleteSessionAndNotify(
      reason,
      {session_key.site,
       net::device_bound_sessions::Session::Id(session_key.id)},
      base::NullCallback());
}

void DeviceBoundSessionManager::DeleteAllSessions(
    net::device_bound_sessions::DeletionReason reason,
    std::optional<base::Time> created_after_time,
    std::optional<base::Time> created_before_time,
    network::mojom::ClearDataFilterPtr filter,
    base::OnceClosure completion_callback) {
  base::RepeatingCallback<bool(const url::Origin&, const net::SchemefulSite&)>
      origin_and_site_matcher;
  if (filter) {
    origin_and_site_matcher = base::BindRepeating(
        // TODO(crbug.com/384437667): Consolidate ClearDataFilter matching logic
        [](const mojom::ClearDataFilter& filter, const url::Origin& origin,
           const net::SchemefulSite& site) {
          bool is_match = base::Contains(filter.origins, origin);
          if (!is_match && !filter.domains.empty()) {
            const std::string etld1_for_origin =
                net::registry_controlled_domains::GetDomainAndRegistry(
                    site.GetURL(), net::registry_controlled_domains::
                                       INCLUDE_PRIVATE_REGISTRIES);
            is_match = base::Contains(filter.domains, etld1_for_origin);
          }

          switch (filter.type) {
            case mojom::ClearDataFilter::Type::KEEP_MATCHES:
              return !is_match;
            case mojom::ClearDataFilter::Type::DELETE_MATCHES:
              return is_match;
          }
        },
        *filter);
  }

  service_->DeleteAllSessions(reason, created_after_time, created_before_time,
                              origin_and_site_matcher,
                              std::move(completion_callback));
}

DeviceBoundSessionManager::ObserverRegistration::ObserverRegistration() =
    default;
DeviceBoundSessionManager::ObserverRegistration::~ObserverRegistration() =
    default;

void DeviceBoundSessionManager::AddObserver(
    const GURL& url,
    mojo::PendingRemote<network::mojom::DeviceBoundSessionAccessObserver>
        observer) {
  auto registration = std::make_unique<ObserverRegistration>();
  registration->remote.Bind(std::move(observer));
  registration->remote.set_disconnect_handler(
      base::BindOnce(&DeviceBoundSessionManager::RemoveObserver,
                     // base::Unretained is safe because `this` owns
                     // `registration`, which owns the callback.
                     base::Unretained(this), registration.get()));
  registration->subscription = service_->AddObserver(
      url,
      base::BindRepeating(&network::mojom::DeviceBoundSessionAccessObserver::
                              OnDeviceBoundSessionAccessed,
                          base::Unretained(registration->remote.get())));
  observer_registrations_.push_back(std::move(registration));
}

void DeviceBoundSessionManager::CreateBoundSessions(
    std::vector<net::device_bound_sessions::SessionParams> params,
    const std::vector<uint8_t>& wrapped_key,
    const std::vector<net::CanonicalCookie>& cookies_to_set,
    const net::CookieOptions& cookie_options,
    CreateBoundSessionsCallback callback) {
  auto barrier_callback = base::BarrierCallback<
      net::device_bound_sessions::SessionError::ErrorType>(
      params.size(),
      base::BindOnce(&DeviceBoundSessionManager::OnCreateBoundSessionsAdded,
                     weak_factory_.GetWeakPtr(), cookies_to_set, cookie_options,
                     std::move(callback)));

  for (net::device_bound_sessions::SessionParams& param : params) {
    GURL fetcher_url = param.fetcher_url;
    service_->AddSession(net::SchemefulSite(fetcher_url), std::move(param),
                         wrapped_key, barrier_callback);
  }
}

void DeviceBoundSessionManager::OnCreateBoundSessionsAdded(
    const std::vector<net::CanonicalCookie>& cookies_to_set,
    const net::CookieOptions& cookie_options,
    CreateBoundSessionsCallback callback,
    std::vector<net::device_bound_sessions::SessionError::ErrorType>
        session_results) {
  if (cookies_to_set.empty()) {
    std::move(callback).Run(std::move(session_results),
                            std::vector<net::CookieInclusionStatus>());
    return;
  }

  auto final_callback = base::BindOnce(
      [](CreateBoundSessionsCallback callback,
         std::vector<net::device_bound_sessions::SessionError::ErrorType>
             create_session_results,
         std::vector<net::CookieAccessResult> results) {
        std::vector<net::CookieInclusionStatus> cookie_results;
        cookie_results.reserve(results.size());
        for (auto& result : results) {
          cookie_results.push_back(std::move(result.status));
        }
        std::move(callback).Run(std::move(create_session_results),
                                std::move(cookie_results));
      },
      std::move(callback), std::move(session_results));

  auto barrier_callback = base::BarrierCallback<net::CookieAccessResult>(
      cookies_to_set.size(), std::move(final_callback));

  for (const auto& cookie : cookies_to_set) {
    cookie_manager_->SetCanonicalCookie(
        cookie, net::cookie_util::SimulatedCookieSource(cookie, "https"),
        cookie_options, barrier_callback);
  }
}

void DeviceBoundSessionManager::RemoveObserver(
    DeviceBoundSessionManager::ObserverRegistration* registration) {
  std::erase_if(observer_registrations_, base::MatchesUniquePtr(registration));
}

}  // namespace network