chromium/chrome/browser/ash/printing/oauth2/authorization_server_data_unittest.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/printing/oauth2/authorization_server_data.h"

#include <string>

#include "base/containers/flat_map.h"
#include "base/values.h"
#include "chrome/browser/ash/printing/oauth2/constants.h"
#include "chrome/browser/ash/printing/oauth2/mock_client_ids_database.h"
#include "chrome/browser/ash/printing/oauth2/status_code.h"
#include "chrome/browser/ash/printing/oauth2/test_authorization_server.h"
#include "net/http/http_status_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace ash {
namespace printing {
namespace oauth2 {
namespace {

// Helper function that checks if `value_dict` contains a field `name` with
// a single-element list that contains a string equals `value`. It doesn't stop
// execution, just logs errors.
void ExpectOneElementListOfStrings(const base::Value::Dict& dict,
                                   const std::string& name,
                                   const std::string& value) {
  const base::Value::List* list = dict.FindList(name);
  ASSERT_TRUE(list);
  ASSERT_EQ(list->size(), 1u);
  ASSERT_TRUE(list->front().is_string());
  EXPECT_EQ(list->front().GetString(), value);
}

TEST(PrintingOAuth2AuthorizationServerDataTest, InitialState) {
  FakeAuthorizationServer server;
  testing::StrictMock<MockClientIdsDatabase> client_ids_database;
  AuthorizationServerData asd(server.GetURLLoaderFactory(),
                              GURL("https://a.b/c"), &client_ids_database);
  EXPECT_EQ(asd.AuthorizationServerURI().spec(), "https://a.b/c");
  EXPECT_EQ(asd.ClientId(), "");
  EXPECT_TRUE(asd.AuthorizationEndpointURI().is_empty());
  EXPECT_TRUE(asd.TokenEndpointURI().is_empty());
  EXPECT_TRUE(asd.RegistrationEndpointURI().is_empty());
  EXPECT_TRUE(asd.RevocationEndpointURI().is_empty());
  EXPECT_FALSE(asd.IsReady());
}

TEST(PrintingOAuth2AuthorizationServerDataTest, MetadataRequest) {
  FakeAuthorizationServer server;
  testing::StrictMock<MockClientIdsDatabase> client_ids_database;
  AuthorizationServerData asd(server.GetURLLoaderFactory(),
                              GURL("https://a.b/c"), &client_ids_database);

  // Simulate fetching client_id from the database.
  EXPECT_CALL(client_ids_database, FetchId)
      .WillOnce([](const GURL& url, StatusCallback callback) {
        EXPECT_EQ(url.spec(), "https://a.b/c");
        std::move(callback).Run(StatusCode::kOK, "abc");
      });

  CallbackResult cr;
  asd.Initialize(BindResult(cr));

  // Simulate processing of Metadata Request (rfc8414, section 3).
  ASSERT_EQ(
      server.ReceiveGET("https://a.b/.well-known/oauth-authorization-server/c"),
      "");
  auto params =
      BuildMetadata("https://a.b/c", "https://a/auth", "https://b/token",
                    "https://c/reg", "https://d/rev");
  server.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, params);

  // Check the callback and the object.
  ASSERT_EQ(cr.status, StatusCode::kOK);
  EXPECT_TRUE(cr.data.empty());
  EXPECT_EQ(asd.AuthorizationServerURI().spec(), "https://a.b/c");
  EXPECT_EQ(asd.ClientId(), "abc");
  EXPECT_EQ(asd.AuthorizationEndpointURI().spec(), "https://a/auth");
  EXPECT_EQ(asd.TokenEndpointURI().spec(), "https://b/token");
  EXPECT_EQ(asd.RegistrationEndpointURI().spec(), "https://c/reg");
  EXPECT_EQ(asd.RevocationEndpointURI().spec(), "https://d/rev");
  EXPECT_TRUE(asd.IsReady());
}

TEST(PrintingOAuth2AuthorizationServerDataTest, RegistrationRequest) {
  FakeAuthorizationServer server;
  testing::NiceMock<MockClientIdsDatabase> client_ids_database;
  AuthorizationServerData asd(server.GetURLLoaderFactory(),
                              GURL("https://a.b/c"), &client_ids_database);
  CallbackResult cr;
  asd.Initialize(BindResult(cr));

  // Simulate processing of Metadata Request (rfc8414, section 3).
  ASSERT_EQ(
      server.ReceiveGET("https://a.b/.well-known/oauth-authorization-server/c"),
      "");
  base::Value::Dict params =
      BuildMetadata("https://a.b/c", "https://a/auth", "https://b/token",
                    "https://c/reg", "https://d/rev");
  server.ResponseWithJSON(net::HttpStatusCode::HTTP_OK, params);

  // Expect saving the new client id.
  EXPECT_CALL(client_ids_database, StoreId)
      .WillOnce([](const GURL& url, const std::string& id) {
        EXPECT_EQ(url.spec(), "https://a.b/c");
        EXPECT_EQ(id, "new_client_id");
      });

  // Simulate processing of Registration Request (rfc7591, section 3).
  ASSERT_EQ(server.ReceivePOSTWithJSON("https://c/reg", params), "");
  ExpectOneElementListOfStrings(params, "redirect_uris", kRedirectURI);
  ExpectOneElementListOfStrings(params, "token_endpoint_auth_method", "none");
  ExpectOneElementListOfStrings(params, "grant_types", "authorization_code");
  ExpectOneElementListOfStrings(params, "response_types", "code");
  params.Set("client_id", "new_client_id");
  server.ResponseWithJSON(net::HttpStatusCode::HTTP_CREATED, params);

  // Check the callback and the object.
  ASSERT_EQ(cr.status, StatusCode::kOK);
  EXPECT_TRUE(cr.data.empty());
  EXPECT_EQ(asd.AuthorizationServerURI().spec(), "https://a.b/c");
  EXPECT_EQ(asd.ClientId(), "new_client_id");
  EXPECT_EQ(asd.AuthorizationEndpointURI().spec(), "https://a/auth");
  EXPECT_EQ(asd.TokenEndpointURI().spec(), "https://b/token");
  EXPECT_EQ(asd.RegistrationEndpointURI().spec(), "https://c/reg");
  EXPECT_EQ(asd.RevocationEndpointURI().spec(), "https://d/rev");
  EXPECT_TRUE(asd.IsReady());
}

}  // namespace
}  // namespace oauth2
}  // namespace printing
}  // namespace ash