| // 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/printer_authenticator.h" | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "base/run_loop.h" | 
 | #include "base/test/bind.h" | 
 | #include "base/test/mock_callback.h" | 
 | #include "chrome/browser/ash/printing/oauth2/mock_authorization_zones_manager.h" | 
 | #include "chrome/browser/ash/printing/oauth2/status_code.h" | 
 | #include "chrome/browser/ash/printing/test_cups_printers_manager.h" | 
 | #include "chromeos/printing/cups_printer_status.h" | 
 | #include "chromeos/printing/printer_configuration.h" | 
 | #include "chromeos/printing/uri.h" | 
 | #include "content/public/test/browser_task_environment.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "url/gurl.h" | 
 |  | 
 | namespace ash::printing { | 
 | namespace { | 
 |  | 
 | // Represents results returned by callback void(StatusCode, const string&). | 
 | struct CallbackResult { | 
 |   oauth2::StatusCode status = oauth2::StatusCode::kUnexpectedError; | 
 |   std::string data; | 
 | }; | 
 |  | 
 | class PrintingPrinterAuthenticatorTest : public testing::Test { | 
 |  public: | 
 |   PrintingPrinterAuthenticatorTest() : printer_(kPrinterId) {} | 
 |  | 
 |  protected: | 
 |   // Init PrintAuthenticator object being tested. Values of parameters define | 
 |   // results returned by simulated UI dialogs. | 
 |   void CreateAuthenticator(oauth2::StatusCode is_trusted_dialog_response = | 
 |                                oauth2::StatusCode::kUnexpectedError, | 
 |                            oauth2::StatusCode signin_dialog_response = | 
 |                                oauth2::StatusCode::kUnexpectedError) { | 
 |     authenticator_ = std::make_unique<PrinterAuthenticator>( | 
 |         &cups_manager_, &auth_manager_, printer_); | 
 |     authenticator_->SetUIResponsesForTesting(is_trusted_dialog_response, | 
 |                                              signin_dialog_response); | 
 |   } | 
 |  | 
 |   // Call PrintAuthenticator::ObtainAccessTokenIfNeeded() and returns result | 
 |   // reported by the callback. | 
 |   CallbackResult CallObtainAccessTokenIfNeeded() { | 
 |     base::MockOnceCallback<void(oauth2::StatusCode, std::string)> callback; | 
 |     CallbackResult result; | 
 |     base::RunLoop loop; | 
 |     EXPECT_CALL(callback, Run) | 
 |         .InSequence(sequence_) | 
 |         .WillOnce( | 
 |             [&result, &loop](oauth2::StatusCode status, std::string data) { | 
 |               result.status = status; | 
 |               result.data = std::move(data); | 
 |               loop.Quit(); | 
 |             }); | 
 |     authenticator_->ObtainAccessTokenIfNeeded(callback.Get()); | 
 |     loop.Run(); | 
 |     return result; | 
 |   } | 
 |  | 
 |   // Mock CupsPrintersManager::FetchPrinterStatus. `auth_mode` is passed to the | 
 |   // object chromeos::CupsPrinterStatus returned in the callback. | 
 |   void ExpectCallFetchPrinterStatus( | 
 |       const chromeos::PrinterAuthenticationInfo& auth_mode) { | 
 |     chromeos::CupsPrinterStatus printer_status(kPrinterId); | 
 |     printer_status.SetAuthenticationInfo(auth_mode); | 
 |     cups_manager_.SetPrinterStatus(printer_status); | 
 |   } | 
 |  | 
 |   // Mock AuthorizationZoneManager::SaveAuthorizationServerAsTrusted. `result` | 
 |   // will be return by the call. | 
 |   void ExpectCallSaveAuthorizationServerAsTrusted(oauth2::StatusCode result) { | 
 |     EXPECT_CALL(auth_manager_, SaveAuthorizationServerAsTrusted(testing::_)) | 
 |         .InSequence(sequence_) | 
 |         .WillOnce([result](const GURL& auth_server) { return result; }); | 
 |   } | 
 |  | 
 |   // Mock AuthorizationZoneManager::InitAuthorization. Callback passed to the | 
 |   // mocked method will be called with parameters set in `result`. | 
 |   void ExpectCallInitAuthorization(CallbackResult result) { | 
 |     EXPECT_CALL(auth_manager_, | 
 |                 InitAuthorization(testing::_, testing::_, testing::_)) | 
 |         .InSequence(sequence_) | 
 |         .WillOnce([result](const GURL& auth_server, const std::string& scope, | 
 |                            oauth2::StatusCallback callback) { | 
 |           std::move(callback).Run(result.status, std::move(result.data)); | 
 |         }); | 
 |   } | 
 |  | 
 |   // Mock AuthorizationZoneManager::FinishAuthorization. Callback passed to the | 
 |   // mocked method will be called with parameters set in `result`. | 
 |   void ExpectCallFinishAuthorization(CallbackResult result) { | 
 |     EXPECT_CALL(auth_manager_, | 
 |                 FinishAuthorization(testing::_, testing::_, testing::_)) | 
 |         .InSequence(sequence_) | 
 |         .WillOnce([result](const GURL& auth_server, const GURL& redirect_url, | 
 |                            oauth2::StatusCallback callback) { | 
 |           std::move(callback).Run(result.status, std::move(result.data)); | 
 |         }); | 
 |   } | 
 |  | 
 |   // Mock AuthorizationZoneManager::GetEndpointAccessToken. Callback passed to | 
 |   // the mocked method will be called with parameters set in `result`. | 
 |   void ExpectCallGetEndpointAccessToken(CallbackResult result) { | 
 |     EXPECT_CALL(auth_manager_, GetEndpointAccessToken(testing::_, testing::_, | 
 |                                                       testing::_, testing::_)) | 
 |         .InSequence(sequence_) | 
 |         .WillOnce([result](const GURL& auth_server, | 
 |                            const chromeos::Uri& ipp_endpoint, | 
 |                            const std::string& scope, | 
 |                            oauth2::StatusCallback callback) { | 
 |           std::move(callback).Run(result.status, std::move(result.data)); | 
 |         }); | 
 |   } | 
 |  | 
 |   const std::string kPrinterId = "printer_id"; | 
 |   const chromeos::PrinterAuthenticationInfo kAuthMode = {"https://auth/server", | 
 |                                                          "scope1 scope2"}; | 
 |   const std::string kAuthURL = "https://auth/server/login?ala=ma;kota"; | 
 |  | 
 |  private: | 
 |   content::BrowserTaskEnvironment task_environment_; | 
 |   testing::Sequence sequence_; | 
 |   TestCupsPrintersManager cups_manager_; | 
 |   testing::StrictMock<oauth2::MockAuthorizationZoneManager> auth_manager_; | 
 |   chromeos::Printer printer_; | 
 |   std::unique_ptr<PrinterAuthenticator> authenticator_; | 
 | }; | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, AuthenticationNotNeeded) { | 
 |   CreateAuthenticator(); | 
 |   ExpectCallFetchPrinterStatus({"", ""}); | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kOK); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, AlreadyAuthenticated) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kOK); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kOK, "access token"}); | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kOK); | 
 |   EXPECT_EQ(result.data, "access token"); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, ServerNotTrusted) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kUntrustedAuthorizationServer); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken( | 
 |       {oauth2::StatusCode::kUntrustedAuthorizationServer}); | 
 |   // Response from IsTrustedDialog: StatusCode::kUntrustedAuthorizationServer | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kUntrustedAuthorizationServer); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, ServerSavedAsTrusted) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kOK); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken( | 
 |       {oauth2::StatusCode::kUntrustedAuthorizationServer}); | 
 |   // Response from IsTrustedDialog: StatusCode::kOK | 
 |   ExpectCallSaveAuthorizationServerAsTrusted(oauth2::StatusCode::kOK); | 
 |   ExpectCallGetEndpointAccessToken( | 
 |       {oauth2::StatusCode::kClientNotRegistered, "error"}); | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kClientNotRegistered); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, InvalidAuthorizationURL) { | 
 |   CreateAuthenticator(); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kAuthorizationNeeded}); | 
 |   ExpectCallInitAuthorization({oauth2::StatusCode::kOK, "invalid_auth_url"}); | 
 |   // Response from SigninDialog: StatusCode::kInvalidURL | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kInvalidURL); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, AuthorizationError) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kOK, | 
 |                       oauth2::StatusCode::kServerError); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kAuthorizationNeeded}); | 
 |   ExpectCallInitAuthorization({oauth2::StatusCode::kOK, kAuthURL}); | 
 |   // Response from SigninDialog: StatusCode::kServerError | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kServerError); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, AccessDenied) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kOK, oauth2::StatusCode::kOK); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kAuthorizationNeeded}); | 
 |   ExpectCallInitAuthorization({oauth2::StatusCode::kOK, kAuthURL}); | 
 |   // Response from SigninDialog: StatusCode::kOK | 
 |   ExpectCallFinishAuthorization({oauth2::StatusCode::kAccessDenied, "error"}); | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kAccessDenied); | 
 |   EXPECT_TRUE(result.data.empty()); | 
 | } | 
 |  | 
 | TEST_F(PrintingPrinterAuthenticatorTest, AuthorizationSuccessful) { | 
 |   CreateAuthenticator(oauth2::StatusCode::kOK, oauth2::StatusCode::kOK); | 
 |   ExpectCallFetchPrinterStatus(kAuthMode); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kAuthorizationNeeded}); | 
 |   ExpectCallInitAuthorization({oauth2::StatusCode::kOK, kAuthURL}); | 
 |   // Response from SigninDialog: StatusCode::kOK | 
 |   ExpectCallFinishAuthorization({oauth2::StatusCode::kOK}); | 
 |   ExpectCallGetEndpointAccessToken({oauth2::StatusCode::kOK, "access token"}); | 
 |   CallbackResult result = CallObtainAccessTokenIfNeeded(); | 
 |   EXPECT_EQ(result.status, oauth2::StatusCode::kOK); | 
 |   EXPECT_EQ(result.data, "access token"); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace ash::printing |